diff --git a/vim/NOTES.md b/vim/NOTES.md index ad9dbd7..2ea8f14 100644 --- a/vim/NOTES.md +++ b/vim/NOTES.md @@ -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 e` 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`. diff --git a/vim/build-ml.ps1 b/vim/build-ml.ps1 index a588c15..80411c7 100644 --- a/vim/build-ml.ps1 +++ b/vim/build-ml.ps1 @@ -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 '; ')) diff --git a/vim/shots/run.png b/vim/shots/run.png index a485291..d526e0d 100644 Binary files a/vim/shots/run.png and b/vim/shots/run.png differ diff --git a/vim/vedit.c b/vim/vedit.c index 1b3f457..9d54f4c 100644 --- a/vim/vedit.c +++ b/vim/vedit.c @@ -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; } diff --git a/vim/vedit.pet b/vim/vedit.pet index 8165488..fcc19ed 100644 --- a/vim/vedit.pet +++ b/vim/vedit.pet @@ -1 +1 @@ -/* VEDIT.C - BUFFER EDITING FOR THE C64 VIM CLONE: INSERT, DELETE, SAVE. */ 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; /* END OFFSET (EXCLUSIVE OF THE $0D) OF LINE LN */ LINEND(LN) INT LN; Û IF (LN + 1 < NLINES) RETURN LPTR[LN + 1] - 1; RETURN BLEN; Ý /* KEEP CX INSIDE THE CURRENT LINE */ CLAMPX() Û INT LEN; LEN = LINEND(CY) - LPTR[CY]; IF (CX > LEN) CX = LEN; IF (CX < 0) CX = 0; RETURN 0; Ý /* ------------------------------------------------------------- EDITING */ /* INSERT BYTE CH AT BUFFER OFFSET POS. 1 ON SUCCESS, 0 IF THE BUFFER IS FULL */ 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]; BFR[POS] = CH; BLEN++; DIRTY = 1; RETURN 1; Ý /* REMOVE N BYTES AT OFFSET POS */ DELRANGE(POS, N) UNSIGNED POS, N; Û UNSIGNED I; FOR (I = POS; I + N < BLEN; I++) BFR[I] = BFR[I + N]; BLEN = BLEN - N; DIRTY = 1; RETURN 0; Ý /* TYPE ONE CHARACTER AT THE CURSOR */ INSCHAR(CH) INT CH; Û INT L; IF (BINS(LPTR[CY] + CX, CH) == 0) RETURN 0; FOR (L = CY + 1; L < NLINES; L++) LPTR[L]++; CX++; 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]; LPTR[CY + 1] = POS + 1; NLINES++; FOR (L = CY + 2; L < NLINES; L++) LPTR[L]++; CY++; CX = 0; RETURN 1; Ý /* 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]--; RETURN 1; Ý /* BACKSPACE: DELETE BEFORE THE CURSOR, OR JOIN ONTO THE PREVIOUS LINE */ BKSPACE() Û INT L, PL; IF (CX > 0) Û CX--; DELCHAR(); RETURN 0; Ý IF (CY == 0) RETURN 0; PL = LINEND(CY - 1) - LPTR[CY - 1]; DELRANGE(LPTR[CY] - 1, 1); FOR (L = CY; L < NLINES - 1; L++) LPTR[L] = LPTR[L + 1]; NLINES--; FOR (L = CY; L < NLINES; L++) LPTR[L]--; CY--; CX = PL; RETURN 0; Ý /* DD: YANK THE CURRENT LINE AND REMOVE IT */ DELLINE() Û INT L, I; UNSIGNED S, E, N; S = LPTR[CY]; E = LINEND(CY); YLEN = E - S; IF (YLEN > 159) YLEN = 159; FOR (I = 0; I < YLEN; I++) YBUF[I] = BFR[S + I]; YOK = 1; IF (NLINES == 1) Û BLEN = 0; DIRTY = 1; Ý 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]; NLINES--; FOR (L = CY; L < NLINES; L++) LPTR[L] -= N; Ý ELSE Û /* LAST LINE: TAKE ITS LEADING $0D */ DELRANGE(S - 1, BLEN - S + 1); NLINES--; CY--; Ý CX = 0; RETURN 0; Ý /* P: PUT THE YANKED LINE BELOW THE CURSOR LINE */ PASTE() Û INT I; IF (YOK == 0) RETURN 0; CX = LINEND(CY) - LPTR[CY]; IF (NEWLINE() == 0) RETURN 0; FOR (I = 0; I < YLEN; I++) INSCHAR(YBUF[I]); CX = 0; RETURN 0; Ý /* CURSOR KEYS, THE SAME IN BOTH MODES. 1 IF K WAS ONE OF THEM */ ARROW(K) INT K; Û IF (K == 17) Û IF (CY + 1 < NLINES) Û CY++; CLAMPX(); Ý RETURN 1; Ý IF (K == 145) Û IF (CY > 0) Û CY--; CLAMPX(); Ý RETURN 1; Ý IF (K == 29) Û CX++; CLAMPX(); RETURN 1; Ý IF (K == 157) Û IF (CX > 0) CX--; RETURN 1; Ý RETURN 0; Ý /* WRITE THE BUFFER TO FNAME, REPLACING ANY OLD COPY. 1 ON SUCCESS */ SAVEF() Û CHAR ON[32]; INT I, F; UNSIGNED J; ON[0] = 64; /* @ */ ON[1] = 48; /* 0 */ ON[2] = 58; /* : */ I = 3; J = 0; WHILE (FNAME[J] && J < 20) ON[I++] = FNAME[J++]; ON[I++] = 44; /* , */ ON[I++] = 83; /* Ó (PETSCII, NOT ASCII) */ ON[I++] = 44; /* , */ ON[I++] = 87; /* × */ ON[I] = 0; F = OPEN(5, 8, 3, ON); IF (F == 0) RETURN 0; FOR (J = 0; J < BLEN; J++) PUTC(BFR[J], 5); IF (BLEN > 0) Û IF (BFR[BLEN - 1] != 13) PUTC(13, 5); Ý CLOSE(5); RETURN 1; Ý \ No newline at end of file +/* VEDIT.C - BUFFER EDITING FOR THE C64 VIM CLONE: INSERT, DELETE, SAVE. */ 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; /* 1 CURSOR ROW, 2 WHOLE SCREEN */ /* END OFFSET (EXCLUSIVE OF THE $0D) OF LINE LN */ LINEND(LN) INT LN; Û IF (LN + 1 < NLINES) RETURN LPTR[LN + 1] - 1; RETURN BLEN; Ý /* KEEP CX INSIDE THE CURRENT LINE */ CLAMPX() Û INT LEN; LEN = LINEND(CY) - LPTR[CY]; IF (CX > LEN) CX = LEN; IF (CX < 0) CX = 0; RETURN 0; Ý /* ------------------------------------------------------------- EDITING */ /* INSERT BYTE CH AT BUFFER OFFSET POS. 1 ON SUCCESS, 0 IF THE BUFFER IS FULL */ BINS(POS, CH) UNSIGNED POS; INT CH; Û IF (BLEN >= 18999) RETURN 0; MLMOVE((UNSIGNED) BFR + POS, (UNSIGNED) BFR + POS + 1, BLEN - POS); BFR[POS] = CH; BLEN++; DIRTY = 1; RETURN 1; Ý /* REMOVE N BYTES AT OFFSET POS */ DELRANGE(POS, N) UNSIGNED POS, N; Û MLMOVE((UNSIGNED) BFR + POS + N, (UNSIGNED) BFR + POS, BLEN - POS - N); BLEN = BLEN - N; DIRTY = 1; RETURN 0; Ý #DEFINE ÍÁØÃÏÌ 80 /* LONGEST LINE THE EDITOR KEEPS */ /* TYPE ONE CHARACTER AT THE CURSOR */ INSCHAR(CH) INT CH; Û IF (LINEND(CY) - LPTR[CY] >= ÍÁØÃÏÌ) Û NOTE = "LINE FULL (80)"; RETURN 0; Ý IF (BINS(LPTR[CY] + CX, CH) == 0) RETURN 0; MLADJ((UNSIGNED) &LPTR[CY + 1], NLINES - CY - 1, 1); CX++; IF (REDRAW < 1) REDRAW = 1; RETURN 1; Ý /* SPLIT THE LINE AT THE CURSOR */ NEWLINE() Û UNSIGNED POS; IF (NLINES >= 599) RETURN 0; POS = LPTR[CY] + CX; IF (BINS(POS, 13) == 0) RETURN 0; MLMOVE((UNSIGNED) &LPTR[CY + 1], (UNSIGNED) &LPTR[CY + 2], (NLINES - CY - 1) * 2); LPTR[CY + 1] = POS + 1; NLINES++; MLADJ((UNSIGNED) &LPTR[CY + 2], NLINES - CY - 2, 1); REDRAW = 2; CY++; CX = 0; RETURN 1; Ý /* DELETE THE CHARACTER UNDER THE CURSOR */ DELCHAR() Û IF (CX >= LINEND(CY) - LPTR[CY]) RETURN 0; DELRANGE(LPTR[CY] + CX, 1); 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 PL; IF (CX > 0) Û CX--; DELCHAR(); RETURN 0; Ý IF (CY == 0) RETURN 0; PL = LINEND(CY - 1) - LPTR[CY - 1]; IF (PL + LINEND(CY) - LPTR[CY] > ÍÁØÃÏÌ) Û NOTE = "JOINED LINE WOULD PASS 80"; RETURN 0; Ý DELRANGE(LPTR[CY] - 1, 1); MLMOVE((UNSIGNED) &LPTR[CY + 1], (UNSIGNED) &LPTR[CY], (NLINES - CY - 1) * 2); NLINES--; MLADJ((UNSIGNED) &LPTR[CY], NLINES - CY, 65535); REDRAW = 2; CY--; CX = PL; RETURN 0; Ý /* DD: YANK THE CURRENT LINE AND REMOVE IT */ DELLINE() Û INT I; UNSIGNED S, E, N; S = LPTR[CY]; E = LINEND(CY); YLEN = E - S; IF (YLEN > ÍÁØÃÏÌ) YLEN = ÍÁØÃÏÌ; FOR (I = 0; I < YLEN; I++) YBUF[I] = BFR[S + I]; YOK = 1; IF (NLINES == 1) Û BLEN = 0; DIRTY = 1; Ý ELSE IF (CY + 1 < NLINES) Û N = LPTR[CY + 1] - S; DELRANGE(S, N); MLMOVE((UNSIGNED) &LPTR[CY + 1], (UNSIGNED) &LPTR[CY], (NLINES - CY - 1) * 2); NLINES--; 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; Ý /* P: PUT THE YANKED LINE BELOW THE CURSOR LINE */ PASTE() Û INT I; IF (YOK == 0) RETURN 0; CX = LINEND(CY) - LPTR[CY]; IF (NEWLINE() == 0) RETURN 0; FOR (I = 0; I < YLEN; I++) INSCHAR(YBUF[I]); CX = 0; RETURN 0; Ý /* CURSOR KEYS, THE SAME IN BOTH MODES. 1 IF K WAS ONE OF THEM */ ARROW(K) INT K; Û IF (K == 17) Û IF (CY + 1 < NLINES) Û CY++; CLAMPX(); Ý RETURN 1; Ý IF (K == 145) Û IF (CY > 0) Û CY--; CLAMPX(); Ý RETURN 1; Ý IF (K == 29) Û CX++; CLAMPX(); RETURN 1; Ý IF (K == 157) Û IF (CX > 0) CX--; RETURN 1; Ý RETURN 0; Ý /* WRITE THE BUFFER TO FNAME, REPLACING ANY OLD COPY. 1 ON SUCCESS */ SAVEF() Û CHAR ON[32]; INT I, F; UNSIGNED J; ON[0] = 64; /* @ */ ON[1] = 48; /* 0 */ ON[2] = 58; /* : */ I = 3; J = 0; WHILE (FNAME[J] && J < 20) ON[I++] = FNAME[J++]; ON[I++] = 44; /* , */ ON[I++] = 83; /* Ó (PETSCII, NOT ASCII) */ ON[I++] = 44; /* , */ ON[I++] = 87; /* × */ ON[I] = 0; F = OPEN(5, 8, 3, ON); IF (F == 0) RETURN 0; FOR (J = 0; J < BLEN; J++) PUTC(BFR[J], 5); IF (BLEN > 0) Û IF (BFR[BLEN - 1] != 13) PUTC(13, 5); Ý CLOSE(5); RETURN 1; Ý \ No newline at end of file diff --git a/vim/vim.c b/vim/vim.c index 689f413..130e00a 100644 --- a/vim/vim.c +++ b/vim/vim.c @@ -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(); diff --git a/vim/vim.pet b/vim/vim.pet index c957569..ad00526 100644 --- a/vim/vim.pet +++ b/vim/vim.pet @@ -1 +1 @@ -/* VIM.C - A SMALL MODAL EDITOR FOR THE COMMODORE 64, WRITTEN FOR POWER C. * * ÉNCREMENT 2 EDITS: IT READS A PETSCII ÓÅÑ FILE INTO ONE BUFFER, KEEPS A LINE * INDEX, SHOWS 24 LINES PLUS A STATUS ROW, AND SUPPORTS * MOVE H J K L / ARROWS, 0 $ GG Ç * INSERT I A Á É O Ï (ÒÕÎ/ÓÔÏÐ LEAVES INSERT MODE, ÄÅÌ BACKSPACES) * CHANGE X DD P * COLON :W :W NAME :WQ :X :Q :Q! * * ÔHE BUFFER HOLDS THE FILE EXACTLY AS IT SITS ON DISK: PETSCII BYTES WITH $0D * BETWEEN LINES. ËEYS ARE COMPARED AS NUMBERS, NEVER AS CHARACTER CONSTANTS, * SO NOTHING DEPENDS ON HOW THE COMPILER TREATS LITERALS. (ÕNSHIFTED LETTERS * ARRIVE AS $41-$5A, SO H = 72; SHIFTED ONES AS $C1-$DA, SO Ç = 199.) */ CHAR *MALLOC(); CHAR *BFR; /* FILE TEXT, $0D SEPARATED */ UNSIGNED BLEN; /* BYTES USED */ UNSIGNED LPTR[600]; /* OFFSET OF EACH LINE START */ INT NLINES; INT CY, CX; /* CURSOR LINE AND COLUMN */ INT TOP; /* FIRST LINE ON SCREEN */ INT LEFTCOL; /* HORIZONTAL SCROLL */ CHAR FNAME[24]; INT MODE; /* 0 NORMAL, 1 INSERT */ INT DIRTY; /* BUFFER CHANGED SINCE THE LAST SAVE */ 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; /* SCREEN ROWS ARE 0..23, THE STATUS ROW IS 24 */ /* SHORTEN FNAME TO WHAT FITS */ SHORTN(N) INT N; Û INT I; I = 0; WHILE (FNAME[I] && I < N) I++; FNAME[I] = 0; RETURN 0; Ý /* A MESSAGE THAT WAITS FOR A KEY */ MSG(S) CHAR *S; Û SCLR(24, 0, 40); SPS(24, 0, S); SKEY(); RETURN 0; Ý /* READ A LINE OF LOWERCASE PETSCII INTO S, ECHOED FROM COLUMN C OF ROW 24 */ GETLN(C, S, MAX) INT C, MAX; CHAR *S; Û INT N, K; N = 0; S[0] = 0; FOR (;;) Û K = SKEY(); IF (K == 13) BREAK; IF (K == 3) Û N = 0; S[0] = 0; BREAK; Ý IF (K == 20) Û IF (N > 0) Û N--; S[N] = 0; Ý Ý ELSE Û IF (K >= 0XC1 && K <= 0XDA) K = K - 0X80; IF (K >= 32 && K != 127 && N < MAX) Û S[N] = K; N++; S[N] = 0; Ý Ý SCLR(24, C, MAX); SPS(24, C, S); Ý RETURN N; Ý /* ASK WHICH FILE TO OPEN */ ASKNAME() Û SCLR(24, 0, 40); SPS(24, 0, "FILE: "); IF (GETLN(6, FNAME, 20) == 0) FNAME[0] = 0; RETURN 0; Ý /* READ FNAME INTO BFR. 1 ON SUCCESS, 0 IF IT COULD NOT BE OPENED */ LOADF() Û INT F, CH, PREV, C; CHAR ON[28]; INT I; IF (FNAME[0] == 0) RETURN 1; I = 0; WHILE (FNAME[I] && I < 20) Û ON[I] = FNAME[I]; I++; Ý ON[I++] = 44; /* , */ ON[I++] = 83; /* Ó (PETSCII, NOT ASCII) */ ON[I++] = 44; /* , */ ON[I++] = 82; /* Ò */ ON[I] = 0; F = OPEN(5, 8, 2, ON); IF (F == 0) RETURN 0; BLEN = 0; PREV = 0; FOR (;;) Û CH = GETC(5); IF (CH < 0) BREAK; /* END OF FILE */ IF (CH == 26) BREAK; IF (CH == 10 && PREV == 13) Û /* CRLF SEEN AS TWO BYTES */ PREV = CH; CONTINUE; Ý IF (CH == 13) C = 13; ELSE IF (CH == 10) C = 13; ELSE C = CH; IF (BLEN < 18999) BFR[BLEN++] = C; PREV = CH; Ý CLOSE(5); RETURN 1; Ý /* FIND THE START OF EVERY LINE */ BUILDIDX() Û UNSIGNED I; NLINES = 1; LPTR[0] = 0; FOR (I = 0; I < BLEN; I++) Û IF (BFR[I] == 13) Û IF (NLINES < 599) Û LPTR[NLINES] = I + 1; NLINES++; Ý Ý Ý RETURN 0; Ý /* THE ":" COMMAND LINE. RETURNS 1 WHEN THE EDITOR SHOULD QUIT */ DOSCMD() Û INT N, P, OK, QF; CHAR CMD[24]; SCLR(24, 0, 40); SPC(24, 0, 58); /* : */ N = GETLN(1, CMD, 20); SCLR(24, 0, 40); IF (N == 0) RETURN 0; IF (CMD[0] == 81) Û /* Q, Q! */ IF (CMD[1] == 33) RETURN 1; IF (DIRTY) Û NOTE = "UNSAVED - :Q! QUITS"; RETURN 0; Ý RETURN 1; Ý OK = 0; QF = 0; P = 1; IF (CMD[0] == 87) OK = 1; /* W, WQ, W NAME */ IF (CMD[0] == 88) Û /* X */ OK = 1; QF = 1; Ý IF (OK == 0) Û NOTE = "UNKNOWN COMMAND"; RETURN 0; Ý IF (CMD[0] == 87 && CMD[1] == 81) Û QF = 1; P = 2; Ý WHILE (CMD[P] == 32) P++; IF (CMD[P]) Û N = 0; WHILE (CMD[P] && N < 20) FNAME[N++] = CMD[P++]; FNAME[N] = 0; Ý IF (FNAME[0] == 0) Û NOTE = "NO FILE NAME"; RETURN 0; Ý IF (SAVEF()) Û DIRTY = 0; NOTE = "WRITTEN"; RETURN QF; Ý NOTE = "CANNOT WRITE"; RETURN 0; Ý MAIN() Û INT K, OK, P; SINIT(); MLINIT(); /* MACHINE CODE ROW PAINTER */ BFR = MALLOC(19000); IF (BFR == 0) Û SPS(0, 0, "OUT OF MEMORY"); SKEY(); RETURN; Ý BLEN = 0; NLINES = 1; LPTR[0] = 0; CY = 0; CX = 0; TOP = 0; LEFTCOL = 0; FNAME[0] = 0; MODE = 0; DIRTY = 0; PEND = 0; NOTE = 0; YLEN = 0; YOK = 0; ASKNAME(); OK = LOADF(); SHORTN(20); BUILDIDX(); IF (OK == 0) Û /* KEEP THE NAME: :W CREATES IT */ NLINES = 1; BLEN = 0; NOTE = "NEW FILE"; Ý FOR (;;) Û SCROLL(); DALL(); K = SKEY(); NOTE = 0; IF (MODE) Û /* ---- INSERT MODE */ IF (K == 3) Û /* ÒÕÎ/ÓÔÏÐ = ÅSC */ MODE = 0; IF (CX > 0) CX--; Ý ELSE IF (K == 13) Û NEWLINE(); Ý ELSE IF (K == 20) Û BKSPACE(); Ý ELSE IF (ARROW(K)) Û CX = CX; Ý ELSE IF (K >= 32 && K < 128) Û INSCHAR(K); Ý ELSE IF (K >= 161) Û INSCHAR(K); Ý CONTINUE; Ý IF (PEND) Û /* ---- SECOND KEY OF DD / GG */ P = PEND; PEND = 0; IF (P == 68 && K == 68) DELLINE(); IF (P == 71 && K == 71) Û CY = 0; CX = 0; Ý CLAMPX(); CONTINUE; Ý IF (ARROW(K)) Û CX = CX; Ý ELSE IF (K == 72) Û /* H */ IF (CX > 0) CX--; Ý ELSE IF (K == 76) Û /* L */ CX++; CLAMPX(); Ý ELSE IF (K == 74) Û /* J */ ARROW(17); Ý ELSE IF (K == 75) Û /* K */ ARROW(145); Ý ELSE IF (K == 48) Û /* 0 */ CX = 0; Ý ELSE IF (K == 36) Û /* $ */ CX = LINEND(CY) - LPTR[CY]; Ý ELSE IF (K == 199) Û /* Ç */ CY = NLINES - 1; CX = 0; Ý ELSE IF (K == 68) Û /* D.. */ PEND = 68; Ý ELSE IF (K == 71) Û /* G.. */ PEND = 71; Ý ELSE IF (K == 88) Û /* X */ DELCHAR(); CLAMPX(); Ý ELSE IF (K == 80) Û /* P */ PASTE(); Ý ELSE IF (K == 73) Û /* I */ MODE = 1; Ý ELSE IF (K == 65) Û /* A */ CX++; CLAMPX(); MODE = 1; Ý ELSE IF (K == 193) Û /* Á */ CX = LINEND(CY) - LPTR[CY]; MODE = 1; Ý ELSE IF (K == 201) Û /* É */ CX = 0; MODE = 1; Ý ELSE IF (K == 79) Û /* O */ CX = LINEND(CY) - LPTR[CY]; NEWLINE(); MODE = 1; Ý ELSE IF (K == 207) Û /* Ï */ CX = 0; NEWLINE(); CY--; MODE = 1; Ý ELSE IF (K == 58) Û /* : */ IF (DOSCMD() == 1) Û SDONE(); RETURN; Ý Ý Ý Ý \ No newline at end of file +/* VIM.C - A SMALL MODAL EDITOR FOR THE COMMODORE 64, WRITTEN FOR POWER C. * * ÉNCREMENT 2 EDITS: IT READS A PETSCII ÓÅÑ FILE INTO ONE BUFFER, KEEPS A LINE * INDEX, SHOWS 24 LINES PLUS A STATUS ROW, AND SUPPORTS * MOVE H J K L / ARROWS, 0 $ GG Ç * INSERT I A Á É O Ï (ÒÕÎ/ÓÔÏÐ LEAVES INSERT MODE, ÄÅÌ BACKSPACES) * CHANGE X DD P * COLON :W :W NAME :WQ :X :Q :Q! * * ÔHE BUFFER HOLDS THE FILE EXACTLY AS IT SITS ON DISK: PETSCII BYTES WITH $0D * BETWEEN LINES. ËEYS ARE COMPARED AS NUMBERS, NEVER AS CHARACTER CONSTANTS, * SO NOTHING DEPENDS ON HOW THE COMPILER TREATS LITERALS. (ÕNSHIFTED LETTERS * ARRIVE AS $41-$5A, SO H = 72; SHIFTED ONES AS $C1-$DA, SO Ç = 199.) */ CHAR *MALLOC(); CHAR *BFR; /* FILE TEXT, $0D SEPARATED */ UNSIGNED BLEN; /* BYTES USED */ UNSIGNED LPTR[600]; /* OFFSET OF EACH LINE START */ INT NLINES; INT CY, CX; /* CURSOR LINE AND COLUMN */ INT TOP; /* FIRST LINE ON SCREEN */ INT LEFTCOL; /* HORIZONTAL SCROLL */ CHAR FNAME[24]; INT MODE; /* 0 NORMAL, 1 INSERT */ INT DIRTY; /* BUFFER CHANGED SINCE THE LAST SAVE */ 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 */ /* SHORTEN FNAME TO WHAT FITS */ SHORTN(N) INT N; Û INT I; I = 0; WHILE (FNAME[I] && I < N) I++; FNAME[I] = 0; RETURN 0; Ý /* A MESSAGE THAT WAITS FOR A KEY */ MSG(S) CHAR *S; Û SCLR(24, 0, 40); SPS(24, 0, S); SKEY(); RETURN 0; Ý /* READ A LINE OF LOWERCASE PETSCII INTO S, ECHOED FROM COLUMN C OF ROW 24 */ GETLN(C, S, MAX) INT C, MAX; CHAR *S; Û INT N, K; N = 0; S[0] = 0; FOR (;;) Û K = SKEY(); IF (K == 13) BREAK; IF (K == 3) Û N = 0; S[0] = 0; BREAK; Ý IF (K == 20) Û IF (N > 0) Û N--; S[N] = 0; Ý Ý ELSE Û IF (K >= 0XC1 && K <= 0XDA) K = K - 0X80; IF (K >= 32 && K != 127 && N < MAX) Û S[N] = K; N++; S[N] = 0; Ý Ý SCLR(24, C, MAX); SPS(24, C, 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; /* Ù */ IF (K == 78) RETURN 0; /* N */ IF (K == 206) RETURN 0; /* Î */ IF (K == 3) RETURN 0; Ý Ý /* ASK WHICH FILE TO OPEN */ ASKNAME() Û SCLR(24, 0, 40); SPS(24, 0, "FILE: "); IF (GETLN(6, FNAME, 20) == 0) FNAME[0] = 0; RETURN 0; Ý /* 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, COL; CHAR ON[28]; INT I; IF (FNAME[0] == 0) RETURN 1; I = 0; WHILE (FNAME[I] && I < 20) Û ON[I] = FNAME[I]; I++; Ý ON[I++] = 44; /* , */ ON[I++] = 83; /* Ó (PETSCII, NOT ASCII) */ ON[I++] = 44; /* , */ ON[I++] = 82; /* Ò */ ON[I] = 0; IF (OPEN(15, 8, 15, "") == 0) RETURN 0; /* STATUS CHANNEL FIRST */ F = OPEN(5, 8, 2, ON); 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 */ IF (CH == 26) BREAK; IF (CH == 10 && PREV == 13) Û /* CRLF SEEN AS TWO BYTES */ PREV = CH; CONTINUE; Ý 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; Ý /* FIND THE START OF EVERY LINE */ BUILDIDX() Û UNSIGNED I; NLINES = 1; LPTR[0] = 0; FOR (I = 0; I < BLEN; I++) Û IF (BFR[I] == 13) Û IF (NLINES < 599) Û LPTR[NLINES] = I + 1; NLINES++; Ý Ý Ý RETURN 0; Ý /* THE ":" COMMAND LINE. RETURNS 1 WHEN THE EDITOR SHOULD QUIT */ DOSCMD() Û INT N, P, OK, QF; CHAR CMD[24]; SCLR(24, 0, 40); SPC(24, 0, 58); /* : */ N = GETLN(1, CMD, 20); SCLR(24, 0, 40); IF (N == 0) RETURN 0; IF (CMD[0] == 81) Û /* Q, Q! */ IF (CMD[1] == 33) RETURN 1; IF (DIRTY) Û NOTE = "UNSAVED - :Q! QUITS"; RETURN 0; Ý RETURN 1; Ý OK = 0; QF = 0; P = 1; IF (CMD[0] == 87) OK = 1; /* W, WQ, W NAME */ IF (CMD[0] == 88) Û /* X */ OK = 1; QF = 1; Ý IF (OK == 0) Û NOTE = "UNKNOWN COMMAND"; RETURN 0; Ý IF (CMD[0] == 87 && CMD[1] == 81) Û QF = 1; P = 2; Ý WHILE (CMD[P] == 32) P++; IF (CMD[P]) Û N = 0; WHILE (CMD[P] && N < 20) FNAME[N++] = CMD[P++]; FNAME[N] = 0; Ý IF (FNAME[0] == 0) Û NOTE = "NO FILE NAME"; RETURN 0; Ý IF (SAVEF()) Û DIRTY = 0; NOTE = "WRITTEN"; RETURN QF; Ý NOTE = "CANNOT WRITE"; RETURN 0; Ý MAIN() Û INT K, OK, P; SINIT(); MLINIT(); /* ÍÌ HELPERS TO $C000: BEFORE MALLOC */ BFR = MALLOC(19000); IF (BFR == 0) Û SPS(0, 0, "OUT OF MEMORY"); SKEY(); RETURN; Ý BLEN = 0; NLINES = 1; LPTR[0] = 0; CY = 0; CX = 0; TOP = 0; LEFTCOL = 0; FNAME[0] = 0; MODE = 0; DIRTY = 0; PEND = 0; NOTE = 0; YLEN = 0; YOK = 0; SPLIT = 0; REDRAW = 2; 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(); DALL(); K = SKEY(); NOTE = 0; IF (MODE) Û /* ---- INSERT MODE */ IF (K == 3) Û /* ÒÕÎ/ÓÔÏÐ = ÅSC */ MODE = 0; IF (CX > 0) CX--; Ý ELSE IF (K == 13) Û NEWLINE(); Ý ELSE IF (K == 20) Û BKSPACE(); Ý ELSE IF (ARROW(K)) Û CX = CX; Ý ELSE IF (K >= 32 && K < 128) Û INSCHAR(K); Ý ELSE IF (K >= 161) Û INSCHAR(K); Ý CONTINUE; Ý IF (PEND) Û /* ---- SECOND KEY OF DD / GG */ P = PEND; PEND = 0; IF (P == 68 && K == 68) DELLINE(); IF (P == 71 && K == 71) Û CY = 0; CX = 0; Ý CLAMPX(); CONTINUE; Ý IF (ARROW(K)) Û CX = CX; Ý ELSE IF (K == 72) Û /* H */ IF (CX > 0) CX--; Ý ELSE IF (K == 76) Û /* L */ CX++; CLAMPX(); Ý ELSE IF (K == 74) Û /* J */ ARROW(17); Ý ELSE IF (K == 75) Û /* K */ ARROW(145); Ý ELSE IF (K == 48) Û /* 0 */ CX = 0; Ý ELSE IF (K == 36) Û /* $ */ CX = LINEND(CY) - LPTR[CY]; Ý ELSE IF (K == 199) Û /* Ç */ CY = NLINES - 1; CX = 0; Ý ELSE IF (K == 68) Û /* D.. */ PEND = 68; Ý ELSE IF (K == 71) Û /* G.. */ PEND = 71; Ý ELSE IF (K == 88) Û /* X */ DELCHAR(); CLAMPX(); Ý ELSE IF (K == 80) Û /* P */ PASTE(); Ý ELSE IF (K == 73) Û /* I */ MODE = 1; Ý ELSE IF (K == 65) Û /* A */ CX++; CLAMPX(); MODE = 1; Ý ELSE IF (K == 193) Û /* Á */ CX = LINEND(CY) - LPTR[CY]; MODE = 1; Ý ELSE IF (K == 201) Û /* É */ CX = 0; MODE = 1; Ý ELSE IF (K == 79) Û /* O */ CX = LINEND(CY) - LPTR[CY]; NEWLINE(); MODE = 1; Ý ELSE IF (K == 207) Û /* Ï */ CX = 0; NEWLINE(); CY--; MODE = 1; Ý ELSE IF (K == 58) Û /* : */ IF (DOSCMD() == 1) Û SDONE(); RETURN; Ý Ý Ý Ý \ No newline at end of file diff --git a/vim/vml.bin b/vim/vml.bin new file mode 100644 index 0000000..f24a598 Binary files /dev/null and b/vim/vml.bin differ diff --git a/vim/vml.c b/vim/vml.c index 67d61ac..c0aea0c 100644 --- a/vim/vml.c +++ b/vim/vml.c @@ -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); +} + diff --git a/vim/vml.pet b/vim/vml.pet index ef693a7..5e573ab 100644 --- a/vim/vml.pet +++ b/vim/vml.pet @@ -1 +1 @@ -/* VML.C - ÇÅÎÅÒÁÔÅÄ BY BUILD-ML.PS1 FROM VROW.S. ÄO NOT EDIT BY HAND. * * ÔHE MACHINE CODE ROW PAINTER, KEPT AS A BYTE ARRAY AND CALLED IN PLACE * THROUGH SYS(). ÉT IS POSITION INDEPENDENT; ITS PARAMETERS GO THROUGH ZERO * PAGE SCRATCH $22-$28, WHICH ÐOWER à LEAVES TO ÍÌ ROUTINES. */ 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 Ý; /* 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; Ý \ No newline at end of file +/* VML.C - ÇÅÎÅÒÁÔÅÄ BY BUILD-ML.PS1 FROM VML.S AND VMLWRAP.C. ÄO NOT EDIT. * * ÔHE MACHINE CODE HELPERS AS A BYTE ARRAY. MLINIT() COPIES THEM TO $Ã000. */ #DEFINE ÍÌÂÍÏÖÅ 0XC161 #DEFINE ÍÌËÐÏÐ 0XC237 #DEFINE ÍÌÌÁÄÊ 0XC1E3 #DEFINE ÍÌÒÏ× 0XC102 #DEFINE ÍÌÓÃÒÎ 0XC000 #DEFINE ÍÌÓÉÚÅ 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 Ý; /* VMLWRAP.C - THE à SIDE OF THE MACHINE CODE HELPERS. BUILD-ML.PS1 APPENDS * THIS TO THE GENERATED BYTE ARRAY TO MAKE VML.C; EDIT IT HERE. * * ÐARAMETERS GO THROUGH A BLOCK AT $C400 (NOT $CF00: THE à STACK LIVES JUST BELOW $D000), THEN SYS() JUMPS TO THE ENTRY. * ÏN 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 < ÍÌÓÉÚÅ; I++) D[I] = MLCODE[I]; RETURN 0; Ý /* CALL AN ENTRY, RETURN WHAT IT LEFT IN Á */ MLGO(ADDR) UNSIGNED ADDR; Û CHAR A, X, Y; A = 0; X = 0; Y = 0; SYS(ADDR, &A, &X, &Y); RETURN A & 255; Ý /* ÔHE 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. ×ORDS 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(ÍÌÒÏ×); 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(ÍÌÓÃÒÎ); 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(ÍÌÂÍÏÖÅ); 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(ÍÌÌÁÄÊ); RETURN 0; Ý /* NEXT KEY FROM THE KERNAL BUFFER, OR 0 */ MLKEY() Û RETURN MLGO(ÍÌËÐÏÐ); Ý \ No newline at end of file diff --git a/vim/vml.s b/vim/vml.s new file mode 100644 index 0000000..57ff091 --- /dev/null +++ b/vim/vml.s @@ -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 diff --git a/vim/vmlwrap.c b/vim/vmlwrap.c new file mode 100644 index 0000000..6d0d3db --- /dev/null +++ b/vim/vmlwrap.c @@ -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); +} diff --git a/vim/vrow.bin b/vim/vrow.bin deleted file mode 100644 index 491ff51..0000000 Binary files a/vim/vrow.bin and /dev/null differ diff --git a/vim/vrow.s b/vim/vrow.s deleted file mode 100644 index 810e49f..0000000 --- a/vim/vrow.s +++ /dev/null @@ -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 diff --git a/vim/vscreen.c b/vim/vscreen.c index 6ce3c9d..bd85796 100644 --- a/vim/vscreen.c +++ b/vim/vscreen.c @@ -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 */ diff --git a/vim/vscreen.pet b/vim/vscreen.pet index ac9d505..88977ed 100644 --- a/vim/vscreen.pet +++ b/vim/vscreen.pet @@ -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; Ý Ý \ No newline at end of file +/* 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; Ý Ý \ No newline at end of file diff --git a/vim/vview.c b/vim/vview.c index 1d10f38..eb03d12 100644 --- a/vim/vview.c +++ b/vim/vview.c @@ -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 */ diff --git a/vim/vview.pet b/vim/vview.pet index 2df3f6f..83c4ff6 100644 --- a/vim/vview.pet +++ b/vim/vview.pet @@ -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 */ \ No newline at end of file +/* 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 */ \ No newline at end of file diff --git a/vim/work.d81 b/vim/work.d81 index 54aa2f5..e5d4403 100644 Binary files a/vim/work.d81 and b/vim/work.d81 differ diff --git a/vim/work.stamp b/vim/work.stamp index 314456f..ec2b301 100644 --- a/vim/work.stamp +++ b/vim/work.stamp @@ -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