diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/a2uint.s | 65 | ||||
| -rw-r--r-- | src/addrs.c | 11 | ||||
| -rw-r--r-- | src/addrs.h | 10 | ||||
| -rw-r--r-- | src/bell.s | 7 | ||||
| -rw-r--r-- | src/cgetc.s | 24 | ||||
| -rw-r--r-- | src/cio.s | 8 | ||||
| -rw-r--r-- | src/cmd.c | 138 | ||||
| -rw-r--r-- | src/complete.c | 10 | ||||
| -rw-r--r-- | src/config.c | 3 | ||||
| -rw-r--r-- | src/config.h | 2 | ||||
| -rw-r--r-- | src/edbox.c | 295 | ||||
| -rw-r--r-- | src/edbox.h | 20 | ||||
| -rw-r--r-- | src/edboxutl.s | 203 | ||||
| -rw-r--r-- | src/exehdr.s | 11 | ||||
| -rw-r--r-- | src/indic8.c | 46 | ||||
| -rw-r--r-- | src/indic8.h | 29 | ||||
| -rw-r--r-- | src/irc.c | 471 | ||||
| -rw-r--r-- | src/irc.h | 36 | ||||
| -rw-r--r-- | src/isnum.s | 18 | ||||
| -rw-r--r-- | src/keyclick.c | 8 | ||||
| -rw-r--r-- | src/keyclick.h | 1 | ||||
| -rw-r--r-- | src/keytab.c | 274 | ||||
| -rw-r--r-- | src/keytab.h | 34 | ||||
| -rw-r--r-- | src/kgetc.h | 2 | ||||
| -rw-r--r-- | src/kgetc.s | 127 | ||||
| -rw-r--r-- | src/main.c | 64 | ||||
| -rw-r--r-- | src/nextarg.s | 78 | ||||
| -rw-r--r-- | src/nio.c | 268 | ||||
| -rw-r--r-- | src/nio.h | 59 | ||||
| -rw-r--r-- | src/permute.s | 78 | ||||
| -rw-r--r-- | src/pollkbd.s | 93 | ||||
| -rw-r--r-- | src/printnum.s | 80 | ||||
| -rw-r--r-- | src/rxtxbuf.h | 5 | ||||
| -rw-r--r-- | src/screen.c | 49 | ||||
| -rw-r--r-- | src/screen.h | 14 | ||||
| -rw-r--r-- | src/sio.s | 16 | ||||
| -rw-r--r-- | src/streq.h | 5 | ||||
| -rw-r--r-- | src/streq.s | 134 | ||||
| -rw-r--r-- | src/timers.h | 8 | ||||
| -rw-r--r-- | src/txbuf.s | 50 |
40 files changed, 1885 insertions, 969 deletions
diff --git a/src/a2uint.s b/src/a2uint.s new file mode 100644 index 0000000..ac8a7ed --- /dev/null +++ b/src/a2uint.s @@ -0,0 +1,65 @@ + + ; replacement for atoi() that doesn't use cc65's bloated ctype. + ; also, doesn't handle signed results (not needed). + + .export _a2uint + .import _isnum + .importzp ptr1, sreg + + result = sreg + temp16 = sreg+2 + +_a2uint: + sta ptr1 + stx ptr1+1 + + lda #0 + sta result + sta result+1 + tay + +@chrloop: + lda (ptr1),y ; get next character + jsr _isnum ; is is a digit? + beq @done ; Z set = non-digit + + ; multiply result by 10 + ; { + lda result + asl + sta temp16 + lda result+1 + rol + sta temp16+1 ; temp16 now result * 2 + + ldx #3 +@x8loop: + asl result + rol result+1 + dex + bne @x8loop + + ; result now result * 8 + clc + lda result + adc temp16 + sta result + lda result+1 + adc temp16+1 + sta result+1 ; result now original result * 10 + ; } + + lda (ptr1),y ; get character again + iny ; point to next char + and #$0f ; de-ASCIIfy + clc + adc result + sta result + bcc @chrloop + inc result+1 + bne @chrloop + +@done: + lda result + ldx result+1 + rts diff --git a/src/addrs.c b/src/addrs.c index b9f0921..c7af1f2 100644 --- a/src/addrs.c +++ b/src/addrs.c @@ -8,10 +8,9 @@ u16 *dlist_bot_lms = u16p(0xbfd4); u16 *dlist_status_lms = u16p(0xbfee); u8 *dlist_last_line = u8p(0xbff0); -u8 *edit_box = u8p(0x0600); /* up to 256 bytes (page 6) */ u8 *edbox_only_dlist = u8p(0x8fb8); -u8 *rx_buf = u8p(0xa000); /* 512 bytes */ -u8 *tx_buf = u8p(0xa200); /* 512 bytes */ +// 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] = { @@ -52,3 +51,9 @@ u8 *screen_lastlines[7] = { at the bottom (2 unused lines), for now at least. addresses: 0xab80 0xaf68 0xb398 0xb780 0xbb68 0xbf50 */ u8 *status_box = (u8 *)0xa798; + +/* 1 byte, leftmost column of GR.1 line at bottom */ +u8 *ind_net_status = (u8 *)0xa7c0; + +/* 1 byte, 2nd column of GR.1 line at bottom */ +u8 *ind_act_status = (u8 *)0xa7c1; diff --git a/src/addrs.h b/src/addrs.h index 67a59c8..4099765 100644 --- a/src/addrs.h +++ b/src/addrs.h @@ -1,6 +1,7 @@ #define FONT_ADDR_HI 0x80 #define DLIST_TOP_ADDR 0xbfa0 #define DLIST_BOT_ADDR 0xbfd0 +#define EDBOX_ADDR 0x0600 #define u8 unsigned char #define u8p(x) ((unsigned char *)x) @@ -21,9 +22,6 @@ extern u16 *dlist_status_lms; or 6 for GR.1 (status box showing) */ extern u8 *dlist_last_line; -/* only one of these (not one per screen) */ -extern u8 *edit_box; - /* display list for Start+E (show only edbox) mode. */ extern u8 *edbox_only_dlist; @@ -38,3 +36,9 @@ extern u8 *screen_lastlines[7]; /* bottom 2 lines, shared by all screens */ extern u8 *status_box; + +/* network status indicator */ +extern u8 *ind_net_status; + +/* activity indicator */ +extern u8 *ind_act_status; @@ -7,6 +7,7 @@ DISTVOL = $a8 PITCH = $40 JIFFIES = $03 +FLASH_COLOR = $08 _bell: lda #<bell_callback @@ -28,10 +29,8 @@ check_flash: lda _bell_type and #2 beq done - lda #$08 + lda #FLASH_COLOR sta COLOR4 - lda #JIFFIES - sta CDTMV2 done: rts @@ -40,6 +39,4 @@ bell_callback: sta AUDC1 sta AUDF1 sta COLOR4 - lda #JIFFIES - sta CDTMV2 rts diff --git a/src/cgetc.s b/src/cgetc.s deleted file mode 100644 index dd9fe26..0000000 --- a/src/cgetc.s +++ /dev/null @@ -1,24 +0,0 @@ -; -; Christian Groessler, November-2002 -; -; get a char from the keyboard -; char cgetc(void) -; - -; Modified version for FujiNetChat. - .include "atari.inc" - .export _cgetc - -_cgetc: -;;; jsr setcursor ; this is unneeded and causes a hole in screen 7 - lda #12 - sta ICAX1Z ; fix problems with direct call to KEYBDV - jsr @1 - ldx #0 - rts - -@1: lda KEYBDV+5 - pha - lda KEYBDV+4 - pha - rts diff --git a/src/cio.s b/src/cio.s deleted file mode 100644 index 69a789a..0000000 --- a/src/cio.s +++ /dev/null @@ -1,8 +0,0 @@ - ;; Call CIO - - .export _ciov - -_ciov: LDX #$00 - JSR $E456 - RTS - @@ -7,9 +7,13 @@ #include "screen.h" #include "edbox.h" #include "config.h" +#include "streq.h" +#include "timers.h" /* A "command" is actually anything the user types, whether or - not it starts with a /character. */ + not it starts with a / character. */ + +extern void num_to_numbuf(unsigned int n); /* printnum.s */ char *command, *arg1, *arg2, *arg3; static char *target; @@ -25,16 +29,14 @@ static void do_j1(void); static void do_list(void); static void do_me(void); static void do_msg(void); -static void do_names(void); -static void do_part(void); static void do_ping(void); static void do_query(void); static void do_quit(void); static void do_quote(void); -static void do_topic(void); static void do_ver(void); static void do_reset(void); static void do_reboot(void); +static void do_optional_chan(void); typedef struct { char *cmd; @@ -46,6 +48,7 @@ typedef struct { OP DEOP VOICE DEVOICE KICK BAN KB IGNORE UNIGNORE MODE INVITE */ cmd_t command_defs[] = { + { "MSG", do_msg, 1 }, /* this must come first, do not move! */ { "AWAY", do_away, 1 }, { "ALERT", do_bell, 1 }, { "CLICK", do_click, 0 }, @@ -59,15 +62,14 @@ cmd_t command_defs[] = { { "LIST", do_list, 1 }, { "M", do_msg, 1 }, { "ME", do_me, 1 }, - { "MSG", do_msg, 1 }, - { "NAMES", do_names, 0 }, - { "PART", do_part, 0 }, + { "NAMES", do_optional_chan, 0 }, + { "PART", do_optional_chan, 0 }, { "PING", do_ping, 0 }, { "Q", do_query, 1 }, { "QUERY", do_query, 1 }, { "QUIT", do_quit, 0 }, { "QUOTE", do_quote, 1 }, - { "TOPIC", do_topic, 0 }, + { "TOPIC", do_optional_chan, 0 }, { "REBOOT", do_reboot, 0 }, { "RESET", do_reset, 0 }, { "VER", do_ver, 0 }, @@ -87,15 +89,8 @@ static void cmd_chan_text(void) { /* 0x02 = ^B = enable bold */ scr_print_active("<\x02"); - scr_print_active(conf->nick); - scr_print_active("\x02"); - - /* - if(!scr_current) { - scr_print_active("/"); - scr_print_active(target); - } - */ + scr_print_active(config.nick); + scr_putc_active('\x02'); scr_print_active("> "); scr_print_active(command); @@ -125,31 +120,6 @@ static void err_no_scr_target(void) { scr_print_current("No channel/nick for screen\n"); } -/* arg points to something like: - "part #channel I'm outta here\0" - after nextarg(), arg points to "part\0" only, and ret points - to "#channel I'm outta here\0". */ -static char *nextarg(char *arg) { - /* iterate over the first word */ - while(*arg && *arg != ' ') - arg++; - - /* if we found a space, replace it with a null terminator */ - if(*arg) - *arg++ = 0; - else - return 0; /* found no space, there's no next arg! */ - - /* skip space(s) */ - while(*arg && *arg == ' ') - arg++; - - if(*arg) - return arg; - - return 0; -} - static char have_commas(void) { if(strchr(arg1, ',')) { err_marker(); @@ -164,7 +134,7 @@ static void join_arg1(void) { txbuf_send(); } -static void mass_join(int scr) { +static void mass_join(char scr) { if(have_commas()) return; do { @@ -204,7 +174,7 @@ static void do_quit(void) { static void pause(void) { OS.rtclok[2] = 0; - while(OS.rtclok[2] < hz) + while(OS.rtclok[2] < timers.hz) /* NOP */; } @@ -217,26 +187,7 @@ static void do_reboot(void) { asm("jmp $e477"); } -static void do_part(void) { - if(arg1[0] == '#') { - target = arg1; - arg2 = nextarg(arg1); - } else { - arg2 = arg1; - } - - if(!target) { - err_no_scr_target(); - return; - } - - txbuf_set_str2("PART ", target); - if(arg2) - txbuf_append_str2(" :", arg2); - txbuf_send(); -} - -static void do_topic(void) { +static void do_optional_chan(void) { if(arg1[0] == '#') { target = arg1; arg2 = nextarg(arg1); @@ -249,28 +200,12 @@ static void do_topic(void) { return; } - txbuf_set_str2("TOPIC ", target); + txbuf_set_str3(cmd_def->cmd, " ", target); if(arg2) txbuf_append_str2(" :", arg2); txbuf_send(); } -static void do_names(void) { - if(arg1[0] == '#') { - target = arg1; - arg2 = nextarg(arg1); - } else { - arg2 = arg1; - } - - if(!target) { - err_no_scr_target(); - return; - } - txbuf_set_str2("NAMES ", target); - txbuf_send(); -} - static void do_server_info(void) { txbuf_send_str("INFO"); } @@ -293,7 +228,7 @@ unsigned int read_rtclok(void) { } static void rtclok_to_numbuf(void) { - itoa(read_rtclok(), numbuf, 10); + num_to_numbuf(read_rtclok()); } void cmd_server_ping(void) { @@ -314,16 +249,18 @@ void cmd_ctcp_ping(char *nick) { do_ctcp_ping(); } -static void do_ctcp_info(void) { - arg2 = "CLIENTINFO"; +static void do_no_arg_ctcp(char *type) { + arg2 = type; arg3 = 0; send_ctcp(); } +static void do_ctcp_info(void) { + do_no_arg_ctcp("CLIENTINFO"); +} + static void do_ctcp_ver(void) { - arg2 = "VERSION"; - arg3 = 0; - send_ctcp(); + do_no_arg_ctcp("VERSION"); } static void do_ctcp(void) { @@ -357,7 +294,7 @@ static void do_me(void) { txbuf_send(); scr_print_current("\x02* "); - scr_print_current(conf->nick); + scr_print_current(config.nick); scr_print_current("\x02 "); scr_print_current(arg1); scr_eol_current(); @@ -395,9 +332,9 @@ static void do_ver(void) { static void do_color(void) { arg2 = nextarg(arg1); - OS.color2 = atoi(arg1); + OS.color2 = a2uint(arg1); if(arg2) - OS.color1 = atoi(arg2); + OS.color1 = a2uint(arg2); } static void do_query(void) { @@ -444,7 +381,7 @@ static void do_click(void) { OS.noclik ^= 1; } -static int cmd_local(void) { +static char cmd_local(void) { arg1 = nextarg(command); /* @@ -486,29 +423,25 @@ static void cmd_slash(void) { cmd_remote(); } -void cmd_command(char *cmd) { - command = cmd; +void cmd_execute(void) { + if(!*edit_box) return; + command = edit_box; if(scr_current > 1) target = scr_get_cur_name(); else target = 0; - if(cmd[0] == '/' && cmd[1] && cmd[1] != '/') + if(command[0] == '/' && command[1] && command[1] != '/') cmd_slash(); else if(target) cmd_chan_text(); else if(scr_current == SCR_PRIV || scr_current == SCR_SERVER) - cmd_send_pm(cmd); + cmd_send_pm(command); else err_no_scr_target(); } -void cmd_execute(void) { - if(!*edit_box) return; - cmd_command(edit_box); -} - void cmd_rejoin_chans(void) { char i; @@ -519,10 +452,10 @@ void cmd_rejoin_chans(void) { } } - if(!*(conf->extra_channels)) + if(!*(config.extra_channels)) return; - strncpy(edit_box, conf->extra_channels, 128); + strncpy(edit_box, config.extra_channels, 128); arg1 = edit_box; mass_join(0); edbox_clear(); @@ -530,6 +463,7 @@ void cmd_rejoin_chans(void) { /* args contains the destination, space, the msg */ void cmd_send_pm(char *args) { + cmd_def = command_defs; /* element 0 of command_defs must be MSG! */ arg1 = args; do_msg(); } diff --git a/src/complete.c b/src/complete.c index 108bc90..e76def1 100644 --- a/src/complete.c +++ b/src/complete.c @@ -1,10 +1,10 @@ #include <atari.h> #include <string.h> -#include <ctype.h> #include "addrs.h" #include "edbox.h" #include "screen.h" #include "irc.h" +#include "streq.h" #define COMP_S1 1 #define COMP_PM 2 @@ -24,10 +24,10 @@ char (*add_to)[25] = comp_pm_nicks; static char pm_nick_pos = 0; /* insertion point for _add() */ static void add_list(const char *n) { - int i; + char i; for(i = 0; i < 25; i++) - if(strncmp(n, add_to[i], 24) == 0) + if(strneq_i(n, add_to[i], 24)) return; /* we already got this one */ strncpy(add_to[pm_nick_pos], n, 24); @@ -46,7 +46,7 @@ void comp_add_pm_chan(const char *n) { } char match(const char *p, const char *q) { - int len; + char len; while(*p == '#') p++; while(*q == '#') q++; @@ -55,7 +55,7 @@ char match(const char *p, const char *q) { if(!len) return 0; while(len--) { - if(tolower(*p) != tolower(*q)) + if(lcase(*p) != lcase(*q)) return 0; p++, q++; } diff --git a/src/config.c b/src/config.c deleted file mode 100644 index f7c8982..0000000 --- a/src/config.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "config.h" - -conf_t *conf = (conf_t *)0x0400; diff --git a/src/config.h b/src/config.h index 6a4bfa9..ff25ea1 100644 --- a/src/config.h +++ b/src/config.h @@ -14,3 +14,5 @@ typedef struct { } conf_t; extern conf_t *conf; + +#define config (*(conf_t *)0x0400) diff --git a/src/edbox.c b/src/edbox.c index d5728c2..0725379 100644 --- a/src/edbox.c +++ b/src/edbox.c @@ -4,34 +4,29 @@ #include "addrs.h" #include "screen.h" #include "edbox.h" -#include "keyclick.h" #include "complete.h" - -/* TODO: tab completion */ - -char *old_edbox[EDBOX_SIZE]; -static u16 old_len; - -int edbox_visible = 0; -static u16 edbox_pos; /* range 0 to EDBOX_SIZE - 1 */ -u16 edbox_len; /* idem */ +#include "keytab.h" +#include "irc.h" + +/* private API stuff (not in edbox.h) that's been rewritten in asm. */ +void hide_cursor(void); +void show_cursor(void); +void storechr(char c); +void copy_to_old(void); +void restore_old(void); +void backspace(void); +void inschr(char c); + +char old_edbox[EDBOX_SIZE]; +char old_len; +char typeover; + +char edbox_visible = 0; +char edbox_pos; /* range 0 to EDBOX_SIZE - 1 */ +char edbox_len; /* idem */ void (*edbox_callback)(void); -static void hide_cursor(void) { - edit_box[edbox_pos] &= 0x7f; -} - -static void show_cursor(void) { - edit_box[edbox_pos] |= 0x80; -} - -void edbox_clear(void) { - memset(edit_box, 0, EDBOX_SIZE); - edbox_pos = edbox_len = 0; - show_cursor(); // not needed? seems it is.. -} - void edbox_show(void) { u16 addr; @@ -40,7 +35,7 @@ void edbox_show(void) { else addr = (u16)edit_box + edbox_pos - 79; - scr_waitvcount(116); + scr_waitvcount_116(); *dlist_status_lms = addr; *dlist_last_line = 0x02; /* ANTIC GR.0 */ @@ -54,50 +49,37 @@ void edbox_hide(void) { scr_refresh(); } +void move_right(void) { + memmove(edit_box + edbox_pos + 1, edit_box + edbox_pos, EDBOX_MAXPOS - edbox_pos); +} + /* note: c will never be an EOL. idea: when the edbox is completely full (240 chars), go ahead and pretend the user pressed Return (call edbox_callback, etc). not sure if this is more or less annoying than just refusing to accept more input until Return is pressed. */ void edbox_putc(char c) { - extern void __fastcall__ bell(void); - if(!c) return; /* no inserting nulls */ - memmove(edit_box + edbox_pos + 1, edit_box + edbox_pos, EDBOX_SIZE - edbox_pos - 1); - edit_box[edbox_pos] = c; - if(edbox_pos < EDBOX_SIZE - 1) { - edbox_pos++; - edbox_len++; - } else { + /* cannot insert *or* typeover at end of buffer! */ + if(edbox_pos == EDBOX_MAXPOS) { bell(); + return; } -} -static void copy_to_old(void) { - if(!edbox_len) return; - memcpy(old_edbox, edit_box, edbox_len); - memset(old_edbox + edbox_len, 0, (EDBOX_SIZE - 1) - edbox_len); - old_len = edbox_len; -} - -static void restore_old(void) { - edbox_clear(); - hide_cursor(); - memcpy(edit_box, old_edbox, old_len); - edbox_pos = edbox_len = old_len; + if(typeover) { + storechr(c); + edbox_pos++; + } else { + inschr(c); + } } -static void fake_keystroke(char c) { - keyclick(); - OS.ch = 0xff; - edbox_putc(c); -} -static void del_char(void) { +void del_char(void) { if(!edbox_len) return; - memmove(edit_box + edbox_pos, edit_box + edbox_pos + 1, EDBOX_SIZE - edbox_pos - 1); + memmove(edit_box + edbox_pos, edit_box + edbox_pos + 1, EDBOX_MAXPOS - edbox_pos); edbox_len--; } @@ -106,12 +88,6 @@ static void del_to_end(void) { del_char(); } -static void backspace(void) { - if(!edbox_pos) return; - edbox_pos--; - del_char(); -} - static void up(void) { if(!edbox_len) { restore_old(); @@ -145,14 +121,6 @@ static void word_left(char del) { if(del && !edbox_pos) edit_box[edbox_pos] = /* ' ' */ 0; } -static void del_word(void) { - word_left(1); -} - -static void back_word(void) { - word_left(0); -} - static void forward_word(void) { while(edbox_pos < edbox_len && edit_box[edbox_pos] == ' ') edbox_pos++; @@ -161,64 +129,101 @@ static void forward_word(void) { } static void del_to_start(void) { - while(edbox_pos) backspace(); + if(!edbox_pos) return; + memmove(edit_box, edit_box + edbox_pos, EDBOX_MAXPOS - edbox_pos); + edbox_len -= edbox_pos; + bzero(edit_box + edbox_len, EDBOX_MAXPOS - edbox_len); + edbox_pos = 0; } -static void normal_keystroke(void) { - char c; +void left(void) { + if(edbox_pos) edbox_pos--; +} + +void right(void) { + if(edbox_pos < edbox_len) edbox_pos++; +} - c = cgetc(); +void edbox_keystroke(char c) { + if(c == CH_ESC) { + start_latch = 1; + return; + } - if(c != CH_TAB) + if(c != XCH_TAB) comp_complete_done(); + if(c >= XCH_SCR1 && c <= XCH_SCR7) { + start_keystroke(c & 0x7f); + return; + } else if(c == XCH_ACTIVE) { + start_keystroke('a'); + return; + } + + edbox_show(); + hide_cursor(); + switch(c) { case CH_EOL: - // hide_cursor(); // already done by the caller copy_to_old(); edbox_hide(); if(edbox_callback) (*edbox_callback)(); edbox_clear(); break; - case CH_CLR: - edbox_hide(); - /* fall thru */ - case CH_DELLINE: - edbox_clear(); + case XCH_TAB: + comp_complete(); break; - case 0x15: /* ^U */ - del_to_start(); + case XCH_UP: + up(); break; - case 0x0b: /* ^K */ - del_to_end(); + case XCH_DOWN: + down(); + break; + case XCH_LEFT: + left(); break; - case CH_DEL: - backspace(); + case XCH_RIGHT: + right(); break; - case 0x02: /* ^B */ - back_word(); + case XCH_LWORD: + word_left(0); break; - case 0x06: /* ^F */ + case XCH_RWORD: forward_word(); break; - case 0x17: /* ^W */ - del_word(); + case XCH_CLS: + edbox_clear(); + edbox_hide(); + return; + case CH_DELCHR: + case 0x18: /* ^X */ + del_char(); break; - case CH_CURS_LEFT: - if(edbox_pos) edbox_pos--; + case CH_DELLINE: + edbox_clear(); break; - case CH_CURS_RIGHT: - if(edbox_pos < edbox_len) edbox_pos++; + case XCH_INSCHR: + inschr(' '); break; - case CH_CURS_UP: - up(); + case CH_INSLINE: + typeover = !typeover; break; - case CH_CURS_DOWN: - down(); + case 0x15: /* ^U */ + del_to_start(); break; - case CH_DELCHR: - del_char(); + case 0x0b: /* ^K */ + del_to_end(); + break; + case XCH_BS: + if(!edbox_len) + edbox_hide(); + else + backspace(); + break; + case 0x17: /* ^W */ + word_left(1); break; case 0x01: /* ^A */ edbox_pos = 0; @@ -226,101 +231,11 @@ static void normal_keystroke(void) { case 0x05: /* ^E */ edbox_pos = edbox_len; break; - case CH_TAB: - comp_complete(); - break; default: edbox_putc(c); break; } -} - -void edbox_keystroke(void) { - char c; - - while(OS.ch == 0xff) - ; - - /* filter out all ctrl-shift key combos except the ones - we actually support */ - if(OS.ch == 0xce) { - /* ctrl-shift-up, same as ^B = back 1 word */ - OS.ch = 0x95; - } else if(OS.ch == 0xcf) { - /* ctrl-shift-down, same as ^F = forward 1 word */ - OS.ch = 0xb8; - } else if(OS.ch > 0xbf) { - OS.ch = 0xff; - return; - } - - edbox_show(); - - c = 0; - - switch(OS.ch) { - case 0xa0: /* key: ctrl [ */ - c = 0x7b; /* ascii: { */ - break; - case 0xa2: /* key: ctrl ] */ - c = 0x7d; /* ascii: } */ - break; - case 0x1c: /* key: ESC */ - c = 0x60; /* ascii: ` */ - break; - case 0x5c: /* key: shift ESC */ - case 0x9c: /* key: ctrl ESC */ - c = 0x7e; /* ascii: ~ */ - break; - case 0x3c: /* caps */ - case 0x7c: /* shift-caps */ - case 0xbc: /* ctrl-caps */ - OS.shflok ^= 0x40; - keyclick(); - OS.ch = 0xff; - return; - break; - case 0x27: /* atari key */ - case 0x67: /* ...w/shift */ - case 0xa7: /* ...w/ctrl */ - c = 0x02; /* ^B = IRC bold formatting char */ - break; - case 0x9a: /* ctrl-3 (crash if cgetc() reads it!) */ - case 0x9e: /* ctrl-2 */ - case 0x6c: /* shift-tab */ - case 0xac: /* ctrl-tab */ - OS.ch = 0xff; /* ignore it! */ - return; - break; - default: - break; - } - hide_cursor(); - - if(c) { - fake_keystroke(c); - } else { - normal_keystroke(); - } - - if(edbox_visible) edbox_show(); show_cursor(); -} - -void edbox_addchr(char c) { - edit_box[edbox_len++] = c; - edbox_pos = edbox_len; -} - -void edbox_space(void) { - edbox_addchr(' '); -} - -void edbox_set(char *contents) { - edbox_clear(); - while(*contents) { - edit_box[edbox_len++] = *contents++; - } - edbox_pos = edbox_len; + if(edbox_visible) edbox_show(); } diff --git a/src/edbox.h b/src/edbox.h index 14dad03..e4069a0 100644 --- a/src/edbox.h +++ b/src/edbox.h @@ -3,12 +3,16 @@ /**** public API ****/ #define EDBOX_SIZE 240 +#define EDBOX_MAXPOS 239 -extern int edbox_visible; -extern u16 edbox_len; +typedef char edbox_t[EDBOX_SIZE]; +#define edit_box (*(edbox_t *)EDBOX_ADDR) + +extern char edbox_visible; +extern char edbox_len; /* clear the contents of the edit box (whether it's visible or not) */ -void edbox_clear(void); +void __fastcall__ edbox_clear(void); /* make the edit box visible */ void edbox_show(void); @@ -19,18 +23,18 @@ void edbox_hide(void); /* put one character into the edit box. */ void edbox_putc(char c); -/* wait for a keystroke, insert its character into the edit box. if Return +/* pass a keystroke, insert its character into the edit box. if Return is pressed, edbox_callback gets called (if it's set!) */ -void edbox_keystroke(void); +void edbox_keystroke(char c); /* called when the user presses Return */ extern void (*edbox_callback)(void); /* set edit box contents (clears out whatever was there) */ -void edbox_set(char *contents); +void __fastcall__ edbox_set(char *contents); /* append a space to the edit box */ -void edbox_addchr(char c); +void __fastcall__ edbox_addchr(char c); /* append a space to the edit box */ -void edbox_space(void); +void __fastcall__ edbox_space(void); diff --git a/src/edboxutl.s b/src/edboxutl.s new file mode 100644 index 0000000..dc89798 --- /dev/null +++ b/src/edboxutl.s @@ -0,0 +1,203 @@ +; asm rewrites of functions originally written in C, for edbox.c. +; old C code here as comments, for reference. + + .export _edbox_addchr, _edbox_space, _edbox_set, _edbox_clear + .export _hide_cursor, _show_cursor, _storechr + .export _copy_to_old, _restore_old, _backspace, _inschr + .import _edbox_len, _edbox_pos, _old_edbox, _old_len, _typeover + .import _del_char, _bell, _move_right + .importzp ptr1 + + ; EDBOX_SIZE + 1, see edbox.h + EDBOX_SIZE_PLUS_1 = $f1 + EDBOX_SIZE = $f0 + EDBOX_MAXPOS = $ef + + EDBOX_ADDR = $0600 ; MUST agree with EDBOX_ADDR from addrs.h! + +; this compiled to like 31 bytes of code... +; this version is 7 bytes. +;; void storechr(char c) { +;; edit_box[edbox_pos] = c; +;; } +_storechr: + ldx _edbox_pos + sta EDBOX_ADDR,x + rts + +;; void hide_cursor(void) { +;; edit_box[edbox_pos] &= 0x7f; +;; } +_hide_cursor: + ldx _edbox_pos + lda EDBOX_ADDR,x + and #$7f +store_curs: + sta EDBOX_ADDR,x + rts + +;; void show_cursor(void) { +;; edit_box[edbox_pos] |= 0x80; +;; } +_show_cursor: + ldx _edbox_pos + lda EDBOX_ADDR,x + ora #$80 + bne store_curs + +;; static void copy_to_old(void) { +;; if(!edbox_len) return; +;; memcpy(old_edbox, edit_box, edbox_len); +;; bzero(old_edbox + edbox_len, EDBOX_MAXPOS - edbox_len); +;; old_len = edbox_len; +;; } +_copy_to_old: + lda _edbox_len + beq nope + ldx #0 +@l: + lda EDBOX_ADDR,x + sta _old_edbox,x + inx + cpx #EDBOX_SIZE + bne @l + lda _edbox_len + sta _old_len +nope: + rts + +;; static void restore_old(void) { +;; edbox_clear(); +;; hide_cursor(); +;; memcpy(edit_box, old_edbox, old_len); +;; edbox_pos = edbox_len = old_len; +;; } +_restore_old: + lda _old_len + beq nope + ldx #0 +@l: + lda _old_edbox,x + sta EDBOX_ADDR,x + inx + cpx #EDBOX_SIZE + bne @l + lda _old_len + sta _edbox_len + sta _edbox_pos + rts + +;; static void backspace(void) { +;; if(!edbox_pos) return; +;; edbox_pos--; +;; if(typeover) +;; edit_box[edbox_pos] = ' '; +;; else +;; del_char(); +;; } +_backspace: + lda _edbox_pos + beq @nope + dec _edbox_pos + lda _typeover + bne @spc + jmp _del_char +@spc: + lda #$20 + ldx _edbox_pos + sta EDBOX_ADDR,x +@nope: + rts + +;; void inschr(char c) { +;; if(edbox_len == EDBOX_MAXPOS) { +;; /* buffer full, can't insert */ +;; bell(); +;; return; +;; } +;; move_right(); +;; edbox_len++; +;; storechr(c); +;; edbox_pos++; +;; } +_inschr: + pha + lda _edbox_len + cmp #EDBOX_MAXPOS + bne @ok + pla + jmp _bell +@ok: + jsr _move_right + inc _edbox_len + pla + jsr _storechr + inc _edbox_pos + rts + +;; void edbox_clear(void) { +;; bzero(edit_box, EDBOX_SIZE + 1); +;; edbox_pos = edbox_len = 0; +;; show_cursor(); // not needed? seems it is.. +;; } + +_edbox_clear: + ldx #0 + txa +@l: + sta EDBOX_ADDR,x + inx + cpx #EDBOX_SIZE_PLUS_1 + bne @l + sta _edbox_pos + sta _edbox_len + jmp _show_cursor + +;; void edbox_space(void) { +;; edbox_addchr(' '); +;; } +_edbox_space: + lda #$20 + ; fall thru... + +;; void edbox_addchr(char c) { +;; edit_box[edbox_len++] = c; +;; edbox_pos = edbox_len; +;; } + +_edbox_addchr: + ldx _edbox_len + sta EDBOX_ADDR,x + inx + stx _edbox_len + stx _edbox_pos + rts + +;; void edbox_set(char *contents) { +;; edbox_clear(); +;; while(*contents) { +;; edit_box[edbox_len++] = *contents++; +;; } +;; edbox_pos = edbox_len; +;; } + +; notice the asm version does bounds checking, which the C version +; didn't do. +_edbox_set: + sta ptr1 + stx ptr1+1 + jsr _edbox_clear + ldy #0 +@l: + lda (ptr1),y + sta EDBOX_ADDR,y + beq @done ; if we hit a 0 byte in the input, we're done + inx + iny + cpy #EDBOX_SIZE_PLUS_1 ; if we hit the edbox size limit, we're done + bne @l + dey +@done: + sty _edbox_len + sty _edbox_pos + rts diff --git a/src/exehdr.s b/src/exehdr.s new file mode 100644 index 0000000..7abb7c1 --- /dev/null +++ b/src/exehdr.s @@ -0,0 +1,11 @@ +; This file defines the EXE header and main chunk load header for Atari executables + + .export __EXEHDR__: absolute = 1 + .import __MAIN_START__, __BSS_LOAD__ + +.segment "EXEHDR" + .word $FFFF + +.segment "MAINHDR" + .word __MAIN_START__ + .word __BSS_LOAD__ - 1 diff --git a/src/indic8.c b/src/indic8.c index 33d7d31..260994d 100644 --- a/src/indic8.c +++ b/src/indic8.c @@ -2,25 +2,11 @@ #include <atari.h> #include "addrs.h" - -extern char hz; - -#define NET_TX_CHR 0x5c /* COLOR1 up-arrow */ -#define NET_RX_CHR 0x5d /* COLOR1 down-arrow */ -#define NET_ERR_CHR 0xe1 /* COLOR3 exclamation mark */ -#define NET_IDLE_CHR 0 /* null (renders as a space) */ - -#define J_CHR 0x4a /* COLOR1 J */ -#define P_CHR 0x50 /* COLOR1 P */ -#define Q_CHR 0x51 /* COLOR1 Q */ -#define M_CHR 0x4d /* COLOR1 M */ -#define CHAN_CHR 0x63 /* COLOR1 # */ - -char *ind_net_status; /* initialized in screen.c, scr_init() */ +#include "indic8.h" +#include "timers.h" static void ind_start_timer(void) { - /* 3.7 sec is just what The_Doctor__ ordered! */ - OS.cdtmv5 = (hz / 10) * 37; + OS.cdtmv5 = timers.net_ind_time; } void ind_net_rx(void) { @@ -39,32 +25,12 @@ void ind_net_idle(void) { *ind_net_status = NET_IDLE_CHR; } -static void ind_act(char c) { - ind_net_status[1] = c; +void ind_act(char c) { + *ind_act_status = c; ind_start_timer(); } -void ind_act_pm(void) { - ind_act(M_CHR); -} - -void ind_act_chantext(void) { - ind_act(CHAN_CHR); -} - -void ind_act_join(void) { - ind_act(J_CHR); -} - -void ind_act_part(void) { - ind_act(P_CHR); -} - -void ind_act_quit(void) { - ind_act(Q_CHR); -} - void ind_check_timer(void) { if(!OS.cdtmv5) - ind_net_status[1] = 0; + *ind_act_status = 0; } diff --git a/src/indic8.h b/src/indic8.h index 4a28d41..5524bb7 100644 --- a/src/indic8.h +++ b/src/indic8.h @@ -1,18 +1,27 @@ -extern char *ind_net_status; +#define NET_TX_CHR 0x5c /* COLOR1 up-arrow */ +#define NET_RX_CHR 0x5d /* COLOR1 down-arrow */ +#define NET_ERR_CHR 0xe1 /* COLOR3 exclamation mark */ +#define NET_IDLE_CHR 0 /* null (renders as a space) */ + +#define J_CHR 0x4a /* COLOR1 J */ +#define P_CHR 0x50 /* COLOR1 P */ +#define Q_CHR 0x51 /* COLOR1 Q */ +#define M_CHR 0x4d /* COLOR1 M */ +#define N_CHR 0x4e /* COLOR1 N */ +#define CHAN_CHR 0x63 /* COLOR1 # */ + +void ind_act(char c); void ind_net_rx(void); void ind_net_tx(void); void ind_net_down(void); void ind_net_idle(void); -void ind_act_pm(void); -void ind_act_chantext(void); -void ind_act_join(void); -void ind_act_part(void); -void ind_act_quit(void); +#define ind_act_pm() ind_act(M_CHR) +#define ind_act_notice() ind_act(N_CHR) +#define ind_act_chantext() ind_act(CHAN_CHR) +#define ind_act_join() ind_act(J_CHR) +#define ind_act_part() ind_act(P_CHR) +#define ind_act_quit() ind_act(Q_CHR) void ind_check_timer(void); - -/* -void ind_clear(void); -*/ @@ -1,13 +1,11 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> #include "irc.h" #include "screen.h" #include "edbox.h" #include "numerics.h" -#include "keyclick.h" #include <atari.h> #include <conio.h> @@ -15,6 +13,10 @@ #include "config.h" #include "indic8.h" #include "complete.h" +#include "keytab.h" +#include "kgetc.h" +#include "streq.h" +#include "timers.h" #ifndef VERSION #define VERSION "?????" @@ -24,14 +26,17 @@ char *msg_src, *msg_cmd, *msg_dest, *msg_text; char *msg_args[MAX_MSG_ARGS]; -int msg_argcount; +char msg_argcount; char irc_away = 0; char bell_type; char hide_motd; +char start_latch = 0; +char new_scr_status; +char need_rejoin; +char self_src; static char msgbuf[MAX_MSG] = { 0 }; -static char *msg; /* with source removed */ static int msgbuf_len = 0; static char regged = 0, hilite = 0; @@ -44,6 +49,9 @@ char last_chan[33]; /* without a screen */ static int minutes, last_read_min; +/* see pollkbd.s */ +extern void poll_keyboard(void); + /* static void join_channel(void) { txbuf_set_str2("JOIN ", channel); @@ -52,7 +60,7 @@ static void join_channel(void) { */ static void send_nick(void) { - txbuf_set_str2("NICK ", conf->nick); + txbuf_set_str2("NICK ", config.nick); txbuf_send(); } @@ -64,15 +72,8 @@ static void print_reason(void) { scr_eol_active(); } -static void do_pong(void) { - if(conf->show_ping) - scr_print_server("PING/PONG\n"); - txbuf_set_str2("PONG ", msg_args[0]); - txbuf_send(); -} - static void bold(void) { - scr_print_active("\x02"); + scr_putc_active('\x02'); } static void hilite_bold(void) { @@ -82,29 +83,29 @@ static void hilite_bold(void) { static void do_chan_nick(void) { if(hilite) { bell(); - scr_hilite_active(); + new_scr_status = SCR_HILITE; } hilite_bold(); - scr_print_active("<"); + scr_putc_active('<'); hilite_bold(); scr_print_active(msg_src); if(scr_active == SCR_SERVER) { /* if we don't have a window for it */ - scr_print_active("/"); + scr_putc_active('/'); scr_print_active(msg_dest); } hilite_bold(); - scr_print_active(">"); + scr_putc_active('>'); hilite_bold(); - scr_print_active(" "); + scr_putc_active(' '); } static void do_priv_nick(void) { if(msg_src) { - scr_print_active("*"); + scr_putc_active('*'); scr_print_active(msg_src); scr_print_active("* "); - scr_hilite_active(); + new_scr_status = SCR_HILITE; bell(); } } @@ -113,43 +114,37 @@ static void do_priv_nick(void) { nobody's ping time will ever be more than 9 minutes. anyone that lagged will have been disconnected from the server already. if this assumption turns out to be false, the ping time displayed - will be wrong (module ~9 mins). I don't think it's ever going to be + will be wrong (modulo ~9 mins). I don't think it's ever going to be a real problem. Why do it this way? Because using longs instead of ints bloats the - code by 778 bytes! */ + code by 778 bytes! + note to self: using a div_t and div() seemed like it might make + the compiled code smaller, but it grew by ~50 bytes. avoid. + */ static void print_ping_time(char *p) { static unsigned int now, pingtime; static unsigned int sec, frac; now = read_rtclok(); - pingtime = (unsigned int)atoi(p); + pingtime = (unsigned int)a2uint(p); /* correct for rtclock rollover (every ~9 mins) */ if(now < pingtime) now |= 0x8000; pingtime = now - pingtime; - sec = pingtime / hz; - frac = pingtime % hz; - frac *= 1000; - frac /= (hz * 10); + sec = pingtime / timers.hz; + frac = pingtime % timers.hz; + frac *= 100; + frac /= timers.hz; scr_print_active("*** "); scr_print_active(msg_src); scr_print_active(" lag: "); - itoa(sec, numbuf, 10); - scr_print_active(numbuf); - scr_print_active("."); - itoa(frac, numbuf, 10); - scr_print_active(numbuf); - scr_print_active(" sec"); - - /* - // for debugging: - scr_print_active(" "); - itoa(pingtime, numbuf, 10); - scr_print_active(numbuf); - */ + scr_act_printnum(sec); + scr_putc_active('.'); + scr_act_printnum(frac); + scr_putc_active('s'); } static void do_server_pong(void) { @@ -159,8 +154,12 @@ static void do_server_pong(void) { scr_eol_active(); } +static void print_ctcp(void) { + scr_print_active("*** CTCP "); +} + /* FIXME: this isn't very fast */ -static void do_ctcp(int is_notice) { +static void do_ctcp(char is_notice) { static char *p, *ctcp_type, *resp; resp = 0; @@ -180,9 +179,9 @@ static void do_ctcp(int is_notice) { if(p && streq_i(ctcp_type, "PING")) { print_ping_time(p); } else { - scr_print_active("*** CTCP "); + print_ctcp(); scr_print_active(ctcp_type); - scr_print_active(" response from "); + scr_print_active(" resp from "); scr_print_active(msg_src); if(p) { scr_print_active(": "); @@ -195,15 +194,15 @@ static void do_ctcp(int is_notice) { if(streq_i(ctcp_type, "ACTION")) { scr_print_active("* "); scr_print_active(msg_src); - scr_print_active(" "); + scr_putc_active(' '); scr_print_active(p); scr_eol_active(); return; } - scr_print_active("*** CTCP "); + print_ctcp(); scr_print_active(ctcp_type); - scr_print_active(" request from "); + scr_print_active(" req from "); scr_print_active(msg_src); scr_eol_active(); @@ -219,18 +218,23 @@ static void do_ctcp(int is_notice) { } txbuf_set_str3("NOTICE ", msg_src, " :\x01"); - txbuf_append_str3(ctcp_type, " ", resp); - txbuf_append_str("\x01"); + txbuf_append_str(ctcp_type); + txbuf_append_chr(' '); + txbuf_append_str(resp); + txbuf_append_chr('\x01'); txbuf_send(); } } static void do_privmsg(void) { /* TODO: this shouldn't be case-sensitive */ - if(strstr(msg_text, conf->nick)) + /* + if(strstr(msg_text, config.nick)) hilite = 1; else hilite = 0; + */ + hilite = find_nick(); if(*msg_text == '\x01') { do_ctcp(0); @@ -242,7 +246,10 @@ static void do_privmsg(void) { ind_act_chantext(); } else { do_priv_nick(); - ind_act_pm(); + if(*msg_cmd == 'N') + ind_act_notice(); + else + ind_act_pm(); } scr_print_active(msg_text); @@ -260,23 +267,22 @@ static void do_notice(void) { static void do_join(void) { ind_act_join(); - if(streq_i(conf->nick, msg_src)) { - scr_print_active("You have "); + if(self_src) { + scr_print_active("You"); } else { scr_print_active("\x02=\x02"); scr_print_active(msg_src); - scr_print_active(" has "); } - scr_print_active("joined "); + scr_print_active(" joined "); scr_print_active(msg_dest); scr_eol_active(); } static void do_nick(void) { - /* Do not overwrite conf->nick with bogus data! sometimes when + /* Do not overwrite config.nick with bogus data! sometimes when we get disconnected, upon reconnect we get a partial message. if it's a NICK, missing its destination argument, we end up - blowing away conf->nick, and subsequent reconnect attempts + blowing away config.nick, and subsequent reconnect attempts fail with "USER: not enough parameters". This is purely a band-aid; a proper solution involves rewriting parse_msg() so it knows how many args each msg type needs, and refuses to @@ -285,9 +291,9 @@ static void do_nick(void) { return; // ind_act_none(); - if(streq_i(conf->nick, msg_src)) { + if(self_src) { scr_print_active("You are "); - strncpy(conf->nick, msg_dest, 32); + strncpy(config.nick, msg_dest, 32); } else { scr_print_active(msg_src); scr_print_active(" is "); @@ -309,95 +315,66 @@ static void do_part(void) { scr_print_active(msg_src); scr_print_active(" has left "); scr_print_active(msg_dest); - if(msg_text) { - scr_print_active(": "); - scr_print_active(msg_text); - } - scr_eol_active(); + print_reason(); } static void do_topic(void) { scr_print_active(msg_src); - scr_print_active(" has set the topic of "); + scr_print_active(" sets the "); scr_print_active(msg_dest); - scr_print_active(" to: "); + scr_print_active(" topic to: "); scr_print_active(msg_text); scr_eol_active(); - /* TODO: set topic in the screen! */ } static void do_kick(void) { scr_print_active(msg_src); - scr_print_active(" has kicked "); + scr_print_active(" kicks "); scr_print_active(msg_args[1]); scr_print_active(" from "); scr_print_active(msg_dest); print_reason(); } -static void do_mode(void) { - int i; - if(msg_src) - scr_print_active(msg_src); - else - scr_print_active("Server"); - scr_print_active(" sets mode: "); - for(i = 0; i < msg_argcount; i++) { - scr_print_active(msg_args[i]); - scr_print_active(" "); - } - if(msg_text) scr_print_active(msg_text); - scr_eol_active(); -} - /* numerics call this with arg==1, since arg 0 is always our nick. other commands call with arg==0 to print everything. */ -static void do_catchall(int arg) { +static void do_catchall(char arg) { if(msg_src) { scr_print_active(msg_src); - scr_print_active(" "); + scr_putc_active(' '); } scr_print_active(msg_cmd); for(; arg < msg_argcount; arg++) { - scr_print_active(" "); + scr_putc_active(' '); scr_print_active(msg_args[arg]); } if(msg_text) { - scr_print_active(" "); + scr_putc_active(' '); scr_print_active(msg_text); } scr_eol_active(); } -/* permutes last character (doesn't add one), so for "Bob" you get: - Bo_, Bo1 through Bo9, BoA through BoZ - Gives a total of 36 replacement nicks to try. - Eventually we run out and start repeating, but by then the IRC - server will have disconnected us. - */ -static void permute_nick(void) { - char *last; - - last = conf->nick + strlen(conf->nick) - 1; - - if((*last >= '1' && *last < '9') || (*last >= 'A' && *last < 'Z')) { - (*last)++; +static void do_mode(void) { + if(msg_src) { + scr_print_active(msg_src); + msg_src = 0; /* don't let do_catchall() print it again */ } else { - switch(*last) { - case '_': *last = '1'; break; - case '9': *last = 'A'; break; - default: *last = '_'; break; - } + scr_print_active("Server"); } + scr_print_active(" sets mode:"); + do_catchall(0); } +extern void permute_nick(void); + /* see: https://defs.ircdocs.horse/ */ static void do_forward_chan(void) { - char s; + static char s; if(msg_argcount > 2 && msg_args[1][0] == '#' && msg_args[2][0] == '#') { s = scr_getbyname(msg_args[1]); @@ -408,13 +385,15 @@ static void do_forward_chan(void) { } static void do_numeric(void) { - unsigned int num = atoi(msg_cmd); + static unsigned int num; + + num = a2uint(msg_cmd); switch(num) { /* use the server's idea of what our nick is, in case it got truncated. */ case RPL_WELCOME: - strcpy(conf->nick, msg_args[0]); + strcpy(config.nick, msg_args[0]); regged = 1; do_catchall(1); break; @@ -438,11 +417,13 @@ static void do_numeric(void) { */ case ERR_NICKNAMEINUSE: - do_catchall(0); - if(!regged) { + if(regged) { + scr_activate(scr_current); + } else { permute_nick(); send_nick(); } + do_catchall(1); break; /* don't print these, just noise */ @@ -450,18 +431,20 @@ static void do_numeric(void) { break; case RPL_MOTD: - /* FIXME: this prevents the user using /MOTD on purpose, too */ if(!hide_motd) - do_catchall(0); + do_catchall(1); break; /* don't print, but do trigger rejoin */ case RPL_ENDOFMOTD: case ERR_NOMOTD: hide_motd = 0; - cmd_rejoin_chans(); - if(scr_names[2][0] == '#') - scr_display(2); + if(need_rejoin) { + cmd_rejoin_chans(); + if(scr_names[2][0] == '#') + scr_display(2); + need_rejoin = 0; + } break; case RPL_NAMREPLY: @@ -493,17 +476,32 @@ static void do_numeric(void) { do_forward_chan(); break; + case RPL_AWAY: + scr_print_active(msg_args[1]); + scr_print_active(" is away: "); + scr_print_active(msg_text); + scr_eol_active(); + break; + default: + if(num >= 400 && num < 600) + scr_activate(scr_current); do_catchall(1); break; } } +#if 0 static void invalid_msg(char type) { scr_print(SCR_SERVER, "??? unknown, type "); scr_putc(SCR_SERVER, type); scr_putc(SCR_SERVER, '\n'); } +#endif + +static char cmd_is(char *cmd) { + return streq_i(msg_cmd, cmd); +} void select_screen(void) { char s; @@ -519,7 +517,7 @@ void select_screen(void) { } else { s = scr_getbyname(msg_src); if(!s) { - if(streq_i(msg_cmd, "PRIVMSG")) { /* or maybe NOTICE? */ + if(cmd_is("PRIVMSG")) { /* or maybe NOTICE? */ strncpy(last_pm_nick, msg_src, 32); comp_add_pm_nick(last_pm_nick); s = SCR_PRIV; @@ -532,34 +530,50 @@ void select_screen(void) { } static void dispatch_msg(void) { + self_src = streq_i(config.nick, msg_src); + /* at this point, we know the message source and destination, so: */ + /* FIXME: maybe we know... */ select_screen(); + new_scr_status = SCR_OTHER; - if(streq_i(msg_cmd, "PRIVMSG")) { + if(cmd_is("PRIVMSG")) { + new_scr_status = SCR_ACTIVE; do_privmsg(); - } else if(streq_i(msg_cmd, "NOTICE")) { + } else if(cmd_is("NOTICE")) { do_notice(); - } else if(streq_i(msg_cmd, "JOIN")) { + } else if(cmd_is("JOIN")) { do_join(); - } else if(streq_i(msg_cmd, "NICK")) { + } else if(cmd_is("NICK")) { do_nick(); - } else if(streq_i(msg_cmd, "QUIT")) { + } else if(cmd_is("QUIT")) { do_quit(); - } else if(streq_i(msg_cmd, "PART")) { + } else if(cmd_is("PART")) { do_part(); - } else if(streq_i(msg_cmd, "TOPIC")) { + } else if(cmd_is("TOPIC")) { do_topic(); - } else if(streq_i(msg_cmd, "KICK")) { + } else if(cmd_is("KICK")) { do_kick(); - } else if(streq_i(msg_cmd, "MODE")) { + } else if(cmd_is("MODE")) { do_mode(); - } else if(streq_i(msg_cmd, "PONG")) { - if(*msg_text != 'A') do_server_pong(); - } else if(isdigit(msg_cmd[0])) { + } else if(cmd_is("PONG")) { + if(*msg_text == 'A') { + return; /* do not set screen status */ + } else { + do_server_pong(); + } + } else if(isnum(msg_cmd[0])) { do_numeric(); } else { do_catchall(0); } + + if(scr_active != scr_current) { + if(scr_status[scr_active] < new_scr_status) { + scr_status[scr_active] = new_scr_status; + scr_show_status(scr_current); + } + } } /* msgbuf contains a complete message from the server, whose @@ -568,73 +582,52 @@ static void dispatch_msg(void) { static void parse_msg(void) { char *p; - msg_cmd = msg_text = msg_src = msg_dest = 0; - msg = msgbuf; - /* ignore empty message */ - if(!*msg) return; + if(!*msgbuf) return; - /* - scr_print_active("RAW: "); - scr_print_active(msg); - scr_eol_active(); - */ - - /* if there's a final multiword arg... */ - /* FIXME: channel names can have colons, which breaks this... */ - p = strstr(msg + 1, " :"); /* +1 to skip leading colon in msg source */ - if(p) { - msg_text = p + 2; - *p = 0; - } + msg_cmd = msg_text = msg_src = msg_dest = 0; + memset(msg_args, 0, sizeof(msg_args)); /* first token is either the source (with a :) or a command (without) */ - p = strtok(msg, " "); - if(!p) { - invalid_msg('1'); - return; - } - - if(*p == ':') { - msg_src = p + 1; /* generally :irc.example.com or :nick!user@host */ - msg_cmd = strtok(0, " "); + if(*msgbuf == ':') { + msg_src = msgbuf + 1; /* generally :irc.example.com or :nick!user@host */ + msg_cmd = nextarg(msgbuf); } else { msg_src = 0; /* no source supplied */ - msg_cmd = p; + msg_cmd = msgbuf; } + p = nextarg(msg_cmd); + #if 0 if(!msg_cmd) { invalid_msg('2'); return; } + #endif /* special case for ping, treat as 1 arg, even if it has space and no : */ - if(streq_i(msg_cmd, "PING")) { - msg_argcount = 1; - msg_args[0] = msg_cmd + 6; - do_pong(); + if(cmd_is("PING")) { + txbuf_set_str2("PONG ", msg_cmd + 6); + txbuf_send(); return; - } else { - for(msg_argcount = 0; msg_argcount < MAX_MSG_ARGS; msg_argcount++) { - p = strtok(0, " "); - if(p) { - msg_args[msg_argcount] = p; - /* if any arg is a channel name, use it for the dest */ - if(*p == '#') - msg_dest = p; - } else { - break; - } - } } - /* - if(msg_dest) { - scr_print_current("got here, msg_dest is: "); - scr_print_current(msg_dest); - scr_eol_current(); + for(msg_argcount = 0; msg_argcount < MAX_MSG_ARGS; msg_argcount++) { + if(!p) break; + + if(*p == ':') { + msg_text = p + 1; + break; + } + + msg_args[msg_argcount] = p; + + /* if any arg is a channel name, use it for the dest */ + if(*p == '#') + msg_dest = p; + + p = nextarg(p); } - */ if(!msg_dest) { if(msg_argcount) @@ -644,9 +637,9 @@ static void parse_msg(void) { } if(msg_src) { - if((p = strstr(msg_src, "!"))) { + if((p = strchr(msg_src, '!'))) { *p = '\0'; - } else if(strstr(msg_src, ".")) { + } else if(strchr(msg_src, '.')) { msg_src = 0; } } @@ -662,31 +655,30 @@ static void irc_split_Lines(void) { char *p = rx_buf; for(i = 0; i < rxbuflen; i++) { - msgbuf[msgbuf_len] = *p; - if(*p == CH_EOL) { - // msgbuf[msgbuf_len + 1] = '\0'; - /* do not include the EOL */ - msgbuf[msgbuf_len] = '\0'; - parse_msg(); - msgbuf_len = 0; - } else { - msgbuf_len++; + /* skip ASCII \r character */ + if(*p != 0x0d) { + if(*p == 0x0a) { + /* got ASCII \n */ + msgbuf[msgbuf_len] = '\0'; + parse_msg(); + msgbuf_len = 0; + } else { + msgbuf[msgbuf_len++] = *p; + } } p++; } } -/* TODO: there needs to be a scr_printnum() */ void print_errnum(void) { extern unsigned char err; scr_print_current("Error #"); - itoa(err, numbuf, 10); - scr_print_current(numbuf); + scr_cur_printnum(err); } static void start_minute_timer() { OS.cdtmf4 = 0xff; - OS.cdtmv4 = 60 * hz; + OS.cdtmv4 = timers.one_sec; } static char service_minute_timer() { @@ -716,17 +708,16 @@ static char service_minute_timer() { } else { /* idle >=121 sec, already sent a ping, got nothing back. we timed out. */ - bell(); /* for testing only */ - scr_print_current("Server timed out"); + scr_print_current("Server timed out\n"); return 0; } } -int irc_read(void) { +char irc_read(void) { if(!trip) return 1; last_read_min = minutes; - err = nstatus(conf->url); + err = nstatus(); if(err != 1) { scr_display(SCR_SERVER); @@ -749,7 +740,7 @@ int irc_read(void) { ind_net_rx(); if(rxbuflen > 0) { - err = nread(conf->url, rx_buf, rxbuflen); + err = nread_rxbuf(); if(err != 1) { ind_net_down(); print_errnum(); @@ -774,20 +765,25 @@ void irc_register(void) { /* 2nd arg: local (UNIX) username, just use the nick */ /* 3rd arg: "real" name */ - txbuf_set_str3("USER ", conf->nick, " 0 * :"); - txbuf_append_str(conf->real_name); + txbuf_set_str3("USER ", config.nick, " 0 * :"); + txbuf_append_str(config.real_name); txbuf_send(); send_nick(); } static void scrollback() { - OS.ch = 0xff; + char c; scr_scrollback(); - while(OS.ch == 0xff) + while(!keypress()) irc_read(); - keyclick(); - OS.ch = 0xff; + c = kgetc(); + if(c == '-') { + scr_scrollback_bonus(); + while(!keypress()) + irc_read(); + kgetc(); + } scr_end_scrollback(); } @@ -804,22 +800,23 @@ static void hunt_screen(signed char dir) { scr_display(s); } -static char *get_cur_chan(void) { - if(scr_current == SCR_SERVER && last_chan[0]) - return last_chan; - else if ((scr_current > 1) && (scr_names[scr_current][0] == '#')) +static char *get_cur(void) { + if((scr_current > 1) && (scr_names[scr_current][0] == '#')) return scr_names[scr_current]; else return 0; } +static char *get_cur_chan(void) { + if(scr_current == SCR_SERVER && last_chan[0]) + return last_chan; + else return get_cur(); +} + static char *get_cur_nick(void) { if(scr_current == SCR_PRIV && last_pm_nick[0]) return last_pm_nick; - else if (scr_current > 1 && scr_names[scr_current][0] != '#') - return scr_names[scr_current]; - else - return 0; + else return get_cur(); } static void send2_with_space(char *s1, char *s2) { @@ -857,7 +854,7 @@ static char cur_is_query(void) { /* count backwards here, because the [server] screen is 0, it's the least interesting one. */ -char find_scr_with_status(int status) { +char find_scr_with_status(char status) { signed char i; for(i = MAX_SCREENS - 1; i != -1; i--) { @@ -874,6 +871,8 @@ void switch_to_active() { i = find_scr_with_status(SCR_HILITE); if(i == 0xff) i = find_scr_with_status(SCR_ACTIVE); + if(i == 0xff) + i = find_scr_with_status(SCR_OTHER); if(i != 0xff) { scr_prev = scr_current; scr_display(i); @@ -918,13 +917,14 @@ static void toggle_edbox_only(void) { OS.sdlst = edbox_only_dlist; } -static void start_keystroke(void) { - char i, s; +void start_keystroke(char c) { + char s; - i = cgetc(); + start_latch = 0; + if(c == CH_ESC) return; - if(i >= '1' && i <= '7') { - s = i - '1'; + if(c >= '1' && c <= '7') { + s = c - '1'; if(s != scr_current) { if(scr_status[s] != SCR_UNUSED) { scr_prev = scr_current; @@ -934,32 +934,32 @@ static void start_keystroke(void) { return; } - switch(tolower(i)) { - case CH_CURS_UP: + switch(lcase(c)) { + case XCH_UP: case '-': scrollback(); return; case 0x18: /* ^X */ send_cur_chan_cmd("PART"); /* fall thru */ - case CH_ESC: + case 'x': scr_prev = SCR_PRIV; scr_destroy(scr_current); return; - case CH_CURS_LEFT: + case XCH_LEFT: case '+': scr_prev = scr_current; hunt_screen(-1); return; - case CH_CURS_RIGHT: + case XCH_RIGHT: case '*': scr_prev = scr_current; hunt_screen(1); return; - case CH_TAB: - i = scr_current; + case XCH_TAB: + c = scr_current; scr_display(scr_prev); - scr_prev = i; + scr_prev = c; return; case 'q': if(scr_current == SCR_PRIV && *last_pm_nick) { @@ -1007,29 +1007,17 @@ static void start_keystroke(void) { } } -static void keystroke(void) { - if(OS.ch == 0xff) return; - if(irc_away) { - txbuf_send_str("AWAY"); - irc_away = 0; - } - if(GTIA_READ.consol == 6) { /* start pressed */ - start_keystroke(); - } else { - edbox_keystroke(); - } -} - /* only exits on error (e.g. connection closed, which might be via /QUIT). */ void irc_loop(void) { /* this stuff happens on every connect. */ - hide_motd = conf->hide_motd; + hide_motd = config.hide_motd; msgbuf[0] = msgbuf_len = regged = irc_away = minutes = 0; + need_rejoin = 1; start_minute_timer(); while(1) { ind_check_timer(); - if(conf->atract_away) { + if(config.atract_away) { if(!irc_away && (OS.atract & 0x80)) { irc_away = 1; txbuf_send_str("AWAY :ATRACT mode"); @@ -1038,6 +1026,9 @@ void irc_loop(void) { if(!irc_read() || !service_minute_timer()) { return; } - keystroke(); + OS.cdtmv3 = 0; + do { + poll_keyboard(); + } while(OS.cdtmv3); } } @@ -1,32 +1,29 @@ -#define FNET_TRANSLATION 3 #define MAX_IRC_MSG_LEN 512 - -#define streq(x,y) !strcmp(x,y) -#define streq_i(x,y) !strcasecmp(x,y) +#include "rxtxbuf.h" /**** main.c */ -extern char *rx_buf; -extern unsigned short rxbuflen; + +extern unsigned int rxbuflen; +extern unsigned int txbuflen; + extern unsigned char err; extern unsigned char trip; -extern char hz; extern char reconnect_timeout; -extern unsigned int txbuflen; -extern char *tx_buf; - /* clears the transmit buffer. */ void txbuf_init(void); +void txbuf_append_chr(char c); + /* appends a string to the transmit buffer, updates txbuflen. */ void txbuf_append_str(const char *str); void txbuf_append_str2(const char *s1, const char *s2); -void txbuf_append_str3(const char *s1, const char *s2, const char *s3); +// void txbuf_append_str3(const char *s1, const char *s2, const char *s3); /* clears the transmit buffer, then appends a string to it. */ void txbuf_set_str(const char *str); -/* as txbuf_set_str2(), but multiple strings. */ +/* as txbuf_set_str(), but multiple strings. */ void txbuf_set_str2(const char *s1, const char *s2); void txbuf_set_str3(const char *s1, const char *s2, const char *s3); @@ -44,13 +41,13 @@ void fn_disconnect(void); /**** irc.c */ #define MAX_MSG_ARGS 8 -extern char bell_type; extern char numbuf[10]; extern char *msg_src, *msg_cmd, *msg_dest, *msg_text; extern char *msg_args[MAX_MSG_ARGS]; -extern int msg_argcount; +extern char msg_argcount; extern char irc_away; extern char bell_type; +extern char start_latch; extern char last_pm_nick[33]; extern char last_chan[33]; @@ -65,6 +62,8 @@ void print_errnum(void); void __fastcall__ bell(void); /* see src/bell.s */ +void start_keystroke(char c); + /**** cmd.c */ void cmd_command(char *cmd); void cmd_execute(void); @@ -74,3 +73,12 @@ void cmd_send_pm(char *args); void cmd_ctcp_ping(char *nick); void cmd_server_ping(void); unsigned int read_rtclok(void); /* irc.c needs this one so it's not static */ + +/* see isnum.s */ +extern char __fastcall__ isnum(char c); + +/* see a2uint.s */ +extern unsigned int __fastcall__ a2uint(char *str); + +/* nextarg.s */ +extern char *nextarg(char *arg); diff --git a/src/isnum.s b/src/isnum.s new file mode 100644 index 0000000..7aa3251 --- /dev/null +++ b/src/isnum.s @@ -0,0 +1,18 @@ + + .export _isnum + + ; isdigit() replacement that avoids cc65's ctype bloat. + ; returns 0 in A/X for non-digit, non-zero for digit. + ; *also* when calling from asm, the Z flag is set for + ; non-digit, clear for digit. +_isnum: + cmp #'0' + bcc ret0 + cmp #'9'+1 + bcs ret0 + lda #1 + .byte $2c +ret0: + lda #0 + tax + rts diff --git a/src/keyclick.c b/src/keyclick.c deleted file mode 100644 index d1e7505..0000000 --- a/src/keyclick.c +++ /dev/null @@ -1,8 +0,0 @@ -#include <atari.h> -#include <conio.h> - -/* sound the keyclick. obeys the NOCLIK flag on XL/XE. */ -void keyclick(void) { - OS.ch = 0; - cgetc(); -} diff --git a/src/keyclick.h b/src/keyclick.h deleted file mode 100644 index b73a212..0000000 --- a/src/keyclick.h +++ /dev/null @@ -1 +0,0 @@ -extern void keyclick(void); diff --git a/src/keytab.c b/src/keytab.c new file mode 100644 index 0000000..5fd8963 --- /dev/null +++ b/src/keytab.c @@ -0,0 +1,274 @@ +#include "keytab.h" + +/* +This table is a modified version of the one from the XL OS ROM. +Atari uses $80 to mean "unused". Here, I use 0 to mean unused. +Also, Atari's table is only 192 bytes. This is 256 bytes because +it includes ctrl-shift combos. +*/ + +char keytab[256] = { +// no modifier + 0x6c, // 0 = l + 0x6a, // 1 = j + 0x3b, // 2 = ; + XCH_SCR1, // 3 = 1200XL F1 + XCH_SCR2, // 4 = 1200XL F2 + 0x6b, // 5 = k + 0x2b, // 6 = + + 0x2a, // 7 = * + 0x6f, // 8 = o + 0x00, // 9 = (unused) + 0x70, // 10 = p + 0x75, // 11 = u + 0x9b, // 12 = EOL + 0x69, // 13 = i + 0x2d, // 14 = - + 0x3d, // 15 = = + 0x76, // 16 = v + 0x00, // 17 = (unused) + 0x63, // 18 = c + XCH_SCR3, // 19 = 1200XL F3 + XCH_SCR4, // 20 = 1200XL F4 + 0x62, // 21 = b + 0x78, // 22 = x + 0x7a, // 23 = z + 0x34, // 24 = 4 + 0x00, // 25 = (unused) + 0x33, // 26 = 3 + 0x36, // 27 = 6 + 0x1b, // 28 = esc + 0x35, // 29 = 5 + 0x32, // 30 = 2 + 0x31, // 31 = 1 + 0x2c, // 32 = , + 0x20, // 33 = space + 0x2e, // 34 = . + 0x6e, // 35 = n + 0x00, // 36 = (unused) + 0x6d, // 37 = m + 0x2f, // 38 = / + 0x02, // 39 = inverse (toggle bold) + 0x72, // 40 = r + 0x00, // 41 = (unused) + 0x65, // 42 = e + 0x79, // 43 = y + XCH_TAB, // 44 = tab + 0x74, // 45 = t + 0x77, // 46 = w + 0x71, // 47 = q + 0x39, // 48 = 9 + 0x00, // 49 = (unused) + 0x30, // 50 = 0 + 0x37, // 51 = 7 + XCH_BS, // 52 = ~ + 0x38, // 53 = 8 + 0x3c, // 54 = < + 0x3e, // 55 = > + 0x66, // 56 = f + 0x68, // 57 = h + 0x64, // 58 = d + 0x00, // 59 = (unused) + XCH_CAPS, // 60 = caps toggle + 0x67, // 61 = g + 0x73, // 62 = s + 0x61, // 63 = a + +// shift + 0x4c, // 64 = L + 0x4a, // 65 = J + 0x3a, // 66 = : + XCH_SCR5, // 67 = 1200XL Shift-F1 + XCH_SCR6, // 68 = 1200XL Shift-F2 + 0x4b, // 69 = K + 0x5c, // 70 = "\" + 0x5e, // 71 = ^ + 0x4f, // 72 = O + 0x00, // 73 = (unused) + 0x50, // 74 = P + 0x55, // 75 = U + 0x9b, // 76 = EOL + 0x49, // 77 = I + 0x5f, // 78 = _ + 0x7c, // 79 = | + 0x56, // 80 = V + 0x00, // 81 = (unused) + 0x43, // 82 = C + XCH_SCR7, // 83 = 1200XL Shift-F3 + XCH_ACTIVE, // 84 = 1200XL Shift-F4 + 0x42, // 85 = B + 0x58, // 86 = X + 0x5a, // 87 = Z + 0x24, // 88 = $ + 0x00, // 89 = (unused) + 0x23, // 90 = # + 0x26, // 91 = & + 0x60, // 92 = ` (atari shift-esc) + 0x25, // 93 = % + 0x22, // 94 = " + 0x21, // 95 = ! + 0x5b, // 96 = [ + 0x20, // 97 = space + 0x5d, // 98 = ] + 0x4e, // 99 = N + 0x00, // 100 = (unused) + 0x4d, // 101 = M + 0x3f, // 102 = ? + 0x00, // 103 = inverse + 0x52, // 104 = R + 0x00, // 105 = (unused) + 0x45, // 106 = E + 0x59, // 107 = Y + 0x00, // 108 = shift-tab + 0x54, // 109 = T + 0x57, // 110 = W + 0x51, // 111 = Q + 0x28, // 112 = ( + 0x00, // 113 = (unused) + 0x29, // 114 = ) + 0x27, // 115 = ' + 0x9c, // 116 = del line + 0x40, // 117 = @ + XCH_CLS, // 118 = } + 0x9d, // 119 = ins line + 0x46, // 120 = F + 0x48, // 121 = H + 0x44, // 122 = D + 0x00, // 123 = (unused) + XCH_CAPS, // 124 = caps lock + 0x47, // 125 = G + 0x53, // 126 = S + 0x41, // 127 = A + +// ctrl + 0x0c, // 128 = ^L + 0x00, // 129 = ^J + 0x00, // 130 = ^:, unused + 0x00, // 131 = (unused) + 0x00, // 132 = (unused) + 0x0b, // 133 = ^K + XCH_LEFT, // 134 = atari left arrow + XCH_RIGHT, // 135 = atari right arrow + 0x0f, // 136 = ^O + 0x00, // 137 = (unused) + 0x10, // 138 = ^P + 0x15, // 139 = ^U + 0x9b, // 140 = EOL + XCH_ITAL, // 141 = ^I: IRC italics format + XCH_UP, // 142 = atari up arrow + XCH_DOWN, // 143 = atari down arrow + 0x16, // 144 = ^V + 0x00, // 145 = (unused) + 0x03, // 146 = ^C + 0x00, // 147 = 1200XL ctrl-F3 (?) + 0x00, // 148 = (unused) + XCH_LWORD, // 149 = ^B + 0x18, // 150 = ^X + 0x1a, // 151 = ^Z + XCH_SCR4, // 152 = inverse 4 (switch to screen 4) + 0x00, // 153 = (unused) + XCH_SCR3, // 154 = inverse 3 (switch to screen 3) + XCH_SCR6, // 155 = inverse 6 (switch to screen 6) + 0x7e, // 156 = ~ (tilde, aka atari ctrl-esc) + XCH_SCR5, // 157 = inverse 5 (switch to screen 5) + XCH_SCR2, // 158 = inverse 2 (switch to screen 2) + XCH_SCR1, // 159 = inverse 1 (switch to screen 1) + 0x7b, // 160 = { + 0x20, // 161 = space + 0x7d, // 162 = } + 0x0e, // 163 = ^N + 0x00, // 164 = (unused) + 0x00, // 165 = ^M + 0x00, // 166 = (unused) + 0x00, // 167 = inverse + 0x12, // 168 = ^R + 0x00, // 169 = (unused) + 0x05, // 170 = ^E + 0x19, // 171 = ^Y + 0x00, // 172 = ctrl-tab + 0x14, // 173 = ^T + 0x17, // 174 = ^W + 0x11, // 175 = ^Q + 0x00, // 176 = inverse 9 (if we ever support 9 screens) + 0x00, // 177 unused? not XCH_SCR1? + 0x00, // 178 = inverse 0 (if we ever support 10 screens) + XCH_SCR7, // 179 = inverse 7 (switch to screen 7) + 0xfe, // 180 = del chr + 0x00, // 181 = inverse 8 (if we ever support 8 screens) + XCH_CLS, // 182 = } + 0xff, // 183 = ins chr + XCH_RWORD, // 184 = ^F + 0x08, // 185 = ^H + 0x04, // 186 = ^D + 0x00, // 187 = (unused) + 0x00, // 188 = ctrl-caps (unused) + 0x07, // 189 = ^G + 0x13, // 190 = ^S + 0x01, // 191 = ^A + +// ctrl+shift + 0x00, // 192 + 0x00, // 193 + 0x00, // 194 + 0x00, // 195 + 0x00, // 196 + 0x00, // 197 + 0x00, // 198 + 0x00, // 199 + 0x00, // 200 + 0x00, // 201 + 0x00, // 202 + XCH_UNDER, // 203 + 0x00, // 204 + 0x00, // 205 + XCH_LWORD, // 206, ctrl+shift+up + XCH_RWORD, // 207, ctrl+shift+down + 0x00, // 208 + 0x00, // 209 + 0x00, // 210 + 0x00, // 211 + 0x00, // 212 + 0x00, // 213 + 0x00, // 214 + 0x00, // 215 + 0x00, // 216 + 0x00, // 217 + 0x00, // 218 + 0x00, // 219 + 0x00, // 220 + 0x00, // 221 + 0x00, // 222 + 0x00, // 223 + 0x00, // 224 + 0x00, // 225 + 0x00, // 226 + 0x00, // 227 + 0x00, // 228 + 0x00, // 229 + 0x00, // 230 + 0x00, // 231 + 0x00, // 232 + 0x00, // 233 + 0x00, // 234 + 0x00, // 235 + 0x00, // 236 + 0x00, // 237 + 0x00, // 238 + 0x00, // 239 + 0x00, // 240 + 0x00, // 241 + 0x00, // 242 + 0x00, // 243 + 0x00, // 244 + 0x00, // 245 + 0x00, // 246 + 0x00, // 247 + 0x00, // 248 + 0x00, // 249 + 0x00, // 250 + 0x00, // 251 + 0x00, // 252 + 0x00, // 253 + 0x00, // 254 + 0x00, // 255 +}; diff --git a/src/keytab.h b/src/keytab.h new file mode 100644 index 0000000..04d3df9 --- /dev/null +++ b/src/keytab.h @@ -0,0 +1,34 @@ + +#define XCH_SCR1 0xb1 +#define XCH_SCR2 0xb2 +#define XCH_SCR3 0xb3 +#define XCH_SCR4 0xb4 +#define XCH_SCR5 0xb5 +#define XCH_SCR6 0xb6 +#define XCH_SCR7 0xb7 +/* when/if we have more screens: +#define XCH_SCR8 0xb8 +#define XCH_SCR9 0xb9 +#define XCH_SCR0 0xb0 +*/ + +#define XCH_TAB 0x80 +#define XCH_UP 0x81 +#define XCH_DOWN 0x82 +#define XCH_LEFT 0x83 +#define XCH_RIGHT 0x84 +#define XCH_LWORD 0x85 +#define XCH_RWORD 0x86 +#define XCH_CLS 0x87 +#define XCH_BS 0x88 +#define XCH_ACTIVE 0x89 +#define XCH_CAPS 0x8a + +/* these are just control characters */ +#define XCH_ITAL 0x1d +#define XCH_UNDER 0x1f + +/* cc65's atari.h should define this but doesn't */ +#define XCH_INSCHR 0xff + +extern char keytab[256]; diff --git a/src/kgetc.h b/src/kgetc.h new file mode 100644 index 0000000..3f96810 --- /dev/null +++ b/src/kgetc.h @@ -0,0 +1,2 @@ +char __fastcall__ kgetc(void); +char __fastcall__ keypress(void); diff --git a/src/kgetc.s b/src/kgetc.s new file mode 100644 index 0000000..524e348 --- /dev/null +++ b/src/kgetc.s @@ -0,0 +1,127 @@ +; kgetc() is a replacement for cc65's cgetc(). It does *not* call the +; OS's K: "get one byte" routine to decode keycodes into (AT)ASCII. It +; *does* support all 255 keycodes, meaning ctrl+shift combos work. + +; kgetc() actually does call the OS K: handler to produce a keyclick, but +; it does so with a fixed keycode (12, the Return key) and throws away +; the result. This means we can turn off the keyclick even on an 800. +; The OS NOCLIK location is used on the 800, too. + +; There is no blocking for another keystroke, if a key like Escape or +; Inverse is pressed. The ctrl-1 keystroke is detected, even though +; the OS "pre-empts" it (we never see $b1 in CH). + +; There is no inverse video mode! INVFLG is never set, and is ignored. +; There is no Control-lock either, although Caps-lock exists. + +; Unlike cgetc(), kgetc() doesn't need to be able to return 0 as a valid +; character. So instead, 0 is used to mean "ignore keystroke". + +; Since inverse numbers can't be returned, I've reused their ATASCII codes +; for 'switch window' with ctrl-1 through 7. + +; kgetc() blocks if no key has been pressed when it's called. to do +; non-blocking reads: +; if(keypress()) +; c = kgetc(); +; /* c is 0 if either no key or a non-ATASCII keystroke was pressed, +; e.g. caps lock or an unused ctrl-shift-letter. */ + + .include "atari.inc" + .export _kgetc, _keypress + .import _keytab + + XCH_CAPS = $8a ; must agree with keytab.h + + ; note: _keypress returns true/false, but also the Z + ; flag reflects the return status (Z clear for true). +_keypress: + ldx CH + cpx #$ff + bne ret1 ; if CH != $ff, a key is pressed... + ldx SSFLAG + bne ret1 ; if SSFLAG != 0, a key is pressed... + ldx HELPFG + bne ret1 ; if HELPFG != 0, a key (Help) is pressed... + txa ; return(0); + .byte $2c ; BIT ABS, skip LDA imm below +ret1: + lda #1 + rts + +_kgetc: + ldx HELPFG + beq read_ch ; Help key pressed? + lda #0 + sta HELPFG ; yes, clear it + ldx #$54 ; 1200XL Shift-F4 keycode + bne decode + +read_ch: + ldx CH + cpx #$ff + bne decode + ldx SSFLAG + beq read_ch + lda #0 + sta SSFLAG + ldx #$9f ; real Ctrl-1 keycode + +decode: + lda _keytab,x + beq done ; 0 in the table means ignore the keystroke (not even a click) + + cmp #XCH_CAPS + bne gotkey + + lda SHFLOK ; caps was pressed. we use SHFLOK different from how the OS does. + eor #$20 + sta SHFLOK + lda #0 + beq click_and_done + +gotkey: ; ATASCII code in A here, don't overwrite + cmp #'z'+1 ; only apply caps lock to a-z + bcs noshf + cmp #'a' + bcc noshf + sbc SHFLOK +noshf: + ldx NOCLIK + bne done ; obey the XL OS's keyclick disable flag, even on an 800 + + ; sound the keyclick (let the OS do it) +click_and_done: + pha + jsr keyclick + pla + +done: + ldx #$ff + stx CH + rts + +keyclick: + lda NOCLIK + bne return ; disabled + lda #0 + sta SSFLAG + lda #12 ; EOL (could be any regular key but we need 12 below) + sta CH ; fall thru to cgetc... + +; ripped from the cc65 lib src, and modified. +; original code by Christian Groessler, November-2002. +;_cgetc: + ;lda #12 ; already in A + sta ICAX1Z ; fix problems with direct call to KEYBDV + jsr callit +return: + ;ldx #0 + rts + +callit: + lda KEYBDV+5 + pha + lda KEYBDV+4 + pha + rts @@ -10,49 +10,41 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <ctype.h> -#include <conio.h> // for cgetc() only #include "nio.h" #include "irc.h" #include "screen.h" #include "edbox.h" #include "config.h" -#include "keyclick.h" +#include "kgetc.h" #include "indic8.h" +#include "timers.h" unsigned char err; // error code of last operation. unsigned char trip = 0; // if trip == 1, fujinet is asking us for attention. char old_enabled = 0; // were interrupts enabled for old vector void *old_vprced; // old PROCEED vector, restored on exit. -unsigned short rxbuflen; // RX buffer length +unsigned int rxbuflen; // RX buffer length unsigned int txbuflen; // TX buffer length -char hz; /* 50 for PAL, 60 for NSTC */ char reconnect_timeout = 1; -/* TODO: user modes (default +iw), fg/bg color... */ - extern void ih(); // defined in intr.s void txbuf_init(void) { txbuflen = tx_buf[0] = 0; } -void txbuf_append_str(const char *str) { - while(*str) { - tx_buf[txbuflen++] = *str++; - } -} - void txbuf_append_str2(const char *s1, const char *s2) { txbuf_append_str(s1); txbuf_append_str(s2); } +/* void txbuf_append_str3(const char *s1, const char *s2, const char *s3) { txbuf_append_str(s1); txbuf_append_str(s2); txbuf_append_str(s3); } +*/ void txbuf_set_str(const char *str) { txbuf_init(); @@ -73,12 +65,15 @@ void txbuf_send(void) { /* don't send empty buffer */ if(!txbuflen) return; - /* always terminate with EOL */ - if(tx_buf[txbuflen - 1] != '\n') - tx_buf[txbuflen++] = '\n'; + /* always terminate with *ASCII* CRLF. + DO NOT USE '\n' or even '\x0a', cc65 turns it into $9b! */ + // tx_buf[txbuflen++] = 0x0d; + // tx_buf[txbuflen++] = 0x0a; + txbuf_append_chr(0x0d); + txbuf_append_chr(0x0a); ind_net_tx(); - nwrite(conf->url, tx_buf, txbuflen); + nwrite_txbuf(); ind_net_idle(); txbuf_init(); } @@ -92,10 +87,10 @@ void txbuf_send_str(const char *str) { int fn_connect(void) { scr_display(SCR_SERVER); scr_print_current("Connecting to: "); - scr_print_current(conf->url); + scr_print_current(config.url); scr_eol_current(); - err = nopen(conf->url, FNET_TRANSLATION); + err = nopen(); if(err != SUCCESS) { scr_print_current("Connection failed: "); @@ -118,7 +113,7 @@ int fn_connect(void) { } void fn_disconnect(void) { - nclose(conf->url); + nclose(); // Restore old PROCEED interrupt. PIA.pactl &= ~1; // disable interrupts @@ -130,9 +125,9 @@ void init_channels(void) { char i; for(i = 0; i < MAX_SCREENS - 2; i++) { - if(conf->channels[i][0]) { + if(config.channels[i][0]) { scr_status[i + 2] = SCR_INACTIVE; - strcpy(scr_names[i + 2], conf->channels[i]); + strcpy(scr_names[i + 2], config.channels[i]); } } } @@ -145,37 +140,30 @@ void reconnect(void) { OS.cdtmf3 = OS.ch = 0xff; if(reconnect_timeout) { - OS.cdtmv3 = reconnect_timeout * hz; + OS.cdtmv3 = reconnect_timeout * timers.hz; scr_print_current(" or wait "); - itoa(reconnect_timeout, numbuf, 10); - scr_print_current(numbuf); + scr_cur_printnum(reconnect_timeout); scr_print_current(" sec"); if(reconnect_timeout < 64) reconnect_timeout <<= 1; } scr_print_current(" to reconnect.\n"); - while(OS.cdtmf3 == 0xff && OS.ch == 0xff) + while(OS.cdtmf3 == 0xff && !keypress()) /* NOP */; - if(OS.ch != 0xff) { - keyclick(); - OS.ch = 0xff; - } + if(keypress()) kgetc(); } void main(void) { - bell_type = conf->alert_type; /* TODO: have bell.s read staight from the struct */ + bell_type = config.alert_type; /* TODO: have bell.s read staight from the struct */ OS.shflok = 0; // turn off shift-lock. OS.soundr = 0; // Turn off SIO beeping sound - OS.color2 = conf->colors[0]; /* text BG, user-selected */ - // OS.color1 = (conf->colors[1] & 0x0f) | 0xc0; /* green (at user's brightness) */ - OS.color1 = conf->colors[1]; + OS.color2 = config.colors[0]; /* text BG, user-selected */ + OS.color1 = config.colors[1]; OS.color0 = 0x06; /* grey for inactive */ - OS.color3 = 0x46; /* red for highlight (not used yet) */ - OS.noclik = conf->disable_keyclick; - - hz = (GTIA_READ.pal & 0x0e) ? 60 : 50; + OS.color3 = 0x46; /* red for highlight */ + OS.noclik = config.disable_keyclick; edbox_clear(); scr_init(); diff --git a/src/nextarg.s b/src/nextarg.s new file mode 100644 index 0000000..f01cc04 --- /dev/null +++ b/src/nextarg.s @@ -0,0 +1,78 @@ + + ; in C: + +;; /* arg points to something like: +;; "part #channel I'm outta here\0" +;; after nextarg(), arg points to "part\0" only, and ret points +;; to "#channel I'm outta here\0". */ +;; char *nextarg(char *arg) { +;; /* iterate over the first word */ +;; while(*arg && *arg != ' ') +;; arg++; +;; +;; /* if we found a space, replace it with a null terminator */ +;; if(*arg) +;; *arg++ = 0; +;; else +;; return 0; /* found no space, there's no next arg! */ +;; +;; /* skip space(s) */ +;; while(*arg && *arg == ' ') +;; arg++; +;; +;; if(*arg) +;; return arg; +;; +;; return 0; +;; } + +; ...which compiles to ~175 bytes. we can do better in asm. + + .export _nextarg + .importzp ptr1 + +incptr1: + inc ptr1 + bne @ret + inc ptr1+1 +@ret: + rts ; always returns with Z flag clear (unless we roll over $FFFF -> 0!) + +_nextarg: + sta ptr1 + stx ptr1+1 + + ldy #0 ; actually this stays 0 the while time + + ; skips over the first word (aka sequence of non-spaces) +@skipword: + lda (ptr1),y + beq @ret0 ; found a null byte, return null (there is no next arg). + cmp #' ' + beq @foundspc + jsr incptr1 + bne @skipword ; branch always + +@foundspc: + ; ptr1 now points to a space. + tya ; 0 + sta (ptr1),y ; null out the space + + jsr incptr1 +@skipspc: + lda (ptr1),y + beq @ret0 ; found a null byte, return null (there is no next arg). + cmp #' ' + bne @done + jsr incptr1 + bne @skipspc + +@done: + lda ptr1 + ldx ptr1+1 + rts + +@ret0: + lda #0 + tax + rts @@ -2,201 +2,113 @@ * N: I/O */ -#include "nio.h" -#include "sio.h" +/* "stripped down" version that harcodes some parameters! Don't + use this as example code for writing your own fujinet app! */ + #include <atari.h> #include <stddef.h> +#include "nio.h" +#include "sio.h" +#include "config.h" +#include "rxtxbuf.h" #define TIMEOUT 0x1f /* approx 30 seconds */ -unsigned char nunit(char* devicespec) -{ - unsigned char unit=1; - - // Set unit to 1 unless explicitly specified. - if (devicespec[1]==':') - unit=1; - else if (devicespec[2]==':') - unit=devicespec[1]-0x30; // convert from alpha to integer. - else - unit=1; - - return unit; +#define UNIT 1 /* only support one FujiNet connection! */ + +/* 0 = no translation (used to use 3, CRLF) */ +#define TRANS 0 + +static char get_status(void) { + if(OS.dcb.dstats != SUCCESS) { + // something went wrong + // do we need to return extended status? + if(OS.dcb.dstats == DERROR) { + nstatus(); + return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended error. + } + } + return OS.dcb.dstats; // Return SIO error or success } -unsigned char nopen(char* devicespec, unsigned char trans) -{ - unsigned char unit=nunit(devicespec); - - OS.dcb.ddevic = DFUJI; // Fuji Device Identifier - OS.dcb.dunit = unit; // Unit number integer 1 through 4 - OS.dcb.dcomnd = 'O'; // Open - OS.dcb.dstats = DWRITE; // sending to to SIO device - OS.dcb.dbuf = devicespec; // eg: N:TCP// - OS.dcb.dtimlo = TIMEOUT; // approximately 30 second timeout - OS.dcb.dbyt = 256; // max size of our device spec - OS.dcb.daux1 = OUPDATE; // Read and write - OS.dcb.daux2 = trans; // CR/LF translation - siov(); - - if (OS.dcb.dstats!=SUCCESS) - { - // something went wrong - // do we need to return extended status? - if (OS.dcb.dstats==DERROR) - { - nstatus(devicespec); - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended error. - } - } - return OS.dcb.dstats; // Return SIO error or success +static void set_defaults(void) { + OS.dcb.ddevic = DFUJI; // Fuji Device Identifier + OS.dcb.dunit = UNIT; // Unit number integer 1 through 4 + OS.dcb.dtimlo = TIMEOUT; // approximately 30 second timeout } -unsigned char nclose(char* devicespec) -{ - unsigned char unit=nunit(devicespec); - - OS.dcb.ddevic = DFUJI; - OS.dcb.dunit = unit; - OS.dcb.dcomnd = 'C'; // Close - OS.dcb.dstats = 0x00; - OS.dcb.dbuf = NULL; - OS.dcb.dtimlo = TIMEOUT; - OS.dcb.dbyt = 0; - OS.dcb.daux = 0; - siov(); - - if (OS.dcb.dstats!=SUCCESS) - { - // something went wrong - // do we need to return extended status? - if (OS.dcb.dstats==DERROR) - { - nstatus(devicespec); - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended error. - } - } - return OS.dcb.dstats; // Return SIO error or success. +char nopen(void) { + set_defaults(); + OS.dcb.dcomnd = 'O'; // Open + OS.dcb.dstats = DWRITE; // sending to to SIO device + OS.dcb.dbuf = config.url; // eg: N:TCP// + OS.dcb.dbyt = 256; // max size of our device spec + OS.dcb.daux1 = OUPDATE; // Read and write + OS.dcb.daux2 = TRANS; // CR/LF translation + siov(); + + return get_status(); } -unsigned char nstatus(char* devicespec) -{ - unsigned char unit=nunit(devicespec); - - OS.dcb.ddevic = DFUJI; - OS.dcb.dunit = unit; - OS.dcb.dcomnd = 'S'; // status - OS.dcb.dstats = DREAD; - OS.dcb.dbuf = OS.dvstat; - OS.dcb.dtimlo = TIMEOUT; - OS.dcb.dbyt = sizeof(OS.dvstat); - OS.dcb.daux = 0; - siov(); - - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended status -} +char nclose(void) { + set_defaults(); + OS.dcb.dcomnd = 'C'; // Close + OS.dcb.dstats = 0x00; + OS.dcb.dbuf = NULL; + OS.dcb.dbyt = 0; + OS.dcb.daux = 0; + siov(); -unsigned char nread(char* devicespec, unsigned char* buf, unsigned short len) -{ - unsigned char unit=nunit(devicespec); - - OS.dcb.ddevic = DFUJI; - OS.dcb.dunit = unit; - OS.dcb.dcomnd = 'R'; // read - OS.dcb.dstats = DREAD; - OS.dcb.dbuf = buf; - OS.dcb.dtimlo = TIMEOUT; - OS.dcb.dbyt = OS.dcb.daux = len; // Set the buffer size AND daux with length - siov(); - - if (OS.dcb.dstats!=SUCCESS) - { - // something went wrong - // do we need to return extended status? - if (OS.dcb.dstats==DERROR) - { - nstatus(devicespec); - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended error. - } - } - return OS.dcb.dstats; // Return SIO error or success. + return get_status(); } -unsigned char nwrite(char* devicespec, unsigned char* buf, unsigned short len) -{ - unsigned char unit=nunit(devicespec); - - OS.dcb.ddevic = DFUJI; - OS.dcb.dunit = unit; - OS.dcb.dcomnd = 'W'; // write - OS.dcb.dstats = DWRITE; - OS.dcb.dbuf = buf; - OS.dcb.dtimlo = TIMEOUT; - OS.dcb.dbyt = OS.dcb.daux = len; - siov(); - - if (OS.dcb.dstats!=SUCCESS) - { - // something went wrong - // do we need to return extended status? - if (OS.dcb.dstats==DERROR) - { - nstatus(devicespec); - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended error. - } - } - return OS.dcb.dstats; // Return SIO error or success. +char nstatus(void) { + set_defaults(); + OS.dcb.dcomnd = 'S'; // status + OS.dcb.dstats = DREAD; + OS.dcb.dbuf = OS.dvstat; + OS.dcb.dbyt = sizeof(OS.dvstat); + OS.dcb.daux = 0; + siov(); + + return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended status } -/* https://fujinet.online/wiki/?p=SIO-Command-%24FF-Reset-FujiNet */ -unsigned char nreset(void) { - OS.dcb.ddevic = 0x70; - OS.dcb.dunit = 1; - OS.dcb.dcomnd = 0xff; /* reset */ - OS.dcb.dstats = DWRITE; - OS.dcb.dbuf = 0; - OS.dcb.dtimlo = TIMEOUT; - OS.dcb.dbyt = 0; - OS.dcb.daux = 0; - siov(); - - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended status +char nread_rxbuf(void) { + extern unsigned int rxbuflen; + set_defaults(); + OS.dcb.dcomnd = 'R'; // read + OS.dcb.dstats = DREAD; + OS.dcb.dbuf = rx_buf; + OS.dcb.dbyt = OS.dcb.daux = rxbuflen; // Set the buffer size AND daux with length + siov(); + + return get_status(); } +char nwrite_txbuf(void) { + extern unsigned int txbuflen; + set_defaults(); + OS.dcb.dcomnd = 'W'; // write + OS.dcb.dstats = DWRITE; + OS.dcb.dbuf = tx_buf; + OS.dcb.dbyt = OS.dcb.daux = txbuflen; + siov(); + + return get_status(); +} -/* IRC doesn't need this */ -#if 0 -unsigned char nlogin(char* devicespec, char *login, char *password) -{ - unsigned char unit=nunit(devicespec); - - OS.dcb.ddevic=0x71; - OS.dcb.dunit=unit; - OS.dcb.dcomnd=0xFD; - OS.dcb.dstats=0x80; - OS.dcb.dbuf=login; - OS.dcb.dtimlo=0x1f; - OS.dcb.dbyt=256; - OS.dcb.daux=0; - siov(); - - if (OS.dcb.dstats!=1) - { - nstatus(devicespec); - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return ext err - } - - OS.dcb.dcomnd=0xFE; - OS.dcb.dstats=0x80; - OS.dcb.dbuf=password; - siov(); - - if (OS.dcb.dstats!=1) - { - nstatus(devicespec); - return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return ext err - } - - return OS.dcb.dstats; +/* https://fujinet.online/wiki/?p=SIO-Command-%24FF-Reset-FujiNet */ +char nreset(void) { + set_defaults(); + OS.dcb.ddevic = 0x70; + OS.dcb.dcomnd = 0xff; /* reset */ + OS.dcb.dstats = DWRITE; + OS.dcb.dbuf = 0; + OS.dcb.dbyt = 0; + OS.dcb.daux = 0; + siov(); + + return OS.dvstat[DVSTAT_EXTENDED_ERROR]; // return extended status } -#endif + @@ -22,58 +22,11 @@ #define DVSTAT_PROTOCOL 2 #define DVSTAT_EXTENDED_ERROR 3 -/** - * Open N: device with devicespec - * @param devicespec - an N: device spec, e.g. N:TCP://FOO.COM:1234/ - * @param translation mode, 0=none, 1=cr, 2=lf, 3=cr/lf - * @return error code, or 1 if successful. - */ -unsigned char nopen(char* devicespec, unsigned char trans); - -/** - * Close N: device with devicespec - * @param devicespec - an N: device spec to close (the unit number is extracted) - * @return error code, or 1 if successful. - */ -unsigned char nclose(char* devicespec); - -/** - * Get status of specific N: device - * @param devicespec - an N: device spec to status (the unit number is extracted) - * @return error code, or 1 if successful, DVSTAT is also filled with status info. - * - * Format of DVSTAT: - * OS.dcb.dvstat[0] = # of bytes waiting LO - * OS.dcb.dvstat[1] = # of bytes waiting HI - * OS.dcb.dvstat[2] = reserved - * OS.dcb.dvstat[3] = Error code of last I/O operation. !1 = error. - */ -unsigned char nstatus(char* devicespec); - -/** - * Read # of bytes from specific N: device. - * @param devicespec - an N: device spec to read bytes from. - * @param buf - The buffer to read into, must be at least as big as len. - * @param len - The # of bytes to read (up to 65535) - * @return error code, or 1 if successful, buf is filled with data. - */ -unsigned char nread(char* devicespec, unsigned char* buf, unsigned short len); - -/** - * Write # of bytes to specific N: device. - * @param devicespec - an N: device spec to write to. - * @param buf - The buffer to write to device, should be at least as big as len. - * @param len - The # of bytes to write (up to 65535) - * @return error code, or 1 if successful, buf is filled with data. - */ -unsigned char nwrite(char* devicespec, unsigned char* buf, unsigned short len); - -/** - * Send username and password credentials - * @param devicespec - The devicespec. - * @param login - The username to send - * @param password - The password to send - */ -// unsigned char nlogin(char* devicespec, char* login, char* password); +char nopen(void); +char nclose(void); +char nstatus(void); +char nread_rxbuf(void); +char nwrite_txbuf(void); +char nreset(void); #endif /* NIO_H */ diff --git a/src/permute.s b/src/permute.s new file mode 100644 index 0000000..d4206ea --- /dev/null +++ b/src/permute.s @@ -0,0 +1,78 @@ + .export _permute_nick + .importzp ptr1 + +;permutes last character (doesn't add one), so for "Bob" you get: +;Bo_, Bo1 through Bo9, BoA through BoZ +;Gives a total of 36 replacement nicks to try. +;Eventually we run out and start repeating, but by then the IRC +;server will have disconnected us. + +;; static void permute_nick(void) { +;; static char *last; +;; +;; last = conf->nick + strlen(conf->nick) - 1; +;; +;; if((*last >= '1' && *last < '9') || (*last >= 'A' && *last < 'Z')) { +;; (*last)++; +;; } else { +;; switch(*last) { +;; case '_': *last = '1'; break; +;; case '9': *last = 'A'; break; +;; default: *last = '_'; break; +;; } +;; } +;; } + +;; WARNING: the address of conf->nick is hardcoded here as $0480. +;; So is its length (25). + + nick = $0480 + +_permute_nick: + ldy #0 +@l1: + lda nick,y + beq perm + iny + cpy #$18 ; stop after 24 chars + bne @l1 + +perm: + dey ; last non-null character. + lda nick,y + + tax + + cpx #'1' + bcc nonnum + cpx #'8'+1 + bcc inc_and_ret + +nonnum: + cpx #'A' + bcc nonaplha + cpx #'Z' + bcc inc_and_ret + +nonaplha: + cpx #'_' + bne non_uscore + ldx #'1' + bne ret + +non_uscore: + cpx #'9' + bne non_9 + ldx #'A' + bne ret + +non_9: + ldx #'_' + bne ret + +inc_and_ret: + inx +ret: + txa + sta nick,y + rts diff --git a/src/pollkbd.s b/src/pollkbd.s new file mode 100644 index 0000000..2d44976 --- /dev/null +++ b/src/pollkbd.s @@ -0,0 +1,93 @@ +; in C: +;; void poll_keyboard(void) { +;; char c; +;; +;; if(!keypress()) return; +;; +;; /* have to latch start status because doing a keyclick clears CONSOL */ +;; if(GTIA_READ.consol == 6) start_latch = 1; +;; +;; c = kgetc(); +;; if(!c) return; +;; +;; /* maybe this shouldn't happen until user presses Enter in the edbox? +;; would let lurkers lurk and read scrollback... */ +;; if(irc_away) { +;; txbuf_send_str("AWAY"); +;; irc_away = 0; +;; } +;; +;; if(start_latch) { /* start pressed */ +;; start_keystroke(c); +;; } else { +;; edbox_keystroke(c); +;; OS.cdtmv3 = hz / 2; +;; } +;; } + +; compiles to ~128 bytes. + + .include "atari.inc" + + .export _poll_keyboard + .import _keypress, _kgetc, _start_latch, _start_keystroke + .import _edbox_keystroke, _txbuf_send_str, _irc_away + + _hz = $f0 ; must agree with timers.h! + + .rodata +away: + .byte "AWAY",0 + + .code +_poll_keyboard: + ;; if(!keypress()) return; + jsr _keypress + beq @ret ; Z flag set = no keypress + + ;; if(GTIA_READ.consol == 6) start_latch = 1; + lda CONSOL + cmp #6 + bne @nostart + sta _start_latch ; 6, not 1 (doesn't matter so long as it's non-zero) + +@nostart: + ; c = kgetc(); + jsr _kgetc ; note that _kgetc can set _start_latch too + tax ; just to set Z flag based on return value in A + ; if(!c) return; + beq @ret ; if _kgetc returned 0, we're done + + ; if(irc_away) { + ldx _irc_away + beq @noaway + ; irc_away = 0; + ldx #0 + stx _irc_away + ; txbuf_send_str("AWAY"); + pha ; _txbuf_send_str will clobber A so stash it + lda #<away + ldx #>away + jsr _txbuf_send_str + ; } + pla +@noaway: + ; if(start_latch) { /* start pressed */ + ldx _start_latch + beq @nolatch + ; start_keystroke(c); + ldx #0 + jmp _start_keystroke +@nolatch: + ; } else { + ;;;; ldx #0 ; X already 0 + ; edbox_keystroke(c); + jsr _edbox_keystroke + ; OS.cdtmv3 = hz / 2; + lda _hz + lsr + sta CDTMV3 + ; } + +@ret: + rts diff --git a/src/printnum.s b/src/printnum.s new file mode 100644 index 0000000..8b97db9 --- /dev/null +++ b/src/printnum.s @@ -0,0 +1,80 @@ +; print a number to the screen. +; based on Ullrich von Bassewitz's itoa.s from the cc65 lib src. +; it's modified to: +; - only support base 10. +; - only support unsigned int. +; - print the result directly to the fnchat screen, or +; - store the result directly in numbuf. + + .importzp sreg + .import _scr_putc_active, _scr_current, _scr_active, _numbuf + .export _scr_cur_printnum, _scr_act_printnum, _num_to_numbuf + + ; sreg+2 bit 7 is a flag that means "print" if set, or "store in numbuf: + ; if clear. also, the bottom bits of sreg+2 are the buffer position, when + ; bit 7 is clear. + +_num_to_numbuf: + ldy #0 + sty sreg+2 + beq printnum + +_scr_cur_printnum: + ldy _scr_current + sty _scr_active + +_scr_act_printnum: + ldy #$80 + sty sreg+2 + +printnum: + sta sreg + stx sreg+1 + + lda #0 + pha ; sentinel + +divloop: + ldy #$10 ; 16-bit remainder + lda #0 + +shift: + asl sreg + rol sreg+1 + rol a + cmp #$0a ; radix + bcc nosub + sbc #$0a + inc sreg + +nosub: + dey + bne shift + + ora #$30 ; make it an ASCII digit + pha ; save digit on stack + + ; are we done yet? + lda sreg + ora sreg+1 + bne divloop ; nope! + + ; get the results from the stack and print. +digitloop: + pla + beq done ; found sentinel? we're done. + ldy sreg+2 + bpl store + jsr _scr_putc_active + jmp digitloop + +store: + sta _numbuf,y + iny + lda #0 + sta _numbuf,y + sty sreg+2 + beq digitloop ; branch always + +done: + rts diff --git a/src/rxtxbuf.h b/src/rxtxbuf.h new file mode 100644 index 0000000..888092a --- /dev/null +++ b/src/rxtxbuf.h @@ -0,0 +1,5 @@ +typedef char buf512_t[512]; +#define RXBUF_ADDR 0xa000 +#define TXBUF_ADDR 0xa200 +#define rx_buf (*(buf512_t *)RXBUF_ADDR) +#define tx_buf (*(buf512_t *)TXBUF_ADDR) diff --git a/src/screen.c b/src/screen.c index 350a60c..02cc45a 100644 --- a/src/screen.c +++ b/src/screen.c @@ -5,9 +5,14 @@ #include "screen.h" #include "edbox.h" #include "indic8.h" +#include "streq.h" #define SDLST ((u16 *)0x0230) +/* if DOS isn't being used, the config will carve up the $0700-$1fff + area for extra scrollback. */ +unsigned int *bonus_addrs = (unsigned int *)0xd4; /* aka FR0 */ + char scr_status[MAX_SCREENS]; /* the screen that's currently displaying */ @@ -20,18 +25,26 @@ char scr_names[7][32]; static char xpos; -void scr_waitvcount(u8 c) { - while(ANTIC.vcount < c) +void scr_waitvcount_116(void) { + while(ANTIC.vcount < 116) /* NOP */; } static void scr_clear(char s) { + if(bonus_addrs[s]) { + memset(bonus_addrs[s], 0, 1000); + strcpy(bonus_addrs[s], "This is bonus scrollback!"); + } memset(screen_top_addrs[s], 0, 1000); memset(screen_bot_addrs[s], 0, 1000); memset(scr_names[s], 0, 32); } static void scr_scroll(char s) { + if(bonus_addrs[s]) { + memmove(bonus_addrs[s], bonus_addrs[s] + 40, 960); + memmove(bonus_addrs[s] + 960, screen_top_addrs[s], 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); @@ -43,9 +56,7 @@ void scr_init(void) { OS.sdmctl = 0; /* disappear the screen */ - ind_net_status = status_box + 40; - - scr_waitvcount(112); /* after the last GR.0 line */ + scr_waitvcount_116(); *SDLST = DLIST_BOT_ADDR; OS.chbas = FONT_ADDR_HI; @@ -111,22 +122,29 @@ void scr_display(char s) { scr_status[s] = SCR_INACTIVE; scr_current = s; - scr_waitvcount(112); + scr_waitvcount_116(); *dlist_bot_lms = (u16)screen_bot_addrs[s]; scr_show_status(s); } +void scr_scrollback_bonus(void) { + if(bonus_addrs[scr_current]) { + scr_waitvcount_116(); + *dlist_top_lms = (u16)bonus_addrs[scr_current]; + } +} + void scr_scrollback(void) { // OS.color2 = 0; - scr_waitvcount(112); + scr_waitvcount_116(); *dlist_top_lms = (u16)screen_top_addrs[scr_current]; *SDLST = (u16)dlist_top; } void scr_end_scrollback(void) { // OS.color2 = 192; - scr_waitvcount(112); + scr_waitvcount_116(); *SDLST = (u16)dlist_bot; } @@ -150,6 +168,9 @@ void scr_show_status(char s) { case SCR_ACTIVE: /* color1 */ sc |= 0x40; break; + case SCR_OTHER: /* color2 */ + sc |= 0x80; + break; case SCR_HILITE: /* color3 */ sc |= 0xc0; break; @@ -162,18 +183,12 @@ void scr_show_status(char s) { // *p++ = 0xbe; if(!edbox_visible) { - scr_waitvcount(112); + scr_waitvcount_116(); *dlist_status_lms = (u16)status_box; *dlist_last_line = 0x06; /* ANTIC GR.1 */ } } -void scr_hilite_active(void) { - if(scr_active == scr_current) return; - scr_status[scr_active] = SCR_HILITE; - scr_show_status(scr_current); -} - void scr_refresh(void) { scr_display(scr_current); } @@ -184,7 +199,7 @@ char scr_getbyname(const char *name) { if(!name) return 0; for(i = 2; i < MAX_SCREENS; i++) { - if(strcasecmp(name, scr_names[i]) == 0) + if(streq_i(name, scr_names[i])) return i; } @@ -286,10 +301,12 @@ void scr_print_priv(const char *text) { } void scr_activate(char s) { + /* if(s != scr_current) { if(scr_status[s] != SCR_HILITE) scr_status[s] = SCR_ACTIVE; scr_show_status(scr_current); } + */ scr_active = s; } diff --git a/src/screen.h b/src/screen.h index 787310d..2959b1e 100644 --- a/src/screen.h +++ b/src/screen.h @@ -7,8 +7,9 @@ #define SCR_UNUSED 0 #define SCR_INACTIVE 1 -#define SCR_ACTIVE 2 -#define SCR_HILITE 3 +#define SCR_OTHER 2 +#define SCR_ACTIVE 3 +#define SCR_HILITE 4 #define SCR_SERVER 0 #define SCR_PRIV 1 @@ -56,6 +57,7 @@ void scr_display(char s); note that it's a bad idea to write to the screen while it's scrolled back! */ void scr_scrollback(void); +void scr_scrollback_bonus(void); /* end scrollback mode (display the bottom 23 lines + status) */ void scr_end_scrollback(void); @@ -100,12 +102,14 @@ void scr_print_priv(const char *text); will have to call this. */ void scr_activate(char s); -void scr_hilite_active(void); - char *scr_get_cur_name(void); /* XXX: this really should be in a utils.c or common.c... */ -void scr_waitvcount(u8 c); +void scr_waitvcount_116(void); + +/* print decimal numbers (see printnum.s) */ +void scr_cur_printnum(u16 c); +void scr_act_printnum(u16 c); /**** end of public API ****/ @@ -1,17 +1,3 @@ ;; Call SIO - .export _siov - .export _rtclr - .export _cold_start - -_siov: JSR $E459 - RTS - -_rtclr: LDA #$00 - STA $12 - STA $13 - STA $14 - RTS - -_cold_start: - JMP $E477 + _siov = $e459 diff --git a/src/streq.h b/src/streq.h new file mode 100644 index 0000000..65b6ddb --- /dev/null +++ b/src/streq.h @@ -0,0 +1,5 @@ +extern char lcase(char c); +extern char streq(const char *s1, const char *s2); +extern char streq_i(const char *s1, const char *s2); +extern char strneq_i(const char *s1, const char *s2, char limit); +extern char find_nick(void); diff --git a/src/streq.s b/src/streq.s new file mode 100644 index 0000000..d063a35 --- /dev/null +++ b/src/streq.s @@ -0,0 +1,134 @@ + + .importzp sreg, ptr1, ptr2 + .import popptr1, popax + .import _msg_text + .export _streq, _streq_i, _strneq_i, _lcase, _find_nick + + ; extern __fastcall__ char lcase(char c); + ; extern __fastcall__ char streq(char *s1, char *s2); + ; extern __fastcall__ char streq_i(char *s1, char *s2); + ; extern __fastcall__ char strneq_i(char *s1, char *s2, char len); + ; extern __fastcall__ char find_nick(void); + + ; these are fast and small replacements for standard C library functions. + ; lcase() is a drop-in replacement for tolower(). + ; streq() is basically strcmp() except it returns true for equality, + ; false for inequality (so you can't e.g. use it for sorting). + ; also, it only supports strings up to 256 bytes long. + ; streq_i() is the case-insensitive verson of streq(). + ; strneq_i() is case-insensitive and stops after 'len' characters. + ; find_nick() does a case-insensitive search of msg_text for + ; config.nick. returns 1 if found, 0 if not. NOTE: only searches the + ; first 256 bytes of msg_text! this is a minor infelicity, but people + ; rarely type that much text in one message... + + limit = sreg+3 ; number of characters to compare (0 = 256) + insens = sreg ; bit 7 set = case insensitive, otherwise not + temp = sreg+1 ; nothing to see here, move along :) + temp2 = sreg+2 + + NICK = $0480 ; aka config.nick + +_lcase: + cmp #'Z'+1 + bcs lcret + cmp #'A' + bcc lcret + ora #$20 +lcret: + rts + +_strneq_i: + sta limit + jsr popax + ldy #$80 + sty insens + jmp doit + +_streq_i: + ldy #$80 + .byte $2c ; BIT abs, skip next +_streq: + ldy #0 + sty insens + ldy #0 + sty limit + +doit: + sta ptr2 + stx ptr2+1 + jsr popptr1 ; returns with Y=0 + +cmploop: + lda (ptr2),y + bit insens + bpl no_case_1 + jsr _lcase + +no_case_1: + sta temp + lda (ptr1),y + tax + bit insens + bpl no_case_2 + jsr _lcase + +no_case_2: + sec + sbc temp + bne ret0 + txa + beq ret1 + + iny + cpy limit + bne cmploop + +ret1: + lda #1 + .byte $2c +ret0: + lda #0 + tax + rts + +_find_nick: + lda _msg_text + sta ptr1 + lda _msg_text+1 + sta ptr1+1 + ldy #0 + ; loop over _msg_text, looking for the first character of NICK, + ; but convert both to lowercase before comparing. +@l: + lda (ptr1),y + beq ret0 ; if we hit the end of _msg_text, we're done, no match. + jsr _lcase + sta temp ; temp = lcase(A) + lda NICK ; compare to lcase(NICK[0]) + jsr _lcase + cmp temp + beq @found1 ; found a match for the first char, see if the rest matches. +@next: + iny + bne @l + beq ret0 +@found1: ; found a case-insensitive match for the first char of NICL... + sty temp2 ; save Y so we can pick back up in the @l loop if this isn't a match. + iny ; start with _msg_text char after the initial match. + ldx #1 ; start with 2nd char of NICK +@l2: + lda (ptr1),y + jsr _lcase + sta temp + lda NICK,x + beq ret1 ; if we hit the null terminator of NICK, we have a successful match! + jsr _lcase + cmp temp + bne @nope ; no match, get out of @l2 + iny + inx + bne @l2 ; matched a char, look at the next +@nope: + ldy temp2 ; restore Y + jmp @next ; jump to the bottom of the @l loop. diff --git a/src/timers.h b/src/timers.h new file mode 100644 index 0000000..a3e41f8 --- /dev/null +++ b/src/timers.h @@ -0,0 +1,8 @@ +typedef struct { + char hz; /* 50 for PAL, 60 for NTSC */ + char one_tenth_sec; /* 5 for PAL, 6 for NTSC */ + unsigned int one_sec; /* 1 sec, "hz" times 60 */ + unsigned int net_ind_time; /* 3.7 sec (just what The_Doctor__ ordered) */ +} timers_t; + +#define timers (*(timers_t *)(0xf0)) diff --git a/src/txbuf.s b/src/txbuf.s new file mode 100644 index 0000000..bc1d1c2 --- /dev/null +++ b/src/txbuf.s @@ -0,0 +1,50 @@ +;; void txbuf_append_chr(char c) { +;; tx_buf[txbuflen++] = c; +;; } + +; compiles to 45 bytes. routine below is 29 bytes (~33% smaller) + + tx_buf = $a200 ; MUST agree with src/rxtxbuf.h! + + .import _txbuflen + .export _txbuf_append_chr, _txbuf_append_str + .importzp sreg ; avoid ptr1 & friends, callers may use + +_txbuf_append_chr: + tax + lda #<tx_buf + clc + adc _txbuflen + sta sreg + lda #>tx_buf + adc _txbuflen+1 + sta sreg+1 + ldy #0 + txa + sta (sreg),y + inc _txbuflen + bne ret + inc _txbuflen+1 +ret: + rts ; always returns with Y == 0 + +;; void txbuf_append_str(const char *str) { +;; while(*str) { +;; txbuf_append_chr(*str++); +;; } +;; } + +; compiles to 52 bytes. +; this routine is 22 bytes, ~57% smaller. +_txbuf_append_str: + sta sreg+2 + stx sreg+3 + ldy #0 +@loop: + lda (sreg+2),y + beq ret + jsr _txbuf_append_chr + inc sreg+2 + bne @loop + inc sreg+3 + bne @loop |
