The single-row painter became vml.s at $C000: one sys() paints all 24 rows, memmove and line-index fix-ups run in ML, and the keyboard buffer is popped with IRQs masked. dall() repaints only the cursor row or the whole screen, so j went from 17 to 0-3 jiffies. Loading now checks the drive error channel and splits lines over 80 columns. The temporary jiffy counters used to measure the speedup are removed; the numbers are in NOTES.md. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>main
| @@ -116,6 +116,40 @@ run | |||
| driven from the command line — but the keys race the program's start: type a | |||
| filename and it can be eaten by BASIC before the program runs. | |||
| ## Machine language helpers (vml.s) - what bit us | |||
| - **`sys()` is using zero page `$22-$2A` while your routine runs.** The manual | |||
| lists 34-42 as "temporary storage" for ML, but sys() keeps where to store | |||
| A/X/Y back in there: our routine left a screen pointer in `$25`, so sys() | |||
| wrote X/Y into screen RAM (`$0404`) and then jumped to `$0002`. Every entry in | |||
| `vml.s` saves `$22-$2A`, `$4B-$60`, `$FB-$FC` on entry and restores them | |||
| before returning (`zsave`/`zrest`). The old single-row painter had the same | |||
| bug; it only showed as one wrong cell at the start of the status row. | |||
| - **Do not put anything at `$CF00`.** Power C's C stack starts just under | |||
| `$D000`, and it is set up before `main()` runs, so `highmem(0xc000)` cannot | |||
| move it. The code goes at `$C000` (copied there by `mlinit()` after | |||
| `highmem(0xc000)`, before `malloc`), the parameter block at `$C400`, the | |||
| zero page save area at `$C410`. | |||
| - **Every Power C function call is expensive.** Filling the parameter block | |||
| with one helper call per field made a full repaint cost 7 jiffies; writing | |||
| through one pointer inline brought it down. Keep C out of per-row and | |||
| per-byte loops: one `sys()` paints all 24 rows (`scrn`), memmove (`bmove`) | |||
| and line-index fix-ups (`ladj`) are ML too. | |||
| - **Key repeat.** The Power C shell leaves `$028A` set to repeat every key, and | |||
| VICE `-keybuf` holds each key for wall-clock time while the machine runs at | |||
| warp, so one `j` became seven. `sinit()` puts `$028A` back to 0 (C64 | |||
| default: only cursor keys, space and DEL repeat). | |||
| - Popping the KERNAL keyboard buffer is done in ML with `SEI`, so the IRQ | |||
| cannot append a key half way through the shift. | |||
| Measured with the jiffy clock at `$a2` and a temporary `d<draw> e<op>` readout | |||
| on the status row (since removed): | |||
| | | before | after | | |||
| |---|---|---| | |||
| | `j` (repaint) | 17 jiffies | 0-3 | | |||
| | insert a char near the top of a 9 KB file | 103 jiffies | 14 | | |||
| ## C64 screen facts the editor relies on | |||
| - Screen matrix `$0400`, colour RAM `$D800` (both directly writable). | |||
| @@ -126,7 +160,11 @@ run | |||
| `$60-$7F` minus `$20`, `$A0-$BF` minus `$40`, `$C0-$FF` minus `$80`. | |||
| - Reverse video is the high bit: screen code `+ $80`. That is the cursor block | |||
| and the status row. | |||
| - `$00CC = 0` stops the KERNAL cursor blinking over the editor's own cursor. | |||
| - `$00CC` non-zero stops the KERNAL cursor blinking (0 turns it **on**); with | |||
| it left at 0 the KERNAL blinked one cell of the status row. | |||
| - **Closing the command channel (15) closes every file on the drive.** Open 15 | |||
| first, `open()` the file, read the status from 15 (a missing file still | |||
| opens - only the status says `62`), read the file, close it, close 15 last. | |||
| - Arrow keys are PETSCII `$11` down, `$91` up, `$1D` right, `$9D` left; | |||
| RETURN `$0D`, DEL `$14`. | |||
| @@ -1,31 +1,47 @@ | |||
| # build-ml.ps1 - assemble vrow.s with cc65 and turn the result into vml.c, the | |||
| # Power C unit that holds the machine code as a byte array and calls it. | |||
| # build-ml.ps1 - assemble vml.s with cc65 and generate vml.c, the Power C unit | |||
| # that carries the machine code as a byte array plus its C wrappers. | |||
| # | |||
| # powershell -NoProfile -File build-ml.ps1 | |||
| # | |||
| # Run it whenever vrow.s changes, then build.ps1 as usual. vml.c is generated: | |||
| # edit vrow.s, not vml.c. | |||
| # Run it whenever vml.s or vmlwrap.c changes, then build.ps1 as usual. | |||
| # vml.c is generated: edit vml.s (the code) or vmlwrap.c (the C side). | |||
| # | |||
| # The code is assembled for $C000. At start-up mlinit() calls highmem($C000) so | |||
| # Power C keeps below it, and copies the array up there. Entry addresses come | |||
| # from ld65's label file and are written into vml.c as #defines. | |||
| $ErrorActionPreference = "Stop" | |||
| $here = $PSScriptRoot | |||
| $bin = "D:\Development\Commodore\cc65-snapshot-win32\bin" | |||
| $ca65 = Join-Path $bin "ca65.exe" | |||
| $ld65 = Join-Path $bin "ld65.exe" | |||
| $addr = 0xC020 # where the routine lives; the parameter block is at $C000 | |||
| $cfg = Join-Path $env:TEMP "vrow.cfg" | |||
| $ca65 = (Get-Command ca65 -ErrorAction SilentlyContinue).Source | |||
| if (-not $ca65) { throw "ca65 not found on PATH (cc65 bin folder)" } | |||
| $ld65 = Join-Path (Split-Path $ca65) "ld65.exe" | |||
| $tmp = Join-Path $env:TEMP "vml-build" | |||
| New-Item -ItemType Directory -Force -Path $tmp | Out-Null | |||
| $cfg = Join-Path $tmp "vml.cfg" | |||
| @" | |||
| MEMORY { M: start = `$C020, size = `$0100, file = "%O"; } | |||
| MEMORY { M: start = `$C000, size = `$0400, file = "%O"; } | |||
| SEGMENTS { CODE: load = M, type = ro; } | |||
| "@ | Set-Content $cfg -Encoding ascii | |||
| $obj = Join-Path $env:TEMP "vrow.o" | |||
| $out = Join-Path $here "vrow.bin" | |||
| & $ca65 (Join-Path $here "vrow.s") -o $obj | |||
| $obj = Join-Path $tmp "vml.o" | |||
| $lbl = Join-Path $tmp "vml.lbl" | |||
| $out = Join-Path $here "vml.bin" | |||
| & $ca65 (Join-Path $here "vml.s") -o $obj | |||
| if ($LASTEXITCODE) { throw "ca65 failed" } | |||
| & $ld65 -C $cfg -o $out $obj | |||
| & $ld65 -C $cfg -o $out -Ln $lbl $obj | |||
| if ($LASTEXITCODE) { throw "ld65 failed" } | |||
| # entry points: "al 00C000 .scrn" | |||
| $defs = @() | |||
| foreach ($ln in (Get-Content $lbl)) { | |||
| if ($ln -match '^al\s+([0-9A-Fa-f]+)\s+\.(scrn|row|bmove|ladj|kpop)$') { | |||
| $defs += ("#define ML{0} 0x{1}" -f $matches[2].ToUpper(), $matches[1].Substring(2).ToLower()) | |||
| } | |||
| } | |||
| if ($defs.Count -ne 5) { throw "expected 5 entry labels in $lbl, got $($defs.Count)" } | |||
| $b = [IO.File]::ReadAllBytes($out) | |||
| $rows = @() | |||
| for ($i = 0; $i -lt $b.Length; $i += 12) { | |||
| @@ -33,49 +49,22 @@ for ($i = 0; $i -lt $b.Length; $i += 12) { | |||
| $rows += " " + (($b[$i..($i + $n - 1)] | ForEach-Object { "$_" }) -join ", ") | |||
| } | |||
| $arr = $rows -join ",`n" | |||
| $wrap = Get-Content (Join-Path $here "vmlwrap.c") -Raw | |||
| @" | |||
| /* vml.c - GENERATED by build-ml.ps1 from vrow.s. Do not edit by hand. | |||
| /* vml.c - GENERATED by build-ml.ps1 from vml.s and vmlwrap.c. Do not edit. | |||
| * | |||
| * The machine code row painter, kept as a byte array and called in place | |||
| * through sys(). It is position independent; its parameters go through zero | |||
| * page scratch `$22-`$28, which Power C leaves to ML routines. | |||
| * The machine code helpers as a byte array. mlinit() copies them to `$C000. | |||
| */ | |||
| $($defs -join "`n") | |||
| #define MLSIZE $($b.Length) | |||
| char mlcode[$($b.Length)] = { | |||
| $arr | |||
| }; | |||
| /* nothing to install: the code runs where the array sits */ | |||
| mlinit() | |||
| { | |||
| return 0; | |||
| } | |||
| /* paint the screen row at address dst: len bytes from src, cursor at column cur | |||
| (255 = none), rev = 128 for an all reverse row */ | |||
| mlrow(src, len, dst, cur, rev) | |||
| unsigned src, dst; | |||
| int len, cur, rev; | |||
| { | |||
| char *p; | |||
| char a, x, y; | |||
| p = (char *) 0x22; | |||
| p[0] = src & 255; | |||
| p[1] = src >> 8; | |||
| p[2] = len; | |||
| p[3] = dst & 255; | |||
| p[4] = dst >> 8; | |||
| p[5] = cur; | |||
| p[6] = rev; | |||
| a = 0; | |||
| x = 0; | |||
| y = 0; | |||
| sys((unsigned) mlcode, &a, &x, &y); /* c64 form: no bank argument */ | |||
| return 0; | |||
| } | |||
| "@ | Set-Content (Join-Path $here "vml.c") -Encoding utf8 | |||
| Write-Host ("vrow.bin {0} bytes -> vml.c" -f $b.Length) | |||
| $wrap | |||
| "@ | Set-Content (Join-Path $here "vml.c") -Encoding ascii | |||
| Write-Host ("vml.bin {0} bytes -> vml.c ({1})" -f $b.Length, ($defs -join '; ')) | |||
| @@ -8,6 +8,7 @@ extern char *note; | |||
| extern char fname[]; | |||
| extern char ybuf[]; | |||
| extern int ylen, yok; | |||
| extern int redraw; /* 1 cursor row, 2 whole screen */ | |||
| /* end offset (exclusive of the $0d) of line ln */ | |||
| linend(ln) | |||
| @@ -35,10 +36,8 @@ bins(pos, ch) | |||
| unsigned pos; | |||
| int ch; | |||
| { | |||
| unsigned i; | |||
| if (blen >= 18999) return 0; | |||
| for (i = blen; i > pos; i--) bfr[i] = bfr[i - 1]; | |||
| mlmove((unsigned) bfr + pos, (unsigned) bfr + pos + 1, blen - pos); | |||
| bfr[pos] = ch; | |||
| blen++; | |||
| dirty = 1; | |||
| @@ -49,39 +48,43 @@ int ch; | |||
| delrange(pos, n) | |||
| unsigned pos, n; | |||
| { | |||
| unsigned i; | |||
| for (i = pos; i + n < blen; i++) bfr[i] = bfr[i + n]; | |||
| mlmove((unsigned) bfr + pos + n, (unsigned) bfr + pos, blen - pos - n); | |||
| blen = blen - n; | |||
| dirty = 1; | |||
| return 0; | |||
| } | |||
| #define MAXCOL 80 /* longest line the editor keeps */ | |||
| /* type one character at the cursor */ | |||
| inschar(ch) | |||
| int ch; | |||
| { | |||
| int l; | |||
| if (linend(cy) - lptr[cy] >= MAXCOL) { | |||
| note = "line full (80)"; | |||
| return 0; | |||
| } | |||
| if (bins(lptr[cy] + cx, ch) == 0) return 0; | |||
| for (l = cy + 1; l < nlines; l++) lptr[l]++; | |||
| mladj((unsigned) &lptr[cy + 1], nlines - cy - 1, 1); | |||
| cx++; | |||
| if (redraw < 1) redraw = 1; | |||
| return 1; | |||
| } | |||
| /* split the line at the cursor */ | |||
| newline() | |||
| { | |||
| int l; | |||
| unsigned pos; | |||
| if (nlines >= 599) return 0; | |||
| pos = lptr[cy] + cx; | |||
| if (bins(pos, 13) == 0) return 0; | |||
| for (l = nlines; l > cy + 1; l--) lptr[l] = lptr[l - 1]; | |||
| mlmove((unsigned) &lptr[cy + 1], (unsigned) &lptr[cy + 2], | |||
| (nlines - cy - 1) * 2); | |||
| lptr[cy + 1] = pos + 1; | |||
| nlines++; | |||
| for (l = cy + 2; l < nlines; l++) lptr[l]++; | |||
| mladj((unsigned) &lptr[cy + 2], nlines - cy - 2, 1); | |||
| redraw = 2; | |||
| cy++; | |||
| cx = 0; | |||
| return 1; | |||
| @@ -90,18 +93,17 @@ newline() | |||
| /* delete the character under the cursor */ | |||
| delchar() | |||
| { | |||
| int l; | |||
| if (cx >= linend(cy) - lptr[cy]) return 0; | |||
| delrange(lptr[cy] + cx, 1); | |||
| for (l = cy + 1; l < nlines; l++) lptr[l]--; | |||
| mladj((unsigned) &lptr[cy + 1], nlines - cy - 1, 65535); | |||
| if (redraw < 1) redraw = 1; | |||
| return 1; | |||
| } | |||
| /* backspace: delete before the cursor, or join onto the previous line */ | |||
| bkspace() | |||
| { | |||
| int l, pl; | |||
| int pl; | |||
| if (cx > 0) { | |||
| cx--; | |||
| @@ -110,10 +112,16 @@ bkspace() | |||
| } | |||
| if (cy == 0) return 0; | |||
| pl = linend(cy - 1) - lptr[cy - 1]; | |||
| if (pl + linend(cy) - lptr[cy] > MAXCOL) { | |||
| note = "joined line would pass 80"; | |||
| return 0; | |||
| } | |||
| delrange(lptr[cy] - 1, 1); | |||
| for (l = cy; l < nlines - 1; l++) lptr[l] = lptr[l + 1]; | |||
| mlmove((unsigned) &lptr[cy + 1], (unsigned) &lptr[cy], | |||
| (nlines - cy - 1) * 2); | |||
| nlines--; | |||
| for (l = cy; l < nlines; l++) lptr[l]--; | |||
| mladj((unsigned) &lptr[cy], nlines - cy, 65535); | |||
| redraw = 2; | |||
| cy--; | |||
| cx = pl; | |||
| return 0; | |||
| @@ -122,13 +130,13 @@ bkspace() | |||
| /* dd: yank the current line and remove it */ | |||
| delline() | |||
| { | |||
| int l, i; | |||
| int i; | |||
| unsigned s, e, n; | |||
| s = lptr[cy]; | |||
| e = linend(cy); | |||
| ylen = e - s; | |||
| if (ylen > 159) ylen = 159; | |||
| if (ylen > MAXCOL) ylen = MAXCOL; | |||
| for (i = 0; i < ylen; i++) ybuf[i] = bfr[s + i]; | |||
| yok = 1; | |||
| @@ -138,15 +146,17 @@ delline() | |||
| } else if (cy + 1 < nlines) { | |||
| n = lptr[cy + 1] - s; | |||
| delrange(s, n); | |||
| for (l = cy; l < nlines - 1; l++) lptr[l] = lptr[l + 1]; | |||
| mlmove((unsigned) &lptr[cy + 1], (unsigned) &lptr[cy], | |||
| (nlines - cy - 1) * 2); | |||
| nlines--; | |||
| for (l = cy; l < nlines; l++) lptr[l] -= n; | |||
| mladj((unsigned) &lptr[cy], nlines - cy, 0 - n); | |||
| } else { /* last line: take its leading $0d */ | |||
| delrange(s - 1, blen - s + 1); | |||
| nlines--; | |||
| cy--; | |||
| } | |||
| cx = 0; | |||
| redraw = 2; | |||
| return 0; | |||
| } | |||
| @@ -28,6 +28,7 @@ int pend; /* first key of a two key command: d or g */ | |||
| char *note; /* one-shot message for the status row */ | |||
| char ybuf[160]; /* the yanked line */ | |||
| int ylen, yok; | |||
| int redraw; /* 1 cursor row, 2 whole screen */ | |||
| /* screen rows are 0..23, the status row is 24 */ | |||
| @@ -89,6 +90,24 @@ char *s; | |||
| return n; | |||
| } | |||
| /* a yes/no question on the status row: 1 for y, 0 for n or run/stop */ | |||
| askyn(q) | |||
| char *q; | |||
| { | |||
| int k; | |||
| sclr(24, 0, 40); | |||
| sps(24, 0, q); | |||
| for (;;) { | |||
| k = skey(); | |||
| if (k == 89) return 1; /* y */ | |||
| if (k == 217) return 1; /* Y */ | |||
| if (k == 78) return 0; /* n */ | |||
| if (k == 206) return 0; /* N */ | |||
| if (k == 3) return 0; | |||
| } | |||
| } | |||
| /* ask which file to open */ | |||
| askname() | |||
| { | |||
| @@ -98,10 +117,39 @@ askname() | |||
| return 0; | |||
| } | |||
| /* read fname into bfr. 1 on success, 0 if it could not be opened */ | |||
| /* the drive's error channel (already open as file 15): the two digit code of | |||
| the last command. 0-19 are fine ("00, ok"), 62 is file not found. open() of | |||
| a missing file succeeds on a cbm drive - only the error channel tells. | |||
| do not close 15 while another file is open: closing the command channel | |||
| closes every file on the drive. */ | |||
| dosst() | |||
| { | |||
| int ch, n, d; | |||
| n = 0; | |||
| d = 0; | |||
| for (;;) { | |||
| ch = getc(15); | |||
| if (ch < 0) break; | |||
| if (ch == 13) break; | |||
| if (d < 2) { | |||
| if (ch >= 48 && ch <= 57) { | |||
| n = n * 10 + ch - 48; | |||
| d++; | |||
| } | |||
| } | |||
| } | |||
| if (d < 2) return 99; | |||
| return n; | |||
| } | |||
| int split; /* a long line was split on load */ | |||
| /* read fname into bfr. 1 on success, 0 if the file is not there. | |||
| lines longer than 80 are split - the editor's line limit */ | |||
| loadf() | |||
| { | |||
| int f, ch, prev, c; | |||
| int f, ch, prev, c, col; | |||
| char on[28]; | |||
| int i; | |||
| @@ -117,10 +165,21 @@ loadf() | |||
| on[i++] = 82; /* R */ | |||
| on[i] = 0; | |||
| if (open(15, 8, 15, "") == 0) return 0; /* status channel first */ | |||
| f = open(5, 8, 2, on); | |||
| if (f == 0) return 0; | |||
| if (f == 0) { | |||
| close(15); | |||
| return 0; | |||
| } | |||
| if (dosst() >= 20) { | |||
| close(5); | |||
| close(15); | |||
| return 0; | |||
| } | |||
| blen = 0; | |||
| prev = 0; | |||
| col = 0; | |||
| split = 0; | |||
| for (;;) { | |||
| ch = getc(5); | |||
| if (ch < 0) break; /* end of file */ | |||
| @@ -132,10 +191,18 @@ loadf() | |||
| if (ch == 13) c = 13; | |||
| else if (ch == 10) c = 13; | |||
| else c = ch; | |||
| if (c != 13 && col >= 80) { /* 80 columns at most */ | |||
| if (blen < 18999) bfr[blen++] = 13; | |||
| col = 0; | |||
| split = 1; | |||
| } | |||
| if (blen < 18999) bfr[blen++] = c; | |||
| if (c == 13) col = 0; | |||
| else col++; | |||
| prev = ch; | |||
| } | |||
| close(5); | |||
| close(15); /* last: it closes everything */ | |||
| return 1; | |||
| } | |||
| @@ -218,7 +285,7 @@ main() | |||
| int k, ok, p; | |||
| sinit(); | |||
| mlinit(); /* machine code row painter */ | |||
| mlinit(); /* ML helpers to $c000: before malloc */ | |||
| bfr = malloc(19000); | |||
| if (bfr == 0) { | |||
| sps(0, 0, "out of memory"); | |||
| @@ -240,16 +307,27 @@ main() | |||
| note = 0; | |||
| ylen = 0; | |||
| yok = 0; | |||
| split = 0; | |||
| redraw = 2; | |||
| askname(); | |||
| ok = loadf(); | |||
| shortn(20); | |||
| for (;;) { /* a file to edit */ | |||
| askname(); | |||
| shortn(20); | |||
| if (fname[0] == 0) { /* no name: an empty buffer */ | |||
| ok = 1; | |||
| break; | |||
| } | |||
| ok = loadf(); | |||
| if (ok) break; | |||
| if (askyn("not found - create it? (y/n) ")) break; | |||
| } | |||
| buildidx(); | |||
| if (ok == 0) { /* keep the name: :w creates it */ | |||
| nlines = 1; | |||
| blen = 0; | |||
| note = "new file"; | |||
| } | |||
| if (split) note = "long lines split at 80"; | |||
| for (;;) { | |||
| scroll(); | |||
| @@ -1,46 +1,205 @@ | |||
| /* vml.c - GENERATED by build-ml.ps1 from vrow.s. Do not edit by hand. | |||
| /* vml.c - GENERATED by build-ml.ps1 from vml.s and vmlwrap.c. Do not edit. | |||
| * | |||
| * The machine code row painter, kept as a byte array and called in place | |||
| * through sys(). It is position independent; its parameters go through zero | |||
| * page scratch $22-$28, which Power C leaves to ML routines. | |||
| * The machine code helpers as a byte array. mlinit() copies them to $C000. | |||
| */ | |||
| char mlcode[76] = { | |||
| 165, 40, 9, 32, 160, 39, 145, 37, 136, 16, 251, 160, | |||
| 0, 196, 36, 240, 45, 177, 34, 201, 32, 176, 2, 169, | |||
| 32, 201, 192, 144, 4, 233, 128, 176, 22, 201, 160, 144, | |||
| 4, 233, 64, 176, 14, 201, 96, 144, 4, 233, 32, 176, | |||
| 6, 201, 64, 144, 2, 233, 64, 5, 40, 145, 37, 200, | |||
| 208, 207, 165, 39, 201, 255, 240, 7, 168, 177, 37, 73, | |||
| 128, 145, 37, 96 | |||
| #define MLBMOVE 0xc161 | |||
| #define MLKPOP 0xc237 | |||
| #define MLLADJ 0xc1e3 | |||
| #define MLROW 0xc102 | |||
| #define MLSCRN 0xc000 | |||
| #define MLSIZE 660 | |||
| char mlcode[660] = { | |||
| 32, 86, 194, 162, 15, 189, 0, 196, 149, 75, 202, 16, | |||
| 248, 24, 165, 81, 101, 89, 133, 91, 165, 82, 105, 0, | |||
| 133, 92, 165, 91, 10, 133, 93, 165, 92, 42, 133, 94, | |||
| 24, 165, 93, 101, 77, 133, 93, 165, 94, 101, 78, 133, | |||
| 94, 169, 0, 133, 37, 169, 4, 133, 38, 166, 89, 240, | |||
| 14, 24, 165, 37, 105, 40, 133, 37, 144, 2, 230, 38, | |||
| 202, 208, 242, 165, 89, 133, 95, 169, 0, 133, 40, 133, | |||
| 36, 169, 255, 133, 39, 165, 91, 197, 79, 165, 92, 229, | |||
| 80, 176, 114, 160, 0, 177, 93, 133, 251, 200, 177, 93, | |||
| 133, 252, 24, 165, 91, 105, 1, 170, 165, 92, 105, 0, | |||
| 228, 79, 208, 15, 197, 80, 208, 11, 165, 83, 133, 41, | |||
| 165, 84, 133, 42, 76, 155, 192, 160, 2, 177, 93, 56, | |||
| 233, 1, 133, 41, 200, 177, 93, 233, 0, 133, 42, 24, | |||
| 165, 251, 101, 85, 133, 251, 165, 252, 101, 86, 133, 252, | |||
| 56, 165, 41, 229, 251, 170, 165, 42, 229, 252, 144, 10, | |||
| 208, 4, 224, 41, 144, 2, 162, 40, 134, 36, 24, 165, | |||
| 75, 101, 251, 133, 34, 165, 76, 101, 252, 133, 35, 165, | |||
| 95, 197, 87, 208, 4, 165, 88, 133, 39, 32, 21, 193, | |||
| 165, 95, 197, 90, 176, 33, 230, 95, 230, 91, 208, 2, | |||
| 230, 92, 24, 165, 93, 105, 2, 133, 93, 144, 2, 230, | |||
| 94, 24, 165, 37, 105, 40, 133, 37, 144, 2, 230, 38, | |||
| 76, 79, 192, 76, 117, 194, 32, 86, 194, 162, 6, 189, | |||
| 0, 196, 149, 34, 202, 16, 248, 32, 21, 193, 76, 117, | |||
| 194, 165, 40, 9, 32, 160, 39, 145, 37, 136, 16, 251, | |||
| 160, 0, 196, 36, 240, 45, 177, 34, 201, 32, 176, 2, | |||
| 169, 32, 201, 192, 144, 4, 233, 128, 176, 22, 201, 160, | |||
| 144, 4, 233, 64, 176, 14, 201, 96, 144, 4, 233, 32, | |||
| 176, 6, 201, 64, 144, 2, 233, 64, 5, 40, 145, 37, | |||
| 200, 208, 207, 165, 39, 201, 40, 176, 7, 168, 177, 37, | |||
| 73, 128, 145, 37, 96, 32, 86, 194, 173, 0, 196, 133, | |||
| 34, 173, 1, 196, 133, 35, 173, 2, 196, 133, 37, 173, | |||
| 3, 196, 133, 38, 173, 4, 196, 133, 75, 173, 5, 196, | |||
| 133, 76, 165, 37, 197, 34, 165, 38, 229, 35, 144, 52, | |||
| 24, 165, 35, 101, 76, 133, 35, 24, 165, 38, 101, 76, | |||
| 133, 38, 164, 75, 240, 9, 136, 177, 34, 145, 37, 192, | |||
| 0, 208, 247, 166, 76, 240, 18, 198, 35, 198, 38, 160, | |||
| 0, 136, 177, 34, 145, 37, 192, 0, 208, 247, 202, 208, | |||
| 238, 76, 117, 194, 160, 0, 166, 76, 240, 14, 177, 34, | |||
| 145, 37, 200, 208, 249, 230, 35, 230, 38, 202, 208, 242, | |||
| 166, 75, 240, 229, 177, 34, 145, 37, 200, 202, 208, 248, | |||
| 76, 117, 194, 32, 86, 194, 162, 5, 189, 0, 196, 149, | |||
| 75, 202, 16, 248, 165, 75, 133, 34, 165, 76, 133, 35, | |||
| 165, 77, 133, 75, 165, 78, 133, 76, 165, 79, 133, 77, | |||
| 165, 80, 133, 78, 165, 75, 5, 76, 240, 38, 160, 0, | |||
| 24, 177, 34, 101, 77, 145, 34, 200, 177, 34, 101, 78, | |||
| 145, 34, 24, 165, 34, 105, 2, 133, 34, 144, 2, 230, | |||
| 35, 165, 75, 208, 2, 198, 76, 198, 75, 76, 8, 194, | |||
| 76, 117, 194, 120, 165, 198, 240, 22, 162, 0, 173, 119, | |||
| 2, 72, 189, 120, 2, 157, 119, 2, 232, 224, 9, 208, | |||
| 245, 198, 198, 104, 88, 96, 88, 169, 0, 96, 162, 8, | |||
| 181, 34, 157, 16, 196, 202, 16, 248, 162, 21, 181, 75, | |||
| 157, 25, 196, 202, 16, 248, 165, 251, 141, 47, 196, 165, | |||
| 252, 141, 48, 196, 96, 162, 8, 189, 16, 196, 149, 34, | |||
| 202, 16, 248, 162, 21, 189, 25, 196, 149, 75, 202, 16, | |||
| 248, 173, 47, 196, 133, 251, 173, 48, 196, 133, 252, 96 | |||
| }; | |||
| /* nothing to install: the code runs where the array sits */ | |||
| /* vmlwrap.c - the C side of the machine code helpers. build-ml.ps1 appends | |||
| * this to the generated byte array to make vml.c; edit it here. | |||
| * | |||
| * Parameters go through a block at $c400 (not $cf00: the C stack lives just below $d000), then sys() jumps to the entry. | |||
| * On the c64 sys() takes no bank argument: sys(addr, &a, &x, &y). | |||
| */ | |||
| extern char *bfr; | |||
| extern unsigned blen; | |||
| extern unsigned lptr[]; | |||
| extern int nlines, top, leftcol; | |||
| /* move the code to $c000 and keep power c's heap and stack below it. | |||
| must run before malloc() */ | |||
| mlinit() | |||
| { | |||
| char *d; | |||
| int i; | |||
| highmem(0xc000); | |||
| d = (char *) 0xc000; | |||
| for (i = 0; i < MLSIZE; i++) d[i] = mlcode[i]; | |||
| return 0; | |||
| } | |||
| /* paint the screen row at address dst: len bytes from src, cursor at column cur | |||
| (255 = none), rev = 128 for an all reverse row */ | |||
| /* call an entry, return what it left in A */ | |||
| mlgo(addr) | |||
| unsigned addr; | |||
| { | |||
| char a, x, y; | |||
| a = 0; | |||
| x = 0; | |||
| y = 0; | |||
| sys(addr, &a, &x, &y); | |||
| return a & 255; | |||
| } | |||
| /* The wrappers fill the parameter block through one pointer instead of | |||
| calling a helper per field: every power c function call costs a lot, and | |||
| mlscr runs on every key. Words are stored low byte first. */ | |||
| /* paint one row at screen address dst: len bytes from src, cursor at column | |||
| cur (255 = none), rev = 128 for an all reverse row */ | |||
| mlrow(src, len, dst, cur, rev) | |||
| unsigned src, dst; | |||
| int len, cur, rev; | |||
| { | |||
| char *p; | |||
| char a, x, y; | |||
| p = (char *) 0x22; | |||
| p[0] = src & 255; | |||
| p = (char *) 0xc400; | |||
| p[0] = src; | |||
| p[1] = src >> 8; | |||
| p[2] = len; | |||
| p[3] = dst & 255; | |||
| p[3] = dst; | |||
| p[4] = dst >> 8; | |||
| p[5] = cur; | |||
| p[6] = rev; | |||
| a = 0; | |||
| x = 0; | |||
| y = 0; | |||
| sys((unsigned) mlcode, &a, &x, &y); /* c64 form: no bank argument */ | |||
| mlgo(MLROW); | |||
| return 0; | |||
| } | |||
| /* paint screen rows r0..r1 from the buffer; the cursor sits at screen row | |||
| crow (255 = none), column ccol */ | |||
| mlscr(r0, r1, crow, ccol) | |||
| int r0, r1, crow, ccol; | |||
| { | |||
| char *p; | |||
| unsigned v; | |||
| p = (char *) 0xc400; | |||
| v = (unsigned) bfr; | |||
| p[0] = v; | |||
| p[1] = v >> 8; | |||
| v = (unsigned) lptr; | |||
| p[2] = v; | |||
| p[3] = v >> 8; | |||
| p[4] = nlines; | |||
| p[5] = nlines >> 8; | |||
| p[6] = top; | |||
| p[7] = top >> 8; | |||
| p[8] = blen; | |||
| p[9] = blen >> 8; | |||
| p[10] = leftcol; | |||
| p[11] = leftcol >> 8; | |||
| p[12] = crow; | |||
| p[13] = ccol; | |||
| p[14] = r0; | |||
| p[15] = r1; | |||
| mlgo(MLSCRN); | |||
| return 0; | |||
| } | |||
| /* memmove: n bytes from 'from' to 'to', overlap safe */ | |||
| mlmove(from, to, n) | |||
| unsigned from, to, n; | |||
| { | |||
| char *p; | |||
| if (n == 0) return 0; | |||
| p = (char *) 0xc400; | |||
| p[0] = from; | |||
| p[1] = from >> 8; | |||
| p[2] = to; | |||
| p[3] = to >> 8; | |||
| p[4] = n; | |||
| p[5] = n >> 8; | |||
| mlgo(MLBMOVE); | |||
| return 0; | |||
| } | |||
| /* add d to n words starting at address a (line index fix-ups) */ | |||
| mladj(a, n, d) | |||
| unsigned a, n, d; | |||
| { | |||
| char *p; | |||
| if (n == 0) return 0; | |||
| p = (char *) 0xc400; | |||
| p[0] = a; | |||
| p[1] = a >> 8; | |||
| p[2] = n; | |||
| p[3] = n >> 8; | |||
| p[4] = d; | |||
| p[5] = d >> 8; | |||
| mlgo(MLLADJ); | |||
| return 0; | |||
| } | |||
| /* next key from the kernal buffer, or 0 */ | |||
| mlkey() | |||
| { | |||
| return mlgo(MLKPOP); | |||
| } | |||
| @@ -0,0 +1,439 @@ | |||
| ; vml.s - machine code helpers for the c64 vim clone (called from power c). | |||
| ; | |||
| ; Assembled for $C000. vml.c holds the bytes; mlinit() calls highmem($C000) | |||
| ; so power c keeps its heap and stack below, then copies the code up here. | |||
| ; Parameters are written by C into a block at $C400 (PB) before each call; | |||
| ; entry addresses come from the ld65 label file (see build-ml.ps1). | |||
| ; | |||
| ; Zero page: 34-42 ($22-$2A), 75-96 ($4B-$60) and 251-252 ($FB-$FC) are the | |||
| ; temporaries power c leaves to machine language - but sys() itself is using | |||
| ; some of them while our code runs (it keeps where to store A/X/Y back), so | |||
| ; every entry that touches zero page saves all three ranges first (zsave) and | |||
| ; puts them back before it returns (zrest). Without that, sys() wrote X/Y | |||
| ; through our screen pointer and then jumped into zero page. | |||
| ; | |||
| ; Entries | |||
| ; scrn paint screen rows r0..r1 straight from the buffer and line index | |||
| ; row paint one row from a byte string (the status row) | |||
| ; bmove overlapping block move (memmove) | |||
| ; ladj add a 16 bit delta to n consecutive words (line index fix-up) | |||
| ; kpop take one key from the kernal keyboard buffer, irqs masked: A = key or 0 | |||
| SAVE = $C410 ; zsave's copy of the zero page we use | |||
| PB = $C400 ; above the code; NOT $CF00 - power c's stack lives under $D000 | |||
| ; paint's inputs | |||
| src = $22 ; 2 text to show (petscii) | |||
| len = $24 ; 1 bytes of it, 0..40 | |||
| dst = $25 ; 2 screen address of the row | |||
| cur = $27 ; 1 column to invert, $FF = none | |||
| rev = $28 ; 1 $00 normal, $80 whole row reverse | |||
| endp = $29 ; 2 scrn: end offset of the line | |||
| ; scrn's copy of its parameter block: PB+0..PB+15 -> $4B..$5A | |||
| bfr = $4B ; 2 buffer address | |||
| lptr = $4D ; 2 address of lptr[0] | |||
| nl = $4F ; 2 number of lines | |||
| top = $51 ; 2 first line on screen | |||
| blen = $53 ; 2 bytes in buffer | |||
| lcol = $55 ; 2 horizontal scroll | |||
| crow = $57 ; 1 screen row of the cursor ($FF none) | |||
| ccol = $58 ; 1 screen column of the cursor | |||
| r0 = $59 ; 1 first row to paint | |||
| r1 = $5A ; 1 last row to paint (inclusive) | |||
| ln = $5B ; 2 line being painted | |||
| lp = $5D ; 2 -> lptr[ln] | |||
| rowc = $5F ; 1 screen row being painted | |||
| st = $FB ; 2 start offset of the visible text | |||
| .segment "CODE" | |||
| ; ---------------------------------------------------------------- scrn | |||
| scrn: jsr zsave | |||
| ldx #15 | |||
| @cp: lda PB,x | |||
| sta bfr,x | |||
| dex | |||
| bpl @cp | |||
| clc ; ln = top + r0 | |||
| lda top | |||
| adc r0 | |||
| sta ln | |||
| lda top+1 | |||
| adc #0 | |||
| sta ln+1 | |||
| lda ln ; lp = lptr + 2*ln | |||
| asl a | |||
| sta lp | |||
| lda ln+1 | |||
| rol a | |||
| sta lp+1 | |||
| clc | |||
| lda lp | |||
| adc lptr | |||
| sta lp | |||
| lda lp+1 | |||
| adc lptr+1 | |||
| sta lp+1 | |||
| lda #0 ; dst = $0400 + 40*r0 | |||
| sta dst | |||
| lda #4 | |||
| sta dst+1 | |||
| ldx r0 | |||
| beq @d1 | |||
| @dl: clc | |||
| lda dst | |||
| adc #40 | |||
| sta dst | |||
| bcc @dn | |||
| inc dst+1 | |||
| @dn: dex | |||
| bne @dl | |||
| @d1: lda r0 | |||
| sta rowc | |||
| @row: lda #0 | |||
| sta rev | |||
| sta len | |||
| lda #$FF | |||
| sta cur | |||
| lda ln ; past the last line: a blank row | |||
| cmp nl | |||
| lda ln+1 | |||
| sbc nl+1 | |||
| bcs @paint | |||
| ldy #0 ; st = lptr[ln] | |||
| lda (lp),y | |||
| sta st | |||
| iny | |||
| lda (lp),y | |||
| sta st+1 | |||
| clc ; is ln the last line? | |||
| lda ln | |||
| adc #1 | |||
| tax | |||
| lda ln+1 | |||
| adc #0 | |||
| cpx nl | |||
| bne @mid | |||
| cmp nl+1 | |||
| bne @mid | |||
| lda blen ; last line ends at blen | |||
| sta endp | |||
| lda blen+1 | |||
| sta endp+1 | |||
| jmp @gotend | |||
| @mid: ldy #2 ; else at lptr[ln+1] - 1 (before the $0d) | |||
| lda (lp),y | |||
| sec | |||
| sbc #1 | |||
| sta endp | |||
| iny | |||
| lda (lp),y | |||
| sbc #0 | |||
| sta endp+1 | |||
| @gotend: | |||
| clc ; st += lcol | |||
| lda st | |||
| adc lcol | |||
| sta st | |||
| lda st+1 | |||
| adc lcol+1 | |||
| sta st+1 | |||
| sec ; len = min(40, end - st), 0 if negative | |||
| lda endp | |||
| sbc st | |||
| tax | |||
| lda endp+1 | |||
| sbc st+1 | |||
| bcc @src ; scrolled past the end of this line | |||
| bne @full | |||
| cpx #41 | |||
| bcc @setn | |||
| @full: ldx #40 | |||
| @setn: stx len | |||
| @src: clc ; src = bfr + st | |||
| lda bfr | |||
| adc st | |||
| sta src | |||
| lda bfr+1 | |||
| adc st+1 | |||
| sta src+1 | |||
| lda rowc ; the cursor row? | |||
| cmp crow | |||
| bne @paint | |||
| lda ccol | |||
| sta cur | |||
| @paint: jsr paint | |||
| lda rowc ; next row, until r1 is done | |||
| cmp r1 | |||
| bcs @done | |||
| inc rowc | |||
| inc ln | |||
| bne @l1 | |||
| inc ln+1 | |||
| @l1: clc | |||
| lda lp | |||
| adc #2 | |||
| sta lp | |||
| bcc @l2 | |||
| inc lp+1 | |||
| @l2: clc | |||
| lda dst | |||
| adc #40 | |||
| sta dst | |||
| bcc @l3 | |||
| inc dst+1 | |||
| @l3: jmp @row | |||
| @done: jmp zrest | |||
| ; ---------------------------------------------------------------- row | |||
| ; PB+0 src, PB+2 len, PB+3 dst, PB+5 cur, PB+6 rev | |||
| row: jsr zsave | |||
| ldx #6 | |||
| @cp: lda PB,x | |||
| sta src,x | |||
| dex | |||
| bpl @cp | |||
| jsr paint | |||
| jmp zrest | |||
| ; ---------------------------------------------------------------- paint | |||
| ; clear the row to blanks, copy len bytes converted petscii -> screen code, | |||
| ; then invert the cursor cell | |||
| paint: lda rev | |||
| ora #$20 | |||
| ldy #39 | |||
| @clr: sta (dst),y | |||
| dey | |||
| bpl @clr | |||
| ldy #0 | |||
| @cp: cpy len | |||
| beq @cur | |||
| lda (src),y | |||
| cmp #$20 | |||
| bcs @ok | |||
| lda #$20 ; control codes show as a blank | |||
| @ok: cmp #$C0 | |||
| bcc @l1 | |||
| sbc #$80 | |||
| bcs @st | |||
| @l1: cmp #$A0 | |||
| bcc @l2 | |||
| sbc #$40 | |||
| bcs @st | |||
| @l2: cmp #$60 | |||
| bcc @l3 | |||
| sbc #$20 | |||
| bcs @st | |||
| @l3: cmp #$40 | |||
| bcc @st | |||
| sbc #$40 | |||
| @st: ora rev | |||
| sta (dst),y | |||
| iny | |||
| bne @cp | |||
| @cur: lda cur | |||
| cmp #40 | |||
| bcs @fin ; $FF (none) or off screen | |||
| tay | |||
| lda (dst),y | |||
| eor #$80 | |||
| sta (dst),y | |||
| @fin: rts | |||
| ; ---------------------------------------------------------------- bmove | |||
| ; PB+0 from, PB+2 to, PB+4 count. Overlap safe. | |||
| mfrom = $22 | |||
| mto = $25 | |||
| mcnt = $4B | |||
| bmove: jsr zsave | |||
| lda PB | |||
| sta mfrom | |||
| lda PB+1 | |||
| sta mfrom+1 | |||
| lda PB+2 | |||
| sta mto | |||
| lda PB+3 | |||
| sta mto+1 | |||
| lda PB+4 | |||
| sta mcnt | |||
| lda PB+5 | |||
| sta mcnt+1 | |||
| lda mto ; to < from: copy upwards from the start | |||
| cmp mfrom | |||
| lda mto+1 | |||
| sbc mfrom+1 | |||
| bcc @fwd | |||
| ; to >= from: copy downwards from the end | |||
| clc ; point both at their last whole page | |||
| lda mfrom+1 | |||
| adc mcnt+1 | |||
| sta mfrom+1 | |||
| clc | |||
| lda mto+1 | |||
| adc mcnt+1 | |||
| sta mto+1 | |||
| ldy mcnt ; the odd bytes at the top first | |||
| beq @bpg | |||
| @bodd: dey | |||
| lda (mfrom),y | |||
| sta (mto),y | |||
| cpy #0 | |||
| bne @bodd | |||
| @bpg: ldx mcnt+1 ; then whole pages, highest first | |||
| beq @out | |||
| @bnext: dec mfrom+1 | |||
| dec mto+1 | |||
| ldy #0 | |||
| @bpl: dey | |||
| lda (mfrom),y | |||
| sta (mto),y | |||
| cpy #0 | |||
| bne @bpl | |||
| dex | |||
| bne @bnext | |||
| @out: jmp zrest | |||
| @fwd: ldy #0 | |||
| ldx mcnt+1 ; whole pages | |||
| beq @fodd | |||
| @fpg: lda (mfrom),y | |||
| sta (mto),y | |||
| iny | |||
| bne @fpg | |||
| inc mfrom+1 | |||
| inc mto+1 | |||
| dex | |||
| bne @fpg | |||
| @fodd: ldx mcnt ; then the rest | |||
| beq @out | |||
| @fo: lda (mfrom),y | |||
| sta (mto),y | |||
| iny | |||
| dex | |||
| bne @fo | |||
| jmp zrest | |||
| ; ---------------------------------------------------------------- ladj | |||
| ; PB+0 address of the first word, PB+2 how many, PB+4 delta | |||
| aptr = $22 | |||
| acnt = $4B | |||
| adel = $4D | |||
| ladj: jsr zsave | |||
| ldx #5 | |||
| @cp: lda PB,x | |||
| sta $4B,x | |||
| dex | |||
| bpl @cp | |||
| lda $4B ; the address goes to $22 for (zp),y | |||
| sta aptr | |||
| lda $4C | |||
| sta aptr+1 | |||
| lda $4D ; count to acnt, delta to adel | |||
| sta acnt | |||
| lda $4E | |||
| sta acnt+1 | |||
| lda $4F | |||
| sta adel | |||
| lda $50 | |||
| sta adel+1 | |||
| @lp: lda acnt | |||
| ora acnt+1 | |||
| beq @done | |||
| ldy #0 | |||
| clc | |||
| lda (aptr),y | |||
| adc adel | |||
| sta (aptr),y | |||
| iny | |||
| lda (aptr),y | |||
| adc adel+1 | |||
| sta (aptr),y | |||
| clc | |||
| lda aptr | |||
| adc #2 | |||
| sta aptr | |||
| bcc @nc | |||
| inc aptr+1 | |||
| @nc: lda acnt | |||
| bne @dc | |||
| dec acnt+1 | |||
| @dc: dec acnt | |||
| jmp @lp | |||
| @done: jmp zrest | |||
| ; ---------------------------------------------------------------- kpop | |||
| kpop: sei | |||
| lda $C6 | |||
| beq @none | |||
| ldx #0 | |||
| lda $0277 | |||
| pha | |||
| @sh: lda $0278,x | |||
| sta $0277,x | |||
| inx | |||
| cpx #9 | |||
| bne @sh | |||
| dec $C6 | |||
| pla | |||
| cli | |||
| rts | |||
| @none: cli | |||
| lda #0 | |||
| rts | |||
| ; ---------------------------------------------------------------- zsave / zrest | |||
| ; $22-$2A -> SAVE+0..8, $4B-$60 -> SAVE+9..30, $FB-$FC -> SAVE+31..32 | |||
| zsave: ldx #8 | |||
| @a: lda $22,x | |||
| sta SAVE,x | |||
| dex | |||
| bpl @a | |||
| ldx #21 | |||
| @b: lda $4B,x | |||
| sta SAVE+9,x | |||
| dex | |||
| bpl @b | |||
| lda $FB | |||
| sta SAVE+31 | |||
| lda $FC | |||
| sta SAVE+32 | |||
| rts | |||
| zrest: ldx #8 | |||
| @a: lda SAVE,x | |||
| sta $22,x | |||
| dex | |||
| bpl @a | |||
| ldx #21 | |||
| @b: lda SAVE+9,x | |||
| sta $4B,x | |||
| dex | |||
| bpl @b | |||
| lda SAVE+31 | |||
| sta $FB | |||
| lda SAVE+32 | |||
| sta $FC | |||
| rts | |||
| .export scrn, row, bmove, ladj, kpop | |||
| @@ -0,0 +1,134 @@ | |||
| /* vmlwrap.c - the C side of the machine code helpers. build-ml.ps1 appends | |||
| * this to the generated byte array to make vml.c; edit it here. | |||
| * | |||
| * Parameters go through a block at $c400 (not $cf00: the C stack lives just below $d000), then sys() jumps to the entry. | |||
| * On the c64 sys() takes no bank argument: sys(addr, &a, &x, &y). | |||
| */ | |||
| extern char *bfr; | |||
| extern unsigned blen; | |||
| extern unsigned lptr[]; | |||
| extern int nlines, top, leftcol; | |||
| /* move the code to $c000 and keep power c's heap and stack below it. | |||
| must run before malloc() */ | |||
| mlinit() | |||
| { | |||
| char *d; | |||
| int i; | |||
| highmem(0xc000); | |||
| d = (char *) 0xc000; | |||
| for (i = 0; i < MLSIZE; i++) d[i] = mlcode[i]; | |||
| return 0; | |||
| } | |||
| /* call an entry, return what it left in A */ | |||
| mlgo(addr) | |||
| unsigned addr; | |||
| { | |||
| char a, x, y; | |||
| a = 0; | |||
| x = 0; | |||
| y = 0; | |||
| sys(addr, &a, &x, &y); | |||
| return a & 255; | |||
| } | |||
| /* The wrappers fill the parameter block through one pointer instead of | |||
| calling a helper per field: every power c function call costs a lot, and | |||
| mlscr runs on every key. Words are stored low byte first. */ | |||
| /* paint one row at screen address dst: len bytes from src, cursor at column | |||
| cur (255 = none), rev = 128 for an all reverse row */ | |||
| mlrow(src, len, dst, cur, rev) | |||
| unsigned src, dst; | |||
| int len, cur, rev; | |||
| { | |||
| char *p; | |||
| p = (char *) 0xc400; | |||
| p[0] = src; | |||
| p[1] = src >> 8; | |||
| p[2] = len; | |||
| p[3] = dst; | |||
| p[4] = dst >> 8; | |||
| p[5] = cur; | |||
| p[6] = rev; | |||
| mlgo(MLROW); | |||
| return 0; | |||
| } | |||
| /* paint screen rows r0..r1 from the buffer; the cursor sits at screen row | |||
| crow (255 = none), column ccol */ | |||
| mlscr(r0, r1, crow, ccol) | |||
| int r0, r1, crow, ccol; | |||
| { | |||
| char *p; | |||
| unsigned v; | |||
| p = (char *) 0xc400; | |||
| v = (unsigned) bfr; | |||
| p[0] = v; | |||
| p[1] = v >> 8; | |||
| v = (unsigned) lptr; | |||
| p[2] = v; | |||
| p[3] = v >> 8; | |||
| p[4] = nlines; | |||
| p[5] = nlines >> 8; | |||
| p[6] = top; | |||
| p[7] = top >> 8; | |||
| p[8] = blen; | |||
| p[9] = blen >> 8; | |||
| p[10] = leftcol; | |||
| p[11] = leftcol >> 8; | |||
| p[12] = crow; | |||
| p[13] = ccol; | |||
| p[14] = r0; | |||
| p[15] = r1; | |||
| mlgo(MLSCRN); | |||
| return 0; | |||
| } | |||
| /* memmove: n bytes from 'from' to 'to', overlap safe */ | |||
| mlmove(from, to, n) | |||
| unsigned from, to, n; | |||
| { | |||
| char *p; | |||
| if (n == 0) return 0; | |||
| p = (char *) 0xc400; | |||
| p[0] = from; | |||
| p[1] = from >> 8; | |||
| p[2] = to; | |||
| p[3] = to >> 8; | |||
| p[4] = n; | |||
| p[5] = n >> 8; | |||
| mlgo(MLBMOVE); | |||
| return 0; | |||
| } | |||
| /* add d to n words starting at address a (line index fix-ups) */ | |||
| mladj(a, n, d) | |||
| unsigned a, n, d; | |||
| { | |||
| char *p; | |||
| if (n == 0) return 0; | |||
| p = (char *) 0xc400; | |||
| p[0] = a; | |||
| p[1] = a >> 8; | |||
| p[2] = n; | |||
| p[3] = n >> 8; | |||
| p[4] = d; | |||
| p[5] = d >> 8; | |||
| mlgo(MLLADJ); | |||
| return 0; | |||
| } | |||
| /* next key from the kernal buffer, or 0 */ | |||
| mlkey() | |||
| { | |||
| return mlgo(MLKPOP); | |||
| } | |||
| @@ -1,69 +0,0 @@ | |||
| ; vrow.s - draw one screen row for the c64 vim clone (called from power c). | |||
| ; | |||
| ; Position independent: no absolute jumps and no absolute data, so the bytes can | |||
| ; sit in a C array and be called at wherever the linker put that array. | |||
| ; | |||
| ; Parameters live in zero page scratch that Power C leaves to ML routines | |||
| ; (decimal 34-42 = $22-$2A), poked by C before the call (see vml.c): | |||
| ; $22/$23 src address of the first byte of text (petscii) | |||
| ; $24 len bytes of text to show, 0..40 | |||
| ; $25/$26 dst screen RAM address of the row's first cell | |||
| ; $27 cur column to show reverse (the cursor), $FF = none | |||
| ; $28 rev $00 normal text, $80 whole row reverse (status row) | |||
| ; | |||
| ; The row is cleared to blanks, the text is converted petscii -> screen code | |||
| ; and copied, then the cursor cell is flipped to reverse video. Codes below | |||
| ; $20 show as a blank. Colour RAM is set once at start-up, not here. | |||
| ; | |||
| ; Build: build-ml.ps1 (ca65 + ld65 -> vrow.bin -> vml.c). | |||
| src = $22 | |||
| len = $24 | |||
| dst = $25 | |||
| cur = $27 | |||
| rev = $28 | |||
| .segment "CODE" | |||
| entry: lda rev ; clear the row | |||
| ora #$20 | |||
| ldy #39 | |||
| clr: sta (dst),y | |||
| dey | |||
| bpl clr | |||
| ldy #0 | |||
| cp: cpy len | |||
| beq done | |||
| lda (src),y | |||
| cmp #$20 | |||
| bcs ok | |||
| lda #$20 | |||
| ok: cmp #$C0 ; petscii -> screen code | |||
| bcc l1 | |||
| sbc #$80 | |||
| bcs st ; carry is still set: an unconditional branch | |||
| l1: cmp #$A0 | |||
| bcc l2 | |||
| sbc #$40 | |||
| bcs st | |||
| l2: cmp #$60 | |||
| bcc l3 | |||
| sbc #$20 | |||
| bcs st | |||
| l3: cmp #$40 | |||
| bcc st | |||
| sbc #$40 | |||
| st: ora rev | |||
| sta (dst),y | |||
| iny | |||
| bne cp ; y never wraps (max 40): unconditional | |||
| done: lda cur | |||
| cmp #$FF | |||
| beq fin | |||
| tay | |||
| lda (dst),y | |||
| eor #$80 | |||
| sta (dst),y | |||
| fin: rts | |||
| @@ -93,10 +93,12 @@ sinit() | |||
| *p = 0; | |||
| p = (char *) 0xd021; | |||
| *p = 6; | |||
| p = (char *) 0x00cc; | |||
| *p = 0; | |||
| p = (char *) 0x00cc; /* kernal cursor blink: non-zero = off */ | |||
| *p = 1; | |||
| p = (char *) 0x0091; /* run/stop flag: drop whatever boot left */ | |||
| *p = 0; | |||
| p = (char *) 0x028a; /* key repeat: c64 default, cursor keys */ | |||
| *p = 0; /* only (the power c shell leaves all on) */ | |||
| for (i = 0; i < 1000; i++) { | |||
| scr[i] = 32; | |||
| colr[i] = 1; | |||
| @@ -140,22 +142,11 @@ sdone() | |||
| * echoes through the screen editor and only hands characters over after RETURN. | |||
| */ | |||
| /* next key, or 0 if none is waiting */ | |||
| /* next key, or 0 if none is waiting. the pop runs in machine code with irqs | |||
| masked, so the kernal cannot append a key half way through the shift */ | |||
| skeyp() | |||
| { | |||
| int n, k, i; | |||
| char *cnt, *buf; | |||
| cnt = (char *) 0x00c6; | |||
| n = *cnt; | |||
| n = n & 255; | |||
| if (n == 0) return 0; | |||
| buf = (char *) 0x0277; | |||
| k = buf[0]; | |||
| k = k & 255; | |||
| for (i = 1; i < n; i++) buf[i - 1] = buf[i]; | |||
| *cnt = (char) (n - 1); | |||
| return k; | |||
| return mlkey(); | |||
| } | |||
| /* wait for a key */ | |||
| @@ -1 +1 @@ | |||
| /* VSCREEN.C - SCREEN AND KEYBOARD FOR THE C64 VIM CLONE. * * ÔHE EDITOR WRITES STRAIGHT INTO THE SCREEN MATRIX AT $0400 AND COLOUR RAM AT * $D800 RATHER THAN GOING THROUGH THE KERNAL, AND IT SELECTS THE LOWER/UPPER * CASE CHARACTER SET AT $1800 SO THAT PETSCII $41-$5A DRAWS AS REAL LOWERCASE. * * ÅVERYTHING HERE RETURNS INT, SO THE OTHER COMPILATION UNIT CAN CALL THESE * WITHOUT DECLARING THEM (POWER C IS Ë&Ò Ã). */ CHAR *SCR; /* SCREEN MATRIX, $0400 */ CHAR *COLR; /* COLOUR RAM, $D800 */ /* PETSCII BYTE -> SCREEN CODE */ S2V(P) INT P; Û IF (P >= 0XC0) RETURN P - 0X80; IF (P >= 0XA0) RETURN P - 0X40; IF (P >= 0X60) RETURN P - 0X20; IF (P >= 0X40) RETURN P - 0X40; RETURN P; Ý /* RAW SCREEN CODE AT ROW R, COLUMN C */ SRAW(R, C, V) INT R, C, V; Û INT I; IF (R < 0) RETURN 0; IF (R > 24) RETURN 0; IF (C < 0) RETURN 0; IF (C > 39) RETURN 0; I = R * 40 + C; SCR[I] = V; COLR[I] = 1; RETURN 0; Ý /* ONE PETSCII CHARACTER */ SPC(R, C, P) INT R, C, P; Û SRAW(R, C, S2V(P)); RETURN 0; Ý /* ONE PETSCII CHARACTER, INVERTED: THE CURSOR AND THE STATUS ROW */ SRPC(R, C, P) INT R, C, P; Û SRAW(R, C, S2V(P) + 128); RETURN 0; Ý /* A REVERSED SPACE, FOR THE CURSOR SITTING PAST THE END OF A LINE */ SBLK(R, C) INT R, C; Û SRAW(R, C, 160); RETURN 0; Ý /* A ZERO TERMINATED PETSCII STRING */ SPS(R, C, S) INT R, C; CHAR *S; Û WHILE (*S) SPC(R, C++, *S++); RETURN 0; Ý /* N BLANKS FROM (R,C) */ SCLR(R, C, N) INT R, C, N; Û WHILE (N-- > 0) SRAW(R, C++, 32); RETURN 0; Ý /* TAKE OVER THE MACHINE: BLACK ON BLUE, LOWER CASE, NO BLINKING CURSOR */ SINIT() Û INT I; CHAR *P; SCR = (CHAR *) 0X0400; COLR = (CHAR *) 0XD800; P = (CHAR *) 0XD018; *P = 0X17; P = (CHAR *) 0XD020; *P = 0; P = (CHAR *) 0XD021; *P = 6; P = (CHAR *) 0X00CC; *P = 0; P = (CHAR *) 0X0091; /* RUN/STOP FLAG: DROP WHATEVER BOOT LEFT */ *P = 0; FOR (I = 0; I < 1000; I++) Û SCR[I] = 32; COLR[I] = 1; Ý RETURN 0; Ý /* GIVE THE MACHINE BACK THE WAY BASIC EXPECTS IT */ SDONE() Û CHAR *P; CHAR A, X, Y; P = (CHAR *) 0XD018; *P = 0X15; P = (CHAR *) 0XD020; *P = 14; P = (CHAR *) 0XD021; *P = 6; /* DO NOT RETURN TO BASIC: POWER C OWNS ZERO PAGE 43-74, WHICH IS WHERE BASIC KEEPS ITS PROGRAM AND VARIABLE POINTERS, SO BASIC CARRIES ON WITH GARBAGE AND PRINTS A MANGLED ERROR INSTEAD OF ÒÅÁÄÙ. RESTART THE MACHINE THROUGH THE KERNAL RESET INSTEAD - BANNER AND ÒÅÁÄÙ, LIKE SWITCHING ON. THE FILE HAS BEEN SAVED (OR ABANDONED WITH :Q!) BY NOW. */ A = 0; X = 0; Y = 0; SYS(0XFCE2, &A, &X, &Y); /* NEVER COMES BACK */ RETURN 0; Ý /* ---------------------------------------------------------------- KEYBOARD * * ËEYS COME STRAIGHT OUT OF THE KERNAL'S KEYBOARD BUFFER: $C6 SAYS HOW MANY * ARE WAITING AND THEY QUEUE FROM $0277. ÔHE KERNAL'S OWN IRQ HANDLER SCANS * THE KEYBOARD MATRIX AND FILLS THAT BUFFER, SO THIS GETS EACH PRESS AS IT * HAPPENS - AND IT ALSO CATCHES ANYTHING TYPED BEFORE THE PROGRAM STARTED. * * (ÇÅÔÉÎ THROUGH SYS() ONCE "CRASHED" - BUT THAT WAS SYS() CALLED WITH THE * C128 BANK ARGUMENT; ON THE C64 IT IS SYS(ADDR, &A, &X, &Y).) ÁND THE LIBRARY'S GETCHAR() IS LINE BUFFERED - IT * ECHOES THROUGH THE SCREEN EDITOR AND ONLY HANDS CHARACTERS OVER AFTER ÒÅÔÕÒÎ. */ /* NEXT KEY, OR 0 IF NONE IS WAITING */ SKEYP() Û INT N, K, I; CHAR *CNT, *BUF; CNT = (CHAR *) 0X00C6; N = *CNT; N = N & 255; IF (N == 0) RETURN 0; BUF = (CHAR *) 0X0277; K = BUF[0]; K = K & 255; FOR (I = 1; I < N; I++) BUF[I - 1] = BUF[I]; *CNT = (CHAR) (N - 1); RETURN K; Ý /* WAIT FOR A KEY */ SKEY() Û INT K; FOR (;;) Û K = SKEYP(); IF (K != 0) RETURN K; Ý Ý | |||
| /* VSCREEN.C - SCREEN AND KEYBOARD FOR THE C64 VIM CLONE. * * ÔHE EDITOR WRITES STRAIGHT INTO THE SCREEN MATRIX AT $0400 AND COLOUR RAM AT * $D800 RATHER THAN GOING THROUGH THE KERNAL, AND IT SELECTS THE LOWER/UPPER * CASE CHARACTER SET AT $1800 SO THAT PETSCII $41-$5A DRAWS AS REAL LOWERCASE. * * ÅVERYTHING HERE RETURNS INT, SO THE OTHER COMPILATION UNIT CAN CALL THESE * WITHOUT DECLARING THEM (POWER C IS Ë&Ò Ã). */ CHAR *SCR; /* SCREEN MATRIX, $0400 */ CHAR *COLR; /* COLOUR RAM, $D800 */ /* PETSCII BYTE -> SCREEN CODE */ S2V(P) INT P; Û IF (P >= 0XC0) RETURN P - 0X80; IF (P >= 0XA0) RETURN P - 0X40; IF (P >= 0X60) RETURN P - 0X20; IF (P >= 0X40) RETURN P - 0X40; RETURN P; Ý /* RAW SCREEN CODE AT ROW R, COLUMN C */ SRAW(R, C, V) INT R, C, V; Û INT I; IF (R < 0) RETURN 0; IF (R > 24) RETURN 0; IF (C < 0) RETURN 0; IF (C > 39) RETURN 0; I = R * 40 + C; SCR[I] = V; COLR[I] = 1; RETURN 0; Ý /* ONE PETSCII CHARACTER */ SPC(R, C, P) INT R, C, P; Û SRAW(R, C, S2V(P)); RETURN 0; Ý /* ONE PETSCII CHARACTER, INVERTED: THE CURSOR AND THE STATUS ROW */ SRPC(R, C, P) INT R, C, P; Û SRAW(R, C, S2V(P) + 128); RETURN 0; Ý /* A REVERSED SPACE, FOR THE CURSOR SITTING PAST THE END OF A LINE */ SBLK(R, C) INT R, C; Û SRAW(R, C, 160); RETURN 0; Ý /* A ZERO TERMINATED PETSCII STRING */ SPS(R, C, S) INT R, C; CHAR *S; Û WHILE (*S) SPC(R, C++, *S++); RETURN 0; Ý /* N BLANKS FROM (R,C) */ SCLR(R, C, N) INT R, C, N; Û WHILE (N-- > 0) SRAW(R, C++, 32); RETURN 0; Ý /* TAKE OVER THE MACHINE: BLACK ON BLUE, LOWER CASE, NO BLINKING CURSOR */ SINIT() Û INT I; CHAR *P; SCR = (CHAR *) 0X0400; COLR = (CHAR *) 0XD800; P = (CHAR *) 0XD018; *P = 0X17; P = (CHAR *) 0XD020; *P = 0; P = (CHAR *) 0XD021; *P = 6; P = (CHAR *) 0X00CC; /* KERNAL CURSOR BLINK: NON-ZERO = OFF */ *P = 1; P = (CHAR *) 0X0091; /* RUN/STOP FLAG: DROP WHATEVER BOOT LEFT */ *P = 0; P = (CHAR *) 0X028A; /* KEY REPEAT: C64 DEFAULT, CURSOR KEYS */ *P = 0; /* ONLY (THE POWER C SHELL LEAVES ALL ON) */ FOR (I = 0; I < 1000; I++) Û SCR[I] = 32; COLR[I] = 1; Ý RETURN 0; Ý /* GIVE THE MACHINE BACK THE WAY BASIC EXPECTS IT */ SDONE() Û CHAR *P; CHAR A, X, Y; P = (CHAR *) 0XD018; *P = 0X15; P = (CHAR *) 0XD020; *P = 14; P = (CHAR *) 0XD021; *P = 6; /* DO NOT RETURN TO BASIC: POWER C OWNS ZERO PAGE 43-74, WHICH IS WHERE BASIC KEEPS ITS PROGRAM AND VARIABLE POINTERS, SO BASIC CARRIES ON WITH GARBAGE AND PRINTS A MANGLED ERROR INSTEAD OF ÒÅÁÄÙ. RESTART THE MACHINE THROUGH THE KERNAL RESET INSTEAD - BANNER AND ÒÅÁÄÙ, LIKE SWITCHING ON. THE FILE HAS BEEN SAVED (OR ABANDONED WITH :Q!) BY NOW. */ A = 0; X = 0; Y = 0; SYS(0XFCE2, &A, &X, &Y); /* NEVER COMES BACK */ RETURN 0; Ý /* ---------------------------------------------------------------- KEYBOARD * * ËEYS COME STRAIGHT OUT OF THE KERNAL'S KEYBOARD BUFFER: $C6 SAYS HOW MANY * ARE WAITING AND THEY QUEUE FROM $0277. ÔHE KERNAL'S OWN IRQ HANDLER SCANS * THE KEYBOARD MATRIX AND FILLS THAT BUFFER, SO THIS GETS EACH PRESS AS IT * HAPPENS - AND IT ALSO CATCHES ANYTHING TYPED BEFORE THE PROGRAM STARTED. * * (ÇÅÔÉÎ THROUGH SYS() ONCE "CRASHED" - BUT THAT WAS SYS() CALLED WITH THE * C128 BANK ARGUMENT; ON THE C64 IT IS SYS(ADDR, &A, &X, &Y).) ÁND THE LIBRARY'S GETCHAR() IS LINE BUFFERED - IT * ECHOES THROUGH THE SCREEN EDITOR AND ONLY HANDS CHARACTERS OVER AFTER ÒÅÔÕÒÎ. */ /* NEXT KEY, OR 0 IF NONE IS WAITING. THE POP RUNS IN MACHINE CODE WITH IRQS MASKED, SO THE KERNAL CANNOT APPEND A KEY HALF WAY THROUGH THE SHIFT */ SKEYP() Û RETURN MLKEY(); Ý /* WAIT FOR A KEY */ SKEY() Û INT K; FOR (;;) Û K = SKEYP(); IF (K != 0) RETURN K; Ý Ý | |||
| @@ -8,6 +8,7 @@ extern char *note; | |||
| extern char fname[]; | |||
| extern char ybuf[]; | |||
| extern int ylen, yok; | |||
| extern int redraw; | |||
| /* decimal of v into t at p, returns the position after it */ | |||
| dnum(t, p, v) | |||
| @@ -42,34 +43,6 @@ scroll() | |||
| return 0; | |||
| } | |||
| /* draw screen row sr from buffer line top+sr */ | |||
| drow(sr) | |||
| int sr; | |||
| { | |||
| int ln, n, cc; | |||
| unsigned i, e, d; | |||
| d = 1024 + sr * 40; /* screen RAM address of the row */ | |||
| ln = top + sr; | |||
| if (ln >= nlines) { | |||
| mlrow(0, 0, d, 255, 0); | |||
| return 0; | |||
| } | |||
| i = lptr[ln] + leftcol; | |||
| e = linend(ln); | |||
| n = 0; | |||
| if (e > i) n = e - i; | |||
| if (n > 40) n = 40; | |||
| cc = 255; /* cursor column, 255 = not here */ | |||
| if (ln == cy) { | |||
| cc = cx - leftcol; | |||
| if (cc < 0) cc = 255; | |||
| if (cc > 39) cc = 255; | |||
| } | |||
| mlrow((unsigned) bfr + i, n, d, cc, 0); /* machine code does the rest */ | |||
| return 0; | |||
| } | |||
| /* the status row: message, mode or name on the left; modified flag; line/total */ | |||
| dstat() | |||
| { | |||
| @@ -96,14 +69,36 @@ dstat() | |||
| return 0; | |||
| } | |||
| /* what the screen showed last time, so a key that only moves the cursor | |||
| repaints two rows instead of 24 */ | |||
| int ltop, lleft, lcy; | |||
| /* repaint what changed, then the status row. redraw is raised by the editing | |||
| code: 1 = the cursor line changed, 2 = lines moved, repaint everything. | |||
| (no double bar: power c cannot type it, see NOTES.md) */ | |||
| dall() | |||
| { | |||
| int r; | |||
| int crow, ccol, orow, full; | |||
| for (r = 0; r < 24; r++) drow(r); | |||
| crow = cy - top; | |||
| ccol = cx - leftcol; | |||
| full = 0; | |||
| if (redraw == 2) full = 1; | |||
| if (top != ltop) full = 1; | |||
| if (leftcol != lleft) full = 1; | |||
| if (full) { | |||
| mlscr(0, 23, crow, ccol); /* one call paints 24 rows */ | |||
| } else { | |||
| orow = lcy - top; /* take the cursor off its old row */ | |||
| if (orow != crow && orow >= 0 && orow < 24) mlscr(orow, orow, crow, ccol); | |||
| mlscr(crow, crow, crow, ccol); | |||
| } | |||
| ltop = top; | |||
| lleft = leftcol; | |||
| lcy = cy; | |||
| redraw = 0; | |||
| dstat(); | |||
| return 0; | |||
| } | |||
| /* end of vview.c */ | |||
| @@ -1 +1 @@ | |||
| /* VVIEW.C - DRAWING FOR THE C64 VIM CLONE: SCROLL, ROWS, STATUS ROW. */ EXTERN CHAR *BFR; EXTERN UNSIGNED BLEN; EXTERN UNSIGNED LPTR[]; EXTERN INT NLINES, CY, CX, TOP, LEFTCOL, MODE, DIRTY; EXTERN CHAR *NOTE; EXTERN CHAR FNAME[]; EXTERN CHAR YBUF[]; EXTERN INT YLEN, YOK; /* DECIMAL OF V INTO T AT P, RETURNS THE POSITION AFTER IT */ DNUM(T, P, V) CHAR *T; INT P, V; Û CHAR D[6]; INT N; N = 0; IF (V == 0) Û T[P++] = 48; RETURN P; Ý WHILE (V > 0 && N < 5) Û D[N++] = 48 + (V % 10); V = V / 10; Ý WHILE (N > 0) T[P++] = D[--N]; RETURN P; Ý /* BRING THE CURSOR BACK INTO VIEW */ SCROLL() Û IF (CY < TOP) TOP = CY; IF (CY > TOP + 23) TOP = CY - 23; IF (TOP < 0) TOP = 0; IF (CX < LEFTCOL) LEFTCOL = CX; IF (CX > LEFTCOL + 39) LEFTCOL = CX - 39; IF (LEFTCOL < 0) LEFTCOL = 0; RETURN 0; Ý /* DRAW SCREEN ROW SR FROM BUFFER LINE TOP+SR */ DROW(SR) INT SR; Û INT LN, N, CC; UNSIGNED I, E, D; D = 1024 + SR * 40; /* SCREEN ÒÁÍ ADDRESS OF THE ROW */ LN = TOP + SR; IF (LN >= NLINES) Û MLROW(0, 0, D, 255, 0); RETURN 0; Ý I = LPTR[LN] + LEFTCOL; E = LINEND(LN); N = 0; IF (E > I) N = E - I; IF (N > 40) N = 40; CC = 255; /* CURSOR COLUMN, 255 = NOT HERE */ IF (LN == CY) Û CC = CX - LEFTCOL; IF (CC < 0) CC = 255; IF (CC > 39) CC = 255; Ý MLROW((UNSIGNED) BFR + I, N, D, CC, 0); /* MACHINE CODE DOES THE REST */ RETURN 0; Ý /* THE STATUS ROW: MESSAGE, MODE OR NAME ON THE LEFT; MODIFIED FLAG; LINE/TOTAL */ DSTAT() Û CHAR T[40]; CHAR *S; INT I, N, P; IF (NOTE != 0) S = NOTE; ELSE IF (MODE) S = "-- INSERT --"; ELSE IF (FNAME[0] == 0) S = "[NO NAME]"; ELSE S = FNAME; N = 0; FOR (I = 0; S[I] && I < 26; I++) T[N++] = S[I]; WHILE (N < 28) T[N++] = 32; IF (DIRTY) T[N++] = 43; /* + */ ELSE T[N++] = 32; WHILE (N < 31) T[N++] = 32; P = DNUM(T, N, CY + 1); T[P++] = 47; /* / */ P = DNUM(T, P, NLINES); MLROW((UNSIGNED) T, P, 1984, 255, 128); /* ROW 24 = $0400 + 960, REVERSE */ RETURN 0; Ý DALL() Û INT R; FOR (R = 0; R < 24; R++) DROW(R); DSTAT(); RETURN 0; Ý /* END OF VVIEW.C */ | |||
| /* VVIEW.C - DRAWING FOR THE C64 VIM CLONE: SCROLL, ROWS, STATUS ROW. */ EXTERN CHAR *BFR; EXTERN UNSIGNED BLEN; EXTERN UNSIGNED LPTR[]; EXTERN INT NLINES, CY, CX, TOP, LEFTCOL, MODE, DIRTY; EXTERN CHAR *NOTE; EXTERN CHAR FNAME[]; EXTERN CHAR YBUF[]; EXTERN INT YLEN, YOK; EXTERN INT REDRAW; /* DECIMAL OF V INTO T AT P, RETURNS THE POSITION AFTER IT */ DNUM(T, P, V) CHAR *T; INT P, V; Û CHAR D[6]; INT N; N = 0; IF (V == 0) Û T[P++] = 48; RETURN P; Ý WHILE (V > 0 && N < 5) Û D[N++] = 48 + (V % 10); V = V / 10; Ý WHILE (N > 0) T[P++] = D[--N]; RETURN P; Ý /* BRING THE CURSOR BACK INTO VIEW */ SCROLL() Û IF (CY < TOP) TOP = CY; IF (CY > TOP + 23) TOP = CY - 23; IF (TOP < 0) TOP = 0; IF (CX < LEFTCOL) LEFTCOL = CX; IF (CX > LEFTCOL + 39) LEFTCOL = CX - 39; IF (LEFTCOL < 0) LEFTCOL = 0; RETURN 0; Ý /* THE STATUS ROW: MESSAGE, MODE OR NAME ON THE LEFT; MODIFIED FLAG; LINE/TOTAL */ DSTAT() Û CHAR T[40]; CHAR *S; INT I, N, P; IF (NOTE != 0) S = NOTE; ELSE IF (MODE) S = "-- INSERT --"; ELSE IF (FNAME[0] == 0) S = "[NO NAME]"; ELSE S = FNAME; N = 0; FOR (I = 0; S[I] && I < 26; I++) T[N++] = S[I]; WHILE (N < 28) T[N++] = 32; IF (DIRTY) T[N++] = 43; /* + */ ELSE T[N++] = 32; WHILE (N < 31) T[N++] = 32; P = DNUM(T, N, CY + 1); T[P++] = 47; /* / */ P = DNUM(T, P, NLINES); MLROW((UNSIGNED) T, P, 1984, 255, 128); /* ROW 24 = $0400 + 960, REVERSE */ RETURN 0; Ý /* WHAT THE SCREEN SHOWED LAST TIME, SO A KEY THAT ONLY MOVES THE CURSOR REPAINTS TWO ROWS INSTEAD OF 24 */ INT LTOP, LLEFT, LCY; /* REPAINT WHAT CHANGED, THEN THE STATUS ROW. REDRAW IS RAISED BY THE EDITING CODE: 1 = THE CURSOR LINE CHANGED, 2 = LINES MOVED, REPAINT EVERYTHING. (NO DOUBLE BAR: POWER C CANNOT TYPE IT, SEE ÎÏÔÅÓ.MD) */ DALL() Û INT CROW, CCOL, OROW, FULL; CROW = CY - TOP; CCOL = CX - LEFTCOL; FULL = 0; IF (REDRAW == 2) FULL = 1; IF (TOP != LTOP) FULL = 1; IF (LEFTCOL != LLEFT) FULL = 1; IF (FULL) Û MLSCR(0, 23, CROW, CCOL); /* ONE CALL PAINTS 24 ROWS */ Ý ELSE Û OROW = LCY - TOP; /* TAKE THE CURSOR OFF ITS OLD ROW */ IF (OROW != CROW && OROW >= 0 && OROW < 24) MLSCR(OROW, OROW, CROW, CCOL); MLSCR(CROW, CROW, CROW, CCOL); Ý LTOP = TOP; LLEFT = LEFTCOL; LCY = CY; REDRAW = 0; DSTAT(); RETURN 0; Ý /* END OF VVIEW.C */ | |||
| @@ -1,6 +1,6 @@ | |||
| vim=8E13FCED4A0CAFD92855B9281D268368 | |||
| vscreen=7773151D0C575F00E919F19875CB498F | |||
| vview=F3C7D092B8080C956D9EBD7B95800CD5 | |||
| vim=D571CD44594114AF849B8C6C15366001 | |||
| vscreen=30C2D474C6B87917CD09BA23BBDF9A7C | |||
| vview=FDB755840D15E1F32FAFF1214F246BF8 | |||
| mltest=E4BDE10600DE08AF766FFB32A6D9A3E5 | |||
| vedit=8BE6C95F2BDA01EC535A8C51CD666881 | |||
| vml=B9A7F164619D4675A2E78B48F02C179D | |||
| vedit=3BC34F6146A6C300555BDB65BC08E256 | |||
| vml=975E31324614BD5A546A523B34E02A64 | |||
Powered by TurnKey Linux.