Преглед изворни кода

vim: paint and edit in machine code, redraw only changed rows

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
Daniel Covington пре 1 недеља
родитељ
комит
6e6c16aa23
20 измењених фајлова са 993 додато и 229 уклоњено
  1. +39
    -1
      vim/NOTES.md
  2. +39
    -50
      vim/build-ml.ps1
  3. BIN
      vim/shots/run.png
  4. +32
    -22
      vim/vedit.c
  5. +1
    -1
      vim/vedit.pet
  6. +85
    -7
      vim/vim.c
  7. +1
    -1
      vim/vim.pet
  8. BIN
      vim/vml.bin
  9. +182
    -23
      vim/vml.c
  10. +1
    -1
      vim/vml.pet
  11. +439
    -0
      vim/vml.s
  12. +134
    -0
      vim/vmlwrap.c
  13. BIN
      vim/vrow.bin
  14. +0
    -69
      vim/vrow.s
  15. +7
    -16
      vim/vscreen.c
  16. +1
    -1
      vim/vscreen.pet
  17. +26
    -31
      vim/vview.c
  18. +1
    -1
      vim/vview.pet
  19. BIN
      vim/work.d81
  20. +5
    -5
      vim/work.stamp

+ 39
- 1
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<draw> e<op>` readout
on the status row (since removed):

| | before | after |
|---|---|---|
| `j` (repaint) | 17 jiffies | 0-3 |
| insert a char near the top of a 9 KB file | 103 jiffies | 14 |

## C64 screen facts the editor relies on

- Screen matrix `$0400`, colour RAM `$D800` (both directly writable).
@@ -126,7 +160,11 @@ run
`$60-$7F` minus `$20`, `$A0-$BF` minus `$40`, `$C0-$FF` minus `$80`.
- Reverse video is the high bit: screen code `+ $80`. That is the cursor block
and the status row.
- `$00CC = 0` stops the KERNAL cursor blinking over the editor's own cursor.
- `$00CC` non-zero stops the KERNAL cursor blinking (0 turns it **on**); with
it left at 0 the KERNAL blinked one cell of the status row.
- **Closing the command channel (15) closes every file on the drive.** Open 15
first, `open()` the file, read the status from 15 (a missing file still
opens - only the status says `62`), read the file, close it, close 15 last.
- Arrow keys are PETSCII `$11` down, `$91` up, `$1D` right, `$9D` left;
RETURN `$0D`, DEL `$14`.



+ 39
- 50
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 '; '))

BIN
vim/shots/run.png Прегледај датотеку

Before After
Width: 384  |  Height: 272  |  Size: 1.6KB Width: 384  |  Height: 272  |  Size: 1.7KB

+ 32
- 22
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;
}



+ 1
- 1
vim/vedit.pet
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 85
- 7
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();


+ 1
- 1
vim/vim.pet
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку



+ 182
- 23
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);
}


+ 1
- 1
vim/vml.pet
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 439
- 0
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

+ 134
- 0
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);
}


+ 0
- 69
vim/vrow.s Прегледај датотеку

@@ -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

+ 7
- 16
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 */


+ 1
- 1
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; Ý Ý
/* 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; Ý Ý

+ 26
- 31
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 */

+ 1
- 1
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 */
/* 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 */


+ 5
- 5
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

Loading…
Откажи
Сачувај

Powered by TurnKey Linux.