aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/a2uint.s65
-rw-r--r--src/addrs.c11
-rw-r--r--src/addrs.h10
-rw-r--r--src/bell.s7
-rw-r--r--src/cgetc.s24
-rw-r--r--src/cio.s8
-rw-r--r--src/cmd.c138
-rw-r--r--src/complete.c10
-rw-r--r--src/config.c3
-rw-r--r--src/config.h2
-rw-r--r--src/edbox.c295
-rw-r--r--src/edbox.h20
-rw-r--r--src/edboxutl.s203
-rw-r--r--src/exehdr.s11
-rw-r--r--src/indic8.c46
-rw-r--r--src/indic8.h29
-rw-r--r--src/irc.c471
-rw-r--r--src/irc.h36
-rw-r--r--src/isnum.s18
-rw-r--r--src/keyclick.c8
-rw-r--r--src/keyclick.h1
-rw-r--r--src/keytab.c274
-rw-r--r--src/keytab.h34
-rw-r--r--src/kgetc.h2
-rw-r--r--src/kgetc.s127
-rw-r--r--src/main.c64
-rw-r--r--src/nextarg.s78
-rw-r--r--src/nio.c268
-rw-r--r--src/nio.h59
-rw-r--r--src/permute.s78
-rw-r--r--src/pollkbd.s93
-rw-r--r--src/printnum.s80
-rw-r--r--src/rxtxbuf.h5
-rw-r--r--src/screen.c49
-rw-r--r--src/screen.h14
-rw-r--r--src/sio.s16
-rw-r--r--src/streq.h5
-rw-r--r--src/streq.s134
-rw-r--r--src/timers.h8
-rw-r--r--src/txbuf.s50
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;
diff --git a/src/bell.s b/src/bell.s
index 6a27503..e64fb6b 100644
--- a/src/bell.s
+++ b/src/bell.s
@@ -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
-
diff --git a/src/cmd.c b/src/cmd.c
index 106eda8..63838ad 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -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);
-*/
diff --git a/src/irc.c b/src/irc.c
index 8536d2c..5aee5e7 100644
--- a/src/irc.c
+++ b/src/irc.c
@@ -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);
}
}
diff --git a/src/irc.h b/src/irc.h
index 0035444..7d8f735 100644
--- a/src/irc.h
+++ b/src/irc.h
@@ -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
diff --git a/src/main.c b/src/main.c
index 068322d..1f533b3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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
diff --git a/src/nio.c b/src/nio.c
index 399b06d..eaa27b8 100644
--- a/src/nio.c
+++ b/src/nio.c
@@ -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
+
diff --git a/src/nio.h b/src/nio.h
index bc63da8..01aa61e 100644
--- a/src/nio.h
+++ b/src/nio.h
@@ -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 ****/
diff --git a/src/sio.s b/src/sio.s
index 3dd9191..829fbcb 100644
--- a/src/sio.s
+++ b/src/sio.s
@@ -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