aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2026-03-01 02:00:23 -0500
committerB. Watson <urchlay@slackware.uk>2026-03-01 02:00:23 -0500
commit6521e971cdb772c53256d84a54c8d7b2d1ffa632 (patch)
tree28af564e7a12dd629d791e815834b3782177ec0c
parentd3a19b319e8ecdf111de44ffa9847474301edcbb (diff)
downloadfujinet-chat-6521e971cdb772c53256d84a54c8d7b2d1ffa632.tar.gz
Scrollback!
-rw-r--r--Makefile4
-rw-r--r--font_dl.asm117
-rw-r--r--memsetup.asm2
-rw-r--r--size.pl6
-rw-r--r--src/addrs.c67
-rw-r--r--src/addrs.h32
-rw-r--r--src/atari.cfg4
-rw-r--r--src/edbox.c16
-rw-r--r--src/keyclick.h1
-rw-r--r--src/keyclick.s53
-rw-r--r--src/main.c3
-rw-r--r--src/screen.c50
-rw-r--r--src/screen.h16
-rw-r--r--uitest/test.c6
14 files changed, 273 insertions, 104 deletions
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(<MAP>) {
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') {