From 7d7cf2d1e0f13dc095c4b085633772b2fab08eb1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Sun, 28 Jan 2024 21:33:12 +0100 Subject: [PATCH 01/23] Implement aslax7/shlax7/asrax7/shrax7 --- libsrc/runtime/aslax7.s | 21 +++++++++++++++++ libsrc/runtime/asrax7.s | 18 ++++++++++++++ libsrc/runtime/shrax7.s | 18 ++++++++++++++ src/cc65/codegen.c | 16 +++++++++++++ src/cc65/codeinfo.c | 4 ++++ test/val/lib_runtime_aslax7.c | 44 +++++++++++++++++++++++++++++++++++ test/val/lib_runtime_asrax7.c | 44 +++++++++++++++++++++++++++++++++++ test/val/lib_runtime_shlax7.c | 44 +++++++++++++++++++++++++++++++++++ test/val/lib_runtime_shrax7.c | 44 +++++++++++++++++++++++++++++++++++ 9 files changed, 253 insertions(+) create mode 100644 libsrc/runtime/aslax7.s create mode 100644 libsrc/runtime/asrax7.s create mode 100644 libsrc/runtime/shrax7.s create mode 100644 test/val/lib_runtime_aslax7.c create mode 100644 test/val/lib_runtime_asrax7.c create mode 100644 test/val/lib_runtime_shlax7.c create mode 100644 test/val/lib_runtime_shrax7.c diff --git a/libsrc/runtime/aslax7.s b/libsrc/runtime/aslax7.s new file mode 100644 index 000000000..533ee55e6 --- /dev/null +++ b/libsrc/runtime/aslax7.s @@ -0,0 +1,21 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shlax7, aslax7 + +aslax7: +shlax7: ; XXXXXXXL AAAAAAAl + tay + txa + lsr ; XXXXXXXL -> 0XXXXXXX, L->C + tya + ror ; AAAAAAAl -> LAAAAAAA, l->C + tax + lda #$00 ; LAAAAAAA 00000000 + ror ; LAAAAAAA l0000000 + rts + + ; 10 bytes, 16 cycles + rts diff --git a/libsrc/runtime/asrax7.s b/libsrc/runtime/asrax7.s new file mode 100644 index 000000000..3c9cce681 --- /dev/null +++ b/libsrc/runtime/asrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, signed +; + + .export asrax7 + +asrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + dex ; 11111111 XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/libsrc/runtime/shrax7.s b/libsrc/runtime/shrax7.s new file mode 100644 index 000000000..31712ca72 --- /dev/null +++ b/libsrc/runtime/shrax7.s @@ -0,0 +1,18 @@ +; +; Miloslaw Smyk, 2024 +; +; CC65 runtime: Scale the primary register by 128, unsigned +; + + .export shrax7 + +shrax7: ; HXXXXXXL hAAAAAAl + asl ; AAAAAAA0, h->C + txa + rol ; XXXXXXLh, H->C + ldx #$00 ; 00000000 XXXXXXLh + bcc :+ + inx ; 0000000H XXXXXXLh if C +: rts + + ; 12 cycles max, 9 bytes diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index c2bdfcd63..69dcc1c6c 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -3260,6 +3260,14 @@ void g_asr (unsigned flags, unsigned long val) } val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shrax7"); + } else { + AddCodeLine ("jsr asrax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shrax4"); @@ -3402,6 +3410,14 @@ void g_asl (unsigned flags, unsigned long val) AddCodeLine ("lda #$00"); val -= 8; } + if (val == 7) { + if (flags & CF_UNSIGNED) { + AddCodeLine ("jsr shlax7"); + } else { + AddCodeLine ("jsr aslax7"); + } + val = 0; + } if (val >= 4) { if (flags & CF_UNSIGNED) { AddCodeLine ("jsr shlax4"); diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 88f8a5138..427bfc52b 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -99,6 +99,7 @@ static const FuncInfo FuncInfoTable[] = { { "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "aslax7", REG_AX, PSTATE_ALL | REG_AXY }, { "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -108,6 +109,7 @@ static const FuncInfo FuncInfoTable[] = { { "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "asrax7", REG_AX, PSTATE_ALL | REG_AX }, { "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -245,6 +247,7 @@ static const FuncInfo FuncInfoTable[] = { { "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shlax7", REG_AX, PSTATE_ALL | REG_AXY }, { "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, @@ -254,6 +257,7 @@ static const FuncInfo FuncInfoTable[] = { { "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, { "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 }, + { "shrax7", REG_AX, PSTATE_ALL | REG_AX }, { "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 }, { "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, { "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 }, diff --git a/test/val/lib_runtime_aslax7.c b/test/val/lib_runtime_aslax7.c new file mode 100644 index 000000000..ea8f0b375 --- /dev/null +++ b/test/val/lib_runtime_aslax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for aslax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %d << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != -32768); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %d << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_asrax7.c b/test/val/lib_runtime_asrax7.c new file mode 100644 index 000000000..942efea1d --- /dev/null +++ b/test/val/lib_runtime_asrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for asrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + signed int ai = -32768, ti, refi; + signed char ac = -128, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != -32768); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != -128); + + return 0; +} diff --git a/test/val/lib_runtime_shlax7.c b/test/val/lib_runtime_shlax7.c new file mode 100644 index 000000000..9a4869438 --- /dev/null +++ b/test/val/lib_runtime_shlax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shlax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai << 4; + refi = refi << 3; + + ti = ai << 7; + + if (ti != refi) { + printf("wrong result on int %u << 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != 0); + + do { + refc = ac << 4; + refc = refc << 3; + + tc = ac << 7; + + if (tc != refc) { + printf("wrong result on char %u << 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != 0); + + return 0; +} diff --git a/test/val/lib_runtime_shrax7.c b/test/val/lib_runtime_shrax7.c new file mode 100644 index 000000000..db7356d0e --- /dev/null +++ b/test/val/lib_runtime_shrax7.c @@ -0,0 +1,44 @@ +/* + !!DESCRIPTION!! A small test for shrax7. + !!ORIGIN!! + !!LICENCE!! + !!AUTHOR!! +*/ + +#include +#include +#include +#include +#include + +int main (void) +{ + unsigned int ai = 0, ti, refi; + unsigned char ac = 0, tc, refc; + + do { + refi = ai >> 4; + refi = refi >> 3; + + ti = ai >> 7; + + if (ti != refi) { + printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); + return 1; + } + } while (ai != 0); + + do { + refc = ac >> 4; + refc = refc >> 3; + + tc = ac >> 7; + + if (tc != refc) { + printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); + return 1; + } + } while (ac != 0); + + return 0; +} From 7594af553ae3a189e7a7f9cf2f59c47af51441eb Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Sun, 28 Jan 2024 17:00:02 +0100 Subject: [PATCH 02/23] Fix #2388 Reopen stdin in binary mode instead of closing/opening --- test/ref/test_gets.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/ref/test_gets.c b/test/ref/test_gets.c index ee5b6fd58..003da5569 100644 --- a/test/ref/test_gets.c +++ b/test/ref/test_gets.c @@ -30,9 +30,7 @@ char *argv[]; #endif { /* Fake stdin with the reference file */ - fclose(stdin); - stdin = fopen(INFILE, "r"); - if (stdin == NULL) { + if (freopen(INFILE, "rb", stdin) == NULL) { return EXIT_FAILURE; } From ba3607102263c73af18105902a8e50063618bd96 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Tue, 30 Jan 2024 17:51:00 +0100 Subject: [PATCH 03/23] Rewrite fputs in assembly -28 bytes, -1% cycles --- libsrc/common/fputs.c | 28 ---------------------------- libsrc/common/fputs.s | 42 ++++++++++++++++++++++++++++++++++++++++++ test/ref/test_fputs.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 28 deletions(-) delete mode 100644 libsrc/common/fputs.c create mode 100644 libsrc/common/fputs.s create mode 100644 test/ref/test_fputs.c diff --git a/libsrc/common/fputs.c b/libsrc/common/fputs.c deleted file mode 100644 index be476a3f0..000000000 --- a/libsrc/common/fputs.c +++ /dev/null @@ -1,28 +0,0 @@ -/* -** int fputs (const char* s, FILE* f); -** -** Ullrich von Bassewitz, 11.08.1998 -*/ - - - -#include -#include -#include -#include "_file.h" - - - -int __fastcall__ fputs (const char* s, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - return EOF; - } - - /* Write the string */ - return write (f->f_fd, s, strlen (s)); -} - - - diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s new file mode 100644 index 000000000..e70374058 --- /dev/null +++ b/libsrc/common/fputs.s @@ -0,0 +1,42 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputs (const char* s, register FILE* f) +; + + .export _fputs + .importzp ptr1, ptr2 + .import _write, _strlen + .import swapstk, pushax, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputs: + sta ptr1 + stx ptr1+1 + + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #_FOPEN ; Check for file open + beq ret_eof + txa + and #(_FERROR|_FEOF); Check for error/eof + bne ret_eof + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + ldx #$00 + jsr swapstk ; Push fd, get s + + jsr pushax ; Push s + + jsr _strlen ; Get length + + ; Write + jmp _write + +ret_eof: + jmp returnFFFF diff --git a/test/ref/test_fputs.c b/test/ref/test_fputs.c new file mode 100644 index 000000000..ad0552317 --- /dev/null +++ b/test/ref/test_fputs.c @@ -0,0 +1,40 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +FILE *in, *out; +char buf[512], err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + strcpy(buf, "test"); + if (fputs(buf, in) != EOF) { + printf("Error: can fputs to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while (fgets(buf, 512, in) != NULL) { + fputs(buf, stdout); + } + + fclose(in); + return 0; +} From 1a5a7d67a7ebb4881a90234b596fdee4612b09ef Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Tue, 30 Jan 2024 17:59:33 +0100 Subject: [PATCH 04/23] Rewrite fputc in assembly -36 bytes, -12% cycles --- libsrc/common/fputc.c | 41 ------------------------ libsrc/common/fputc.s | 72 +++++++++++++++++++++++++++++++++++++++++++ libsrc/common/fputs.s | 4 +-- test/ref/test_fputc.c | 39 +++++++++++++++++++++++ 4 files changed, 113 insertions(+), 43 deletions(-) delete mode 100644 libsrc/common/fputc.c create mode 100644 libsrc/common/fputc.s create mode 100644 test/ref/test_fputc.c diff --git a/libsrc/common/fputc.c b/libsrc/common/fputc.c deleted file mode 100644 index b623949d3..000000000 --- a/libsrc/common/fputc.c +++ /dev/null @@ -1,41 +0,0 @@ -/* -** fputc.c -** -** Ullrich von Bassewitz, 02.06.1998 -*/ - - - -#include -#include -#include "_file.h" - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - - -int __fastcall__ fputc (int c, register FILE* f) -{ - /* Check if the file is open or if there is an error condition */ - if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) { - goto ReturnEOF; - } - - /* Write the byte */ - if (write (f->f_fd, &c, 1) != 1) { - /* Error */ - f->f_flags |= _FERROR; -ReturnEOF: - return EOF; - } - - /* Return the byte written */ - return c & 0xFF; -} - - - diff --git a/libsrc/common/fputc.s b/libsrc/common/fputc.s new file mode 100644 index 000000000..4633f24f1 --- /dev/null +++ b/libsrc/common/fputc.s @@ -0,0 +1,72 @@ +; +; Colin Leroy-Mira, 2024 +; +; int __fastcall__ fputc (int c, FILE* f); +; + + .export _fputc + .importzp ptr1 + .import _write + .import pushax, pusha0, popax, incsp2 + .import pushptr1, popptr1, returnFFFF + + .include "stdio.inc" + .include "_file.inc" + +_fputc: + sta ptr1 + stx ptr1+1 + + jsr popax ; Get char, as we'll have + sta c ; to return it anyway + stx c+1 + + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #_FOPEN ; Check for file open + beq ret_eof + txa + and #(_FERROR|_FEOF); Check for error/eof + bne ret_eof + + jsr pushptr1 ; Backup fp pointer + + ; Push _write parameters + ldy #_FILE::f_fd + lda (ptr1),y + jsr pusha0 + + lda #c + jsr pushax + + lda #$01 + ldx #$00 + + ; Write + jsr _write + + ; Check for errors + cmp #$01 + bne set_ferror + + ; Return char + lda c + ldx #$00 + jmp incsp2 ; Drop fp pointer copy + +ret_eof: + jmp returnFFFF + +set_ferror: + jsr popptr1 + lda #_FERROR + ldy #_FILE::f_flags + ora (ptr1),y + sta (ptr1),y + jmp returnFFFF + + .bss + +c: .res 2 diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s index e70374058..00415aef1 100644 --- a/libsrc/common/fputs.s +++ b/libsrc/common/fputs.s @@ -32,9 +32,9 @@ _fputs: jsr swapstk ; Push fd, get s jsr pushax ; Push s - + jsr _strlen ; Get length - + ; Write jmp _write diff --git a/test/ref/test_fputc.c b/test/ref/test_fputc.c new file mode 100644 index 000000000..a19aeafaf --- /dev/null +++ b/test/ref/test_fputc.c @@ -0,0 +1,39 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +FILE *in, *out; +int c, err; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + if (fputc(c, in) != EOF) { + printf("Error: can fputc to a file opened for reading\n"); + return EXIT_FAILURE; + } + clearerr(in); + + while ((c = fgetc(in)) != EOF) { + fputc(c, stdout); + } + + fclose(in); + return 0; +} From 2b97735d5d150f1ce9d568b10fd878fa503ca232 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Tue, 30 Jan 2024 18:11:10 +0100 Subject: [PATCH 05/23] Factorize file pointer check --- libsrc/common/checkferror.s | 24 ++++++++++++++++++++++++ libsrc/common/fgetc.s | 13 ++++--------- libsrc/common/fputc.s | 10 ++-------- libsrc/common/fputs.s | 10 ++-------- 4 files changed, 32 insertions(+), 25 deletions(-) create mode 100644 libsrc/common/checkferror.s diff --git a/libsrc/common/checkferror.s b/libsrc/common/checkferror.s new file mode 100644 index 000000000..736fa3ccd --- /dev/null +++ b/libsrc/common/checkferror.s @@ -0,0 +1,24 @@ +; +; Colin Leroy-Mira, 2024 +; +; Helper to check for file opened, not eof, not ferror +; Expects file pointer in ptr1, +; Returns with Z flag set if everything is OK, +; Destroys A, X, Y, +; Sets file flags in A +; + + .export checkferror + .importzp ptr1 + + .include "_file.inc" + +checkferror: + ldy #_FILE::f_flags + lda (ptr1),y + tax + and #(_FOPEN|_FERROR|_FEOF); Check for file open, error/eof + tay + txa + cpy #_FOPEN + rts diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s index 777696b7a..98e6bb2f7 100644 --- a/libsrc/common/fgetc.s +++ b/libsrc/common/fgetc.s @@ -5,7 +5,8 @@ ; .export _fgetc - .import _read, pusha0, pushax, popptr1, incsp2, returnFFFF + .import _read, checkferror + .import pusha0, pushax, popptr1, incsp2, returnFFFF .importzp ptr1 .include "stdio.inc" @@ -16,16 +17,10 @@ _fgetc: stx ptr1+1 jsr pushax ; Backup our ptr - ldy #_FILE::f_flags - lda (ptr1),y - tax - and #_FOPEN ; Check for file open - beq ret_eof - txa - and #(_FERROR|_FEOF); Check for error/eof + jsr checkferror bne ret_eof - txa + tax and #_FPUSHBACK ; Check for pushed back char beq do_read diff --git a/libsrc/common/fputc.s b/libsrc/common/fputc.s index 4633f24f1..358723538 100644 --- a/libsrc/common/fputc.s +++ b/libsrc/common/fputc.s @@ -6,7 +6,7 @@ .export _fputc .importzp ptr1 - .import _write + .import _write, checkferror .import pushax, pusha0, popax, incsp2 .import pushptr1, popptr1, returnFFFF @@ -21,13 +21,7 @@ _fputc: sta c ; to return it anyway stx c+1 - ldy #_FILE::f_flags - lda (ptr1),y - tax - and #_FOPEN ; Check for file open - beq ret_eof - txa - and #(_FERROR|_FEOF); Check for error/eof + jsr checkferror bne ret_eof jsr pushptr1 ; Backup fp pointer diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s index 00415aef1..b79a4707f 100644 --- a/libsrc/common/fputs.s +++ b/libsrc/common/fputs.s @@ -6,7 +6,7 @@ .export _fputs .importzp ptr1, ptr2 - .import _write, _strlen + .import _write, _strlen, checkferror .import swapstk, pushax, returnFFFF .include "stdio.inc" @@ -16,13 +16,7 @@ _fputs: sta ptr1 stx ptr1+1 - ldy #_FILE::f_flags - lda (ptr1),y - tax - and #_FOPEN ; Check for file open - beq ret_eof - txa - and #(_FERROR|_FEOF); Check for error/eof + jsr checkferror bne ret_eof ; Push _write parameters From afd8ee627e4e71900f9cbacda20e010fa529684d Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Tue, 30 Jan 2024 19:52:48 +0100 Subject: [PATCH 06/23] Remove useless branching code in fgets --- libsrc/common/fgets.s | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libsrc/common/fgets.s b/libsrc/common/fgets.s index 465658191..172ca10dd 100644 --- a/libsrc/common/fgets.s +++ b/libsrc/common/fgets.s @@ -90,9 +90,7 @@ read_loop: : cmp #$0A ; Stop at \n beq done - - clc - bcc read_loop + bne read_loop got_eof: lda didread From ce606bb19e293339d31d82888fe7659d11b64b9d Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Wed, 31 Jan 2024 08:11:46 +0100 Subject: [PATCH 07/23] Fix tests... --- test/val/lib_runtime_aslax7.c | 4 ++-- test/val/lib_runtime_asrax7.c | 4 ++-- test/val/lib_runtime_shlax7.c | 4 ++-- test/val/lib_runtime_shrax7.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/val/lib_runtime_aslax7.c b/test/val/lib_runtime_aslax7.c index ea8f0b375..7e3b4796a 100644 --- a/test/val/lib_runtime_aslax7.c +++ b/test/val/lib_runtime_aslax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %d << 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != -32768); + } while (++ai != -32768); do { refc = ac << 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %d << 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != -128); + } while (++ac != -128); return 0; } diff --git a/test/val/lib_runtime_asrax7.c b/test/val/lib_runtime_asrax7.c index 942efea1d..3cdf2aab8 100644 --- a/test/val/lib_runtime_asrax7.c +++ b/test/val/lib_runtime_asrax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != -32768); + } while (++ai != -32768); do { refc = ac >> 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != -128); + } while (++ac != -128); return 0; } diff --git a/test/val/lib_runtime_shlax7.c b/test/val/lib_runtime_shlax7.c index 9a4869438..d2073ca3a 100644 --- a/test/val/lib_runtime_shlax7.c +++ b/test/val/lib_runtime_shlax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %u << 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != 0); + } while (++ai != 0); do { refc = ac << 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %u << 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != 0); + } while (++ac != 0); return 0; } diff --git a/test/val/lib_runtime_shrax7.c b/test/val/lib_runtime_shrax7.c index db7356d0e..d3bc0db73 100644 --- a/test/val/lib_runtime_shrax7.c +++ b/test/val/lib_runtime_shrax7.c @@ -26,7 +26,7 @@ int main (void) printf("wrong result on int %d >> 7: %04X, expected %04X\n", ai, ti, refi); return 1; } - } while (ai != 0); + } while (++ai != 0); do { refc = ac >> 4; @@ -38,7 +38,7 @@ int main (void) printf("wrong result on char %d >> 7: %04X, expected %04X\n", ac, tc, refc); return 1; } - } while (ac != 0); + } while (++ac != 0); return 0; } From 1e300bf768b949498895062b157d71aa3ac9bbc1 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Thu, 1 Feb 2024 20:13:05 +0100 Subject: [PATCH 08/23] Add test case for issue #2395 --- test/todo/bug2395.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 test/todo/bug2395.c diff --git a/test/todo/bug2395.c b/test/todo/bug2395.c new file mode 100644 index 000000000..4f4d2a6d0 --- /dev/null +++ b/test/todo/bug2395.c @@ -0,0 +1,51 @@ + +/* bug #2395: Bitwise operators with a boolean expression fail when optimized */ + +#include +#include +#include + +unsigned char a, b; +unsigned char c = 199; +unsigned char d = 100; + +int main(void) { + int fails = 0; + + a = c ^ (d != 0); + b = c ^ 1; + + printf("%u ^ (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("XOR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c | (d != 0); + b = c | 1; + + printf("%u | (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("OR error: a %d instead of %d\n", a, b); + fails++; + } + + a = c & (d != 0); + b = c & 1; + + printf("%u & (%u != 0) => %u\n", c, d, a); + if (a != b) { + printf("AND error: a %d instead of %d\n", a, b); + fails++; + } + printf("%d errors\n", fails); + +#ifdef __OPT__ + return fails; +#else + /* Force exit failure on non-optimised version, which works, + * otherwise it breaks the build + */ + return 1; +#endif +} From 96d55e3703de9a9b079cc7f4da8e6b5ed82fb6e5 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 2 Feb 2024 19:00:33 +0800 Subject: [PATCH 09/23] Fixed optimization for char-size bitwise XOR/OR/AND when the rhs operand is complicated. --- src/cc65/coptstop.c | 6 +++--- test/{todo => val}/bug2395.c | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename test/{todo => val}/bug2395.c (100%) diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c index 7e024ae88..402f16b97 100644 --- a/src/cc65/coptstop.c +++ b/src/cc65/coptstop.c @@ -1488,7 +1488,7 @@ static const OptFuncDesc FuncTable[] = { }; static const OptFuncDesc FuncRegATable[] = { - { "tosandax", Opt_a_tosand, REG_NONE, OP_NONE }, + { "tosandax", Opt_a_tosand, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "toseqax", Opt_a_toseq, REG_NONE, OP_NONE }, { "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE }, @@ -1496,13 +1496,13 @@ static const OptFuncDesc FuncRegATable[] = { { "tosleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosltax", Opt_a_tosult, REG_NONE, OP_NONE }, { "tosneax", Opt_a_tosne, REG_NONE, OP_NONE }, - { "tosorax", Opt_a_tosor, REG_NONE, OP_NONE }, + { "tosorax", Opt_a_tosor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tossubax", Opt_a_tossub, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, { "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE }, { "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE }, { "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE }, { "tosultax", Opt_a_tosult, REG_NONE, OP_NONE }, - { "tosxorax", Opt_a_tosxor, REG_NONE, OP_NONE }, + { "tosxorax", Opt_a_tosxor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT }, }; #define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0])) diff --git a/test/todo/bug2395.c b/test/val/bug2395.c similarity index 100% rename from test/todo/bug2395.c rename to test/val/bug2395.c From 54b423a99efbfec9634d68cbddc596f13da89f7f Mon Sep 17 00:00:00 2001 From: Bob Andrews Date: Fri, 2 Feb 2024 13:13:57 +0100 Subject: [PATCH 10/23] fix test --- test/val/bug2395.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/val/bug2395.c b/test/val/bug2395.c index 4f4d2a6d0..07c5cd7c5 100644 --- a/test/val/bug2395.c +++ b/test/val/bug2395.c @@ -40,12 +40,5 @@ int main(void) { } printf("%d errors\n", fails); -#ifdef __OPT__ return fails; -#else - /* Force exit failure on non-optimised version, which works, - * otherwise it breaks the build - */ - return 1; -#endif } From c4c6967e4a557915707f549e01cc24205b68265b Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Fri, 2 Feb 2024 19:34:45 +0100 Subject: [PATCH 11/23] Enable Windows tests on pull requests --- .github/workflows/build-on-pull-request.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 045bc048d..6217c42a2 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -54,7 +54,7 @@ jobs: make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=x86_64-w64-mingw32- build_windows: - name: Build (Windows) + name: Build and Test (Windows) runs-on: windows-latest steps: @@ -79,4 +79,14 @@ jobs: - name: Build app (x64 release) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=x64 + - name: Build utils (MinGW) + shell: cmd + run: make -j2 util SHELL=cmd + - name: Build the platform libraries (make lib) + shell: cmd + run: make -j2 lib QUIET=1 SHELL=cmd + + - name: Run the regression tests (make test) + shell: cmd + run: make test QUIET=1 SHELL=cmd From 1fab179cb467e13c581ec65f635658cb9acf3264 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Thu, 1 Feb 2024 20:05:57 +0100 Subject: [PATCH 12/23] a BIT of 65C02 optimisations Use BIT immediate instead of AND when reloading A is required afterwards. Add an fread unit test as the optimisation touches fread. Sprinkle a few zero page indexed while we're at it. --- libsrc/common/_printf.s | 29 +++++++++++++++-- libsrc/common/fgetc.s | 9 +++++- libsrc/common/fread.s | 32 +++++++++++++++++-- libsrc/common/fwrite.s | 15 ++++++--- test/ref/test_fread.c | 70 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 test/ref/test_fread.c diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s index a0074583e..d7eeb072d 100644 --- a/libsrc/common/_printf.s +++ b/libsrc/common/_printf.s @@ -13,6 +13,7 @@ .import _strlower, _strlen .macpack generic + .macpack cpu ; ---------------------------------------------------------------------------- ; We will store variables into the register bank in the zeropage. Define @@ -37,7 +38,11 @@ FCount = ptr2 GetFormatChar: ldy #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (Format) + .else lda (Format),y + .endif IncFormatPtr: inc Format bne @L1 @@ -110,7 +115,11 @@ GetIntArg: lda (ArgList),y tax dey + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (ArgList) + .else lda (ArgList),y + .endif rts ; ---------------------------------------------------------------------------- @@ -135,9 +144,9 @@ ReadInt: pha ; Save digit value lda ptr1 ldx ptr1+1 - asl ptr1 + asl a rol ptr1+1 ; * 2 - asl ptr1 + asl a rol ptr1+1 ; * 4, assume carry clear adc ptr1 sta ptr1 @@ -265,10 +274,16 @@ Save: lda regbank,y ; Initialize the output counter in the output descriptor to zero lda #0 + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (OutData) + ldy #$01 + sta (OutData),y + .else tay sta (OutData),y iny sta (OutData),y + .endif ; Get the output function from the output descriptor and remember it @@ -338,7 +353,11 @@ MainLoop: sta (sp),y dey lda FCount + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (sp) + .else sta (sp),y + .endif jsr CallOutFunc ; Call the output function ; We're back from out(), or we didn't call it. Check for end of string. @@ -551,10 +570,16 @@ CheckCount: jsr GetIntArg sta ptr1 stx ptr1+1 ; Get user supplied pointer + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (OutData) ; Low byte of OutData->ccount + sta (ptr1) + ldy #1 + .else ldy #0 lda (OutData),y ; Low byte of OutData->ccount sta (ptr1),y iny + .endif lda (OutData),y ; High byte of OutData->ccount sta (ptr1),y jmp MainLoop ; Done diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s index 98e6bb2f7..34d4df3aa 100644 --- a/libsrc/common/fgetc.s +++ b/libsrc/common/fgetc.s @@ -12,6 +12,8 @@ .include "stdio.inc" .include "_file.inc" + .macpack cpu + _fgetc: sta ptr1 stx ptr1+1 @@ -20,11 +22,16 @@ _fgetc: jsr checkferror bne ret_eof + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FPUSHBACK ; Check for pushed back char + beq do_read + .else tax and #_FPUSHBACK ; Check for pushed back char beq do_read - txa + .endif + and #<(~_FPUSHBACK) ; Reset flag sta (ptr1),y diff --git a/libsrc/common/fread.s b/libsrc/common/fread.s index 647a7f2c8..b39b9d748 100644 --- a/libsrc/common/fread.s +++ b/libsrc/common/fread.s @@ -20,6 +20,7 @@ .include "_file.inc" .macpack generic + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -47,13 +48,21 @@ ldy #_FILE::f_flags lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN ; Is the file open? + .else and #_FOPEN ; Is the file open? + .endif beq @L1 ; Branch if no ; Check if the stream is in an error state + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FERROR + .else lda (file),y ; get file->f_flags again and #_FERROR + .endif beq @L2 ; File not open or in error state @@ -65,11 +74,19 @@ ; Remember if we have a pushed back character and reset the flag. -@L2: tax ; X = 0 +@L2: .if (.cpu .bitand ::CPU_ISET_65SC02) + ldx #$00 + bit #_FPUSHBACK + .else + tax ; X = 0 lda (file),y and #_FPUSHBACK + .endif beq @L3 + + .if (.not .cpu .bitand ::CPU_ISET_65SC02) lda (file),y + .endif and #<~_FPUSHBACK sta (file),y ; file->f_flags &= ~_FPUSHBACK; inx ; X = 1 @@ -118,12 +135,20 @@ ; Copy the buffer pointer into ptr1, and increment the pointer value passed ; to read() by one, so read() starts to store data at buf+1. + .if (.cpu .bitand ::CPU_ISET_65SC02) + lda (sp) + sta ptr1 + add #1 + sta (sp) + ldy #1 + .else ldy #0 lda (sp),y sta ptr1 add #1 sta (sp),y iny + .endif lda (sp),y sta ptr1+1 adc #0 @@ -134,8 +159,12 @@ ldy #_FILE::f_pushback lda (file),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + sta (ptr1) ; *buf = file->f_pushback; + .else ldy #0 sta (ptr1),y ; *buf = file->f_pushback; + .endif ; Restore the low byte of count and decrement count by one. This may result ; in count being zero, so check for that. @@ -210,4 +239,3 @@ .bss save: .res 2 pb: .res 1 - diff --git a/libsrc/common/fwrite.s b/libsrc/common/fwrite.s index 861feb120..e7151da95 100644 --- a/libsrc/common/fwrite.s +++ b/libsrc/common/fwrite.s @@ -8,7 +8,7 @@ .export _fwrite .import _write - .import pushax, incsp6, addysp, ldaxysp, pushwysp, return0 + .import pushax, pusha0, incsp6, addysp, ldaxysp, pushwysp, return0 .import tosumulax, tosudivax .importzp ptr1 @@ -16,6 +16,7 @@ .include "errno.inc" .include "_file.inc" + .macpack cpu ; ------------------------------------------------------------------------ ; Code @@ -33,7 +34,11 @@ ldy #_FILE::f_flags lda (ptr1),y + .if (.cpu .bitand ::CPU_ISET_65SC02) + bit #_FOPEN + .else and #_FOPEN ; Is the file open? + .endif bne @L2 ; Branch if yes ; File not open @@ -45,7 +50,9 @@ ; Check if the stream is in an error state -@L2: lda (ptr1),y ; get file->f_flags again +@L2: .if (.not .cpu .bitand ::CPU_ISET_65SC02) + lda (ptr1),y ; get file->f_flags again + .endif and #_FERROR bne @L1 @@ -53,8 +60,7 @@ ldy #_FILE::f_fd lda (ptr1),y - ldx #$00 - jsr pushax ; file->f_fd + jsr pusha0 ; file->f_fd ldy #9 jsr pushwysp ; buf @@ -123,4 +129,3 @@ .bss file: .res 2 - diff --git a/test/ref/test_fread.c b/test/ref/test_fread.c new file mode 100644 index 000000000..5d180d723 --- /dev/null +++ b/test/ref/test_fread.c @@ -0,0 +1,70 @@ +/* + !!DESCRIPTION!! fgets test + !!LICENCE!! Public domain +*/ + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +FILE *in, *out; +char buf[32]; + +#define INFILE "cf.in" + +int main(int argc,char **argv) +{ + static char outfile_path[FILENAME_MAX+1]; + static int r; + + sprintf(outfile_path, "%s.test.out", argv[0]); + + out = fopen(outfile_path, "wb"); + if (out == NULL) { + return EXIT_FAILURE; + } + if (fread(buf, 1, sizeof(buf), out) != NULL) { + printf("Error, could fread with write-only file\n"); + return 1; + } + if (!ferror(out)) { + printf("Error: file pointer should be in error state\n"); + } + fclose(out); + unlink(outfile_path); + + in = fopen(INFILE, "rb"); + if (in == NULL) { + return EXIT_FAILURE; + } + + /* Test that ungetc doesn't break fread */ + buf[0] = fgetc(in); + ungetc(buf[0], in); + + r = fread(buf, 1, sizeof(buf), out); + + if (r == 0) { + printf("Error: could not start reading.\n"); + } + fwrite(buf, 1, r, stdout); + + /* Finish reading file. */ + while ((r = fread(buf, 1, sizeof(buf), out)) != 0) + { + fwrite(buf, 1, r, stdout); + } + + if (!feof(in)) + { + printf("We should have EOF!\n"); + } + + fclose(in); + return 0; +} From 934de685bcc6501631c51ccb96e9f499ecb4709c Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sat, 3 Feb 2024 01:34:21 +0100 Subject: [PATCH 13/23] this is pr #2194 - removed ramfont.o --- cfg/kim1-mtu60k.cfg | 41 ++ cfg/kim1-mtuE000.cfg | 41 ++ samples/kim1/Makefile | 61 +- samples/kim1/font.rom | 0 samples/kim1/kimGFX.c | 290 ++++++++++ samples/kim1/kimLife.c | 144 +++++ samples/kim1/kimTest.c | 262 +++++++++ samples/kim1/ramfont.asm | 272 +++++++++ samples/kim1/subs.asm | 1140 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 2248 insertions(+), 3 deletions(-) create mode 100644 cfg/kim1-mtu60k.cfg create mode 100644 cfg/kim1-mtuE000.cfg create mode 100644 samples/kim1/font.rom create mode 100644 samples/kim1/kimGFX.c create mode 100644 samples/kim1/kimLife.c create mode 100644 samples/kim1/kimTest.c create mode 100644 samples/kim1/ramfont.asm create mode 100644 samples/kim1/subs.asm diff --git a/cfg/kim1-mtu60k.cfg b/cfg/kim1-mtu60k.cfg new file mode 100644 index 000000000..4f24a4bf4 --- /dev/null +++ b/cfg/kim1-mtu60k.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $2000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = %S, size = $E000 - %S - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/cfg/kim1-mtuE000.cfg b/cfg/kim1-mtuE000.cfg new file mode 100644 index 000000000..5f93cc13f --- /dev/null +++ b/cfg/kim1-mtuE000.cfg @@ -0,0 +1,41 @@ +# kim1-mtu60k.cfg (4k) +# +# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM +# +# ld65 --config kim1-mtu60k.cfg -o .bin .o + +FEATURES { + STARTADDRESS: default = $E000; + CONDES: segment = STARTUP, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = STARTUP, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; +} + +SYMBOLS { + __STACKSIZE__: type = weak, value = $0080; # 128 byte program stack + __STARTADDRESS__: type = export, value = %S; +} + +MEMORY { + ZP: file = %O, define = yes, start = $0000, size = $00EE; + CPUSTACK: file = "", define = yes, start = $0100, size = $0100; + RAM: file = %O, define = yes, start = $2000, size = $E000 - $2000 - __STACKSIZE__; + MAINROM: file = "", define = yes, start = $E000, size = $1000; + TOP: file = "", define = yes, start = $F000, size = $1000; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, define = yes; + STARTUP: load = RAM, type = ro, define = yes; + CODE: load = RAM, type = ro, define = yes; + RODATA: load = RAM, type = ro, define = yes; + ONCE: load = RAM, type = ro, define = yes; + DATA: load = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; +} + diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile index 74c415fdc..08bb2a780 100644 --- a/samples/kim1/Makefile +++ b/samples/kim1/Makefile @@ -31,9 +31,12 @@ else LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65) endif -EXELIST_kim1 = \ - kimHello.bin \ - kimSieve.bin +EXELIST_kim1 = \ + kimHello.bin \ + kimSieve.bin \ + kimLife.bin \ + kimTest.bin \ + kimGFX.bin ifneq ($(EXELIST_$(SYS)),) samples: $(EXELIST_$(SYS)) @@ -50,13 +53,65 @@ else @echo > $(NULLDEV) endif +subs.o: subs.asm + $(AS) subs.asm -o subs.o + +ramfont.o: ramfont.asm + $(AS) ramfont.asm -o ramfont.o + +kimLife.bin: kimLife.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimLife.bin kimLife.c + +kimTest.bin: kimTest.c + $(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimTest.bin kimTest.c + +kimGFX.bin: kimGFX.c subs.o ramfont.o + $(CL) -t kim1 --listing kimGFX.lst -C kim1-mtuE000.cfg -o kimGFX.bin kimGFX.c subs.o ramfont.o -Ln kimgfx.lbl + kimSieve.bin: kimSieve.c $(CL) -t kim1 -C kim1-60k.cfg -O -o kimSieve.bin kimSieve.c kimHello.bin: kimHello.c $(CL) -t kim1 -O -o kimHello.bin kimHello.c +# To build an intel-format file for the CORSHAM SD card reader + +kimLife.hex: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.hex -Intel -address-length=2 + +kimTest.hex: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.hex -Intel -address-length=2 + +kimGFX.hex: kimGFX.bin ramfont.o + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.hex -Intel -address-length=2 + +# To build a paper tape file for uploading to the KIM-1 via terminal + +kimLife.ptp: kimLife.bin + srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.ptp -MOS_Technologies + +kimGFX.ptp: kimGFX.bin + srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.ptp -MOS_Technologies + +kimTest.ptp: kimTest.bin + srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.ptp -MOS_Technologies + clean: @$(DEL) kimSieve.bin 2>$(NULLDEV) @$(DEL) kimHello.bin 2>$(NULLDEV) + @$(DEL) kimLife.bin 2>$(NULLDEV) + @$(DEL) kimLife.ptp 2>$(NULLDEV) + @$(DEL) kimLife.hex 2>$(NULLDEV) + @$(DEL) kimTest.bin 2>$(NULLDEV) + @$(DEL) kimTest.ptp 2>$(NULLDEV) + @$(DEL) kimTest.hex 2>$(NULLDEV) + @$(DEL) kimGFX.bin 2>$(NULLDEV) + @$(DEL) kimGFX.ptp 2>$(NULLDEV) + @$(DEL) kimGFX.hex 2>$(NULLDEV) + @$(DEL) kimgfx.lbl 2>$(NULLDEV) + @$(DEL) kimGFX.lst 2>$(NULLDEV) + @$(DEL) subs.o 2>$(NULLDEV) + @$(DEL) ramfont.o 2>$(NULLDEV) + + diff --git a/samples/kim1/font.rom b/samples/kim1/font.rom new file mode 100644 index 000000000..e69de29bb diff --git a/samples/kim1/kimGFX.c b/samples/kim1/kimGFX.c new file mode 100644 index 000000000..45daafa1e --- /dev/null +++ b/samples/kim1/kimGFX.c @@ -0,0 +1,290 @@ +// -------------------------------------------------------------------------- +// Simple Graphics Test for KIM-1 with MTU Visible Memory Board +// +// Assumes the MTU Visible Memory Board mapped at 0xA000 for 8K of video RAM +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include // For printf +#include // For rand, srand +#include // For memcpy +#include + +typedef unsigned char byte; + +extern void ClearScreen(void); // In subs.asm +extern void ScrollScreen(void); +extern void DrawCircle(void); +extern void SetPixel(void); +extern void ClearPixel(void); +extern void DrawChar(void); +extern void Demo(void); +extern void __fastcall__ Delay(byte loops); +extern void __fastcall__ DrawLine(byte bSet); +extern byte __fastcall__ AscToPet(byte in); +extern byte __fastcall__ PetToAsc(byte in); +extern byte __fastcall__ ReverseBits(byte in); +extern void __fastcall__ CharOut(byte asci_char); +extern byte __fastcall__ getch(); +extern unsigned char font8x8_basic[256][8]; + +extern int x1cord; +extern int y1cord; +extern int x2cord; +extern int y2cord; +extern int cursorX; +extern int cursorY; + +// If in zeropage: +// +// #pragma zpsym("x1cord") +// #pragma zpsym("x2cord") +// #pragma zpsym("y1cord") +// #pragma zpsym("y2cord") + +// Screen memory is placed at A000-BFFF, 320x200 pixels, mapped right to left within each horizontal byte + +byte * screen = (byte *) 0xA000; + +// Cursor position + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define CHARWIDTH 8 +#define CHARHEIGHT 8 +#define BYTESPERROW (SCREEN_WIDTH / 8) +#define BYTESPERCHARROW (BYTESPERROW * 8) +#define CHARSPERROW (SCREEN_WIDTH / CHARWIDTH) +#define ROWSPERCOLUMN (SCREEN_HEIGHT / CHARHEIGHT) + +// SETPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Draws a pixel on the screen in white or black at pixel pos x, y + +void SETPIXEL(int x, int y, byte b) +{ + x1cord = x; + y1cord = y; + + if (b) + SetPixel(); + else + ClearPixel(); +} + +// DRAWPIXEL +// +// 0 <= x < 320 +// 0 <= y < 200 +// +// Turns on a screen pixel at pixel pos x,y + +void DRAWPIXEL(int x, int y) +{ + x1cord = x; + y1cord = y; + SetPixel(); +} + +int c; + +void DrawText(char * psz) +{ + while (*psz) + { + while (cursorX >= CHARSPERROW) + { + cursorX -= CHARSPERROW; + cursorY += 1; + } + + // If we've gone off the bottom of the screen, we scroll the screen and back up to the last line again + + if (cursorY >= ROWSPERCOLUMN) + { + cursorY = ROWSPERCOLUMN - 1; + ScrollScreen(); + } + + // If we output a newline we advanced the cursor down one line and reset it to the left + + if (*psz == 0x0A) + { + cursorX = 0; + cursorY++; + psz++; + } + else + { + c = *psz; + + __asm__ ("ldx %v", cursorX); + __asm__ ("ldy %v", cursorY); + __asm__ ("lda %v", c); + DrawChar(); + cursorX++; + psz++; + } + } +} + +void DrawTextAt(int x, int y, char * psz) +{ + cursorX = x; + cursorY = y; + DrawText(psz); +} + +// Something like Bresenham's algorithm for drawing a line +/* +void DrawLine(int x0, int y0, int x1, int y1, byte val) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + while (1) + { + SETPIXEL(x0, y0, val); + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 > -dx) + { + err -= dy; + x0 += sx; + } + if (e2 < dy) + { + err += dx; + y0 += sy; + } + } +} +*/ + +// DrawCircle +// +// Draw a circle without sin, cos, or floating point! + +void DrawCircleC(int x0, int y0, int radius, byte) +{ + x1cord = x0; + y1cord = y0; + y2cord = radius; + DrawCircle(); +} + +void DrawLineC(int x1, int y1, int x2, int y2, byte bSet) +{ + x1cord = x1; + y1cord = y1; + x2cord = x2; + y2cord = y2; + DrawLine(bSet); +} + +// MirrorFont +// +// RAM font is backwards left-right relative to the way memory is laid out on the KIM-1, so we swap all the +// bytes in place by reversing the order of the bits in every byte + +void MirrorFont() +{ + int c; + byte * pb = (byte *) font8x8_basic; + + for (c = 0; c < 128 * 8; c++) + pb[c] = ReverseBits(pb[c]); +} + +// DrawScreenMoire +// +// Draws a moire pattern on the screen without clearing it first + +void DrawMoire(int left, int top, int right, int bottom, byte pixel) +{ + int x, y; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, pixel); + + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, pixel); +} + +void DrawScreenMoire(int left, int top, int right, int bottom) +{ + int x, y; + + DrawLineC(left, top, right, top, 1); + DrawLineC(left, bottom, right, bottom, 1); + DrawLineC(left, top, left, bottom, 1); + DrawLineC(right, top, right, bottom, 1); + + left++; top++; right--; bottom--; + + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 1); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 1); + for (x = left; x < right; x += 6) + DrawLineC(x, top, right - x + left, bottom, 0); + for (y = top; y < bottom; y += 6) + DrawLineC(left, y, right, bottom - y + top, 0); + +} + +int main (void) +{ + + int i; + int c = 0; + + Demo(); + + CharOut('R'); + CharOut('E'); + CharOut('A'); + CharOut('D'); + CharOut('Y'); + CharOut('.'); + CharOut('\n'); + + + while(1) + { + c = toupper(getch()); + if (c != EOF) + CharOut(c); + } + + // Clear the screen memory + while(1) + { + Demo(); + DrawScreenMoire(0,30, 319, 199); + Delay(10); + + Demo(); + for (i = 5; i < 80; i+=5) + { + DrawCircleC(SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH/4, SCREEN_HEIGHT/2 + 20, i, 1); + DrawCircleC(SCREEN_WIDTH*3/4, SCREEN_HEIGHT/2 + 20, i, 1); + } + + Delay(10); + + } + + printf("Done, exiting...\r\n"); + return 0; +} diff --git a/samples/kim1/kimLife.c b/samples/kim1/kimLife.c new file mode 100644 index 000000000..fb1696b9e --- /dev/null +++ b/samples/kim1/kimLife.c @@ -0,0 +1,144 @@ +// -------------------------------------------------------------------------- +// Conway's Game of Life for KIM-1 +// +// Assumes the MTU Visible Memory Board mapped at 0x8000 for 8K of video RAM +// +// Dave Plummer on a rainy Thursday +// +// davepl@davepl.com +// -------------------------------------------------------------------------- + +#include // For printf +#include // For rand, srand +#include // For memcpy + +typedef unsigned char byte; + +// World size + +#define WIDTH 320 +#define HEIGHT 200 +#define NUMBITS 64000 +#define NUMBYTES 8000 +#define DENSITY 50 + +// Screen memory is placed at 8000, our world copy at A000, and they use the same layout so +// that we can memcpy from one to the other without translating + +byte * world = (byte *) 0x8000; +byte * new_world = (byte *) 0xA000; + +// BITARRAY +// +// Access individual bits in a block of memory + +// Access to the screen bitmap + +byte GETBIT(byte *p, int n) +{ + return (p[n >> 3] & (1 << (n & 7))) ? 1 : 0; +} + +void SETBIT(byte *p, int n) +{ + p[n >> 3] |= (1 << (n & 7)); +} + +void CLRBIT(byte *p, int n) +{ + p[n >> 3] &= ~(1 << (n & 7)); +} + +void SETPIXEL(byte * p, int x, int y, byte b) +{ + if (b) + SETBIT(p, y * WIDTH + x); + else + CLRBIT(p, y * WIDTH + x); +} + +byte GETPIXEL(byte *p, int x, int y) +{ + return GETBIT(p, y * WIDTH + x); +} + +// RandomFillWorld +// +// Populates the initial world with random cells + +void RandomFillWorld() +{ + int x, y; + + // I need a better way to see the RNG or it'll be the same game every time! + srand(0); + for (x = 0; x < WIDTH; x++) + { + for (y = 0; y < HEIGHT; y++) + { + byte b = ((rand() % 100) < DENSITY) ? 1 : 0; + SETPIXEL(world, x, y, b); + } + } +} + +// CountNeighbors +// +// Count the number of live cells around the given spot, excluding the actual spot specified + +int CountNeighbors(int x, int y) +{ + int i, j, nx, ny, count = 0; + + for (j = -1; j <= 1; j++) + { + for (i = -1; i <= 1; i++) + { + if (i != 0 || j != 0) + { + nx = (x + i + WIDTH) % WIDTH; + ny = (y + j + HEIGHT) % HEIGHT; + count += GETPIXEL(world, nx, ny) ? 1 : 0; + } + } + } + return count; +} + +// UpdateWorld +// +// Applies the rules of Conway's Game of Life to the cells + +void UpdateWorld() +{ + int x, y; + + for (y = 0; y < HEIGHT; y++) + { + for (x = 0; x < WIDTH; x++) + { + int neighbors = CountNeighbors(x, y); + if (GETPIXEL(world, x, y)) + SETPIXEL(new_world, x, y, (neighbors == 2 || neighbors == 3)); + else + SETPIXEL(new_world, x, y, (neighbors == 3)); + } + } +} + +int main (void) +{ + printf("\r\nStarting Conway's Game of Life: Randomizing World...\r\n"); + RandomFillWorld(); + printf("World Ready, Running!\r\n"); + + for (;;) + { + UpdateWorld(); + printf("["); + memcpy(world, new_world, NUMBYTES); + printf("]"); + } + + return 0; +} diff --git a/samples/kim1/kimTest.c b/samples/kim1/kimTest.c new file mode 100644 index 000000000..273aeb584 --- /dev/null +++ b/samples/kim1/kimTest.c @@ -0,0 +1,262 @@ +// -------------------------------------------------------------------------- +// Diagnostics Test for KIM-1 +// +// Dave Plummer +// davepl@davepl.com +// +// Memory test examples by Michael Barr +// +// -------------------------------------------------------------------------- + +#include // For printf +#include // For rand, srand +#include // For memcpy + +typedef unsigned char byte; + +// RepeatChar +// +// Outputs a given character N times + +void RepeatChar(char c, size_t count) +{ + while (count--) + putc(c, stdout); +} + +/********************************************************************** + * + * Function: memTestDataBus() + * + * Description: Test the data bus wiring in a memory region by + * performing a walking 1's test at a fixed address + * within that region. The address (and hence the + * memory region) is selected by the caller. + * + * Returns: 0 if the test succeeds. + * A non-zero result is the first pattern that failed. + * + **********************************************************************/ + +byte memTestDataBus(volatile byte * address) +{ + byte pattern; + + // Perform a walking 1's test at the given address. + + for (pattern = 1; pattern != 0; pattern <<= 1) + { + // Write the test pattern. + *address = pattern; + + // Read it back and check it + if (*address != pattern) + { + printf("\r\nmemTestDataBus: FAILED at %04x with pattern %02x\r\n", address, pattern); + return (pattern); + } + } + + return (0); +} + +/********************************************************************** + * + * Function: memTestAddressBus() + * + * Description: Test the address bus wiring in a memory region by + * performing a walking 1's test on the relevant bits + * of the address and checking for aliasing. This test + * will find single-bit address failures such as stuck + * -high, stuck-low, and shorted pins. The base address + * and size of the region are selected by the caller. + * + * Notes: For best results, the selected base address should + * have enough LSB 0's to guarantee single address bit + * changes. For example, to test a 64-Kbyte region, + * select a base address on a 64-Kbyte boundary. Also, + * select the region size as a power-of-two--if at all + * possible. + * + * Returns: NULL if the test succeeds. + * A non-zero result is the first address at which an + * aliasing problem was uncovered. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestAddressBus(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long addressMask = (nBytes/sizeof(byte) - 1); + unsigned long offset; + unsigned long testOffset; + + byte pattern = (byte) 0xAAAAAAAA; + byte antipattern = (byte) 0x55555555; + + + //Write the default pattern at each of the power-of-two offsets. + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + baseAddress[offset] = pattern; + } + + // Check for address bits stuck high. + + testOffset = 0; + baseAddress[testOffset] = antipattern; + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[offset]); + } + if (offset % 1024 == 0) + printf("."); + } + + baseAddress[testOffset] = pattern; + + + // Check for address bits stuck low or shorted. + + for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1) + { + baseAddress[testOffset] = antipattern; + + if (baseAddress[0] != pattern) + { + return ((byte *) &baseAddress[testOffset]); + } + + for (offset = 1; (offset & addressMask) != 0; offset <<= 1) + { + if ((baseAddress[offset] != pattern) && (offset != testOffset)) + { + printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern); + return ((byte *) &baseAddress[testOffset]); + } + } + baseAddress[testOffset] = pattern; + } + return (NULL); +} + +/********************************************************************** + * + * Function: memTestDevice() + * + * Description: Test the integrity of a physical memory device by + * performing an increment/decrement test over the + * entire region. In the process every storage bit + * in the device is tested as a zero and a one. The + * base address and the size of the region are + * selected by the caller. + * + * Returns: NULL if the test succeeds. + * + * A non-zero result is the first address at which an + * incorrect value was read back. By examining the + * contents of memory, it may be possible to gather + * additional information about the problem. + * + **********************************************************************/ + +byte * memTestDevice(volatile byte * baseAddress, unsigned long nBytes) +{ + unsigned long offset; + unsigned long nWords = nBytes / sizeof(byte); + + byte pattern; + byte antipattern; + + + // Fill memory with a known pattern. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + baseAddress[offset] = pattern; + + // Check each location and invert it for the second pass. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + if (baseAddress[offset] != pattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with pattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + + antipattern = ~pattern; + baseAddress[offset] = antipattern; + + } + + // Check each location for the inverted pattern and zero it. + + for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) + { + if (offset % 1024 == 0) + printf("%04X ", (int) &baseAddress[offset]); + + antipattern = ~pattern; + if (baseAddress[offset] != antipattern) + { + printf("\r\nmemTestDevice: FAILED at %04x with antipattern %02x\r\n", (int) &baseAddress[offset], pattern); + return ((byte *) &baseAddress[offset]); + } + } + + return (NULL); +} + +// TestMemory +// +// Run all three memory tests + +byte TestMemory(byte * startAddress, unsigned long size) +{ + if ((memTestDataBus(startAddress) != 0) || + (memTestAddressBus(startAddress, size) != NULL) || + (memTestDevice(startAddress, size) != NULL)) + { + return (-1); + } + else + { + return (0); + } +} + +int main (void) +{ + printf("\r\nTesting KIM-1...\r\n"); + RepeatChar('-', 39); + + printf("\r\nTesting RIOT RAM: 1780-17BF\r\n"); + if (TestMemory((byte *)0x1780, 0x17BF - 0x1780)) + return 0; + + printf("\r\nTesting RIOT RAM: 17C0-17E6\r\n"); + if (TestMemory((byte *)0x17C0, 0x17E6 - 0x17C0)) + return 0; + + printf("\r\nTesting Memory: 0400-13FF\r\n"); + if (TestMemory((byte *)0x0400, 0x13FF - 0x0400)) + return 0; + + printf("\r\nTesting Memory: 4000-DFFF\r\n"); + if (TestMemory((byte *)0x4000, 0xDFFF - 0x4000)) + return 0; + + printf("\r\nPASS!\r\n"); + return 1; +} + + diff --git a/samples/kim1/ramfont.asm b/samples/kim1/ramfont.asm new file mode 100644 index 000000000..ac0b2c9cd --- /dev/null +++ b/samples/kim1/ramfont.asm @@ -0,0 +1,272 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: ramfont.s +; Magnetic OCR (check number style) Font data +; +;----------------------------------------------------------------------------------- + +.segment "CODE" +.export _font8x8_basic + +_font8x8_basic: + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 0 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 1 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 2 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 3 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 4 + .byte $7e, $40, $40, $7c, $60, $60, $7e, $00 ; PETSCII code 5 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 6 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 7 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 8 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 9 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 10 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 11 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 12 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 13 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 14 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 15 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 16 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 17 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 18 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 19 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 20 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 21 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 22 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 23 + .byte $42, $42, $66, $18, $66, $62, $62, $00 ; PETSCII code 24 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 25 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 26 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 27 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 28 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 29 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 30 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 31 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 32 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 33 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 34 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 35 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 36 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 37 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 38 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 39 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 40 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 41 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 42 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 43 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 44 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 45 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 46 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 47 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 48 + .byte $18, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 49 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 50 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 51 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 52 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 53 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 54 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 55 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 56 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 57 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 58 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 59 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 60 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 61 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 62 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 63 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 64 + .byte $08, $1c, $3e, $7f, $7f, $1c, $3e, $00 ; PETSCII code 65 + .byte $10, $10, $10, $10, $10, $10, $10, $10 ; PETSCII code 66 + .byte $00, $00, $00, $ff, $00, $00, $00, $00 ; PETSCII code 67 + .byte $00, $00, $ff, $00, $00, $00, $00, $00 ; PETSCII code 68 + .byte $00, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 69 + .byte $00, $00, $00, $00, $00, $ff, $00, $00 ; PETSCII code 70 + .byte $20, $20, $20, $20, $20, $20, $20, $20 ; PETSCII code 71 + .byte $04, $04, $04, $04, $04, $04, $04, $04 ; PETSCII code 72 + .byte $00, $00, $00, $00, $e0, $10, $08, $08 ; PETSCII code 73 + .byte $08, $08, $08, $04, $03, $00, $00, $00 ; PETSCII code 74 + .byte $08, $08, $08, $10, $e0, $00, $00, $00 ; PETSCII code 75 + .byte $80, $80, $80, $80, $80, $80, $80, $ff ; PETSCII code 76 + .byte $80, $40, $20, $10, $08, $04, $02, $01 ; PETSCII code 77 + .byte $01, $02, $04, $08, $10, $20, $40, $80 ; PETSCII code 78 + .byte $ff, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 79 + .byte $ff, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 80 + .byte $00, $3c, $7e, $7e, $7e, $7e, $3c, $00 ; PETSCII code 81 + .byte $00, $00, $00, $00, $00, $00, $ff, $00 ; PETSCII code 82 + .byte $36, $7f, $7f, $7f, $3e, $1c, $08, $00 ; PETSCII code 83 + .byte $40, $40, $40, $40, $40, $40, $40, $40 ; PETSCII code 84 + .byte $00, $00, $00, $00, $03, $04, $08, $08 ; PETSCII code 85 + .byte $81, $42, $24, $18, $18, $24, $42, $81 ; PETSCII code 86 + .byte $00, $3c, $42, $42, $42, $42, $3c, $00 ; PETSCII code 87 + .byte $08, $1c, $2a, $77, $2a, $08, $08, $00 ; PETSCII code 88 + .byte $02, $02, $02, $02, $02, $02, $02, $02 ; PETSCII code 89 + .byte $08, $1c, $3e, $7f, $3e, $1c, $08, $00 ; PETSCII code 90 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 91 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 92 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 93 + .byte $00, $00, $01, $3e, $54, $14, $14, $00 ; PETSCII code 94 + .byte $ff, $7f, $3f, $1f, $0f, $07, $03, $01 ; PETSCII code 95 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 96 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 97 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 98 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 99 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 100 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 101 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 102 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 103 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 104 + .byte $ff, $fe, $fc, $f8, $f0, $e0, $c0, $80 ; PETSCII code 105 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 106 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 107 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 108 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 109 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 110 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 111 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 112 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 113 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 114 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 115 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 116 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 117 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 118 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 119 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 120 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 121 + .byte $01, $01, $01, $01, $01, $01, $01, $ff ; PETSCII code 122 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 123 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 124 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 125 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 126 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 127 + .byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 128 + .byte $00, $00, $3c, $04, $7c, $64, $7c, $00 ; PETSCII code 129 + .byte $40, $40, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 130 + .byte $00, $00, $7e, $42, $60, $62, $7e, $00 ; PETSCII code 131 + .byte $02, $02, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 132 + .byte $00, $00, $7e, $42, $7e, $60, $7e, $00 ; PETSCII code 133 + .byte $1e, $12, $10, $7c, $18, $18, $18, $00 ; PETSCII code 134 + .byte $00, $00, $7e, $42, $62, $7e, $02, $7e ; PETSCII code 135 + .byte $40, $40, $7e, $42, $62, $62, $62, $00 ; PETSCII code 136 + .byte $18, $00, $10, $10, $18, $18, $18, $00 ; PETSCII code 137 + .byte $0c, $00, $08, $0c, $0c, $0c, $44, $7c ; PETSCII code 138 + .byte $40, $40, $44, $48, $78, $64, $64, $00 ; PETSCII code 139 + .byte $10, $10, $10, $10, $18, $18, $18, $00 ; PETSCII code 140 + .byte $00, $00, $7f, $49, $6d, $6d, $6d, $00 ; PETSCII code 141 + .byte $00, $00, $7e, $42, $62, $62, $62, $00 ; PETSCII code 142 + .byte $00, $00, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 143 + .byte $00, $00, $7e, $42, $62, $7e, $40, $40 ; PETSCII code 144 + .byte $00, $00, $7e, $42, $46, $7e, $02, $02 ; PETSCII code 145 + .byte $00, $00, $7e, $40, $60, $60, $60, $00 ; PETSCII code 146 + .byte $00, $00, $7e, $40, $7e, $06, $7e, $00 ; PETSCII code 147 + .byte $10, $10, $7c, $10, $18, $18, $18, $00 ; PETSCII code 148 + .byte $00, $00, $42, $42, $62, $62, $7e, $00 ; PETSCII code 149 + .byte $00, $00, $62, $62, $66, $24, $3c, $00 ; PETSCII code 150 + .byte $00, $00, $49, $49, $6d, $6d, $7f, $00 ; PETSCII code 151 + .byte $00, $00, $42, $42, $3c, $62, $62, $00 ; PETSCII code 152 + .byte $00, $00, $62, $62, $42, $7e, $02, $7e ; PETSCII code 153 + .byte $00, $00, $7e, $06, $18, $60, $7e, $00 ; PETSCII code 154 + .byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 155 + .byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 156 + .byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 157 + .byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 158 + .byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 159 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 160 + .byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 161 + .byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 162 + .byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 163 + .byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 164 + .byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 165 + .byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 166 + .byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 167 + .byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 168 + .byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 169 + .byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 170 + .byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 171 + .byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 172 + .byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 173 + .byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 174 + .byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 175 + .byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 176 + .byte $38, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 177 + .byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 178 + .byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 179 + .byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 180 + .byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 181 + .byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 182 + .byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 183 + .byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 184 + .byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 185 + .byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 186 + .byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 187 + .byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 188 + .byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 189 + .byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 190 + .byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 191 + .byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 192 + .byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 193 + .byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 194 + .byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 195 + .byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 196 + .byte $7e, $40, $40, $78, $60, $60, $7e, $00 ; PETSCII code 197 + .byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 198 + .byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 199 + .byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 200 + .byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 201 + .byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 202 + .byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 203 + .byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 204 + .byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 205 + .byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 206 + .byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 207 + .byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 208 + .byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 209 + .byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 210 + .byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 211 + .byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 212 + .byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 213 + .byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 214 + .byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 215 + .byte $42, $42, $66, $3c, $66, $62, $62, $00 ; PETSCII code 216 + .byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 217 + .byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 218 + .byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 219 + .byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 220 + .byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 221 + .byte $cc, $cc, $33, $33, $cc, $cc, $33, $33 ; PETSCII code 222 + .byte $cc, $66, $33, $99, $cc, $66, $33, $99 ; PETSCII code 223 + .byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 224 + .byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 225 + .byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 226 + .byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 227 + .byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 228 + .byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 229 + .byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 230 + .byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 231 + .byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 232 + .byte $99, $33, $66, $cc, $99, $33, $66, $cc ; PETSCII code 233 + .byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 234 + .byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 235 + .byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 236 + .byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 237 + .byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 238 + .byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 239 + .byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 240 + .byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 241 + .byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 242 + .byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 243 + .byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 244 + .byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 245 + .byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 246 + .byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 247 + .byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 248 + .byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 249 + .byte $01, $02, $44, $48, $50, $60, $40, $00 ; PETSCII code 250 + .byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 251 + .byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 252 + .byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 253 + .byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 254 + .byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 255 diff --git a/samples/kim1/subs.asm b/samples/kim1/subs.asm new file mode 100644 index 000000000..5b525749e --- /dev/null +++ b/samples/kim1/subs.asm @@ -0,0 +1,1140 @@ +;----------------------------------------------------------------------------------- +; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1 +;----------------------------------------------------------------------------------- +; (c) Plummer's Software Ltd, 04/25/2023 Created +; David Plummer +;----------------------------------------------------------------------------------- +; +; File: subs.asm Assembly language subroutines for KIMGFX +; +;----------------------------------------------------------------------------------- + + +.SETCPU "6502" + +.export _ClearScreen +.export _ScrollScreen +.export _SetPixel +.export _ClearPixel +.export _DrawCircle +.export _DrawLine +.export _AscToPet +.export _ReverseBits +.export _DrawChar +.export _CharOut +.export _Demo +.export _Delay +.export _getch + + +.import _font8x8_basic + +; This is the assumed location of the MTU visible memory board's 8K of memory. You can adjust this +; constant to refelct other locations as needed. + +SCREEN = $A000 + +; Note that even though these constants are defined here and respected, there are still going to be +; logic assumptions in GetPixelAddress that assume a 320x200 screen. If you change these, you'll +; need to adjust GetPixelAddress to match. + +SCREEN_WIDTH = 320 +SCREEN_HEIGHT = 200 +SCREEN_BYTES = SCREEN_WIDTH * SCREEN_HEIGHT / 8 +CHARWIDTH = 8 +CHARHEIGHT = 8 +BYTESPERROW = (SCREEN_WIDTH / 8) +BYTESPERCHARROW = (BYTESPERROW * 8) +CHARSPERROW = (SCREEN_WIDTH / CHARWIDTH) +ROWSPERCOLUMN = (SCREEN_HEIGHT / CHARHEIGHT) +LASTROW = SCREEN + SCREEN_BYTES - BYTESPERCHARROW + +.segment "ZEROPAGE" + +btpt: .res 1 + +dest: +dest_lo: .res 1 +dest_hi: .res 1 + +src: +src_lo: .res 1 +src_hi: .res 1 + +adp1: +adp1_lo: .res 1 +adp1_hi: .res 1 + +adp2: +adp2_lo: .res 1 +adp2_hi: .res 1 + +scroll_src: +scroll_src_lo: .res 1 +scroll_src_hi: .res 1 + +scroll_dest: +scroll_dest_lo: .res 1 +scroll_dest_hi: .res 1 + + +.segment "DATA" + +; Arguments for graphics functions + +_x1cord: .res 2 +_x2cord: .res 2 +_y1cord: .res 2 +_y2cord: .res 2 +_cursorX: .res 1 +_cursorY: .res 1 + +; Linedraw + +dx: .res 2 +dy: .res 2 +e2: .res 2 +sx: .res 1 +sy: .res 1 +dltemp: .res 2 +pixel: .res 1 + +; DrawCircle + +xval: .res 2 ; These could move to zeropage for perf, but presume we +yval: .res 2 ; we want to minimize the amount we grow zero page use +err: .res 2 +temp: .res 2 +tempa: .res 1 +tempx: .res 1 +tempy: .res 1 +temp2: .res 2 +x0: .res 2 +y0: .res 2 + +; CharOut + +tempstr: .res 2 + +.export _x1cord ; Make sure these show up on the C side as zero page +.export _x2cord +.export _y1cord +.export _y2cord +.export _cursorX +.export _cursorY + +.segment "CODE" + +;----------------------------------------------------------------------------------- +; GetPixelAddress - Calculate the address of a pixel in the video memory +;----------------------------------------------------------------------------------- +; Based on MTU PIXADR code +;----------------------------------------------------------------------------------- +; In: _x1cord (16-bit) +; _y1cord (16-bit) +; Out: adp1 (16-bit) Address of pixel to set +;----------------------------------------------------------------------------------- + +_GetPixelAddress: + lda _x1cord ; compute bit address first + sta adp1 ; also transfer x1cord to adp1 + and #$07 ; + which is simply the low 3 bits of x + sta btpt + lda _x1cord+1 ; finish transferring x1cord to adp1 + sta adp1+1 + lsr adp1+1 ; double shift adp1 right 3 to get + ror adp1 ; int(xcord/8 ) + lsr adp1+1 + ror adp1 + lsr adp1+1 + ror adp1 + sec ; and temporary storage + lda _y1cord + sta adp2 + sta temp + lda #0 + sbc _y1cord+1 + sta adp2+1 + sta temp+1 + asl adp2 ; compute 40*(y1cord) + rol adp2+1 ; 2*(y1cord) + asl adp2 + rol adp2+1 ; 4*(y1cord) + lda adp2 ; add in temporary save of (y1cord) + clc ; to make 5*(y1cord) + adc temp + sta adp2 + lda adp2+1 + adc temp+1 + sta adp2+1 ; 5*(y1cord) + asl adp2 ; 10*(1cord) + rol adp2+1 + asl adp2 ; 20#(y1cord) + rol adp2+1 + asl adp2 ; 40*(y1cord) + rol adp2+1 + lda adp2 ; add in int(x1cord/8) computed earlier + clc + adc adp1 + sta adp1 + lda adp2+1 + adc adp1+1 + adc #>SCREEN ; add in vmorg*256 + sta adp1+1 ; final result + rts ; return + +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +; +; MSKTB1 is a table of 1 bits corresponding to bit numbers +; MSKTB2 is a table of 0 bits corresponding to bit numbers +;----------------------------------------------------------------------------------- + +msktb1: .byte $80,$40,$20,$10,$08,$04,$02,$01 +msktb2: .byte $7F,$BF,$DF,$EF,$F7,$FB,$FD,$FE + +_Delay: pha + sta temp + txa + pha + tya + pha + +@loopa: ldx #$ff +@loopx: ldy #$ff +@loopy: dey + bne @loopy + dex + bne @loopx + dec temp + bne @loopa + + pla + tay + pla + tax + pla + rts + +;----------------------------------------------------------------------------------- +; SetPixel - Set a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- +; Mask tables for individual pixel subroutines +;----------------------------------------------------------------------------------- + +_SetPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb1,y ; get a byte with that bit =1, others =0 + ldy #0 + ora (adp1),y ; combine the bit with the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearPixel - Clears a pixel in the video memory +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +;----------------------------------------------------------------------------------- + +_ClearPixel: jsr _GetPixelAddress + ldy btpt ; get bit number in y + lda msktb2,y ; get a byte with that bit =0, others =1 + ldy #0 + and (adp1),y ; remove the bit from the addressed vm + sta (adp1),y ; byte + rts + +;----------------------------------------------------------------------------------- +; ClearScreen - Clears the entire video memory (and thus the screen) +;----------------------------------------------------------------------------------- + +_ClearScreen: + lda #$00 + + ldx #SCREEN + stx dest_hi + + ldy #0 +: sta (dest), y ; Loop unwound by a factor of 8, which means our iny before the branchh + iny ; will still work as it's on a page crossing boundary. + sta (dest), y ; This will avoid most of the overhead of the branch. + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + sta (dest), y + iny + bne :- + + inc dest_hi + ldx dest_hi + cpx #>SCREEN + $20 + bne :- + + rts + +;----------------------------------------------------------------------------------- +; ScrollScreen - Scrolls the entire video memory (and thus the screen) up one row +;----------------------------------------------------------------------------------- + +BYTES_TO_MOVE = SCREEN_BYTES - BYTESPERCHARROW +PAGES_TO_MOVE = BYTES_TO_MOVE / 256 + +_ScrollScreen: + pha + tya + pha + txa + pha + + ; Load the source (A140) and destination (A000) addresses + lda #<(SCREEN+BYTESPERCHARROW) + sta scroll_src_lo + lda #>(SCREEN+BYTESPERCHARROW) + sta scroll_src_hi + lda #SCREEN + sta scroll_dest_hi + + ldx #PAGES_TO_MOVE +@outerLoop: + ldy #0 +@innerLoop: ; + ; I could do this faster in self-modifying code (avoiding the zero page overhead) but then it + ; couldn't go into ROM + + lda (scroll_src),y ; I've unwound the loop to do 8 bytes at a time. Since we're doing full pages + sta (scroll_dest),y ; as long as we unwind the loop to do 8 bytes at a time, we know we'll still + iny ; do the final increment on a page boundary. + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + lda (scroll_src),y + sta (scroll_dest),y + iny + bne @innerLoop ; If Y overflows, it will be 0, so won't branch + inc scroll_src_hi + inc scroll_dest_hi + dex + bne @outerLoop + + ; Clear the last line + lda #LASTROW + sta scroll_dest_hi + lda #$00 + ldy #0 +fullPageLoop: + sta (scroll_dest_lo),y + iny + bne fullPageLoop + inc scroll_dest_hi +partialPageLoop: + sta (scroll_dest_lo),y + iny + cpy #BYTESPERCHARROW - 256 ; Only clear up to the 64th byte (256 + 64 == 320) + bne partialPageLoop + + pla + tax + pla + tay + pla + rts + +;----------------------------------------------------------------------------------- +; DrawCircle - Draws a circle in video memory of a given radius at a given coord +;----------------------------------------------------------------------------------- +; x - _x1cord (16-bit) +; y - _y1cord (16-bit) +; radius - _y2cord (16-bit) +;----------------------------------------------------------------------------------- +; Implements the midpoint circle algorithm without floating point or trig functions +;----------------------------------------------------------------------------------- +; int x = radius; +; int y = 0; +; int err = 0; +; +; while (x >= y) +; { +; SETPIXEL(x0 + x, y0 + y, val); +; SETPIXEL(x0 + y, y0 + x, val); +; SETPIXEL(x0 - y, y0 + x, val); +; SETPIXEL(x0 - x, y0 + y, val); +; SETPIXEL(x0 - x, y0 - y, val); +; SETPIXEL(x0 - y, y0 - x, val); +; SETPIXEL(x0 + y, y0 - x, val); +; SETPIXEL(x0 + x, y0 - y, val); +; +; y++; +; err += 1 + 2 * y; +; if (2 * (err - x) + 1 > 0) { +; x--; +; err += 1 - 2 * x; +; } +; } +;----------------------------------------------------------------------------------- + +_DrawCircle: lda _x1cord ; x0 = _x1cord + sta x0 + lda _x1cord+1 + sta x0+1 + lda _y1cord ; y0 = _y1cord + sta y0 + lda _y1cord+1 + sta y0+1 + + lda _y2cord ; x = radius + sta xval + lda _y2cord+1 + sta xval+1 + + lda #$0 ; yval = 0; + sta yval + sta yval+1 + sta err ; err = 0; + sta err+1 +circleloop: + lda xval+1 ; if (xval < yval) we're done; + sec + cmp yval+1 + bcc doneCircle ; if high byteof yval is greater, we can draw + bne doCircle ; it not greater and not equal, is less, so done + lda xval ; in other cases we need to compare the LSB next + cmp yval + bcs doCircle ; if it's less, but MSB was equal, we go draw + +doneCircle: rts + +doCircle: lda x0 ; Draw the first of 8 symmetric quadrant copies + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 - x, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + sec + sbc xval + sta _y1cord + lda y0+1 + sbc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 - x, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 - y, val); + + lda x0 + sec + sbc xval + sta _x1cord + lda x0+1 + sbc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - x, y0 + y, val); + + lda x0 + clc + adc yval + sta _x1cord + lda x0+1 + adc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + y, y0 + x, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + clc + adc yval + sta _y1cord + lda y0+1 + adc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 + y, val); + + lda x0 + clc + adc xval + sta _x1cord + lda x0+1 + adc xval+1 + sta _x1cord+1 + lda y0 + sec + sbc yval + sta _y1cord + lda y0+1 + sbc yval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 + x, y0 - y, val); + + lda x0 + sec + sbc yval + sta _x1cord + lda x0+1 + sbc yval+1 + sta _x1cord+1 + lda y0 + clc + adc xval + sta _y1cord + lda y0+1 + adc xval+1 + sta _y1cord+1 + jsr _SetPixel ; SETPIXEL(x0 - y, y0 + x, val); + + inc yval ; yval++; + bne :+ + inc yval+1 + +: lda yval ; temp = 2 * yval + 1; + asl + sta temp + lda yval+1 + rol + sta temp+1 + inc temp + bne :+ + inc temp+1 +: + lda err ; err += temp + clc + adc temp + sta err + lda err+1 + adc temp+1 + sta err+1 + ; if (2 * (err - xval) + 1 > 0) then dec xval + lda err ; temp = err-xval + sec + sbc xval + sta temp + lda err+1 + sbc xval+1 + sta temp+1 + + asl temp ; temp = 2*(err-xval)+1 + rol temp+1 + inc temp + bne :+ + inc temp+1 +: + lda temp+1 ; if (temp > 0) we'll dec xval + bmi doneLoop ; less than zero, so no dec + bne decxval ; if not zero, go ahead and dec + + lda temp ; MSB is zero so now check the LSB + beq doneLoop ; both bytes are zero, so no dec + +decxval: lda xval ; xval-- + bne :+ + dec xval+1 +: dec xval + +updateerr: lda xval ; temp = xval * 2 + asl + sta temp + lda xval+1 + rol + sta temp+1 + + lda #1 ; temp2 == 1-temp == 1-(xval*2) + sec + sbc temp + sta temp2 + lda #0 + sbc temp+1 + sta temp2+1 + + lda err ; err += 1-(xval*2) + clc + adc temp2 + sta err + lda err+1 + adc temp2+1 + sta err+1 + +doneLoop: jmp circleloop + +;----------------------------------------------------------------------------------- +; Character set translation tables +;----------------------------------------------------------------------------------- + +ascToPetTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$20,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$5f + .byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f + .byte $e0,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$e9,$ea,$eb,$ec,$ed,$ee,$ef + .byte $f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff + +; PETSCI to Ascii lookup table - not current used, so commented out, but can be used to map fonts +; +; + petToAscTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$09,$0d,$11,$93,$0a,$0e,$0f + .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f + .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f + .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f + .byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f + .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f + .byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf + .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df + .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f + .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + .byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f + .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f + .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af + .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf + +;----------------------------------------------------------------------------------- +; PetToAsc - Convert a PETSCII character to ASCII +;----------------------------------------------------------------------------------- +; A - Character to convert +;----------------------------------------------------------------------------------- + +_AscToPet: tay + lda ascToPetTable, y + rts + +_PetToAsc: tay + lda petToAscTable, Y + rts + +;----------------------------------------------------------------------------------- +; ReverseBits - Reverse the bits in a byte +;----------------------------------------------------------------------------------- +; A = octet to be reversed +;----------------------------------------------------------------------------------- + +_ReverseBits: + ldx #8 ; set counter to 8 (for 8 bits) + lda #0 ; initialize A to 0 + sta temp ; clear result byte (accumulator for the reversed octet) +: asl ; shift leftmost bit of input into carry + lda temp ; load the temporary result byte into A + ror ; rotate carry into leftmost bit of result + sta temp ; store the updated result back into memory + dex ; decrement counter + bne :- ; repeat until all bits are processed + lda temp ; load the final reversed byte into A + rts ; return with result in A + +;----------------------------------------------------------------------------------- +; LoadFont - Makes sure the font data is ready to use. This usually requires +; reversing the bits so that they match the bit order of the screen +;----------------------------------------------------------------------------------- + +_LoadFont: ldx #3 + lda #<_font8x8_basic + sta adp1_lo + lda #>_font8x8_basic + sta adp1_hi + ldy #0 +@loop: lda (adp1), y + jsr _ReverseBits + sta (adp1), y + iny + bne @loop + + inc adp1_lo + bne :+ + inc adp1_hi +: dex + bne @loop + rts + +ScreenLineAddresses: + .word SCREEN + 0 * BYTESPERCHARROW, SCREEN + 1 * BYTESPERCHARROW + .word SCREEN + 2 * BYTESPERCHARROW, SCREEN + 3 * BYTESPERCHARROW + .word SCREEN + 4 * BYTESPERCHARROW, SCREEN + 5 * BYTESPERCHARROW + .word SCREEN + 6 * BYTESPERCHARROW, SCREEN + 7 * BYTESPERCHARROW + .word SCREEN + 8 * BYTESPERCHARROW, SCREEN + 9 * BYTESPERCHARROW + .word SCREEN + 10 * BYTESPERCHARROW, SCREEN + 11 * BYTESPERCHARROW + .word SCREEN + 12 * BYTESPERCHARROW, SCREEN + 13 * BYTESPERCHARROW + .word SCREEN + 14 * BYTESPERCHARROW, SCREEN + 15 * BYTESPERCHARROW + .word SCREEN + 16 * BYTESPERCHARROW, SCREEN + 17 * BYTESPERCHARROW + .word SCREEN + 18 * BYTESPERCHARROW, SCREEN + 19 * BYTESPERCHARROW + .word SCREEN + 20 * BYTESPERCHARROW, SCREEN + 21 * BYTESPERCHARROW + .word SCREEN + 22 * BYTESPERCHARROW, SCREEN + 23 * BYTESPERCHARROW + .word SCREEN + 24 * BYTESPERCHARROW + .assert( (* - ScreenLineAddresses) = ROWSPERCOLUMN * 2), error + +;----------------------------------------------------------------------------------- +; DrawChar - Draws an ASCII character at char location x, y +;----------------------------------------------------------------------------------- +; 0 <= x < 40 +; 0 <= y < 25 +; Preserves all registers, but its not very threadsafe or reentrant +;----------------------------------------------------------------------------------- + +_DrawChar: sty tempy + stx tempx + sta tempa + + tya ; Get the address in screen memory where this + asl ; character X/Y cursor pos should be drawn + tay + txa + clc + adc ScreenLineAddresses, y + sta dest_lo + lda ScreenLineAddresses+1, y + adc #0 + sta dest_hi + + lda #0 ; Get the address in font memory where this + sta src_hi ; Petscii chracter lives (after conversion from + lda tempa ; ascii) + + sty temp2 + jsr _AscToPet + ldy temp2 + + asl + rol src_hi + asl + rol src_hi + asl + rol src_hi + clc + adc #<_font8x8_basic ; Add the base address of the font table to the offset + sta src_lo + lda src_hi + adc #>_font8x8_basic + sta src_hi + + ldy #0 ; opy the character def to the screen, one byte at a time + ldx #0 +: lda (src), y ; Copy this byte from the character def to the screen target + sta (dest, x) + lda dest_lo ; Advance to the next "scanline", or pixel row, down + clc + adc #BYTESPERROW + sta dest_hi + + iny + cpy #8 + bne :- + + ldy tempy + ldx tempx + lda tempa + rts + +;----------------------------------------------------------------------------------- +; CursorOn - Turns on the text cursor and draws it at the current cursor pos +;----------------------------------------------------------------------------------- + +CursorOn: ldx _cursorX + ldy _cursorY + lda #'_' + jsr _DrawChar + rts + +CursorOff: ldx _cursorX + ldy _cursorY + lda #' ' + jsr _DrawChar + rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII char in A at the current cursor pos, saves all regs +;----------------------------------------------------------------------------------- + +_CharOut: sta temp + lda #0 + sta temp+1 + txa + pha + tya + pha + + ldx #temp + jsr _DrawText + + pla + tay + pla + tax + rts + +;----------------------------------------------------------------------------------- +; Backspace - Erase the current character and move back one position. Does not +; move back up to previous line +;----------------------------------------------------------------------------------- + +Backspace: lda _cursorX + beq colzero + jsr CursorOff + dec _cursorX + jsr CursorOn +colzero: rts + +;----------------------------------------------------------------------------------- +; DrawText - Draws an ASCII string at the current cursor position +;----------------------------------------------------------------------------------- +; XY - Pointer to the string to draw, stops on NUL or 255 chars later +;----------------------------------------------------------------------------------- + +_DrawText: stx adp1_lo + sty adp1_hi + jsr CursorOff + + ldy #0 +checkHWrap: lda _cursorX + cmp #CHARSPERROW + bcc checkVWrap + lda #0 + sta _cursorX + inc _cursorY + +checkVWrap: lda _cursorY + cmp #ROWSPERCOLUMN + bcc loadChar + jsr _ScrollScreen + lda #ROWSPERCOLUMN-1 + sta _cursorY + +loadChar: lda (adp1), y + beq doneText + + cmp #$0a + bne :+ + + lda #0 ; Back to the left edge + sta _cursorX + inc _cursorY ; Advance to the next line + iny + bne checkHWrap + +: sty temp + ldx _cursorX + ldy _cursorY + jsr _DrawChar + ldy temp + inc _cursorX + iny + bne checkHWrap + +doneText: jsr CursorOn + rts + +demoText1: .byte " *** COMMODORE KIM-1 SHELL V0.1 ***", $0A, $0A + .byte " 60K RAM SYSTEM. 49152 BYTES FREE.", $0A, $0A, $00 +readyText: .byte $0A,"READY.", $0A, 00 + +_Demo: jsr _ClearScreen + lda #0 + sta _cursorX + sta _cursorY + ldx #demoText1 + jsr _DrawText + rts + +_Ready: ldx #readyText + jsr _DrawText + rts + + +;----------------------------------------------------------------------------------- +; DrawLine - Draws a line between two points +;----------------------------------------------------------------------------------- +; _x1cord (16-bit) +; _y1cord ( 8-bit) +; _x2cord (16-bit) +; _y2cord ( 8-bit) +;----------------------------------------------------------------------------------- +; Implements something like Bresenham's algorithm for drawing a line: +;----------------------------------------------------------------------------------- +; void DrawLine(int x0, int y0, int x1, int y1, byte val) +; { +; int dx = abs(_x2cord - _x1cord), sx = _x1cord < _x2cord ? 1 : -1; +; int dy = abs(_y2cord - _y1cord), sy = _y1cord < _y2cord ? 1 : -1; +; int err = (dx > dy ? dx : -dy) / 2, e2; +; +; while (1) +; { +; SETPIXEL(_x1cord, _y1cord, val); +; +; if (_x1cord == _x2cord && _y1cord == _y2cord) +; break; +; +; e2 = err; +; +; if (e2 > -dx) +; { +; err -= dy; +; _x1cord += sx; +; } +; if (e2 < dy) +; { +; err += dx; +; _y1cord += sy; +; } +; } +; } +;----------------------------------------------------------------------------------- + +_DrawLine: sta pixel + + ldx #$01 ; positive x-step for now + stx sx + + ; Calculate dx = (x2cord - X1cord) and see if its positive or not + + lda _x2cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x1cord + sta dx + lda _x2cord+1 + sbc _x1cord+1 + sta dx+1 + bpl calcdy ; dx is positive (dx >= 0), so we're good + + ; dx was negative (dx < 0), so we set sx to -1 and get the absolute + ; value by subtracting the other direction + + ldx #$FF ; negative x-step + stx sx + lda _x1cord ; Calculate dx = (x2cord - X1cord) + sec + sbc _x2cord + sta dx + lda _x1cord+1 + sbc _x2cord+1 + sta dx+1 + + ; Calculate dy = (y2cord - y1cord) and see if its positive or not + +calcdy: ldx #$01 ; positive y-step for now + stx sy + lda _y2cord + sec + sbc _y1cord + sta dy + bcs positivedy ; If y2cord > y1cord, then dy is positive and we're good + + ; dy was negative (dy < 0), so we set sy to -1 and get the absolute value + + ldx #$FF ; negative y-step + stx sy + lda _y1cord + sec + sbc _y2cord + sta dy + + ; Now we have dx and dy, so we can calculate err, but first we need + ; to see if dx > dy or not + +positivedy: lda dx+1 ; Check if dx > dy (both are always positive now) + bne dxgt ; If MSB of dx is greater than zero, then dx > dy since dy is 8-bits + lda dy + cmp dx + bcs dygte + +dxgt: lda dx ; We found dx>dy so set err = dx / 2 + sta err + lda dx+1 + lsr + sta err+1 ; err = dx/2 + ror err + jmp loop + +dygte: lda #0 ; we found dx <= dy so set err = -dy / 2 + sec + sbc dy ; else err = -dy / 2 + ror + ora #$80 + sta err + lda #$FF + sta err+1 + + ; Now we have dx, dy, and err, so we can start drawing pixels + +loop: lda pixel + beq clearpixel + jsr _SetPixel ; Plot the current _x1cord, _y1cord + jmp next +clearpixel: jsr _ClearPixel ; Clear the current _x1cord, _y1cord + +next: lda _x1cord ; if (_x1cord == _x2cord && _y1cord == _y2cord) then we rts + cmp _x2cord + bne noteq + lda _y1cord + cmp _y2cord + bne noteq + lda _x1cord+1 + cmp _x2cord+1 + bne noteq + + rts + +noteq: lda err ; e2 = err + sta e2 + lda err+1 + sta e2+1 + + ; Check the two update conditions for x and y, and update if needed + + lda e2 ; if (e2 > -dx) is the same as if (e2 + dx > 0), so we test that because its easier + clc ; If its true then we dec err and inc _x1cord + adc dx + sta temp + lda e2+1 + adc dx+1 + bmi doneupdatex ; If result is negative, then e2 + dx < 0, so we don't dec err or inc _x1cord + bne stepx ; If MSB is non-zero, then e2 + dx > 0, so we DO dec err and inc _x1cord + lda temp ; If result is zero in MSB, then we check the LSB here + beq doneupdatex ; If LSB is zero, then we don't dec err or inc _x1cord + ; We already know e2 + dx > 0, so LSB can't be negative +stepx: lda sx + bmi decx +incxval: inc _x1cord ; _x1cord += 1 because sx == 1 + bne updatexerr + inc _x1cord+1 + jmp updatexerr + +decx: lda _x1cord ; _x1cord += 1 because sx == 1 + sec + sbc #1 + sta _x1cord + lda _x1cord+1 + sbc #0 + sta _x1cord+1 + +updatexerr: lda err ; err -= dy + sec + sbc dy + sta err + lda err+1 + sbc #0 + sta err+1 + +doneupdatex: lda e2+1 ; if (e2 < dy) then we inc err and inc _y1cord + bmi updateerry ; If MSB is negative, then e2 < dy, so we inc err and inc _y1cord + bne noupdatey ; If the MSB of e2 is set and positive, then we know e2 > dy, so we don't inc err or inc _y1cord + lda e2 + sec + sbc dy + beq noupdatey ; e2 - dy == 0 so we don't inc err or inc _y1cord + bcs noupdatey ; if e2 was large enough that carry never cleared, then e2 > dy do no update + +updateerry: lda err ; err += dx + clc + adc dx + sta err + lda err+1 + adc dx+1 + sta err+1 + +stepy: lda _y1cord + clc + adc sy + sta _y1cord + +noupdatey: jmp loop + +_getch: jsr $1E5A ; Get character using Monitor ROM call + and #$7F ; Clear top bit + cmp #$0D ; Check for '\r' + bne gotch ; ...if CR character + lda #$0A ; Replace with '\n' +gotch: rts From 5acfb027941d3a8adac538078b7ccc587775af60 Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sat, 3 Feb 2024 16:20:17 +0100 Subject: [PATCH 14/23] update actions/checkout@v3 -> actions/checkout@v4 and microsoft/setup-msbuild@v1.1 -> microsoft/setup-msbuild@v2. lets see what happens :) --- .github/workflows/build-on-pull-request.yml | 6 +++--- .github/workflows/snapshot-on-push-master.yml | 8 ++++---- .github/workflows/windows-test-scheduled.yml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 6217c42a2..1064776cf 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -19,7 +19,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -62,10 +62,10 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (x86 debug) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=Win32 diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index 571947c9b..f66b1a745 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -18,10 +18,10 @@ jobs: run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (debug) run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug @@ -44,7 +44,7 @@ jobs: - shell: bash run: git config --global core.autocrlf input - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Do some simple style checks shell: bash @@ -97,7 +97,7 @@ jobs: path: cc65-snapshot-win64.zip - name: Get the online documents repo. - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: cc65/doc # this token will expire, if it does, generate a new one as decribed in https://github.com/cc65/cc65/issues/2065 diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index 451b37f79..25f0d3e50 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -43,11 +43,11 @@ jobs: - name: Checkout source if: steps.check-sha.outputs.cache-hit != 'true' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Add msbuild to PATH if: steps.check-sha.outputs.cache-hit != 'true' - uses: microsoft/setup-msbuild@v1.1 + uses: microsoft/setup-msbuild@v2 - name: Build app (MSVC debug) if: steps.check-sha.outputs.cache-hit != 'true' From 3dfe0330003f19680f2afc9a607bea397b5c9fec Mon Sep 17 00:00:00 2001 From: mrdudz Date: Sat, 3 Feb 2024 17:02:08 +0100 Subject: [PATCH 15/23] update actions/upload-artifact@v3->actions/upload-artifact@v4, actions/cache@v3->actions/cache@v4 --- .github/workflows/build-on-pull-request.yml | 2 +- .github/workflows/snapshot-on-push-master.yml | 6 +++--- .github/workflows/windows-test-scheduled.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml index 1064776cf..146964a30 100644 --- a/.github/workflows/build-on-pull-request.yml +++ b/.github/workflows/build-on-pull-request.yml @@ -44,7 +44,7 @@ jobs: shell: bash run: make -j2 doc - name: Upload a documents snapshot. - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs path: ./html diff --git a/.github/workflows/snapshot-on-push-master.yml b/.github/workflows/snapshot-on-push-master.yml index f66b1a745..d58bff8ed 100644 --- a/.github/workflows/snapshot-on-push-master.yml +++ b/.github/workflows/snapshot-on-push-master.yml @@ -86,12 +86,12 @@ jobs: mv cc65.zip cc65-snapshot-win32.zip - name: Upload a 32-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win32 path: cc65-snapshot-win32.zip - name: Upload a 64-bit Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-win64 path: cc65-snapshot-win64.zip @@ -120,7 +120,7 @@ jobs: - name: Package offline documents. run: 7z a cc65-snapshot-docs.zip ./html/*.* - name: Upload a Documents Snapshot Zip - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cc65-snapshot-docs path: cc65-snapshot-docs.zip diff --git a/.github/workflows/windows-test-scheduled.yml b/.github/workflows/windows-test-scheduled.yml index 25f0d3e50..f72254273 100644 --- a/.github/workflows/windows-test-scheduled.yml +++ b/.github/workflows/windows-test-scheduled.yml @@ -30,7 +30,7 @@ jobs: run: mkdir ~/.cache-sha - name: Cache SHA - uses: actions/cache@v3 + uses: actions/cache@v4 id: check-sha with: path: ~/.cache-sha From 788ae82d30bcf29da0e160cd47532594780fe22b Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Fri, 9 Feb 2024 00:09:16 +0000 Subject: [PATCH 16/23] Fixes to serial driver implementation --- asminc/lynx.inc | 34 +++++++------- libsrc/lynx/crt0.s | 2 +- libsrc/lynx/ser/lynx-comlynx.s | 81 ++++++++++++++++++++-------------- 3 files changed, 67 insertions(+), 50 deletions(-) diff --git a/asminc/lynx.inc b/asminc/lynx.inc index 5ae17f6ef..d65b7f8a9 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -267,22 +267,26 @@ MIKEYHREV = $FD88 MIKEYSREV = $FD89 IODIR = $FD8A IODAT = $FD8B -TxIntEnable = %10000000 -RxIntEnable = %01000000 -TxParEnable = %00010000 -ResetErr = %00001000 -TxOpenColl = %00000100 -TxBreak = %00000010 -ParEven = %00000001 -TxReady = %10000000 -RxReady = %01000000 -TxEmpty = %00100000 -RxParityErr = %00010000 -RxOverrun = %00001000 -RxFrameErr = %00000100 -RxBreak = %00000010 -ParityBit = %00000001 + SERCTL = $FD8C +; SERCTL bit definitions for write operations +TXINTEN = $80 +RXINTEN = $40 +PAREN = $10 +RESETERR = $08 +TXOPEN = $04 +TXBRK = $02 +PAREVEN = $01 +; SERCTL bit definitions for read operations +TXRDY = $80 +RXRDY = $40 +TXEMPTY = $20 +PARERR = $10 +OVERRUN = $08 +FRAMERR = $04 +RXBRK = $02 +PARBIT = $01 + SERDAT = $FD8D SDONEACK = $FD90 CPUSLEEP = $FD91 diff --git a/libsrc/lynx/crt0.s b/libsrc/lynx/crt0.s index 238a2c99d..030f523e9 100644 --- a/libsrc/lynx/crt0.s +++ b/libsrc/lynx/crt0.s @@ -68,7 +68,7 @@ MikeyInitData: .byte $9e,$18,$68,$1f,$00,$00,$00,$00,$00,$ff,$1a,$1b,$04,$0d,$2 ; Disable the TX/RX IRQ; set to 8E1. - lda #%00011101 + lda #PAREN|RESETERR|TXOPEN|PAREVEN ; #%00011101 sta SERCTL ; Clear all pending interrupts. diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 8aa3c838e..85703867b 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -73,7 +73,12 @@ SER_UNINSTALL: ; Must return an SER_ERR_xx code in a/x. SER_CLOSE: - ; Disable interrupts + ; Disable interrupts and stop timer 4 (serial) + lda #$0C ; TXOPEN|RESETERR + sta SERCTL + lda #$00 ; Disable count and no reload + sta TIM4CTLA + ; Done, return an error code lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -108,7 +113,7 @@ SER_OPEN: stz TxPtrIn stz TxPtrOut - ; clock = 8 * 15625 + ; source period is 1 us lda #%00011000 sta TIM4CTLA ldy #SER_PARAMS::BAUDRATE @@ -118,7 +123,7 @@ SER_OPEN: cmp #SER_BAUD_62500 beq setbaudrate - ldx #2 + ldx #3 cmp #SER_BAUD_31250 beq setbaudrate @@ -194,13 +199,14 @@ SER_OPEN: lda #SER_ERR_BAUD_UNAVAIL ldx #0 ; return value is char rts + setprescaler: stx TIM4CTLA bra baudsuccess setbaudrate: stx TIM4BKUP baudsuccess: - ldx #TxOpenColl|ParEven + ldx #TXOPEN|PAREVEN stx contrl ldy #SER_PARAMS::DATABITS ; Databits lda (ptr1),y @@ -218,15 +224,15 @@ baudsuccess: beq checkhs cmp #SER_PAR_SPACE bne @L0 - ldx #TxOpenColl + ldx #TXOPEN stx contrl bra checkhs @L0: - ldx #TxParEnable|TxOpenColl|ParEven + ldx #PAREN|TXOPEN|PAREVEN stx contrl cmp #SER_PAR_EVEN beq checkhs - ldx #TxParEnable|TxOpenColl + ldx #PAREN|TXOPEN stx contrl checkhs: ldx contrl @@ -237,7 +243,7 @@ checkhs: bne invparameter lda SERDAT lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR ; Turn on interrupts for receive sta SERCTL lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -279,24 +285,26 @@ SER_PUT: ina cmp TxPtrOut bne PutByte + lda #SER_ERR_OVERFLOW ldx #0 ; return value is char rts + PutByte: ldy TxPtrIn txa sta TxBuffer,y inc TxPtrIn - bit TxDone - bmi @L1 + bit TxDone ; Check bit 7 of TxDone (TXINTEN) + bmi @L1 ; Was TXINTEN already set? php sei - lda contrl - ora #TxIntEnable|ResetErr - sta SERCTL ; Allow TX-IRQ to hang RX-IRQ + lda contrl ; contrl does not include RXINTEN setting + ora #TXINTEN|RESETERR + sta SERCTL ; Allow TX-IRQ to hang RX-IRQ (no receive while transmitting) sta TxDone - plp + plp ; Restore processor and interrupt enable @L1: lda #SER_ERR_OK .assert SER_ERR_OK = 0, error @@ -308,7 +316,7 @@ PutByte: ; Must return an SER_ERR_xx code in a/x. SER_STATUS: - ldy SerialStat + lda SerialStat ldx #$00 sta (ptr1,x) txa ; Return code = 0 @@ -342,27 +350,32 @@ SER_IRQ: @L0: bit TxDone bmi @tx_irq ; Transmit in progress - ldx SERDAT - lda SERCTL - and #RxParityErr|RxOverrun|RxFrameErr|RxBreak - beq @rx_irq + + ldx SERDAT ; Read received data + lda contrl + and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD + ora #OVERRUN|FRAMERR|RXBRK + bit SERCTL ; Compare with SERCTL + + beq @rx_irq ; No errors so far + tsb SerialStat ; Save error condition - bit #RxBreak + bit #RXBRK ; Check for break signal beq @noBreak + stz TxPtrIn ; Break received - drop buffers stz TxPtrOut stz RxPtrIn stz RxPtrOut @noBreak: lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR sta SERCTL - lda #$10 - sta INTRST bra @IRQexit + @rx_irq: lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR sta SERCTL txa ldx RxPtrIn @@ -370,20 +383,22 @@ SER_IRQ: txa inx -@cont0: cpx RxPtrOut beq @1 stx RxPtrIn - lda #SERIAL_INTERRUPT - sta INTRST bra @IRQexit @1: sta RxPtrIn lda #$80 tsb SerialStat + lda contrl + ora #RXINTEN|RESETERR + sta SERCTL + bra @IRQexit + @tx_irq: - ldx TxPtrOut ; Has all bytes been sent? + ldx TxPtrOut ; Have all bytes been sent? cpx TxPtrIn beq @allSent @@ -393,24 +408,22 @@ SER_IRQ: @exit1: lda contrl - ora #TxIntEnable|ResetErr + ora #TXINTEN|RESETERR sta SERCTL - lda #SERIAL_INTERRUPT - sta INTRST bra @IRQexit @allSent: lda SERCTL ; All bytes sent - bit #TxEmpty + bit #TXEMPTY beq @exit1 bvs @exit1 stz TxDone lda contrl - ora #RxIntEnable|ResetErr + ora #RXINTEN|RESETERR ; Re-enable receive interrupt sta SERCTL +@IRQexit: lda #SERIAL_INTERRUPT sta INTRST -@IRQexit: clc rts From 014f85f226b38f434722ecc085d621e9d3d730d1 Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Fri, 9 Feb 2024 10:42:52 +0000 Subject: [PATCH 17/23] Fixed baud rates --- libsrc/lynx/ser/lynx-comlynx.s | 86 +++++++++++++--------------------- 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 85703867b..9564bcb4f 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -113,12 +113,12 @@ SER_OPEN: stz TxPtrIn stz TxPtrOut - ; source period is 1 us - lda #%00011000 - sta TIM4CTLA ldy #SER_PARAMS::BAUDRATE lda (ptr1),y + ; Source period is 1 us + ldy #%00011000 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_1 + ldx #1 cmp #SER_BAUD_62500 beq setbaudrate @@ -139,6 +139,10 @@ SER_OPEN: cmp #SER_BAUD_2400 beq setbaudrate + ldx #68 + cmp #SER_BAUD_1800 + beq setbaudrate + ldx #103 cmp #SER_BAUD_1200 beq setbaudrate @@ -147,65 +151,48 @@ SER_OPEN: cmp #SER_BAUD_600 beq setbaudrate - ; clock = 6 * 15625 - ldx #%00011010 - stx TIM4CTLA + ; Source period is 4 us + ldy #%00011011 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_8 - ldx #12 - cmp #SER_BAUD_7200 - beq setbaudrate - - ldx #25 - cmp #SER_BAUD_3600 - beq setbaudrate - - ldx #207 - stx TIM4BKUP - - ; clock = 4 * 15625 - ldx #%00011100 + ldx #51 cmp #SER_BAUD_300 - beq setprescaler + beq setbaudrate - ; clock = 6 * 15625 - ldx #%00011110 + ldx #103 cmp #SER_BAUD_150 - beq setprescaler + beq setbaudrate - ; clock = 1 * 15625 - ldx #%00011111 - stx TIM4CTLA - cmp #SER_BAUD_75 - beq baudsuccess + ldx #115 + cmp #SER_BAUD_134_5 + beq setbaudrate ldx #141 cmp #SER_BAUD_110 beq setbaudrate - ; clock = 2 * 15625 - ldx #%00011010 - stx TIM4CTLA - ldx #68 - cmp #SER_BAUD_1800 + ; Source period is 32 us + ldy #%00011101 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_32 + + ldx #51 + cmp #SER_BAUD_75 beq setbaudrate - ; clock = 6 * 15625 - ldx #%00011110 - stx TIM4CTLA - ldx #231 - cmp #SER_BAUD_134_5 + ldx #68 + cmp #SER_BAUD_56_875 + beq setbaudrate + + ldx #77 + cmp #SER_BAUD_50 beq setbaudrate lda #SER_ERR_BAUD_UNAVAIL ldx #0 ; return value is char rts -setprescaler: - stx TIM4CTLA - bra baudsuccess setbaudrate: + sty TIM4CTLA stx TIM4BKUP -baudsuccess: + ldx #TXOPEN|PAREVEN stx contrl ldy #SER_PARAMS::DATABITS ; Databits @@ -368,15 +355,9 @@ SER_IRQ: stz RxPtrIn stz RxPtrOut @noBreak: - lda contrl - ora #RXINTEN|RESETERR - sta SERCTL - bra @IRQexit + bra @exit0 @rx_irq: - lda contrl - ora #RXINTEN|RESETERR - sta SERCTL txa ldx RxPtrIn sta RxBuffer,x @@ -392,10 +373,7 @@ SER_IRQ: sta RxPtrIn lda #$80 tsb SerialStat - lda contrl - ora #RXINTEN|RESETERR - sta SERCTL - bra @IRQexit + bra @exit0 @tx_irq: ldx TxPtrOut ; Have all bytes been sent? @@ -418,6 +396,8 @@ SER_IRQ: beq @exit1 bvs @exit1 stz TxDone + +@exit0: lda contrl ora #RXINTEN|RESETERR ; Re-enable receive interrupt sta SERCTL From 65bce9ecdeda7a2214a27fafa4ccbfcd5daa5449 Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Fri, 9 Feb 2024 12:54:00 +0000 Subject: [PATCH 18/23] Implemented mark and space checks. --- libsrc/lynx/ser/lynx-comlynx.s | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 9564bcb4f..3b6f18af6 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -151,7 +151,7 @@ SER_OPEN: cmp #SER_BAUD_600 beq setbaudrate - ; Source period is 4 us + ; Source period is 8 us ldy #%00011011 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_8 ldx #51 @@ -341,8 +341,9 @@ SER_IRQ: ldx SERDAT ; Read received data lda contrl and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD + tay ora #OVERRUN|FRAMERR|RXBRK - bit SERCTL ; Compare with SERCTL + bit SERCTL ; Check error flags in SERCTL beq @rx_irq ; No errors so far @@ -358,6 +359,15 @@ SER_IRQ: bra @exit0 @rx_irq: + tya + bne @2 ; Parity was enabled so no marker bit check needed + + lda contrl + eor SERCTL ; Should match current parity bit + and #PARBIT ; Check for mark or space value + bne @exit0 + +@2: txa ldx RxPtrIn sta RxBuffer,x From 6cf8ee8eb563f50ceef74605a95e202054e2991c Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Sat, 10 Feb 2024 21:15:05 +0000 Subject: [PATCH 19/23] Removed baud rates from 150 and lower. Fixed tab Replaced uploader references to SERIAL_INTERRUPT --- libsrc/lynx/ser/lynx-comlynx.s | 31 ++----------------------------- libsrc/lynx/uploader.s | 6 +++--- 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 3b6f18af6..486981184 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -158,33 +158,6 @@ SER_OPEN: cmp #SER_BAUD_300 beq setbaudrate - ldx #103 - cmp #SER_BAUD_150 - beq setbaudrate - - ldx #115 - cmp #SER_BAUD_134_5 - beq setbaudrate - - ldx #141 - cmp #SER_BAUD_110 - beq setbaudrate - - ; Source period is 32 us - ldy #%00011101 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_32 - - ldx #51 - cmp #SER_BAUD_75 - beq setbaudrate - - ldx #68 - cmp #SER_BAUD_56_875 - beq setbaudrate - - ldx #77 - cmp #SER_BAUD_50 - beq setbaudrate - lda #SER_ERR_BAUD_UNAVAIL ldx #0 ; return value is char rts @@ -342,8 +315,8 @@ SER_IRQ: lda contrl and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD tay - ora #OVERRUN|FRAMERR|RXBRK - bit SERCTL ; Check error flags in SERCTL + ora #OVERRUN|FRAMERR|RXBRK + bit SERCTL ; Check presence of relevant error flags in SERCTL beq @rx_irq ; No errors so far diff --git a/libsrc/lynx/uploader.s b/libsrc/lynx/uploader.s index df3e5df40..5ce21b489 100644 --- a/libsrc/lynx/uploader.s +++ b/libsrc/lynx/uploader.s @@ -40,14 +40,14 @@ cont1: bra loop1 read_byte: - bit SERCTL + bit SERCTL ; Check for RXRDY ($40) bvc read_byte lda SERDAT rts _UpLoaderIRQ: lda INTSET - and #$10 + and #SERIAL_INTERRUPT bne @L0 clc rts @@ -69,7 +69,7 @@ again: ; last action : clear interrupt ; exit: - lda #$10 + lda #SERIAL_INTERRUPT sta INTRST clc rts From acff429eb8788ab6f1b1e40bb8cef0ac7de94930 Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Sun, 11 Feb 2024 15:33:22 +0000 Subject: [PATCH 20/23] Added redeye check for SER_HS_SW handshake --- asminc/lynx.inc | 16 +++++++++++++--- libsrc/lynx/ser/lynx-comlynx.s | 12 ++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/asminc/lynx.inc b/asminc/lynx.inc index d65b7f8a9..0d34e1c7c 100644 --- a/asminc/lynx.inc +++ b/asminc/lynx.inc @@ -259,16 +259,26 @@ SND_INTERRUPT = TIMER7_INTERRUPT INTRST = $FD80 INTSET = $FD81 + MAGRDY0 = $FD84 MAGRDY1 = $FD85 AUDIN = $FD86 SYSCTL1 = $FD87 MIKEYHREV = $FD88 MIKEYSREV = $FD89 -IODIR = $FD8A -IODAT = $FD8B -SERCTL = $FD8C +IODIR = $FD8A +IODAT = $FD8B +; IODIR and IODAT bit definitions +AUDIN_BIT = $10 ; Note that there is also the address AUDIN +READ_ENABLE = $10 ; Same bit for AUDIN_BIT +RESTLESS = $08 +NOEXP = $04 ; If set, redeye is not connected +CART_ADDR_DATA = $02 +CART_POWER_OFF = $02 ; Same bit for CART_ADDR_DATA +EXTERNAL_POWER = $01 + +SERCTL = $FD8C ; SERCTL bit definitions for write operations TXINTEN = $80 RXINTEN = $40 diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 486981184..7201264b7 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -200,7 +200,18 @@ checkhs: ldy #SER_PARAMS::HANDSHAKE ; Handshake lda (ptr1),y cmp #SER_HS_NONE + beq redeye_ok + cmp #SER_HS_SW ; Software handshake will check for connected redeye bne invparameter + + lda IODAT + and #NOEXP ; Check if redeye bit flag is unset + beq redeye_ok + lda #SER_ERR_NO_DEVICE ; ComLynx cable is not inserted + ldx #0 + rts + +redeye_ok: lda SERDAT lda contrl ora #RXINTEN|RESETERR ; Turn on interrupts for receive @@ -209,6 +220,7 @@ checkhs: .assert SER_ERR_OK = 0, error tax rts + invparameter: lda #SER_ERR_INIT_FAILED ldx #0 ; return value is char From 1deb9e52aec938ae338168035c743b04b91e20d0 Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Sun, 11 Feb 2024 15:46:23 +0000 Subject: [PATCH 21/23] Replaced last literal value for SERCTL --- libsrc/lynx/ser/lynx-comlynx.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 7201264b7..9b007c6e0 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -74,7 +74,7 @@ SER_UNINSTALL: SER_CLOSE: ; Disable interrupts and stop timer 4 (serial) - lda #$0C ; TXOPEN|RESETERR + lda #TXOPEN|RESETERR sta SERCTL lda #$00 ; Disable count and no reload sta TIM4CTLA From 8b172e05bc4c39b53ee1dd05be082c01fea0d7e0 Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Sun, 11 Feb 2024 20:59:08 +0000 Subject: [PATCH 22/23] Applied optimization as per review 42Bastian --- libsrc/lynx/ser/lynx-comlynx.s | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index 9b007c6e0..aa4d71ad3 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -76,8 +76,7 @@ SER_CLOSE: ; Disable interrupts and stop timer 4 (serial) lda #TXOPEN|RESETERR sta SERCTL - lda #$00 ; Disable count and no reload - sta TIM4CTLA + stz TIM4CTLA ; Disable count and no reload ; Done, return an error code lda #SER_ERR_OK From 7d6f3d24d434953679c176708c67a0abc845eaa0 Mon Sep 17 00:00:00 2001 From: Alex Thissen Date: Sun, 11 Feb 2024 23:12:27 +0000 Subject: [PATCH 23/23] Changed sta (ptr1,x) to sta (ptr1) Reset serial status on ser_close Fixed error for saving serial state --- libsrc/lynx/ser/lynx-comlynx.s | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s index aa4d71ad3..c4ae3d5b6 100644 --- a/libsrc/lynx/ser/lynx-comlynx.s +++ b/libsrc/lynx/ser/lynx-comlynx.s @@ -76,7 +76,8 @@ SER_CLOSE: ; Disable interrupts and stop timer 4 (serial) lda #TXOPEN|RESETERR sta SERCTL - stz TIM4CTLA ; Disable count and no reload + stz TIM4CTLA ; Disable count and no reload + stz SerialStat ; Reset status ; Done, return an error code lda #SER_ERR_OK @@ -241,8 +242,8 @@ GetByte: ldy RxPtrOut lda RxBuffer,y inc RxPtrOut + sta (ptr1) ldx #$00 - sta (ptr1,x) txa ; Return code = 0 rts @@ -288,8 +289,8 @@ PutByte: SER_STATUS: lda SerialStat + sta (ptr1) ldx #$00 - sta (ptr1,x) txa ; Return code = 0 rts @@ -327,7 +328,7 @@ SER_IRQ: and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD tay ora #OVERRUN|FRAMERR|RXBRK - bit SERCTL ; Check presence of relevant error flags in SERCTL + and SERCTL ; Check presence of relevant error flags in SERCTL beq @rx_irq ; No errors so far