Power C on the C64 calls ML as sys(addr, &a, &x, &y); the bank argument from the manual's C128 library page made sys() jump to $000F and crash. The ML row painter now runs. Quitting resets through $FCE2 because Power C owns BASIC's zero-page pointers. Notes and Power C skill updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>main
| @@ -193,10 +193,12 @@ Link `stdlib.l` and `syslib.l` via `^`; `math.l` by name. | |||
| - Zero page `20-33`, `43-74`, `253-254` (decimal) are reserved by the system; | |||
| `34-42`, `75-96`, `251-252` are scratch and safe for your ML routines. | |||
| - The tape buffer `$033C-$03FB` is used by the system; do not touch it. | |||
| - Call machine code with `sys(bank, address, &a, &x, &y)` (the manual's example | |||
| uses bank 15); A/X/Y are loaded from the pointers and written back afterwards; | |||
| it returns the carry flag. Example: | |||
| `while (*s++) sys(15, 0xffd2, s, &x, &y);` prints via KERNAL CHROUT. | |||
| - Call machine code on the **C64** with `sys(address, &a, &x, &y)` -- no bank | |||
| argument. The manual's library page shows the **C128** form | |||
| `sys(bank, address, ...)` (bank 15); used on a C64 it jumps to address 15 and | |||
| warm-starts the machine (verified in VICE). A/X/Y are loaded from the | |||
| pointers and written back afterwards; it returns the carry flag. Example: | |||
| `char a, x, y; a = 147; sys(0xffd2, &a, &x, &y);` clears the screen via CHROUT. | |||
| - Pass multiple args to an ML routine by declaring one `float args;` parameter | |||
| so they stay contiguous, and hand the routine the address of that block in X/Y. | |||
| @@ -89,11 +89,18 @@ run | |||
| ## Calling machine language: sys() and the keyboard | |||
| - `sys(bank, address, &a, &x, &y)` is documented as | |||
| `int bank; unsigned address; char *aptr, *xptr, *yptr;` with `bank` from 0 to | |||
| 15 (BASIC 7 style). **Calling KERNAL GETIN through it crashed the program**; | |||
| the crash warm-starts the C64, which clears the screen, so the only evidence | |||
| was a blank blue screen and `READY.`. | |||
| - **On the C64, `sys` takes no bank: `sys(address, &a, &x, &y)`.** The | |||
| library reference page documents the C128 form `sys(bank, address, ...)` | |||
| (with "bank 15" in its example); only the C64 tutorial section ("add3" at | |||
| `$C000`) shows the four-argument call. Passing the bank makes `15` the jump | |||
| address, so the CPU runs zero page and the C64 warm-starts: blank screen and | |||
| `READY.`, no other evidence. That was the real cause of the earlier | |||
| "GETIN through sys() crashes" and of the first ML row-painter crash. | |||
| - **Do not return from `main()` to BASIC.** Power C reserves zero page 43-74 | |||
| (`$2B-$4A`), which is BASIC's program/variable pointer area, so after the | |||
| program ends BASIC runs on garbage: no `READY.`, or a mangled message such | |||
| as `UNCTIO?VER`. The editor quits with `sys(0xfce2, &a, &x, &y)` (KERNAL | |||
| reset) instead, which comes up with the banner and `READY.`. | |||
| - The library's **`getchar()` is line buffered**: it echoes through the KERNAL's | |||
| screen editor and only hands the characters over after RETURN. Useless for an | |||
| editor. | |||
| @@ -72,7 +72,7 @@ int len, cur, rev; | |||
| a = 0; | |||
| x = 0; | |||
| y = 0; | |||
| sys(15, (unsigned) mlcode, &a, &x, &y); | |||
| sys((unsigned) mlcode, &a, &x, &y); /* c64 form: no bank argument */ | |||
| return 0; | |||
| } | |||
| "@ | Set-Content (Join-Path $here "vml.c") -Encoding utf8 | |||
| @@ -18,7 +18,7 @@ main() | |||
| y = 0; | |||
| p[1] = 2; /* B: about to sys() a bare RTS */ | |||
| for (;;) { } | |||
| sys(15, (unsigned) rts, &a, &x, &y); | |||
| sys((unsigned) rts, &a, &x, &y); | |||
| p[2] = 3; /* C: sys + array address work */ | |||
| txt[0] = 72; | |||
| @@ -41,6 +41,6 @@ int len, cur, rev; | |||
| a = 0; | |||
| x = 0; | |||
| y = 0; | |||
| sys(15, (unsigned) mlcode, &a, &x, &y); | |||
| sys((unsigned) mlcode, &a, &x, &y); /* c64 form: no bank argument */ | |||
| return 0; | |||
| } | |||
| @@ -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(15, (UNSIGNED) MLCODE, &A, &X, &Y); RETURN 0; Ý | |||
| /* 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; Ý | |||
| @@ -107,8 +107,8 @@ sinit() | |||
| /* give the machine back the way basic expects it */ | |||
| sdone() | |||
| { | |||
| int i; | |||
| char *p; | |||
| char a, x, y; | |||
| p = (char *) 0xd018; | |||
| *p = 0x15; | |||
| @@ -116,10 +116,15 @@ sdone() | |||
| *p = 14; | |||
| p = (char *) 0xd021; | |||
| *p = 6; | |||
| for (i = 0; i < 1000; i++) { | |||
| scr[i] = 32; | |||
| colr[i] = 1; | |||
| } | |||
| /* 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 READY. restart the | |||
| machine through the kernal reset instead - banner and READY, 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; | |||
| } | |||
| @@ -130,8 +135,8 @@ sdone() | |||
| * 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. | |||
| * | |||
| * Two obvious alternatives do not work: calling the kernal's GETIN through | |||
| * sys() crashed the program, and the library's getchar() is line buffered - it | |||
| * (GETIN through sys() once "crashed" - but that was sys() called with the | |||
| * c128 bank argument; on the c64 it is sys(addr, &a, &x, &y).) And the library's getchar() is line buffered - it | |||
| * echoes through the screen editor and only hands characters over after RETURN. | |||
| */ | |||
| @@ -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() Û INT I; CHAR *P; P = (CHAR *) 0XD018; *P = 0X15; P = (CHAR *) 0XD020; *P = 14; P = (CHAR *) 0XD021; *P = 6; FOR (I = 0; I < 1000; I++) Û SCR[I] = 32; COLR[I] = 1; Ý 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. * * ÔWO OBVIOUS ALTERNATIVES DO NOT WORK: CALLING THE KERNAL'S ÇÅÔÉÎ THROUGH * SYS() CRASHED THE PROGRAM, AND 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; *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; Ý Ý | |||
| @@ -1,6 +1,6 @@ | |||
| vim=8E13FCED4A0CAFD92855B9281D268368 | |||
| vscreen=FFCDAD177DF3AD43CE2809EA7ACD6029 | |||
| vscreen=7773151D0C575F00E919F19875CB498F | |||
| vview=F3C7D092B8080C956D9EBD7B95800CD5 | |||
| mltest=E4BDE10600DE08AF766FFB32A6D9A3E5 | |||
| vedit=8BE6C95F2BDA01EC535A8C51CD666881 | |||
| vml=D600B54B6821800D24B2986D920E9B12 | |||
| vml=B9A7F164619D4675A2E78B48F02C179D | |||
Powered by TurnKey Linux.