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 | 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. | 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 | ## C64 screen facts the editor relies on | ||||
| - Screen matrix `$0400`, colour RAM `$D800` (both directly writable). | - 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`. | `$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 | - Reverse video is the high bit: screen code `+ $80`. That is the cursor block | ||||
| and the status row. | 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; | - Arrow keys are PETSCII `$11` down, `$91` up, `$1D` right, `$9D` left; | ||||
| RETURN `$0D`, DEL `$14`. | 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 | # 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" | $ErrorActionPreference = "Stop" | ||||
| $here = $PSScriptRoot | $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; } | SEGMENTS { CODE: load = M, type = ro; } | ||||
| "@ | Set-Content $cfg -Encoding ascii | "@ | 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" } | if ($LASTEXITCODE) { throw "ca65 failed" } | ||||
| & $ld65 -C $cfg -o $out $obj | |||||
| & $ld65 -C $cfg -o $out -Ln $lbl $obj | |||||
| if ($LASTEXITCODE) { throw "ld65 failed" } | 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) | $b = [IO.File]::ReadAllBytes($out) | ||||
| $rows = @() | $rows = @() | ||||
| for ($i = 0; $i -lt $b.Length; $i += 12) { | 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 ", ") | $rows += " " + (($b[$i..($i + $n - 1)] | ForEach-Object { "$_" }) -join ", ") | ||||
| } | } | ||||
| $arr = $rows -join ",`n" | $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)] = { | char mlcode[$($b.Length)] = { | ||||
| $arr | $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 fname[]; | ||||
| extern char ybuf[]; | extern char ybuf[]; | ||||
| extern int ylen, yok; | extern int ylen, yok; | ||||
| extern int redraw; /* 1 cursor row, 2 whole screen */ | |||||
| /* end offset (exclusive of the $0d) of line ln */ | /* end offset (exclusive of the $0d) of line ln */ | ||||
| linend(ln) | linend(ln) | ||||
| @@ -35,10 +36,8 @@ bins(pos, ch) | |||||
| unsigned pos; | unsigned pos; | ||||
| int ch; | int ch; | ||||
| { | { | ||||
| unsigned i; | |||||
| if (blen >= 18999) return 0; | 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; | bfr[pos] = ch; | ||||
| blen++; | blen++; | ||||
| dirty = 1; | dirty = 1; | ||||
| @@ -49,39 +48,43 @@ int ch; | |||||
| delrange(pos, n) | delrange(pos, n) | ||||
| unsigned 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; | blen = blen - n; | ||||
| dirty = 1; | dirty = 1; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| #define MAXCOL 80 /* longest line the editor keeps */ | |||||
| /* type one character at the cursor */ | /* type one character at the cursor */ | ||||
| inschar(ch) | inschar(ch) | ||||
| int 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; | 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++; | cx++; | ||||
| if (redraw < 1) redraw = 1; | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| /* split the line at the cursor */ | /* split the line at the cursor */ | ||||
| newline() | newline() | ||||
| { | { | ||||
| int l; | |||||
| unsigned pos; | unsigned pos; | ||||
| if (nlines >= 599) return 0; | if (nlines >= 599) return 0; | ||||
| pos = lptr[cy] + cx; | pos = lptr[cy] + cx; | ||||
| if (bins(pos, 13) == 0) return 0; | 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; | lptr[cy + 1] = pos + 1; | ||||
| nlines++; | nlines++; | ||||
| for (l = cy + 2; l < nlines; l++) lptr[l]++; | |||||
| mladj((unsigned) &lptr[cy + 2], nlines - cy - 2, 1); | |||||
| redraw = 2; | |||||
| cy++; | cy++; | ||||
| cx = 0; | cx = 0; | ||||
| return 1; | return 1; | ||||
| @@ -90,18 +93,17 @@ newline() | |||||
| /* delete the character under the cursor */ | /* delete the character under the cursor */ | ||||
| delchar() | delchar() | ||||
| { | { | ||||
| int l; | |||||
| if (cx >= linend(cy) - lptr[cy]) return 0; | if (cx >= linend(cy) - lptr[cy]) return 0; | ||||
| delrange(lptr[cy] + cx, 1); | 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; | return 1; | ||||
| } | } | ||||
| /* backspace: delete before the cursor, or join onto the previous line */ | /* backspace: delete before the cursor, or join onto the previous line */ | ||||
| bkspace() | bkspace() | ||||
| { | { | ||||
| int l, pl; | |||||
| int pl; | |||||
| if (cx > 0) { | if (cx > 0) { | ||||
| cx--; | cx--; | ||||
| @@ -110,10 +112,16 @@ bkspace() | |||||
| } | } | ||||
| if (cy == 0) return 0; | if (cy == 0) return 0; | ||||
| pl = linend(cy - 1) - lptr[cy - 1]; | 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); | 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--; | nlines--; | ||||
| for (l = cy; l < nlines; l++) lptr[l]--; | |||||
| mladj((unsigned) &lptr[cy], nlines - cy, 65535); | |||||
| redraw = 2; | |||||
| cy--; | cy--; | ||||
| cx = pl; | cx = pl; | ||||
| return 0; | return 0; | ||||
| @@ -122,13 +130,13 @@ bkspace() | |||||
| /* dd: yank the current line and remove it */ | /* dd: yank the current line and remove it */ | ||||
| delline() | delline() | ||||
| { | { | ||||
| int l, i; | |||||
| int i; | |||||
| unsigned s, e, n; | unsigned s, e, n; | ||||
| s = lptr[cy]; | s = lptr[cy]; | ||||
| e = linend(cy); | e = linend(cy); | ||||
| ylen = e - s; | ylen = e - s; | ||||
| if (ylen > 159) ylen = 159; | |||||
| if (ylen > MAXCOL) ylen = MAXCOL; | |||||
| for (i = 0; i < ylen; i++) ybuf[i] = bfr[s + i]; | for (i = 0; i < ylen; i++) ybuf[i] = bfr[s + i]; | ||||
| yok = 1; | yok = 1; | ||||
| @@ -138,15 +146,17 @@ delline() | |||||
| } else if (cy + 1 < nlines) { | } else if (cy + 1 < nlines) { | ||||
| n = lptr[cy + 1] - s; | n = lptr[cy + 1] - s; | ||||
| delrange(s, n); | 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--; | 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 */ | } else { /* last line: take its leading $0d */ | ||||
| delrange(s - 1, blen - s + 1); | delrange(s - 1, blen - s + 1); | ||||
| nlines--; | nlines--; | ||||
| cy--; | cy--; | ||||
| } | } | ||||
| cx = 0; | cx = 0; | ||||
| redraw = 2; | |||||
| return 0; | 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 *note; /* one-shot message for the status row */ | ||||
| char ybuf[160]; /* the yanked line */ | char ybuf[160]; /* the yanked line */ | ||||
| int ylen, yok; | int ylen, yok; | ||||
| int redraw; /* 1 cursor row, 2 whole screen */ | |||||
| /* screen rows are 0..23, the status row is 24 */ | /* screen rows are 0..23, the status row is 24 */ | ||||
| @@ -89,6 +90,24 @@ char *s; | |||||
| return n; | 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 */ | /* ask which file to open */ | ||||
| askname() | askname() | ||||
| { | { | ||||
| @@ -98,10 +117,39 @@ askname() | |||||
| return 0; | 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() | loadf() | ||||
| { | { | ||||
| int f, ch, prev, c; | |||||
| int f, ch, prev, c, col; | |||||
| char on[28]; | char on[28]; | ||||
| int i; | int i; | ||||
| @@ -117,10 +165,21 @@ loadf() | |||||
| on[i++] = 82; /* R */ | on[i++] = 82; /* R */ | ||||
| on[i] = 0; | on[i] = 0; | ||||
| if (open(15, 8, 15, "") == 0) return 0; /* status channel first */ | |||||
| f = open(5, 8, 2, on); | 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; | blen = 0; | ||||
| prev = 0; | prev = 0; | ||||
| col = 0; | |||||
| split = 0; | |||||
| for (;;) { | for (;;) { | ||||
| ch = getc(5); | ch = getc(5); | ||||
| if (ch < 0) break; /* end of file */ | if (ch < 0) break; /* end of file */ | ||||
| @@ -132,10 +191,18 @@ loadf() | |||||
| if (ch == 13) c = 13; | if (ch == 13) c = 13; | ||||
| else if (ch == 10) c = 13; | else if (ch == 10) c = 13; | ||||
| else c = ch; | 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 (blen < 18999) bfr[blen++] = c; | ||||
| if (c == 13) col = 0; | |||||
| else col++; | |||||
| prev = ch; | prev = ch; | ||||
| } | } | ||||
| close(5); | close(5); | ||||
| close(15); /* last: it closes everything */ | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| @@ -218,7 +285,7 @@ main() | |||||
| int k, ok, p; | int k, ok, p; | ||||
| sinit(); | sinit(); | ||||
| mlinit(); /* machine code row painter */ | |||||
| mlinit(); /* ML helpers to $c000: before malloc */ | |||||
| bfr = malloc(19000); | bfr = malloc(19000); | ||||
| if (bfr == 0) { | if (bfr == 0) { | ||||
| sps(0, 0, "out of memory"); | sps(0, 0, "out of memory"); | ||||
| @@ -240,16 +307,27 @@ main() | |||||
| note = 0; | note = 0; | ||||
| ylen = 0; | ylen = 0; | ||||
| yok = 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(); | buildidx(); | ||||
| if (ok == 0) { /* keep the name: :w creates it */ | if (ok == 0) { /* keep the name: :w creates it */ | ||||
| nlines = 1; | nlines = 1; | ||||
| blen = 0; | blen = 0; | ||||
| note = "new file"; | note = "new file"; | ||||
| } | } | ||||
| if (split) note = "long lines split at 80"; | |||||
| for (;;) { | for (;;) { | ||||
| scroll(); | 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() | mlinit() | ||||
| { | { | ||||
| char *d; | |||||
| int i; | |||||
| highmem(0xc000); | |||||
| d = (char *) 0xc000; | |||||
| for (i = 0; i < MLSIZE; i++) d[i] = mlcode[i]; | |||||
| return 0; | 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) | mlrow(src, len, dst, cur, rev) | ||||
| unsigned src, dst; | unsigned src, dst; | ||||
| int len, cur, rev; | int len, cur, rev; | ||||
| { | { | ||||
| char *p; | char *p; | ||||
| char a, x, y; | |||||
| p = (char *) 0x22; | |||||
| p[0] = src & 255; | |||||
| p = (char *) 0xc400; | |||||
| p[0] = src; | |||||
| p[1] = src >> 8; | p[1] = src >> 8; | ||||
| p[2] = len; | p[2] = len; | ||||
| p[3] = dst & 255; | |||||
| p[3] = dst; | |||||
| p[4] = dst >> 8; | p[4] = dst >> 8; | ||||
| p[5] = cur; | p[5] = cur; | ||||
| p[6] = rev; | p[6] = rev; | ||||
| a = 0; | |||||
| x = 0; | |||||
| y = 0; | |||||
| sys((unsigned) mlcode, &a, &x, &y); /* c64 form: no bank argument */ | |||||
| mlgo(MLROW); | |||||
| return 0; | 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 = 0; | ||||
| p = (char *) 0xd021; | p = (char *) 0xd021; | ||||
| *p = 6; | *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 = (char *) 0x0091; /* run/stop flag: drop whatever boot left */ | ||||
| *p = 0; | *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++) { | for (i = 0; i < 1000; i++) { | ||||
| scr[i] = 32; | scr[i] = 32; | ||||
| colr[i] = 1; | colr[i] = 1; | ||||
| @@ -140,22 +142,11 @@ sdone() | |||||
| * echoes through the screen editor and only hands characters over after RETURN. | * 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() | 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 */ | /* 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 fname[]; | ||||
| extern char ybuf[]; | extern char ybuf[]; | ||||
| extern int ylen, yok; | extern int ylen, yok; | ||||
| extern int redraw; | |||||
| /* decimal of v into t at p, returns the position after it */ | /* decimal of v into t at p, returns the position after it */ | ||||
| dnum(t, p, v) | dnum(t, p, v) | ||||
| @@ -42,34 +43,6 @@ scroll() | |||||
| return 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 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 */ | /* the status row: message, mode or name on the left; modified flag; line/total */ | ||||
| dstat() | dstat() | ||||
| { | { | ||||
| @@ -96,14 +69,36 @@ dstat() | |||||
| return 0; | 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() | 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(); | dstat(); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* end of vview.c */ | /* 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 | mltest=E4BDE10600DE08AF766FFB32A6D9A3E5 | ||||
| vedit=8BE6C95F2BDA01EC535A8C51CD666881 | |||||
| vml=B9A7F164619D4675A2E78B48F02C179D | |||||
| vedit=3BC34F6146A6C300555BDB65BC08E256 | |||||
| vml=975E31324614BD5A546A523B34E02A64 | |||||
Powered by TurnKey Linux.