From 6521e971cdb772c53256d84a54c8d7b2d1ffa632 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Sun, 1 Mar 2026 02:00:23 -0500 Subject: Scrollback! --- Makefile | 4 +- font_dl.asm | 117 ++++++++++++++++++++++++++++++++++----------------------- memsetup.asm | 2 +- size.pl | 6 +-- src/addrs.c | 67 +++++++++++++++++++++++---------- src/addrs.h | 32 ++++++++++++---- src/atari.cfg | 4 +- src/edbox.c | 16 ++++++-- src/keyclick.h | 1 + src/keyclick.s | 53 ++++++++++++++++++++++++++ src/main.c | 3 +- src/screen.c | 50 ++++++++++++++++++------ src/screen.h | 16 ++++++-- uitest/test.c | 6 ++- 14 files changed, 273 insertions(+), 104 deletions(-) create mode 100644 src/keyclick.h create mode 100644 src/keyclick.s diff --git a/Makefile b/Makefile index e48f3c3..7b8aa9a 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,6 @@ memsetup.xex: memsetup.asm font_dl.xex: font_dl.asm font.dat -test: - cl65 -C src/atari.cfg -t atari -o testmain.xex src/addrs.c src/edbox.c src/screen.c uitest/test.c +test: memsetup.xex font_dl.xex + cl65 -C src/atari.cfg -t atari -o testmain.xex src/addrs.c src/edbox.c src/screen.c src/keyclick.s uitest/test.c cat memsetup.xex font_dl.xex testmain.xex > test.xex diff --git a/font_dl.asm b/font_dl.asm index bbb2b98..28599e8 100644 --- a/font_dl.asm +++ b/font_dl.asm @@ -1,56 +1,77 @@ - *= $a000 + + *= $8000 .incbin "font.dat" -dlist + +; screen RAM starts right after the font. +; 7 double-height screens, 50 lines each. +scr0_top + *= * + 1000 +scr1_top + *= * + 1000 +scr2_top + *= * + 1000 + +; can't cross a 4K boundary... + *= $9000 +scr3_top + *= * + 1000 +scr4_top + *= * + 1000 +scr5_top + *= * + 1000 +scr6_top + +; shoehorn the transmit & receive buffers here + *= $a000 +rx_buf ; 512 bytes + *= $a200 +tx_buf ; 512 bytes + *= $a400 + +scr0_bot + *= * + 1000 +scr1_bot + *= * + 1000 +scr2_bot + *= * + 1000 + + *= $b000 +scr3_bot + *= * + 1000 +scr4_bot + *= * + 1000 +scr5_bot + *= * + 1000 +scr6_bot + + *= * + 1000 +dlist_top + *= * + 48 +dlist_bot + *= * + 48 + + *= dlist_top ; used when scrolled up .byte $70, $70, $30 ; 2 8-line blanks, 1 4-line blank .byte $42 ; LMS GR.0 dl_top_lms - .word $a500 ; base address of 1st screen + .word scr0_top + .byte $02, $02, $02, $02, $02, $02, $02, $02 + .byte $02, $02, $02, $02, $02, $02, $02, $02 + .byte $02, $02, $02, $02, $02, $02, $02, $02 ; 24 GR.0 lines + .byte $41 ; JVB + .word dlist_top + + *= dlist_bot ; used when not scrolled up + .byte $70, $70, $30 ; 2 8-line blanks, 1 4-line blank + .byte $42 ; LMS GR.0 +dl_bot_lms + .word scr0_bot .byte $02, $02, $02, $02, $02, $02, $02, $02 .byte $02, $02, $02, $02, $02, $02, $02, $02 .byte $02, $02, $02, $02, $02, $02 ; 22 GR.0 lines - .byte $00 ; 1 blank line - .byte $42 ; another LMS (edit/status box) -dl_bottom_lms - .word $a480 ; edit box base address - .word $02 ; 2nd line of edit box + .byte $42 ; LMS GR.0 +dl_status_lms + .word scr0_bot+920 ; line 23 .byte $41 ; JVB - .word dlist - -status_box - *= * + 80 ; 2 lines - -edit_box - *= * + 160 ; 4 lines - -end_boxes ; currently $a515 - -; screens are 920 bytes each (23 lines * 40), cannot -; cross the 4K boundary at $b000, and must end -; before the ROM (or unwired addresses) at $c000. - -; on a 52K machine (expanded 800 or XL/XE running Translator), -; we'd have another 4K of RAM from $c000-$cfff. With Translator, -; we also could have 2K at $d800-$dfff (the floating point ROM, -; which we don't use)... and we wouldn't have to have the font -; at $a000, it could be plopped right into the OS default location -; at $e000. maybe this will happen someday. meanwhile: - - *= $b000 - (920 * 3) -screen0 - *= * + 920 -screen1 - *= * + 920 -screen2 ; ends 1 byte before $b000 boundary - *= * + 920 -screen3 ; starts at $b000 boundary - *= * + 920 -screen4 - *= * + 920 -screen5 - *= * + 920 -screen6 - *= * + 920 -end_screens ; currently $be60 - -; 7 screens ain't so bad, really. eventually though, we want to -; add scrollback to them, meaning fewer of them. sigh. + .word dlist_bot + diff --git a/memsetup.asm b/memsetup.asm index 49d8149..3fe19d1 100644 --- a/memsetup.asm +++ b/memsetup.asm @@ -1,6 +1,6 @@ *= $2000 memsetup - lda #$a0 + lda #$80 sta $6a ; RAMTOP sta $02e6 ; MEMTOP high lda #0 diff --git a/size.pl b/size.pl index f6cb19f..c38c274 100644 --- a/size.pl +++ b/size.pl @@ -1,11 +1,11 @@ #!/usr/bin/perl -w # from src/atari.cfg: -my $code_start = 0x2000; -my $stack_size = 0x0800; +my $code_start = 0x0700; +my $stack_size = 0x0100; # from memsetup.asm: -my $ramtop = 0xa000; +my $ramtop = 0x8000; open MAP, "client.xex.map" or die $!; while() { diff --git a/src/addrs.c b/src/addrs.c index 8c4ad1b..5154109 100644 --- a/src/addrs.c +++ b/src/addrs.c @@ -1,27 +1,56 @@ #include "addrs.h" -u8 *dlist = u8p(DLIST_ADDR); -u16 *dlist_top_lms = u16p(0xa404); -u16 *dlist_bottom_lms = u16p(0xa41e); -u8 *status_box = u8p(0xa425); /* 80 bytes */ -u8 *edit_box = u8p(0xa475); /* 160 bytes */ +u8 *dlist_top = u8p(DLIST_TOP_ADDR); +u8 *dlist_bot = u8p(DLIST_BOT_ADDR); -u8 *screen_addrs[7] = { - u8p(0xa538), - u8p(0xa8d0), - u8p(0xac68), +u16 *dlist_top_lms = u16p(0xbfa4); +u16 *dlist_bot_lms = u16p(0xbfd4); +u16 *dlist_status_lms = u16p(0xbfed); + +u8 *edit_box = u8p(0x0600); /* up to 256 bytes (page 6) */ +u8 *rx_buf = u8p(0xa000); /* 512 bytes */ +u8 *tx_buf = u8p(0xa200); /* 512 bytes */ + +/* 1000 bytes (25 40-char lines) apiece. */ +u8 *screen_top_addrs[7] = { + u8p(0x8400), + u8p(0x87e8), + u8p(0x8bd0), + u8p(0x9000), + u8p(0x93e8), + u8p(0x97d0), + u8p(0x9bb8) +}; + +/* 1000 bytes (25 40-char lines) apiece. */ +u8 *screen_bot_addrs[7] = { + u8p(0xa400), + u8p(0xa7e8), + u8p(0xabd0), u8p(0xb000), - u8p(0xb398), - u8p(0xb730), - u8p(0xbac8) + u8p(0xb3e8), + u8p(0xb7d0), + u8p(0xbbb8) }; -u8 *screen_botlines[7] = { - u8p(0xa8a8), - u8p(0xac40), - u8p(0xafd8), +/* 40 bytes (1 line), third from the bottom of each _bot_addr */ +u8 *screen_lastlines[7] = { + u8p(0xa770), + u8p(0xab58), + u8p(0xaf40), u8p(0xb370), - u8p(0xb708), - u8p(0xbaa0), - u8p(0xbe38) + u8p(0xb758), + u8p(0xbb40), + u8p(0xbf28) +}; + +/* 80 bytes (2 lines), bottom 2 of each _bot_addr */ +u8 *status_boxes[7] = { + u8p(0xa798), + u8p(0xab80), + u8p(0xaf68), + u8p(0xb398), + u8p(0xb780), + u8p(0xbb68), + u8p(0xbf50) }; diff --git a/src/addrs.h b/src/addrs.h index 6d44a68..dd9f23d 100644 --- a/src/addrs.h +++ b/src/addrs.h @@ -1,15 +1,33 @@ -#define FONT_ADDR_HI 0xa0 -#define DLIST_ADDR 0xa400 +#define FONT_ADDR_HI 0x80 +#define DLIST_TOP_ADDR 0xbfa0 +#define DLIST_BOT_ADDR 0xbfd0 #define u8 unsigned char #define u8p(x) ((unsigned char *)x) #define u16 unsigned int #define u16p(x) ((unsigned int *)x) -extern u8 *dlist; +/* dlist_bot is the main one, dlist_top is used when + scrolled back. */ +extern u8 *dlist_top, *dlist_bot; + extern u16 *dlist_top_lms; -extern u16 *dlist_bottom_lms; -extern u8 *status_box; +extern u16 *dlist_bot_lms; + +/* points to either edit_box or one of the status_boxes[] */ +extern u16 *dlist_status_lms; + +/* only one of these (not one per screen) */ extern u8 *edit_box; -extern u8 *screen_addrs[7]; -extern u8 *screen_botlines[7]; + +/* used for scrollback */ +extern u8 *screen_top_addrs[7]; + +/* normal (non-scrollback) display */ +extern u8 *screen_bot_addrs[7]; + +/* address of last line of _bot, where printing actually happens */ +extern u8 *screen_lastlines[7]; + +/* bottom 2 lines of the _bot display list for each screen */ +extern u8 *status_boxes[7]; diff --git a/src/atari.cfg b/src/atari.cfg index ea190f7..97b265e 100644 --- a/src/atari.cfg +++ b/src/atari.cfg @@ -1,5 +1,5 @@ FEATURES { - STARTADDRESS: default = $2000; + STARTADDRESS: default = $0700; } SYMBOLS { __EXEHDR__: type = import; @@ -16,7 +16,7 @@ MEMORY { # "main program" load chunk MAINHDR: file = %O, start = $0000, size = $0004; - MAIN: file = %O, define = yes, start = %S, size = $BC20 - __STACKSIZE__ - __RESERVED_MEMORY__ - %S; + MAIN: file = %O, define = yes, start = %S, size = $8000 - __STACKSIZE__ - __RESERVED_MEMORY__ - %S; TRAILER: file = %O, start = $0000, size = $0006; } SEGMENTS { diff --git a/src/edbox.c b/src/edbox.c index 9231b50..0903cb9 100644 --- a/src/edbox.c +++ b/src/edbox.c @@ -4,6 +4,7 @@ #include "addrs.h" #include "screen.h" #include "edbox.h" +#include "keyclick.h" /* TODO: tab completion */ @@ -37,7 +38,7 @@ void edbox_show(void) { scr_waitvcount(116); - *dlist_bottom_lms = addr; + *dlist_status_lms = addr; show_cursor(); } @@ -96,6 +97,7 @@ void edbox_readline(char *dest, char len) { } static void special_keystroke(char c) { + keyclick(); OS.ch = 0xff; edbox_putc(c); } @@ -178,11 +180,9 @@ void edbox_keystroke(void) { ; edbox_show(); - hide_cursor(); - OS.invflg = c = 0; + c = 0; - /* XXX: these keys don't click. */ switch(OS.ch) { case 0xa0: /* key: ctrl [ */ c = 0x7b; /* ascii: { */ @@ -197,6 +197,12 @@ void edbox_keystroke(void) { case 0x9c: /* key: ctrl ESC */ c = 0x7e; /* ascii: ~ */ break; + case 0x3c: /* caps */ + OS.shflok ^= 0x40; + keyclick(); + break; + case 0x7c: /* shift-caps */ + case 0xbc: /* ctrl-caps */ case 0x27: /* atari key */ case 0x67: /* ...w/shift */ case 0x97: /* ...w/ctrl */ @@ -207,6 +213,8 @@ void edbox_keystroke(void) { break; } + hide_cursor(); + if(c) { special_keystroke(c); } else { diff --git a/src/keyclick.h b/src/keyclick.h new file mode 100644 index 0000000..b73a212 --- /dev/null +++ b/src/keyclick.h @@ -0,0 +1 @@ +extern void keyclick(void); diff --git a/src/keyclick.s b/src/keyclick.s new file mode 100644 index 0000000..bbe613c --- /dev/null +++ b/src/keyclick.s @@ -0,0 +1,53 @@ + .include "atari.inc" + .export _keyclick + +; keyclick.s - just what you think it is. + +; How do you make it possible to disable the keyclick on the 400/800? +; Don't use the OS K: handler. + + ;.ifdef REAL_KEYCLICK + .if 1 + +; Copied from OS ROM source, 12 bytes of code. Because of the repeated +; stx WSYNC, you don't want to use this if you're using DLIs. +; Preserves A and Y, returns with X=$ff, N set, Z clear. +_keyclick: + ldx #$7f +@rc1: + stx CONSOL + stx WSYNC + dex + bpl @rc1 + rts + + .else + +; Fake keyclick. 25 bytes of code. Sounds just like the real one, but +; it won't mess up your DLIs because it's interruptible (though DLIs +; might mess it up). Loop timing assumes a GR.0 type display list +; with playfield DMA enabled and P/M DMA disabled. +; Preserves A, returns with X=$ff, Y=0, N set, Z clear. +_keyclick: +@wcount: + ldx VCOUNT ; To make the timing consistent, wait until we're sure + cpx #$10 ; that ANTIC is doing DMA. + bne @wcount + + ldx #$7f +@click: ; I tuned this by ear, then cycle-counted afterward. + stx CONSOL ; 4 + ldy #$07 ; 2 + ;lda #0 ; 2 + nop +@c1: + dey ; inner: 2 + nop ; 2 + bne @c1 ; 3 + ; 7 cycles/loop, *7 = 49, -1 for failed BNE + ; 48 + dex ; 2 + bpl @click ; 3 = 61 + rts + + .endif diff --git a/src/main.c b/src/main.c index be40d95..2685da8 100644 --- a/src/main.c +++ b/src/main.c @@ -29,8 +29,7 @@ unsigned char trip=0; // if trip=1, fujinet is asking us for attention bool old_enabled=false; // were interrupts enabled for old vector void* old_vprced; // old PROCEED vector, restored on exit. unsigned short bw=0; // # of bytes waiting. -unsigned char rx_buf[MAX_IRC_MSG_LEN]; // RX buffer. -unsigned char tx_buf[MAX_IRC_MSG_LEN]; // TX buffer. +extern char *rx_buf, *tx_buf /* 512 bytes each, see addrs.c */ unsigned int txbuflen; // TX buffer length char channel[32] = DEF_CHANNEL; diff --git a/src/screen.c b/src/screen.c index b98a26a..252d39d 100644 --- a/src/screen.c +++ b/src/screen.c @@ -25,14 +25,17 @@ void scr_waitvcount(u8 c) { } static void scr_clear(char s) { - memset(screen_addrs[s], 0, SCREEN_SIZE); + memset(screen_top_addrs[s], 0, 1000); + memset(screen_bot_addrs[s], 0, 1000); memset(scr_names[s], 0, 32); memset(scr_topics[s], 0, LINE_SIZE); } static void scr_scroll(char s) { - memmove(screen_addrs[s], screen_addrs[s] + 40, 880); - memset(screen_botlines[s], 0, 40); + memmove(screen_top_addrs[s], screen_top_addrs[s] + 40, 960); + memmove(screen_top_addrs[s] + 960, screen_bot_addrs[s], 40); + memmove(screen_bot_addrs[s], screen_bot_addrs[s] + 40, 920); + memset(screen_lastlines[s], 0, 40); } void scr_init(void) { @@ -41,7 +44,7 @@ void scr_init(void) { old_dma = OS.sdmctl; OS.sdmctl = 0; scr_waitvcount(112); /* after the last GR.0 line */ - *SDLST = DLIST_ADDR; + *SDLST = DLIST_BOT_ADDR; OS.chbas = FONT_ADDR_HI; for(i = 0; i < MAX_SCREENS; i++) { @@ -106,21 +109,36 @@ void scr_display(char s) { scr_current = s; scr_waitvcount(112); - *dlist_top_lms = (u16)screen_addrs[s]; + *dlist_bot_lms = (u16)screen_bot_addrs[s]; scr_show_status(s); } +void scr_scrollback(void) { + // OS.color2 = 0; + scr_waitvcount(112); + *dlist_top_lms = (u16)screen_top_addrs[scr_current]; + *SDLST = (u16)dlist_top; +} + +void scr_end_scrollback(void) { + // OS.color2 = 192; + scr_waitvcount(112); + *SDLST = (u16)dlist_bot; +} + void scr_show_status(char s) { int i; char *p, sc; - status_box[0] = s + 177; /* inverse number */ - status_box[1] = ':'; - strncpy(status_box + 2, scr_names[s], 32); - strncpy(status_box + 40, scr_topics[s], 40); + /* this part should be moved to _create() */ + p = status_boxes[s]; + p[0] = s + 177; /* inverse number */ + p[1] = ':'; + strncpy(p + 2, scr_names[s], 32); + strncpy(p + 40, scr_topics[s], 40); - p = status_box + 33; + p += 33; for(i = 0; i < MAX_SCREENS; i++) { switch(scr_status[i]) { case SCR_ACTIVE: @@ -134,7 +152,7 @@ void scr_show_status(char s) { } scr_waitvcount(112); - *dlist_bottom_lms = (u16)status_box; + *dlist_status_lms = (u16)status_boxes[s]; } void scr_refresh(void) { @@ -152,6 +170,14 @@ char scr_getbyname(const char *name) { return 0; } +void scr_putc_active(char c) { + scr_putc(scr_active, c); +} + +void scr_eol_active(void) { + scr_putc(scr_active, '\n'); +} + /* TODO: skip color codes (start with 0x03 or 0x04). if we're going to ever support utf-8, decode it here... also, 0x16 is supposed to be reverse video. not widely used/supported. @@ -176,7 +202,7 @@ void scr_putc(char s, char c) { xpos = 0; } - dest = screen_botlines[s] + xpos; + dest = screen_lastlines[s] + xpos; if(c & 0x80) { /* utf-8 (or maybe latin-1), we don't support it yet */ diff --git a/src/screen.h b/src/screen.h index fe8c928..bb70999 100644 --- a/src/screen.h +++ b/src/screen.h @@ -33,9 +33,9 @@ extern char scr_status[MAX_SCREENS]; void scr_init(void); /* creates a screen, if possible. we only get 7; attempts to create - more that that will return -1. on success, returns the screen - number (0-6). the name gets copied to the 1st line of the screen's - status box. + more that that will return 0 (aka the server screen). on success, + returns the screen number (2-6). the name gets copied to the 1st + line of the screen's status box. if display is true, the new screen displays. otherwise it's created "in the background". */ @@ -54,6 +54,14 @@ void scr_set_topic(char s, const char *topic); s's active flag. calls scr_show_status(). */ void scr_display(char s); +/* display the backscroll (top 25 lines) of the current screen. + note that it's a bad idea to write to the screen while it's + scrolled back! */ +void scr_scrollback(void); + +/* end scrollback mode (display the bottom 23 lines + status) */ +void scr_end_scrollback(void); + /* XXX: does this need to be public? */ void scr_show_status(char s); @@ -71,6 +79,8 @@ void scr_activate_name(const char *name); /* print one character to a screen. handles scrolling. will not print an EOL at column 0 (just ignores it). */ void scr_putc(char s, char c); +void scr_putc_active(char c); +void scr_eol_active(void); /* print text to a screen. handles scrolling. this doesn't have to be the screen being displayed, of course; diff --git a/uitest/test.c b/uitest/test.c index 3cd3fc5..a3ae1a9 100644 --- a/uitest/test.c +++ b/uitest/test.c @@ -47,7 +47,11 @@ int main() { if(OS.ch != 0xff) { if(GTIA_READ.consol == 6) { /* start pressed */ i = cgetc(); - if(i >= '1' && i <= '7') { + if(i == 's') { + scr_scrollback(); + cgetc(); + scr_end_scrollback(); + } else if(i >= '1' && i <= '7') { if(scr_status[i - '1'] != SCR_UNUSED) scr_display(i - '1'); } else if(i == 'n') { -- cgit v1.2.3