Select the types of activity you want to include in your feed.
Initial import of msTERM
This is about 4 months of work, but most of the commits were hacks and mistakes and me learning z80 assembly, so I'm sheepishly just rebasing all of it down to a single commit.
···11+#
22+# msTERM
33+#
44+# Copyright (c) 2019 joshua stein <jcs@jcs.org>
55+#
66+# Permission to use, copy, modify, and distribute this software for any
77+# purpose with or without fee is hereby granted, provided that the above
88+# copyright notice and this permission notice appear in all copies.
99+#
1010+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1111+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1212+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1313+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1515+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1616+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1717+#
1818+1919+ASZ80 ?= sdasz80 -l
2020+SDCC ?= sdcc -mz80
2121+2222+OBJ ?= obj/
2323+2424+all: msterm.bin
2525+2626+clean:
2727+ rm -f *.{map,bin,ihx,lst,rel,sym,lk,noi}
2828+2929+crt0.rel: crt0.s
3030+ $(ASZ80) -o $@ $>
3131+3232+isr.rel: isr.s
3333+ $(ASZ80) -o $@ $>
3434+3535+putchar.rel: putchar.s
3636+ $(ASZ80) -o $@ $>
3737+3838+getchar.rel: getchar.s
3939+ $(ASZ80) -o $@ $>
4040+4141+lpt.rel: lpt.s
4242+ $(ASZ80) -o $@ $>
4343+4444+mailstation.rel: mailstation.c
4545+ $(SDCC) -c $@ $>
4646+4747+modem.rel: modem.s
4848+ $(ASZ80) -o $@ $>
4949+5050+mslib.rel: mslib.c
5151+ $(SDCC) -c $@ $>
5252+5353+msterm.rel: msterm.c
5454+ $(SDCC) -c $@ $>
5555+5656+csi.rel: csi.c
5757+ $(SDCC) -c $@ $>
5858+5959+# code-loc must be far enough to hold _HEADER code in crt0
6060+msterm.ihx: crt0.rel isr.rel putchar.rel getchar.rel lpt.rel mailstation.rel \
6161+modem.rel msterm.rel mslib.rel csi.rel
6262+ $(SDCC) --no-std-crt0 --code-loc 0x8100 --data-loc 0x0000 -o $@ $>
6363+6464+msterm.bin: msterm.ihx
6565+ hex2bin msterm.ihx >/dev/null
6666+ @if [ `stat -f '%z' $@` -gt 16384 ]; then \
6767+ echo "$@ overflows a dataflash page, must be <= 16384"; \
6868+ exit 1; \
6969+ fi
7070+7171+upload: all
7272+ nc -N 192.168.1.129 12345 < msterm.bin
+311
crt0.s
···11+; vim:syntax=z8a:ts=8
22+;
33+; crt0
44+; msTERM
55+;
66+; Copyright (c) 2019 joshua stein <jcs@jcs.org>
77+;
88+; Permission to use, copy, modify, and distribute this software for any
99+; purpose with or without fee is hereby granted, provided that the above
1010+; copyright notice and this permission notice appear in all copies.
1111+;
1212+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1313+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1414+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1515+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1616+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1717+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1818+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1919+;
2020+2121+ .module crt0
2222+2323+ .include "mailstation.inc"
2424+ .globl _main
2525+ .globl patch_isr
2626+ .globl _lptrecv
2727+2828+ .area _HEADER (ABS)
2929+3030+ ; when running as a program from dataflash
3131+ ; be sure to change --code-loc parameter in Makefile to 0x4100
3232+ ;.org 0x4000
3333+3434+ ; when running from loader
3535+ .org 0x8000
3636+3737+ jp boot
3838+3939+ .dw (icons)
4040+ .dw (caption)
4141+ .dw (dunno)
4242+4343+dunno:
4444+ .db #0
4545+xpos:
4646+ .dw #0
4747+ypos:
4848+ .dw #0
4949+caption:
5050+ .dw #0x0001 ; ?
5151+ .dw (endcap - caption - 6) ; number of chars
5252+ .dw #0x0006 ; offset to first char
5353+ .ascii "msTERM" ; the caption string
5454+endcap:
5555+5656+icons:
5757+ .dw (icon2 - icon1) ; size of icon1
5858+ .dw (icon1 - icons) ; offset to icon1
5959+ .dw (iconend - icon2) ; size of icon2
6060+ .dw (icon2 - icons) ; offset to icon2
6161+6262+icon1:
6363+ .dw #0x0022 ; icon width (34, 5 bytes per row)
6464+ .db #0x22 ; icon height (34)
6565+6666+ ; icon bitmap, low-order bit displays on the LEFT of each byte!)
6767+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
6868+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
6969+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
7070+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
7171+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0x00,#0xaa,#0xaa
7272+ .db #0x55,#0x55,#0x00,#0x55,#0x55, #0xaa,#0xaa,#0x00,#0xaa,#0xaa
7373+ .db #0x55,#0x55,#0x00,#0x55,#0x55, #0xaa,#0xaa,#0x00,#0xaa,#0xaa
7474+ .db #0x55,#0x55,#0x00,#0x55,#0x55, #0xaa,#0xaa,#0x00,#0xaa,#0xaa
7575+ .db #0x55,#0x55,#0x00,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
7676+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
7777+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
7878+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
7979+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
8080+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
8181+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
8282+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
8383+ .db #0x55,#0x55,#0x55,#0x55,#0x55, #0xaa,#0xaa,#0xaa,#0xaa,#0xaa
8484+8585+icon2:
8686+ ; not used
8787+ .dw #0x0000 ; width
8888+ .db #0x00 ; height
8989+iconend:
9090+9191+boot:
9292+ ; preserve old slot4000 for later
9393+ ld a, (#06)
9494+ ld (startup_slot4000device), a
9595+ ld a, (#05)
9696+ ld (startup_slot4000page), a
9797+9898+ call gsinit ; initialize global variables
9999+ call patch_isr ; install new ISR
100100+ call _main ; main c code
101101+ jp _exit
102102+103103+ ; ordering of segments for the linker
104104+ .area _HOME
105105+106106+ .area _CODE
107107+108108+ .area _GSINIT
109109+110110+gsinit::
111111+112112+ .area _GSFINAL
113113+114114+gsfinal::
115115+ ret
116116+117117+ ; variables
118118+ .area _BSS
119119+120120+startup_slot4000device:
121121+ .ds 1
122122+startup_slot4000page:
123123+ .ds 1
124124+125125+_debug0::
126126+ .db #0
127127+_debug1::
128128+ .db #0
129129+_debug2::
130130+ .db #0
131131+_debug3::
132132+ .db #0
133133+_debug4::
134134+ .db #0
135135+136136+ .area _DATA
137137+138138+ .area _HEAP
139139+140140+ .area _CODE
141141+142142+; exit handler, jump back to loader
143143+_exit::
144144+ ld a, (startup_slot4000device)
145145+ ld (#06), a
146146+ ld a, (startup_slot4000page)
147147+ ld (#05), a
148148+ jp 0x4000
149149+150150+_powerdown_mode::
151151+ call #0x0a6b ; firmware powerdownmode function
152152+153153+_reboot::
154154+ jp 0x0000
155155+156156+; new_mail(unsigned char on)
157157+; toggles 'new mail' light
158158+_new_mail::
159159+ di
160160+ push ix
161161+ ld ix, #0
162162+ add ix, sp
163163+ push hl
164164+ push af
165165+ ld a, 4(ix)
166166+ cp #0
167167+ jr z, light_off
168168+light_on:
169169+ ld a, (p2shadow)
170170+ set 4, a
171171+ jr write_p2
172172+light_off:
173173+ ld a, (p2shadow)
174174+ res 4, a
175175+write_p2:
176176+ ld (hl), a
177177+ out (#0x02), a ; write p2shadow to port2
178178+ pop af
179179+ pop hl
180180+ pop ix
181181+ ei
182182+ ret
183183+184184+; delay(unsigned int millis)
185185+; call mailstation function that delays (stack) milliseconds
186186+_delay::
187187+ push ix
188188+ ld ix, #0
189189+ add ix, sp
190190+ push af
191191+ push bc
192192+ push hl
193193+ ld l, 4(ix)
194194+ ld h, 5(ix)
195195+ push hl
196196+ call #0x0a5c
197197+ pop hl
198198+ pop hl
199199+ pop bc
200200+ pop af
201201+ pop ix
202202+ ret
203203+204204+; void lcd_sleep(void)
205205+; turn the LCD off
206206+_lcd_sleep::
207207+ di
208208+ ld a, (p2shadow)
209209+ and #0b01111111 ; LCD_ON - turn port2 bit 7 off
210210+ ld (p2shadow), a
211211+ out (#0x02), a ; write p2shadow to port2
212212+ ei
213213+ ret
214214+215215+216216+; void lcd_wake(void)
217217+; turn the LCD on
218218+_lcd_wake::
219219+ di
220220+ ld a, (p2shadow)
221221+ or #0b10000000 ; LCD_ON - turn port2 bit 7 on
222222+ ld (p2shadow), a
223223+ out (#0x02), a ; write p2shadow to port2
224224+ ei
225225+ ret
226226+227227+228228+; 8-bit multiplication
229229+; de * a = hl
230230+mult8::
231231+ ld b, #8
232232+ ld hl, #0
233233+mult8_loop:
234234+ add hl, hl
235235+ rlca
236236+ jr nc, mult8_noadd
237237+ add hl, de
238238+mult8_noadd:
239239+ djnz mult8_loop
240240+mult8_out:
241241+ ret
242242+243243+; 16-bit multiplication
244244+; bc * de = hl
245245+mult16:
246246+ ld a, b
247247+ ld b, #16
248248+ ld hl, #0
249249+mult16_loop:
250250+ add hl, hl
251251+ sla c
252252+ rla
253253+ jr nc, mult16_noadd
254254+ add hl, de
255255+mult16_noadd:
256256+ djnz mult16_loop
257257+ ret
258258+259259+260260+; 8-bit division
261261+; divide e by c, store result in a and remainder in b
262262+div8:
263263+ xor a
264264+ ld b, #8
265265+div8_loop:
266266+ rl e
267267+ rla
268268+ sub c
269269+ jr nc, div8_noadd
270270+ add a, c
271271+div8_noadd:
272272+ djnz div8_loop
273273+ ld b,a
274274+ ld a,e
275275+ rla
276276+ cpl
277277+ ret
278278+279279+; 16-bit division
280280+; divide bc by de, store result in bc, remainder in hl
281281+div16:
282282+ ld hl, #0
283283+ ld a, b
284284+ ld b, #8
285285+div16_loop1:
286286+ rla
287287+ adc hl, hl
288288+ sbc hl, de
289289+ jr nc, div16_noadd1
290290+ add hl, de
291291+div16_noadd1:
292292+ djnz div16_loop1
293293+ rla
294294+ cpl
295295+ ld b, a
296296+ ld a, c
297297+ ld c, b
298298+ ld b, #8
299299+div16_loop2:
300300+ rla
301301+ adc hl, hl
302302+ sbc hl, de
303303+ jr nc, div16_noadd2
304304+ add hl, de
305305+div16_noadd2:
306306+ djnz div16_loop2
307307+ rla
308308+ cpl
309309+ ld b, c
310310+ ld c, a
311311+ ret
+335
csi.c
···11+/*
22+ * ANSI CSI parser
33+ * msTERM
44+ *
55+ * Copyright (c) 2019 joshua stein <jcs@jcs.org>
66+ *
77+ * Permission to use, copy, modify, and distribute this software for any
88+ * purpose with or without fee is hereby granted, provided that the above
99+ * copyright notice and this permission notice appear in all copies.
1010+ *
1111+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1212+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1313+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1515+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1616+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1717+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1818+ */
1919+2020+#include <stdio.h>
2121+#include <string.h>
2222+#include <stdlib.h>
2323+2424+#include "mailstation.h"
2525+2626+unsigned char csibuf[TEXT_COLS];
2727+unsigned int csibuflen;
2828+2929+unsigned char in_csi;
3030+3131+void
3232+parseCSI(void)
3333+{
3434+ int x, y, serviced;
3535+ int param1 = -1, param2 = -1;
3636+ char c = csibuf[csibuflen - 1];
3737+ char parambuf[4];
3838+ int parambuflen, off;
3939+#ifdef DEBUG
4040+ char sb[TEXT_COLS];
4141+#endif
4242+4343+ if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z')
4444+ return;
4545+4646+ switch (c) {
4747+ case 'A':
4848+ case 'B':
4949+ case 'C':
5050+ case 'D':
5151+ case 'E':
5252+ case 'F':
5353+ case 'G':
5454+ case 'J':
5555+ case 'K':
5656+ case 'S':
5757+ case 'T':
5858+ case 'd':
5959+ case 'g':
6060+ /* optional multiplier */
6161+ if (c == 'J' || c == 'K')
6262+ param1 = 0;
6363+ else
6464+ param1 = 1;
6565+6666+ if (csibuflen > 1) {
6767+ for (x = 0; x < csibuflen - 1, x < 4; x++) {
6868+ parambuf[x] = csibuf[x];
6969+ parambuf[x + 1] = '\0';
7070+ }
7171+ param1 = atoi(parambuf);
7272+ }
7373+ break;
7474+ case 'H':
7575+ case 'f':
7676+ /* two optional parameters separated by ; each defaulting to 1 */
7777+ param1 = 1;
7878+ param2 = 1;
7979+8080+ y = -1;
8181+ for (x = 0; x < csibuflen; x++) {
8282+ if (csibuf[x] == ';') {
8383+ y = x;
8484+ break;
8585+ }
8686+ }
8787+ if (y == -1)
8888+ /* CSI 17H -> CSI 17; */
8989+ y = csibuflen - 1;
9090+9191+ if (y > 0) {
9292+ for (x = 0; x < y && x < 4; x++) {
9393+ parambuf[x] = csibuf[x];
9494+ parambuf[x + 1] = '\0';
9595+ }
9696+ param1 = atoi(parambuf);
9797+9898+ if (y < csibuflen - 2) {
9999+ parambuf[0] = '\0';
100100+ for (x = 0; x < (csibuflen - 1 - y) && x < 4; x++) {
101101+ parambuf[x] = csibuf[y + 1 + x];
102102+ parambuf[x + 1] = '\0';
103103+ }
104104+ param2 = atoi(parambuf);
105105+ }
106106+ }
107107+ break;
108108+ }
109109+110110+ serviced = 1;
111111+112112+ uncursor();
113113+114114+ switch (c) {
115115+ case 'A': /* CUU - cursor up */
116116+ for (x = 0; x < param1; x++)
117117+ if (cursory > 0)
118118+ cursory--;
119119+ break;
120120+ case 'B': /* CUD - cursor down */
121121+ for (x = 0; x < param1; x++)
122122+ if (cursory < TEXT_ROWS - 1)
123123+ cursory++;
124124+ break;
125125+ case 'C': /* CUF - cursor forward */
126126+ for (x = 0; x < param1; x++)
127127+ if (cursorx < TEXT_COLS)
128128+ cursorx++;
129129+ break;
130130+ case 'D': /* CUB - cursor back */
131131+ for (x = 0; x < param1; x++)
132132+ if (cursorx > 0)
133133+ cursorx--;
134134+ break;
135135+ case 'E': /* CNL - cursor next line */
136136+ cursorx = 0;
137137+ for (x = 0; x < param1; x++)
138138+ if (cursory < TEXT_ROWS - 1)
139139+ cursory++;
140140+ break;
141141+ case 'F': /* CPL - cursor previous line */
142142+ cursorx = 0;
143143+ for (x = 0; x < param1; x++)
144144+ if (cursory > 0)
145145+ cursory--;
146146+ break;
147147+ case 'G': /* CHA - cursor horizontal absolute */
148148+ if (param1 > TEXT_COLS)
149149+ param1 = TEXT_COLS;
150150+ cursorx = param1;
151151+ break;
152152+ case 'H': /* CUP - cursor absolute position */
153153+ case 'f': /* HVP - horizontal vertical position */
154154+ if (param1 - 1 < 0)
155155+ cursory = 0;
156156+ else if (param1 > TEXT_ROWS)
157157+ cursory = TEXT_ROWS - 1;
158158+ else
159159+ cursory = param1 - 1;
160160+161161+ if (param2 - 1 < 0)
162162+ cursorx = 0;
163163+ else if (param2 > TEXT_COLS)
164164+ cursorx = TEXT_COLS - 1;
165165+ else
166166+ cursorx = param2 - 1;
167167+168168+ break;
169169+ case 'J': /* ED - erase in display */
170170+ if (param1 == 0) {
171171+ /* clear from cursor to end of screen */
172172+ for (y = cursory; y < TEXT_ROWS; y++) {
173173+ for (x = 0; x < TEXT_COLS; x++) {
174174+ if (y == cursory && x < cursorx)
175175+ continue;
176176+177177+ putchar_attr(x, y, ' ', 0);
178178+ }
179179+ }
180180+ } else if (param1 == 1) {
181181+ /* clear from cursor to beginning of the screen */
182182+ for (y = cursory; y >= 0; y--) {
183183+ for (x = TEXT_COLS; x >= 0; x--) {
184184+ if (y == cursory && x > cursorx)
185185+ continue;
186186+187187+ putchar_attr(x, y, ' ', 0);
188188+ }
189189+ }
190190+ } else if (param1 == 2) {
191191+ /* clear entire screen */
192192+ for (y = 0; y < TEXT_ROWS; y++) {
193193+ for (x = 0; x < TEXT_COLS; x++)
194194+ putchar_attr(x, y, ' ', 0);
195195+ }
196196+ }
197197+198198+ break;
199199+ case 'K': /* EL - erase in line */
200200+ if (param1 == 0) {
201201+ /* clear from cursor to end of line */
202202+ for (x = cursorx; x < TEXT_COLS; x++)
203203+ putchar_attr(x, cursory, ' ', 0);
204204+ } else if (param1 == 1) {
205205+ /* clear from cursor to beginning of line */
206206+ for (x = cursorx; x >= 0; x--)
207207+ putchar_attr(x, cursory, ' ', 0);
208208+ } else if (param1 == 2) {
209209+ /* clear entire line */
210210+ for (x = 0; x < TEXT_COLS - 1; x++)
211211+ putchar_attr(x, cursory, ' ', 0);
212212+ }
213213+ break;
214214+ case 'S': /* SU - scroll up */
215215+ /* TODO */
216216+ break;
217217+ case 'T': /* SD - scroll down */
218218+ /* TODO */
219219+ break;
220220+ case 'd': /* absolute line number */
221221+ if (param1 < 1)
222222+ cursory = 0;
223223+ else if (param1 > TEXT_ROWS)
224224+ cursory = TEXT_ROWS;
225225+ else
226226+ cursory = param1 - 1;
227227+ break;
228228+ case 'g': /* clear tabs, ignore */
229229+ break;
230230+ case 'h': /* reset, ignore */
231231+ break;
232232+ case 'm': /* graphic changes */
233233+ parambuf[0] = '\0';
234234+ parambuflen = 0;
235235+236236+ off = (cursory * LCD_COLS) + cursorx;
237237+ param2 = screenattrs[off];
238238+239239+ for (x = 0; x < csibuflen; x++) {
240240+ /* all the way to csibuflen to catch 'm' */
241241+ if (csibuf[x] == ';' || csibuf[x] == 'm') {
242242+ param1 = atoi(parambuf);
243243+244244+ switch (param1) {
245245+ case 0: /* reset */
246246+ case 22: /* normal color */
247247+ param2 = 0;
248248+ break;
249249+ case 1: /* bold */
250250+ param2 |= ATTR_BOLD;
251251+ break;
252252+ case 4: /* underline */
253253+ param2 |= ATTR_UNDERLINE;
254254+ break;
255255+ case 7: /* reverse */
256256+ param2 |= ATTR_REVERSE;
257257+ break;
258258+ case 21: /* bold off */
259259+ param2 &= ~(ATTR_BOLD);
260260+ break;
261261+ case 24: /* underline off */
262262+ param2 &= ~(ATTR_UNDERLINE);
263263+ break;
264264+ case 27: /* inverse off */
265265+ param2 &= ~(ATTR_REVERSE);
266266+ break;
267267+ }
268268+269269+ parambuf[0] = '\0';
270270+ parambuflen = 0;
271271+ } else if (parambuflen < 4) {
272272+ parambuf[parambuflen] = csibuf[x];
273273+ parambuflen++;
274274+ parambuf[parambuflen] = '\0';
275275+ }
276276+ }
277277+278278+ putchar_sgr = param2;
279279+ putchar_attr(cursorx, cursory, screenbuf[off], param2);
280280+281281+ break;
282282+ case 'n': /* DSR - device status report */
283283+ if (param1 == 5) {
284284+ /* ok */
285285+ obuf[obuf_pos++] = 27;
286286+ obuf[obuf_pos++] = '[';
287287+ obuf[obuf_pos++] = '0';
288288+ obuf[obuf_pos++] = 'n';
289289+ } else if (param1 == 6) {
290290+ /* CPR - report cursor position */
291291+ obuf[obuf_pos++] = 27;
292292+ obuf[obuf_pos++] = '[';
293293+294294+ itoa(cursory + 1, parambuf, 10);
295295+ for (x = 0; x < sizeof(parambuf); x++) {
296296+ if (parambuf[x] == '\0')
297297+ break;
298298+ obuf[obuf_pos++] = parambuf[x];
299299+ }
300300+ obuf[obuf_pos++] = ';';
301301+302302+ itoa(cursorx + 1, parambuf, 10);
303303+ for (x = 0; x < sizeof(parambuf); x++) {
304304+ if (parambuf[x] == '\0')
305305+ break;
306306+ obuf[obuf_pos++] = parambuf[x];
307307+ }
308308+309309+ obuf[obuf_pos++] = 'R';
310310+ }
311311+ break;
312312+ default:
313313+ /*
314314+ * if the last character is a letter and we haven't serviced
315315+ * it, assume it's a sequence we don't support and should just
316316+ * suppress
317317+ */
318318+ if (c < 65 || (c > 90 && c < 97) || c > 122)
319319+ serviced = 0;
320320+ }
321321+322322+ if (serviced) {
323323+#ifdef DEBUG
324324+ sprintf(sb, "CSI (%d,%d): ", param1, param2);
325325+ for (x = 0; x < csibuflen; x++)
326326+ sprintf(sb, "%s%c", sb, csibuf[x]);
327327+ update_statusbar(sb);
328328+#endif
329329+ csibuflen = 0;
330330+ csibuf[0] = '\0';
331331+ in_csi = 0;
332332+333333+ redraw_screen();
334334+ }
335335+}
+65
cursorx_lookup.inc
···11+; AUTOMATICALLY GENERATED FILE - see tools/generate_cursorx_lookup.rb
22+ .db #0x98 ; 10011000 - col group 19, offset 0
33+ .db #0x9d ; 10011101 - col group 19, offset 5
44+ .db #0x92 ; 10010010 - col group 18, offset 2
55+ .db #0x97 ; 10010111 - col group 18, offset 7
66+ .db #0x8c ; 10001100 - col group 17, offset 4
77+ .db #0x81 ; 10000001 - col group 16, offset 1
88+ .db #0x86 ; 10000110 - col group 16, offset 6
99+ .db #0x7b ; 01111011 - col group 15, offset 3
1010+ .db #0x70 ; 01110000 - col group 14, offset 0
1111+ .db #0x75 ; 01110101 - col group 14, offset 5
1212+ .db #0x6a ; 01101010 - col group 13, offset 2
1313+ .db #0x6f ; 01101111 - col group 13, offset 7
1414+ .db #0x64 ; 01100100 - col group 12, offset 4
1515+ .db #0x59 ; 01011001 - col group 11, offset 1
1616+ .db #0x5e ; 01011110 - col group 11, offset 6
1717+ .db #0x53 ; 01010011 - col group 10, offset 3
1818+ .db #0x48 ; 01001000 - col group 9, offset 0
1919+ .db #0x4d ; 01001101 - col group 9, offset 5
2020+ .db #0x42 ; 01000010 - col group 8, offset 2
2121+ .db #0x47 ; 01000111 - col group 8, offset 7
2222+ .db #0x3c ; 00111100 - col group 7, offset 4
2323+ .db #0x31 ; 00110001 - col group 6, offset 1
2424+ .db #0x36 ; 00110110 - col group 6, offset 6
2525+ .db #0x2b ; 00101011 - col group 5, offset 3
2626+ .db #0x20 ; 00100000 - col group 4, offset 0
2727+ .db #0x25 ; 00100101 - col group 4, offset 5
2828+ .db #0x1a ; 00011010 - col group 3, offset 2
2929+ .db #0x1f ; 00011111 - col group 3, offset 7
3030+ .db #0x14 ; 00010100 - col group 2, offset 4
3131+ .db #0x09 ; 00001001 - col group 1, offset 1
3232+ .db #0x0e ; 00001110 - col group 1, offset 6
3333+ .db #0x03 ; 00000011 - col group 0, offset 3
3434+ .db #0x98 ; 10011000 - col group 19, offset 0
3535+ .db #0x9d ; 10011101 - col group 19, offset 5
3636+ .db #0x92 ; 10010010 - col group 18, offset 2
3737+ .db #0x97 ; 10010111 - col group 18, offset 7
3838+ .db #0x8c ; 10001100 - col group 17, offset 4
3939+ .db #0x81 ; 10000001 - col group 16, offset 1
4040+ .db #0x86 ; 10000110 - col group 16, offset 6
4141+ .db #0x7b ; 01111011 - col group 15, offset 3
4242+ .db #0x70 ; 01110000 - col group 14, offset 0
4343+ .db #0x75 ; 01110101 - col group 14, offset 5
4444+ .db #0x6a ; 01101010 - col group 13, offset 2
4545+ .db #0x6f ; 01101111 - col group 13, offset 7
4646+ .db #0x64 ; 01100100 - col group 12, offset 4
4747+ .db #0x59 ; 01011001 - col group 11, offset 1
4848+ .db #0x5e ; 01011110 - col group 11, offset 6
4949+ .db #0x53 ; 01010011 - col group 10, offset 3
5050+ .db #0x48 ; 01001000 - col group 9, offset 0
5151+ .db #0x4d ; 01001101 - col group 9, offset 5
5252+ .db #0x42 ; 01000010 - col group 8, offset 2
5353+ .db #0x47 ; 01000111 - col group 8, offset 7
5454+ .db #0x3c ; 00111100 - col group 7, offset 4
5555+ .db #0x31 ; 00110001 - col group 6, offset 1
5656+ .db #0x36 ; 00110110 - col group 6, offset 6
5757+ .db #0x2b ; 00101011 - col group 5, offset 3
5858+ .db #0x20 ; 00100000 - col group 4, offset 0
5959+ .db #0x25 ; 00100101 - col group 4, offset 5
6060+ .db #0x1a ; 00011010 - col group 3, offset 2
6161+ .db #0x1f ; 00011111 - col group 3, offset 7
6262+ .db #0x14 ; 00010100 - col group 2, offset 4
6363+ .db #0x09 ; 00001001 - col group 1, offset 1
6464+ .db #0x0e ; 00001110 - col group 1, offset 6
6565+ .db #0x03 ; 00000011 - col group 0, offset 3
+75
font/hexfont2inc.rb
···11+#
22+# hexfont2inc
33+# convert a hex-exported bitmap font (like from gbdfed) to an asm include file
44+# msTERM
55+#
66+# Copyright (c) 2019 joshua stein <jcs@jcs.org>
77+#
88+# Permission to use, copy, modify, and distribute this software for any
99+# purpose with or without fee is hereby granted, provided that the above
1010+# copyright notice and this permission notice appear in all copies.
1111+#
1212+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1313+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1414+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1515+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1616+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1717+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1818+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1919+#
2020+2121+chars = []
2222+char_size = 0
2323+all_bytes = []
2424+2525+if !ARGV[0]
2626+ puts "usage: #{$0} <hex file converted from bdf>"
2727+ exit 1
2828+end
2929+3030+File.open(ARGV[0], "r") do |f|
3131+ char = 0
3232+3333+ while f && !f.eof?
3434+ line = f.gets
3535+3636+ # 0023:A0E0A0E0A000
3737+ if !(m = line.match(/^(....):(.+)$/))
3838+ raise "unexpected format: #{line.inspect}"
3939+ end
4040+4141+ char = m[1].to_i(16)
4242+ char_size = (m[2].length / 2)
4343+4444+ # A0E0A0E0A000
4545+ # -> [ "A0", "e0", "A0", "e0", "A0", "00", "00" ]
4646+ bytes = m[2].scan(/(..)/).flatten
4747+4848+ # -> [ 0xa0, 0xe0, 0xa0, 0xe0, 0xa0, 0x00, 0x00 ]
4949+ bytes = bytes.map{|c| c.to_i(16) }
5050+5151+ # -> [ 101000000, 11100000, ... ]
5252+ # -> [ [ 1, 0, 1, 0, 0, 0, 0, 0 ], [ 1, 1, 1, 0, 0, 0, 0, 0 ], ... ]
5353+ bytes = bytes.map{|c| sprintf("%08b", c).split(//).map{|z| z.to_i } }
5454+5555+ # -> [ [ 0, 0, 0, 0, 0, 1, 0, 1 ], [ 0, 0, 0, 0, 0, 1, 1, 1 ], ... ]
5656+ bytes = bytes.map{|a| a.reverse }
5757+5858+ # -> [ 0x5, 0x7, ... ]
5959+ bytes = bytes.map{|a| a.join.to_i(2) }
6060+6161+ chars[char] = bytes
6262+ end
6363+end
6464+6565+(0 .. 255).each do |c|
6666+ if chars[c] && chars[c].any?
6767+ print ".db " << chars[c].map{|c| sprintf("#0x%02x", c) }.join(", ")
6868+ if c >= 32 && c <= 126
6969+ print "\t; #{sprintf("%.3d", c)} - #{c.chr}"
7070+ end
7171+ print "\n"
7272+ else
7373+ puts ".db " << char_size.times.map{|c| "#0x00" }.join(", ")
7474+ end
7575+end
···11+; vim:syntax=z8a:ts=8
22+;
33+; getchar and other keyboard routines
44+; msTERM
55+;
66+; Copyright (c) 2019 joshua stein <jcs@jcs.org>
77+;
88+; Permission to use, copy, modify, and distribute this software for any
99+; purpose with or without fee is hereby granted, provided that the above
1010+; copyright notice and this permission notice appear in all copies.
1111+;
1212+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1313+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1414+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1515+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1616+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1717+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1818+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1919+;
2020+2121+ .module getchar
2222+2323+ .include "mailstation.inc"
2424+ .globl _lptrecv
2525+2626+ .area _DATA
2727+2828+ ; scancode_table holds three tables of ascii characters which
2929+ ; '_getchar' uses to determine which character to return, depending on
3030+ ; the scancode pressed and the state of the shift and caps lock keys.
3131+ .include "scancodes.inc"
3232+3333+ .area _BSS
3434+3535+keyboardbuffer:
3636+ .ds 2 ; scancode buffer for _getchar
3737+capslock:
3838+ .db #0
3939+4040+ .area _CODE
4141+4242+; unsigned char peekkey(void)
4343+; check for a scancode using the firmware, then look it up in the scancode
4444+; table (respecting the shift key and caps lock as control) and return the
4545+; ascii value of the key in the l register
4646+_peekkey::
4747+ ld de, #keyboardbuffer
4848+ push de
4949+ call get_keycode_from_buffer
5050+ pop de
5151+ ld a, (keyboardbuffer) ; check for caps lock first
5252+ cp #0x60
5353+ jr z, is_caps_lock
5454+ jr not_caps_lock
5555+is_caps_lock:
5656+ ld a, (keyboardbuffer + 1) ; check flags
5757+ bit 0, a ; set=pressed, reset=released
5858+ jp nz, caps_down
5959+ ld a, #0 ; caps lock released
6060+ ld (capslock), a
6161+ jr nokey
6262+caps_down:
6363+ ld a, #1
6464+ ld (capslock), a
6565+ jr nokey
6666+not_caps_lock:
6767+ ld a, (keyboardbuffer + 1) ; check flags
6868+ bit 0, a ; set=pressed, reset=released
6969+ jp z, nokey ; key was released, bail
7070+ bit 6, a ; when set, shift was held down
7171+ jr z, lowercase
7272+capital:
7373+ ld hl, #scancode_table_uppercase
7474+ jr char_offset
7575+lowercase:
7676+ ld a, (capslock)
7777+ cp #1
7878+ jr z, as_control
7979+ ld hl, #scancode_table
8080+ jr char_offset
8181+as_control:
8282+ ld hl, #scancode_table_control
8383+ jr char_offset
8484+char_offset:
8585+ push hl
8686+ ld hl, #50
8787+ push hl
8888+ call _delay
8989+ pop hl
9090+ pop hl
9191+ ld a, (keyboardbuffer)
9292+ ld b, #0
9393+ ld c, a
9494+ add hl, bc
9595+ ld a, (hl)
9696+ ld h, #0
9797+ ld l, a
9898+ ret
9999+nokey:
100100+ ld h, #0
101101+ ld l, #0
102102+ ret
103103+104104+105105+; unsigned char getkey(void)
106106+; peekkey() but loops until a key is available
107107+_getkey::
108108+ call _peekkey
109109+ ld a, l
110110+ cp #0
111111+ jp z, _getkey
112112+ ret
113113+114114+115115+; int getchar(void)
116116+; uses _getkey and filters out non-printables, returns in l register
117117+_getchar::
118118+ call _getkey
119119+ ld a, l
120120+ cp a, #META_KEY_BEGIN
121121+ jr nc, _getchar ; a >=
122122+ ret
123123+124124+125125+; int getkeyorlpt(void)
126126+; alternate calls to peekkey() and lptrecv() to get a byte from whichever
127127+; comes first
128128+; h=1 is for keyboard input, h=2 is for lpt input, h=0 for nothing
129129+_getkeyorlpt::
130130+ call _peekkey2
131131+ ld a, l
132132+ cp #0
133133+ jr z, trylpt
134134+ ld h, #1
135135+ ret
136136+trylpt:
137137+ call _lptrecv
138138+ ld h, #0
139139+ cp #1
140140+ jr z, _getkeyorlpt
141141+ ld h, #2
142142+ ret
143143+144144+; peekkey() with some looping
145145+_peekkey2::
146146+ ld b, #0xff
147147+peekkey2loop:
148148+ push bc
149149+ call _peekkey
150150+ pop bc
151151+ ld a, l
152152+ cp #0
153153+ jr nz, peekkey2out ; loop until a key is available
154154+ djnz peekkey2loop
155155+peekkey2out:
156156+ ret
+122
isr.s
···11+; vim:syntax=z8a:ts=8
22+;
33+; interrupt service routine
44+; msTERM
55+;
66+; Copyright (c) 2019 joshua stein <jcs@jcs.org>
77+;
88+; Permission to use, copy, modify, and distribute this software for any
99+; purpose with or without fee is hereby granted, provided that the above
1010+; copyright notice and this permission notice appear in all copies.
1111+;
1212+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1313+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1414+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1515+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1616+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1717+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1818+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1919+;
2020+2121+ .module isr
2222+2323+ .include "mailstation.inc"
2424+ .globl _modem_isr
2525+2626+ .area _CODE
2727+2828+ ; we're going to put 0xf7 at 0xf800 - 0xf8ff and then put 0xf8 in the
2929+ ; 'i' register. when an interrupt happens in interrupt mode 2, some
3030+ ; garbage will be on the bus and form an address 0xf800+garbage.
3131+ ; the cpu will then read that address+1 which we know will contain
3232+ ; 0xf7f7, and then jump to 0xf7f7, which we contain our own code,
3333+ ; which will just be a 'jp isr' to our real ISR
3434+3535+ .equ isrjump, #0xf7
3636+ .equ isrjumptable, #0xf800
3737+3838+patch_isr::
3939+ di
4040+4141+ ; spray isrjump from isrjumptable to (isrjumptable + 0xff + 1)
4242+ ld hl, #isrjumptable
4343+ ld a, #isrjump
4444+ ld (hl), a
4545+ ld de, #isrjumptable + 1
4646+ ld bc, #0x0100
4747+ ldir ; ld (de), (hl); dec bc until 0
4848+4949+ ; put our jump at isrjump+isrjump
5050+ ld h, #isrjump
5151+ ld l, #isrjump
5252+ ld (hl), #0xc3 ; jp
5353+ inc hl
5454+ ex de, hl
5555+ ld hl, #isr
5656+ ld a, h
5757+ ld b, l
5858+ ex de, hl
5959+ ld (hl), b ; lower address of isr
6060+ inc hl
6161+ ld (hl), a ; upper address of isr
6262+6363+ ld hl, #isrjumptable
6464+ ld a, h
6565+ ld i, a ; interrupts will now be #0xf800 + rand
6666+ im 2 ; enable interrupt mode 2
6767+6868+ xor a ; clear interrupt mask
6969+ set 7, a ; allow interrupt 7 for power button
7070+ set 6, a ; and 6 for modem
7171+ set 2, a ; and 2 for keyboard
7272+ set 1, a ; and 1 for keyboard
7373+ ld (p3shadow), a ; store this mask in p3shadow
7474+ out (0x3), a
7575+ ei ; here we go!
7676+ ret
7777+7878+isr:
7979+ push af
8080+ push bc
8181+ push de
8282+ push hl
8383+ push ix
8484+ push iy
8585+ in a, (0x3)
8686+ bit 7, a ; power button
8787+ jp nz, 0x1940
8888+ bit 6, a ; modem
8989+ jp nz, isr_6
9090+ in a, (0x3) ; why read again? factory isr does
9191+ bit 1, a ; keyboard scan, 64hz
9292+ jp nz, 0x18d4 ; use factory handler
9393+ bit 2, a ; keyboard when button pressed
9494+ jp nz, 0x18e7 ; use factory handler
9595+ xor a ; any other interrupt, just ignore
9696+ out (0x3), a
9797+ jp isrout
9898+isr_7:
9999+ ld a, (p3shadow)
100100+ res 7, a
101101+ out (0x3), a ; reset interrupt
102102+ ld a, (p3shadow)
103103+ out (0x3), a ; set mask back to p3shadow
104104+ call 0x3b19 ; default handler
105105+ jp isrout
106106+isr_6:
107107+ ld a, (p3shadow)
108108+ res 6, a
109109+ out (0x3), a ; reset interrupt
110110+ ld a, (p3shadow)
111111+ out (0x3), a ; set mask back to p3shadow
112112+ call _modem_isr
113113+ jp isrout
114114+isrout:
115115+ pop iy
116116+ pop ix
117117+ pop hl
118118+ pop de
119119+ pop bc
120120+ pop af
121121+ ei
122122+ reti
+239
lpt.s
···11+; vim:syntax=z8a:ts=8
22+;
33+; parallel port routines
44+; msTERM
55+;
66+; Copyright (c) 2019 joshua stein <jcs@jcs.org>
77+;
88+; Permission to use, copy, modify, and distribute this software for any
99+; purpose with or without fee is hereby granted, provided that the above
1010+; copyright notice and this permission notice appear in all copies.
1111+;
1212+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1313+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1414+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1515+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1616+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1717+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1818+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1919+;
2020+2121+ .module lpt
2222+2323+ .include "mailstation.inc"
2424+2525+ .equ CONTROL, #0x2c
2626+ .equ DATA, #0x2d
2727+ .equ STATUS, #0x21
2828+2929+ .equ LPT_BUSY_IN, #0x40
3030+ .equ LPT_BUSY_OUT, #0x08
3131+ .equ LPT_STROBE_IN, #0x80
3232+ .equ LPT_STROBE_OUT, #0x10
3333+ .equ LPT_TRIB_MASK, #0x07
3434+ .equ LPT_DIB_MASK, #0x03
3535+3636+ .area _CODE
3737+3838+; receive a tribble byte from host, return h=1 l=0 on error, else h=0, l=(byte)
3939+lptrecv_tribble:
4040+ push bc
4141+ ld hl, #0 ; h will contain error, l result
4242+ xor a
4343+ out (DATA), a ; drop busy/ack, wait for strobe
4444+ ld b, #0xff ; try a bunch before bailing
4545+wait_for_strobe:
4646+ in a, (STATUS)
4747+ and #LPT_STROBE_IN ; inb(STATUS) & stbin
4848+ jr nz, got_strobe
4949+ jr wait_for_strobe
5050+strobe_failed:
5151+ ld h, #1
5252+ ld l, #0
5353+ jr lptrecv_tribble_out
5454+got_strobe:
5555+ in a, (STATUS)
5656+ sra a
5757+ sra a
5858+ sra a
5959+ and #LPT_TRIB_MASK ; & tribmask
6060+ ld l, a
6161+ ld a, #LPT_BUSY_OUT ; raise busy/ack
6262+ out (DATA), a
6363+ ld b, #0xff ; retry 255 times
6464+wait_for_unstrobe:
6565+ in a, (STATUS)
6666+ and #LPT_STROBE_IN ; inb(STATUS) & stbin
6767+ jr z, lptrecv_tribble_out
6868+ jr wait_for_unstrobe
6969+ ; if we never get unstrobe, that's ok
7070+lptrecv_tribble_out:
7171+ ld a, #LPT_BUSY_OUT ; raise busy/ack
7272+ out (DATA), a
7373+ pop bc
7474+ ret
7575+7676+7777+; unsigned char lptrecv(void)
7878+; receive a full byte from host in three parts
7979+; return h=1 l=0 on error, otherwise h=0, l=(byte)
8080+_lptrecv::
8181+ push bc
8282+ call lptrecv_tribble
8383+ ld a, h
8484+ cp #1
8585+ jr z, lptrecv_err ; bail early if h has an error
8686+ ld b, l
8787+ call lptrecv_tribble
8888+ ld a, h
8989+ cp #1
9090+ jr z, lptrecv_err
9191+ ld a, l
9292+ sla a
9393+ sla a
9494+ sla a
9595+ add b
9696+ ld b, a ; += tribble << 3
9797+ call lptrecv_tribble
9898+ ld a, h
9999+ cp #1
100100+ jr z, lptrecv_err
101101+ ld a, l
102102+ and #LPT_DIB_MASK ; dibmask
103103+ sla a
104104+ sla a
105105+ sla a
106106+ sla a
107107+ sla a
108108+ sla a
109109+ add b ; += (tribble & dibmask) << 6
110110+ ld h, #0
111111+ ld l, a
112112+lptrecv_out:
113113+ pop bc
114114+ ret
115115+lptrecv_err:
116116+ pop bc
117117+ ld hl, #0x0100
118118+ ret
119119+120120+121121+; send a tribble byte in register l to host, return l=0 on success
122122+lptsend_tribble:
123123+ push bc
124124+ ld h, #1
125125+ ld c, #0xff ; 255*255 tries before bailing
126126+wait_for_busy_drop_outer:
127127+ ld b, #0xff
128128+wait_for_busy_drop:
129129+ in a, (STATUS)
130130+ and #LPT_BUSY_IN ; inb(STATUS) & bsyin
131131+ jr z, got_busy_drop
132132+ djnz wait_for_busy_drop
133133+ dec c
134134+ ld a, c
135135+ cp #0
136136+ jr nz, wait_for_busy_drop_outer
137137+busy_drop_failed:
138138+ jr lptsend_tribble_out
139139+got_busy_drop:
140140+ ld a, l
141141+ and #LPT_TRIB_MASK
142142+ or #LPT_STROBE_OUT
143143+ out (DATA), a
144144+ ld c, #0xff ; try 255*255 tries before bailing
145145+wait_for_ack_outer:
146146+ ld b, #0xff
147147+wait_for_ack:
148148+ in a, (STATUS)
149149+ and #LPT_BUSY_IN ; inb(STATUS) & stbin
150150+ jr nz, lptsend_unstrobe
151151+ djnz wait_for_ack
152152+ dec c
153153+ ld a, c
154154+ cp #0
155155+ jr nz, wait_for_ack_outer
156156+ jr lptsend_tribble_out
157157+lptsend_unstrobe:
158158+ ld a, #1
159159+ out (DATA), a
160160+ ld h, #0 ; success
161161+lptsend_tribble_out:
162162+ ld a, #LPT_BUSY_OUT ; raise busy/ack
163163+ out (DATA), a
164164+ pop bc
165165+ ld l, h ; l=1 error, l=0 success
166166+ ld h, #0
167167+ ret
168168+169169+170170+; unsigned int lptsend(unsigned char b)
171171+; returns 0 on success
172172+_lptsend::
173173+ push ix
174174+ ld ix, #0
175175+ add ix, sp
176176+ ld l, 4(ix) ; char to send
177177+ push hl
178178+ call lptsend_tribble
179179+ ld a, l
180180+ pop hl
181181+ cp #0
182182+ jr z, tribble2
183183+ ld hl, #0x1
184184+ jr lptsend_error
185185+tribble2:
186186+ push hl
187187+ sra l
188188+ sra l
189189+ sra l ; b >> 3
190190+ call lptsend_tribble
191191+ ld a, l
192192+ pop hl
193193+ cp #0
194194+ jr z, tribble3
195195+ ld hl, #0x2
196196+ jr lptsend_error
197197+tribble3:
198198+ sra l
199199+ sra l
200200+ sra l
201201+ sra l
202202+ sra l
203203+ sra l ; b >> 6
204204+ call lptsend_tribble
205205+ ld a, l
206206+ cp #0
207207+ jr z, lptsend_out
208208+ ld hl, #0x3
209209+ jr lptsend_error
210210+lptsend_out:
211211+ ld hl, #0
212212+ ld sp, ix
213213+ pop ix
214214+ ret
215215+lptsend_error:
216216+ ld sp, ix
217217+ pop ix
218218+ ret
219219+220220+221221+; send pushed 16-bit as two bytes
222222+_lptsend16::
223223+ push ix
224224+ ld ix, #0
225225+ add ix, sp
226226+ push hl
227227+ ld h, #0
228228+ ld l, 5(ix)
229229+ push hl
230230+ call _lptsend
231231+ pop hl
232232+ ld l, 4(ix)
233233+ push hl
234234+ call _lptsend
235235+ pop hl
236236+ pop hl
237237+ ld sp, ix
238238+ pop ix
239239+ ret
+89
mailstation.c
···11+/*
22+ * mailstation utility functions
33+ * msTERM
44+ *
55+ * Copyright (c) 2019 joshua stein <jcs@jcs.org>
66+ *
77+ * Permission to use, copy, modify, and distribute this software for any
88+ * purpose with or without fee is hereby granted, provided that the above
99+ * copyright notice and this permission notice appear in all copies.
1010+ *
1111+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1212+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1313+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1515+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1616+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1717+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1818+ */
1919+2020+#include <stdio.h>
2121+#include <string.h>
2222+2323+#include "mailstation.h"
2424+2525+volatile unsigned char __at(0x0) mem0;
2626+volatile unsigned char __at(0xdba2) p2shadow;
2727+volatile unsigned char __at(0xdba3) p3shadow;
2828+volatile unsigned char __at(0xdba0) p28shadow;
2929+3030+volatile unsigned char *memp = &mem0;
3131+3232+char *
3333+gets(char *s)
3434+{
3535+ char c;
3636+ unsigned int count = 0;
3737+3838+ for (;;) {
3939+ c = getchar();
4040+ switch(c) {
4141+ case '\b': // backspace
4242+ if (count) {
4343+ putchar ('\b');
4444+ putchar (' ');
4545+ putchar ('\b');
4646+ s--;
4747+ count--;
4848+ }
4949+ break;
5050+ case '\n':
5151+ case '\r': // CR or LF
5252+ putchar('\r');
5353+ putchar('\n');
5454+ *s=0;
5555+ return s;
5656+ default:
5757+ *s++=c;
5858+ count++;
5959+ putchar(c);
6060+ break;
6161+ }
6262+ }
6363+}
6464+6565+void
6666+update_statusbar(char *status, ...)
6767+{
6868+ va_list args;
6969+ char tstatus[255], c;
7070+ char *result = NULL;
7171+ unsigned char x, l;
7272+7373+ va_start(args, status);
7474+ vsprintf(tstatus, status, args);
7575+ va_end(args);
7676+7777+ l = strlen(tstatus);
7878+7979+ for (x = 0; x < TEXT_COLS; x++) {
8080+ if (x >= l)
8181+ c = ' ';
8282+ else
8383+ c = tstatus[x];
8484+8585+ putchar_attr(LCD_ROWS - 1, x, c, ATTR_REVERSE);
8686+ }
8787+8888+ redraw_screen();
8989+}
+171
mailstation.h
···11+/*
22+ * msTERM
33+ *
44+ * Copyright (c) 2019 joshua stein <jcs@jcs.org>
55+ *
66+ * Permission to use, copy, modify, and distribute this software for any
77+ * purpose with or without fee is hereby granted, provided that the above
88+ * copyright notice and this permission notice appear in all copies.
99+ *
1010+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1111+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1212+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1313+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1515+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1616+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1717+ */
1818+1919+#ifndef _INCL_MAILSTATION
2020+#define _INCL_MAILSTATION
2121+2222+#include "meta_keys.h"
2323+2424+/* define some ports - see 0x1b2b */
2525+__sfr __at(0x01) portkeyboard;
2626+__sfr __at(0x02) port2;
2727+__sfr __at(0x05) slot4000page;
2828+__sfr __at(0x06) slot4000device;
2929+__sfr __at(0x07) slot8000page;
3030+__sfr __at(0x08) slot8000device;
3131+__sfr __at(0x09) portpowerstatus;
3232+__sfr __at(0x0d) portcpuclockrate;
3333+__sfr __at(0x28) port28;
3434+3535+/* v2.54 firmware */
3636+extern volatile unsigned char __at(0xdba2) p2shadow;
3737+extern volatile unsigned char __at(0xdba3) p3shadow;
3838+extern volatile unsigned char __at(0xdba0) p28shadow;
3939+4040+/* device IDs that can be swapped into page4000 */
4141+#define DEVICE_CODEFLASH 0x00 // 64 pages
4242+#define DEVICE_RAM 0x01 // 08 pages
4343+#define DEVICE_LCD_LEFT 0x02 // 01 pages
4444+#define DEVICE_DATAFLASH 0x03 // 32 pages
4545+#define DEVICE_LCD_RIGHT 0x04 // 01 pages
4646+#define DEVICE_MODEM 0x05 // 01 pages
4747+4848+/* once DEVICE_LCD_{LEFT,RIGHT} is swapped into page4000, LCD starts here */
4949+#define LCD_START 0x4038
5050+5151+/* LCD parameters (2 screens) */
5252+#define LCD_WIDTH (160 * 2) // 320
5353+#define LCD_HEIGHT 128
5454+#define LCD_COL_GROUPS 20
5555+#define LCD_COL_GROUP_WIDTH 8
5656+5757+#define FONT_WIDTH 5
5858+#define FONT_HEIGHT 8
5959+6060+/* columns of characters */
6161+#define LCD_COLS (LCD_WIDTH / FONT_WIDTH) // 64
6262+#define LCD_ROWS (LCD_HEIGHT / FONT_HEIGHT) // 16
6363+#define TEXT_COLS LCD_COLS // 64
6464+#define TEXT_ROWS (LCD_ROWS - 1) // 15
6565+6666+#define ATTR_DIRTY (1 << 0)
6767+#define ATTR_CURSOR (1 << 1)
6868+#define ATTR_REVERSE (1 << 2)
6969+#define ATTR_BOLD (1 << 3)
7070+#define ATTR_UNDERLINE (1 << 4)
7171+7272+extern char screenbuf[LCD_COLS * LCD_ROWS];
7373+extern char screenattrs[LCD_COLS * LCD_ROWS];
7474+7575+/* for printf */
7676+#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
7777+#define BYTE_TO_BINARY(byte) \
7878+ (byte & 0x80 ? '1' : '0'), \
7979+ (byte & 0x40 ? '1' : '0'), \
8080+ (byte & 0x20 ? '1' : '0'), \
8181+ (byte & 0x10 ? '1' : '0'), \
8282+ (byte & 0x08 ? '1' : '0'), \
8383+ (byte & 0x04 ? '1' : '0'), \
8484+ (byte & 0x02 ? '1' : '0'), \
8585+ (byte & 0x01 ? '1' : '0')
8686+8787+8888+/* for debugging access from asm */
8989+extern unsigned char debug0;
9090+extern unsigned char debug1;
9191+extern unsigned char debug2;
9292+extern unsigned char debug3;
9393+extern unsigned char debug4;
9494+9595+9696+/* crt0.s */
9797+extern void powerdown_mode(void);
9898+extern void new_mail(unsigned char on);
9999+extern void reboot(void);
100100+extern void delay(unsigned int millis);
101101+extern void lcd_paint(void);
102102+103103+104104+/* mslib.c */
105105+extern void uitoa(unsigned int value, char *string, int radix);
106106+extern void itoa(int value, char *string, int radix);
107107+108108+109109+/* csi.c */
110110+extern void parseCSI(void);
111111+extern unsigned char in_csi;
112112+extern unsigned char csibuf[TEXT_COLS];
113113+extern unsigned int csibuflen;
114114+115115+116116+/* putchar.s */
117117+extern unsigned char cursorx;
118118+extern unsigned char cursory;
119119+extern unsigned char putchar_sgr;
120120+extern unsigned char putchar_quick;
121121+extern unsigned char *font_addr;
122122+extern void lcd_cas(unsigned char col);
123123+extern void lcd_sleep(void);
124124+extern void lcd_wake(void);
125125+extern void uncursor(void);
126126+extern void recursor(void);
127127+extern void clear_screen(void);
128128+extern void dirty_screen(void);
129129+extern void redraw_screen(void);
130130+extern void scroll_lcd_half(void);
131131+extern void clear_lcd_half(void);
132132+extern void stamp_char(unsigned char row, unsigned char col);
133133+extern void putchar_attr(unsigned char row, unsigned char col, unsigned char c,
134134+ unsigned char attr);
135135+136136+137137+/* getchar.s */
138138+extern unsigned char getscancode(unsigned char *charbuffer);
139139+extern char *gets(char *s);
140140+extern int getkey(void);
141141+extern int peekkey(void);
142142+extern int getkeyorlpt(void);
143143+144144+145145+/* lpt.s */
146146+extern unsigned char lptsend(unsigned char b);
147147+extern int lptrecv(void);
148148+149149+150150+/* mailstation.c */
151151+extern unsigned char *firmware_version;
152152+extern void setup(void);
153153+extern void update_statusbar(char *status, ...);
154154+155155+156156+/* modem.s */
157157+extern unsigned char modem_curmsr;
158158+extern int modem_init(void);
159159+extern int modem_ier(void);
160160+extern int modem_iir(void);
161161+extern int modem_lcr(void);
162162+extern int modem_lsr(void);
163163+extern char modem_read(void);
164164+extern void modem_write(char c);
165165+166166+167167+/* msterm.c */
168168+extern unsigned char obuf[TEXT_COLS];
169169+extern unsigned char obuf_pos;
170170+171171+#endif
···11+; vim:syntax=z8a:ts=8
22+;
33+; modem routines for Rockwell RCV336DPFSP
44+; https://github.com/jcs/mailstation-tools/blob/master/docs/modem-RCV336DPFSP.pdf
55+; msTERM
66+;
77+; Copyright (c) 2019 joshua stein <jcs@jcs.org>
88+;
99+; Permission to use, copy, modify, and distribute this software for any
1010+; purpose with or without fee is hereby granted, provided that the above
1111+; copyright notice and this permission notice appear in all copies.
1212+;
1313+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1414+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1515+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1616+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1717+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1818+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1919+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2020+;
2121+2222+ .module modem
2323+2424+ .include "mailstation.inc"
2525+2626+ .area _DATA
2727+2828+ ; modem msr
2929+_modem_curmsr::
3030+ .db #0
3131+3232+ .area _CODE
3333+3434+_modem_isr::
3535+ push hl
3636+ call _modem_iir ; read IIR to identify interrupt
3737+ bit #0, l
3838+ jr nz, modem_isr_out ; no interrupt, how did we get here?
3939+ ld a, l
4040+ and #0b00001111 ; mask off high 4 bits
4141+ ld l, a
4242+has_irq:
4343+ and #0b00000110 ; receiver line status, some error
4444+ jr nz, no_rls
4545+ push hl
4646+ call _modem_lsr ; what are we supposed to do with it?
4747+ pop hl
4848+ jr modem_isr_out
4949+no_rls:
5050+ ld a, l
5151+ and #0b00000100 ; received data available or timeout
5252+ jr z, no_rda
5353+ push hl
5454+ call _modem_read
5555+ ld b, l
5656+ pop hl
5757+ ld hl, #modem_buf
5858+ ld a, (modem_buf_pos)
5959+ ld l, a ; 0xf600 + (modembufpos)
6060+ ld (hl), b
6161+ inc a
6262+ ld (modem_buf_pos), a
6363+ jr modem_isr_out
6464+no_rda:
6565+modem_isr_out:
6666+ ; XXX: why do we have to force this? the interrupt should trigger it
6767+ ; in the no_rda jump
6868+ call _modem_msr ; modem status update
6969+ ld a, l
7070+ ld (_modem_curmsr), a
7171+ pop hl
7272+ ret
7373+7474+7575+; void modem_init(void)
7676+; most of this is from 0x33f7 in v2.54 firmware
7777+_modem_init::
7878+ push bc
7979+ push hl
8080+ ld a, #0
8181+ ld (modem_buf_pos), a
8282+ call 0x3dbe ; disable caller id?
8383+ ld a, (p3shadow)
8484+ res 7, a ; disable caller id interrupt
8585+ ld (p3shadow),a
8686+ out (#0x03), a
8787+ in a, (#0x29) ; XXX what is port 29?
8888+ or #0x0c
8989+ out (#0x29), a
9090+ ld a, (p28shadow)
9191+ set 2, a
9292+ res 3, a
9393+ ld (p28shadow), a
9494+ out (#0x28), a ; XXX what is port 28?
9595+ xor a
9696+ ld (#0xe63b), a ; no idea what these shadow vars are
9797+ ld (#0xe63a), a
9898+ ld (#0xe64d), a
9999+ ld (#0xe638), a ; but init them all to 0
100100+; l33f8
101101+ ld a, #0x01
102102+ ld (#0xe638), a
103103+ in a, (#0x06) ; store old slot4000device
104104+ push af
105105+ ld a, (p2shadow) ; read p2shadow
106106+ res 5, a
107107+ ld (p2shadow), a ; write p2shadow
108108+ out (#0x02), a ; also write it to port2
109109+ ld hl, #300
110110+ push hl
111111+ call _delay ; delay 300ms
112112+ pop hl
113113+ ld a, #0
114114+ out (#0x26), a ; turn port 26 off
115115+ ld hl, #100 ; 100
116116+ push hl
117117+ call _delay ; delay 100ms
118118+ pop hl
119119+ ld a, #0x01
120120+ out (#0x26), a
121121+ ld hl, #3000 ; 3000
122122+ push hl
123123+ call _delay ; delay 3 seconds
124124+ pop hl
125125+ ld a, #0x05
126126+ out (#0x06), a ; switch slot4000device to modem
127127+ ld a, #0b11000111 ; 14 byte trigger
128128+ ;ld a, #0b00000111 ; XXX: try 1-byte trigger instead
129129+ ld (#0x4002), a ; FCR = enable FIFO
130130+ ld a, (#0xe62c) ; what variable is at 0xe62c?
131131+ or a
132132+ jr z, skip_dlab
133133+ ld a, #0b10000011
134134+ ld (#0x4003), a ; LCR = DLAB=1, 8n1
135135+ ld a, #0x06
136136+ ld (#0x4000), a ; DLL = 6
137137+ ld a, #0
138138+ ld (#0x4001), a ; DLM = 0 = divisor 6 = baud rate 19200
139139+skip_dlab:
140140+ ld a, #0b00000011
141141+ ld (#0x4003), a ; LCR = DLAB=0, 8n1
142142+ ld a, (#0x4004) ; read MCR
143143+ or #0b00001111
144144+ ld (#0x4004), a ; MCR = enable HINT
145145+ ld (_modem_curmsr), a
146146+ ld a, #0b00001001 ; IER = EDSSI, ERBFI
147147+ ld (#0x4001), a
148148+ ld b, #0x01
149149+ ld c, #0x06
150150+ call 0x0a2f ; jp 0x1afb, do something with port 3
151151+ pop af
152152+ out (#0x06), a ; restore old slot4000device
153153+ call 0x33ca ; init modem vars, activate interrupts
154154+ pop hl
155155+ pop bc
156156+ ret
157157+158158+159159+; char modem_read(void)
160160+; return a byte in hl from the modem FIFO, from 0x3328 in v2.54 firmware
161161+_modem_read::
162162+ ; use hl
163163+ in a, (0x06) ; save old slot4000device
164164+ ld h, a ; into h
165165+ ld a, #0x05
166166+ out (0x06),a ; slot4000device = modem
167167+ ld a, (0x4000) ; read byte from modem
168168+ ld l, a ; into l
169169+ ld a, h
170170+ out (0x06), a ; set old slot4000device
171171+ ld h, #0x00
172172+ ret ; return hl
173173+174174+175175+; void modem_write(char c)
176176+; write a byte to the modem TX FIFO, from 0x33b6 in v2.54 firmware
177177+_modem_write::
178178+ push ix
179179+ ld ix, #0
180180+ add ix, sp
181181+ push hl
182182+ ld a, 4(ix)
183183+ ld l, a
184184+ in a, (0x06)
185185+ ld h, a
186186+ ld a, #0x05
187187+ out (0x06), a
188188+ ld a, l
189189+ ld (0x4000), a
190190+ ld a, h
191191+ out (0x06), a
192192+ pop hl
193193+ pop ix
194194+ ret
195195+196196+197197+; int modem_ier(void)
198198+; return modem IER register in hl, from 0x3339 in v2.54 firmware
199199+_modem_ier::
200200+ in a, (0x06)
201201+ ld h, a
202202+ ld a, #0x05
203203+ out (0x06), a
204204+ ld a, (0x4001) ; read modem IER
205205+ ld l, a
206206+ ld a, h
207207+ out (0x06), a
208208+ ld h, #0x0
209209+ ret
210210+211211+212212+; int modem_iir(void)
213213+; return modem IIR register in hl, from 0x334a in v2.54 firmware
214214+_modem_iir::
215215+ in a, (0x06)
216216+ ld h, a
217217+ ld a, #0x05
218218+ out (0x06), a
219219+ ld a, (0x4002) ; read modem IIR
220220+ ld l, a
221221+ ld a, h
222222+ out (0x06), a
223223+ ld h, #0x0
224224+ ret
225225+226226+227227+; int modem_lcr(void)
228228+; return modem LCR register in hl
229229+_modem_lcr::
230230+ in a, (0x06)
231231+ ld h, a
232232+ ld a, #0x05
233233+ out (0x06), a
234234+ ld a, (0x4003) ; read LCR
235235+ ld l, a
236236+ ld a, h
237237+ out (0x06), a
238238+ ld h, #0x00
239239+ ret
240240+241241+242242+; int modem_lsr(void)
243243+; return modem LSR register in hl
244244+_modem_lsr::
245245+ in a, (0x06)
246246+ ld h, a
247247+ ld a, #0x05
248248+ out (0x06), a
249249+ ld a, (0x4005) ; read LSR
250250+ ld l, a
251251+ ld a, h
252252+ out (0x06), a
253253+ ld h, #0x00
254254+ ret
255255+256256+257257+; int modem_msr(void)
258258+; return modem MSR register in hl
259259+_modem_msr::
260260+ in a, (0x06)
261261+ ld h, a
262262+ ld a, #0x05
263263+ out (0x06), a
264264+ ld a, (0x4006) ; read modem MSR
265265+ ld l, a
266266+ ld a, h
267267+ out (0x06), a
268268+ ld h, #0x0
269269+ ret
+55
mslib.c
···11+/*
22+ * utility functions
33+ * msTERM
44+ *
55+ * Copyright (c) 2019 joshua stein <jcs@jcs.org>
66+ *
77+ * Permission to use, copy, modify, and distribute this software for any
88+ * purpose with or without fee is hereby granted, provided that the above
99+ * copyright notice and this permission notice appear in all copies.
1010+ *
1111+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1212+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1313+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1515+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1616+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1717+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1818+ */
1919+2020+/* From Bela Torok, 1999 <bela.torok@kssg.ch> */
2121+2222+#define NUMBER_OF_DIGITS 16
2323+2424+void
2525+uitoa(unsigned int value, char *string, int radix)
2626+{
2727+ unsigned char index, i;
2828+2929+ index = NUMBER_OF_DIGITS;
3030+ i = 0;
3131+3232+ do {
3333+ string[--index] = '0' + (value % radix);
3434+ if (string[index] > '9')
3535+ string[index] += 'A' - ':';
3636+ value /= radix;
3737+ } while (value != 0);
3838+3939+ do {
4040+ string[i++] = string[index++];
4141+ } while (index < NUMBER_OF_DIGITS);
4242+4343+ string[i] = '\0';
4444+}
4545+4646+void
4747+itoa(int value, char *string, int radix)
4848+{
4949+ if (value < 0 && radix == 10) {
5050+ *string++ = '-';
5151+ uitoa(-value, string, radix);
5252+ } else {
5353+ uitoa(value, string, radix);
5454+ }
5555+}
+303
msterm.c
···11+/*
22+ * msTERM
33+ *
44+ * Copyright (c) 2019 joshua stein <jcs@jcs.org>
55+ *
66+ * Permission to use, copy, modify, and distribute this software for any
77+ * purpose with or without fee is hereby granted, provided that the above
88+ * copyright notice and this permission notice appear in all copies.
99+ *
1010+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1111+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1212+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1313+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1515+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1616+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1717+ */
1818+1919+#include <stdio.h>
2020+#include <string.h>
2121+#include <stdlib.h>
2222+2323+#include "mailstation.h"
2424+2525+/* circular buffer */
2626+unsigned char obuf[TEXT_COLS];
2727+unsigned char obuf_pos;
2828+2929+extern volatile unsigned char mem0;
3030+3131+unsigned char lastkey;
3232+unsigned char esc;
3333+unsigned char old_modem_msr;
3434+3535+#define MODEM_BUF 0xf600
3636+#define MODEM_BUF_POS 0xf700
3737+volatile unsigned char __at(MODEM_BUF) _modem_buf;
3838+volatile unsigned char __at(MODEM_BUF_POS) modem_buf_pos;
3939+4040+#define MODEM_MSR_DCD (1 << 7)
4141+4242+void process_keyboard(void);
4343+void process_input(unsigned char b);
4444+void maybe_update_statusbar(unsigned char force);
4545+4646+#define DEBUG
4747+4848+enum {
4949+ SOURCE_MODEM,
5050+ SOURCE_LPT,
5151+};
5252+unsigned char source;
5353+5454+#define STATUSBAR_CALL " Call "
5555+#define STATUSBAR_HANGUP " Hangup "
5656+#define STATUSBAR_BLANK " "
5757+#define STATUSBAR_SETTINGS " Settings "
5858+unsigned char statusbar_state;
5959+6060+int main(void)
6161+{
6262+ unsigned char *memp = &mem0;
6363+ unsigned char *modem_buf = &_modem_buf;
6464+ unsigned char old_obuf_pos;
6565+ unsigned int b;
6666+ unsigned char old_modem_buf_pos;
6767+6868+restart:
6969+ lastkey = 0;
7070+ esc = 0;
7171+ source = SOURCE_MODEM; //LPT;
7272+ putchar_sgr = 0;
7373+ putchar_quick = 0;
7474+ in_csi = 0;
7575+ csibuflen = 0;
7676+ obuf_pos = 0;
7777+ old_obuf_pos = 0;
7878+ old_modem_msr = 0;
7979+ debug0 = 0;
8080+8181+ clear_screen();
8282+ maybe_update_statusbar(1);
8383+8484+ if (source == SOURCE_MODEM) {
8585+ printf("powering up modem...");
8686+ modem_init();
8787+ printf("\n");
8888+ obuf[obuf_pos++] = 'A';
8989+ obuf[obuf_pos++] = 'T';
9090+ obuf[obuf_pos++] = '\r';
9191+9292+ old_modem_buf_pos = modem_buf_pos;
9393+ }
9494+9595+ for (;;) {
9696+ process_keyboard();
9797+9898+ if (source == SOURCE_MODEM) {
9999+ while (old_modem_buf_pos != modem_buf_pos) {
100100+ if (!putchar_quick)
101101+ putchar_quick = 1;
102102+103103+ //lptsend(modem_buf[old_modem_buf_pos]);
104104+ process_input(modem_buf[old_modem_buf_pos]);
105105+ old_modem_buf_pos++;
106106+ }
107107+108108+ if (putchar_quick) {
109109+ putchar_quick = 0;
110110+ redraw_screen();
111111+ }
112112+ } else if (source = SOURCE_LPT) {
113113+ b = lptrecv();
114114+ if (b <= 0xff)
115115+ process_input(b & 0xff);
116116+ }
117117+118118+ while (old_obuf_pos != obuf_pos) {
119119+120120+ if (source == SOURCE_MODEM) {
121121+#if 0
122122+ lsr = modem_lsr();
123123+ if (lsr & (1 << 5))
124124+#endif
125125+ /* Transmitter Holding Register Empty (THRE) */
126126+ modem_write(obuf[old_obuf_pos]);
127127+ } else if (source == SOURCE_LPT) {
128128+ lptsend(obuf[old_obuf_pos]);
129129+ } else {
130130+ putchar(obuf[old_obuf_pos]);
131131+ }
132132+133133+ old_obuf_pos++;
134134+ }
135135+136136+ maybe_update_statusbar(0);
137137+ }
138138+139139+ return 0;
140140+}
141141+142142+void
143143+maybe_update_statusbar(unsigned char force)
144144+{
145145+ unsigned char s;
146146+ unsigned char old_state = statusbar_state;
147147+148148+ if (modem_curmsr & MODEM_MSR_DCD)
149149+ s = 1; /* DCD, change to 'hangup' */
150150+ else
151151+ s = 0;
152152+153153+ if (s != (statusbar_state & (1 << 0)))
154154+ statusbar_state ^= (1 << 0);
155155+156156+ if ((statusbar_state != old_state) || force) {
157157+ update_statusbar("%s%s%s%s%s",
158158+ statusbar_state & (1 << 0) ? STATUSBAR_HANGUP : STATUSBAR_CALL,
159159+ STATUSBAR_BLANK,
160160+ STATUSBAR_BLANK,
161161+ STATUSBAR_BLANK,
162162+ STATUSBAR_SETTINGS);
163163+ }
164164+}
165165+166166+void
167167+process_keyboard(void)
168168+{
169169+ unsigned char b;
170170+171171+ b = peekkey();
172172+173173+ /* this breaks key-repeat, but it's needed to debounce */
174174+ if (b == 0)
175175+ lastkey = 0;
176176+ else if (b == lastkey)
177177+ b = 0;
178178+ else
179179+ lastkey = b;
180180+181181+ if (b == 0)
182182+ return;
183183+184184+ switch (b) {
185185+ case KEY_POWER:
186186+ __asm
187187+ jp 0x0000
188188+ __endasm;
189189+ break;
190190+ case KEY_F1:
191191+ if (modem_curmsr & MODEM_MSR_DCD) {
192192+ }
193193+194194+ break;
195195+ case KEY_BACK:
196196+ /* send escape */
197197+ obuf[obuf_pos++] = 27;
198198+ break;
199199+ case KEY_PAGE_UP:
200200+ obuf[obuf_pos++] = 27;
201201+ obuf[obuf_pos++] = '[';
202202+ obuf[obuf_pos++] = '5';
203203+ obuf[obuf_pos++] = '~';
204204+ break;
205205+ case KEY_PAGE_DOWN:
206206+ obuf[obuf_pos++] = 27;
207207+ obuf[obuf_pos++] = '[';
208208+ obuf[obuf_pos++] = '6';
209209+ obuf[obuf_pos++] = '~';
210210+ break;
211211+ case KEY_UP:
212212+ obuf[obuf_pos++] = 27;
213213+ obuf[obuf_pos++] = '[';
214214+ obuf[obuf_pos++] = 'A';
215215+ break;
216216+ case KEY_DOWN:
217217+ obuf[obuf_pos++] = 27;
218218+ obuf[obuf_pos++] = '[';
219219+ obuf[obuf_pos++] = 'B';
220220+ break;
221221+ case KEY_LEFT:
222222+ obuf[obuf_pos++] = 27;
223223+ obuf[obuf_pos++] = '[';
224224+ obuf[obuf_pos++] = 'D';
225225+ break;
226226+ case KEY_RIGHT:
227227+ obuf[obuf_pos++] = 27;
228228+ obuf[obuf_pos++] = '[';
229229+ obuf[obuf_pos++] = 'C';
230230+ break;
231231+ case KEY_SIZE:
232232+ //clear_screen();
233233+ redraw_screen();
234234+ break;
235235+ default:
236236+ if (b >= META_KEY_BEGIN)
237237+ return;
238238+239239+ if (source == SOURCE_MODEM && b == '\n')
240240+ b = '\r';
241241+242242+ obuf[obuf_pos++] = b;
243243+ }
244244+}
245245+246246+void
247247+process_input(unsigned char b)
248248+{
249249+ if (in_csi) {
250250+ if (csibuflen >= sizeof(csibuf) - 1) {
251251+ /* going to overflow, dump */
252252+ parseCSI();
253253+ in_csi = 0;
254254+ esc = 0;
255255+ csibuf[0] = '\0';
256256+ csibuflen = 0;
257257+ }
258258+259259+ if (b == 27) {
260260+ /* esc, maybe new csi, dump previous */
261261+ parseCSI();
262262+ in_csi = 0;
263263+ esc = 1;
264264+ csibuf[0] = '\0';
265265+ csibuflen = 0;
266266+ } else {
267267+ csibuf[csibuflen] = b;
268268+ csibuflen++;
269269+ parseCSI();
270270+ }
271271+272272+ return;
273273+ }
274274+275275+ switch (b) {
276276+ case 7: /* visual bell, ping 'new mail' light */
277277+ new_mail(1);
278278+ delay(500);
279279+ new_mail(0);
280280+ break;
281281+ case 9: /* tab */
282282+ while ((cursorx + 1) % 8 != 0)
283283+ putchar(' ');
284284+ break;
285285+ case 27: /* esc */
286286+ if (esc)
287287+ /* our previous esc is literal */
288288+ putchar(b);
289289+ esc = 1;
290290+ break;
291291+ case 26: /* ^Z end of ansi */
292292+ break;
293293+ case 91: /* [ */
294294+ if (esc) {
295295+ esc = 0;
296296+ in_csi = 1;
297297+ break;
298298+ }
299299+ /* fall through */
300300+ default:
301301+ putchar(b);
302302+ }
303303+}
+822
putchar.s
···11+; vim:syntax=z8a:ts=8
22+;
33+; putchar
44+; msTERM
55+;
66+; Copyright (c) 2019 joshua stein <jcs@jcs.org>
77+;
88+; Permission to use, copy, modify, and distribute this software for any
99+; purpose with or without fee is hereby granted, provided that the above
1010+; copyright notice and this permission notice appear in all copies.
1111+;
1212+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1313+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1414+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1515+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1616+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1717+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1818+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1919+;
2020+2121+ .module putchar
2222+2323+ .include "mailstation.inc"
2424+2525+ .area _DATA
2626+2727+ ; screen contents (characters) - should be 0xa000 - 0xa3ff
2828+_screenbuf::
2929+ .ds (LCD_COLS * LCD_ROWS)
3030+_screenbufend::
3131+3232+ ; per-character attributes - should be 0xa400 - 0xa7ff
3333+ ; see ATTR_* constants
3434+_screenattrs::
3535+ .ds (LCD_COLS * LCD_ROWS)
3636+_screenattrsend::
3737+3838+font_data::
3939+ .include "font/spleen-5x8.inc"
4040+4141+ ; lookup table for putchar
4242+ ; left-most 5 bits are col group for lcd_cas
4343+ ; last 3 bits are offset into col group
4444+cursorx_lookup_data::
4545+ .include "cursorx_lookup.inc"
4646+4747+ .area _BSS
4848+4949+_cursorx:: ; cursor x position, 0-indexed
5050+ .db #0
5151+_cursory:: ; cursor y position, 0-indexed
5252+ .db #0
5353+_putchar_sgr:: ; current SGR for putchar()
5454+ .db #0
5555+_putchar_quick:: ; when 1, don't call redraw_screen()
5656+ .db #0
5757+5858+5959+ .area _GSINIT
6060+6161+ call _clear_screen
6262+6363+ .area _CODE
6464+6565+; void lcd_cas(unsigned char col)
6666+; enable CAS, address the LCD column col (in h), and disable CAS
6767+_lcd_cas::
6868+ push ix
6969+ ld ix, #0
7070+ add ix, sp
7171+ push de
7272+ ld a, (p2shadow)
7373+ and #0b11110111 ; CAS(0) - turn port2 bit 3 off
7474+ ld (p2shadow), a
7575+ out (#0x02), a ; write p2shadow to port2
7676+ ld de, #LCD_START
7777+ ld a, 4(ix)
7878+ ld (de), a ; write col argument
7979+ ld a, (p2shadow)
8080+ or #0b00001000 ; CAS(1) - turn port2 bit 3 on
8181+ ld (p2shadow), a
8282+ out (#0x02), a
8383+ pop de
8484+ ld sp, ix
8585+ pop ix
8686+ ret
8787+8888+8989+; void clear_screen(void)
9090+_clear_screen::
9191+ di
9292+ push bc
9393+ push de
9494+ push hl
9595+ in a, (#06)
9696+ ld h, a ; slot4000 device
9797+ in a, (#05)
9898+ ld l, a ; slot4000 page
9999+ ld a, #DEVICE_LCD_RIGHT
100100+ out (#06), a
101101+ push hl
102102+ call _clear_lcd_half
103103+ ld a, #DEVICE_LCD_LEFT
104104+ out (#06), a
105105+ call _clear_lcd_half
106106+ pop hl
107107+ ld a, h
108108+ out (#06), a
109109+ ld a, l
110110+ out (#05), a
111111+reset_cursor:
112112+ xor a
113113+ ld (_cursorx), a
114114+ ld (_cursory), a
115115+ ld (_putchar_sgr), a
116116+zero_screenbuf:
117117+ ld hl, #_screenbuf
118118+ ld de, #_screenbuf + 1
119119+ ld bc, #_screenbufend - _screenbuf
120120+ ld (hl), #' '
121121+ ldir
122122+zero_screenattrs:
123123+ ld hl, #_screenattrs
124124+ ld de, #_screenattrs + 1
125125+ ld bc, #_screenattrsend - _screenattrs
126126+ ld (hl), #0
127127+ ldir
128128+clear_screen_out:
129129+ pop hl
130130+ pop de
131131+ pop bc
132132+ ei
133133+ ret
134134+135135+136136+; void clear_lcd_half(void)
137137+; zero out the current LCD module (must already be in slot4000device)
138138+; from v2.54 firmware at 0x2490
139139+_clear_lcd_half::
140140+ push bc
141141+ push de
142142+ ld b, #20 ; do 20 columns total
143143+clear_lcd_column:
144144+ ld h, #0
145145+ ld a, b
146146+ dec a ; columns are 0-based
147147+ ld l, a
148148+ push hl
149149+ call _lcd_cas
150150+ pop hl
151151+ push bc ; preserve our column counter
152152+ ld hl, #LCD_START
153153+ ld (hl), #0 ; zero out hl, then copy it to de
154154+ ld de, #LCD_START + 1 ; de will always be the next line
155155+ ld bc, #128 - 1 ; iterate (LCD_HEIGHT - 1) times
156156+ ldir ; ld (de), (hl), bc-- until 0
157157+ pop bc ; restore column counter
158158+ djnz clear_lcd_column ; column--, if not zero keep going
159159+clear_done:
160160+ pop de
161161+ pop bc
162162+ ret
163163+164164+165165+; void dirty_screen(void)
166166+; mark every character cell dirty, should follow with redraw_screen()
167167+_dirty_screen::
168168+ push bc
169169+ push hl
170170+ ld hl, #_screenattrs
171171+ ld bc, #(LCD_COLS * LCD_ROWS) - 1
172172+ add hl, bc
173173+dirty_lcd_loop:
174174+ ld a, (hl)
175175+ set #ATTR_BIT_DIRTY, a
176176+ ld (hl), a
177177+ dec hl
178178+ dec bc
179179+ ld a, b
180180+ cp #0
181181+ jr nz, dirty_lcd_loop
182182+ ld a, c
183183+ cp #0
184184+ jr nz, dirty_lcd_loop
185185+ pop hl
186186+ pop bc
187187+ ret
188188+189189+190190+; void redraw_screen(void)
191191+_redraw_screen::
192192+ di
193193+ push bc
194194+ push de
195195+ push hl
196196+ ld b, #0
197197+redraw_rows:
198198+ ld h, b
199199+ ld l, #0
200200+ call screenbuf_offset
201201+ ld de, #_screenattrs
202202+ add hl, de
203203+ ld d, b ; store rows in d
204204+ ld b, #0
205205+redraw_cols:
206206+ ld a, (hl)
207207+ bit #ATTR_BIT_DIRTY, a
208208+ jr z, redraw_cols_next
209209+ res #ATTR_BIT_DIRTY, a
210210+ ld (hl), a
211211+ push bc ; XXX figure out what is corrupting
212212+ push de ; bc and de in stamp_char, these shouldn't be needed
213213+ push hl
214214+ ld h, #0 ; cols
215215+ ld l, b
216216+ push hl
217217+ ld h, #0 ; rows
218218+ ld l, d
219219+ push hl
220220+ call _stamp_char
221221+ pop hl
222222+ pop hl
223223+ pop hl
224224+ pop de
225225+ pop bc
226226+redraw_cols_next:
227227+ inc hl
228228+ inc b
229229+ ld a, b
230230+ cp #LCD_COLS
231231+ jr nz, redraw_cols
232232+ ld b, d
233233+ inc b
234234+ ld a, b
235235+ cp #LCD_ROWS
236236+ jr nz, redraw_rows
237237+redraw_screen_out:
238238+ pop hl
239239+ pop de
240240+ pop bc
241241+ ei
242242+ ret
243243+244244+245245+; void scroll_lcd(void)
246246+; scroll entire screen up by FONT_HEIGHT rows, minus statusbar
247247+_scroll_lcd::
248248+ di
249249+ push bc
250250+ push de
251251+ push hl
252252+ in a, (#06)
253253+ ld h, a ; slot4000 device
254254+ in a, (#05)
255255+ ld l, a ; slot4000 page
256256+ push hl
257257+ ld a, #DEVICE_LCD_LEFT
258258+ out (#06), a
259259+ call _scroll_lcd_half
260260+ ld a, #DEVICE_LCD_RIGHT
261261+ out (#06), a
262262+ call _scroll_lcd_half
263263+ pop hl
264264+ ld a, h
265265+ out (#06), a
266266+ ld a, l
267267+ out (#05), a
268268+shift_bufs:
269269+ ld b, #1
270270+screenbuf_shift_loop:
271271+ ld h, b
272272+ ld l, #0
273273+ call screenbuf_offset
274274+ ld de, #_screenbuf
275275+ add hl, de ; hl = screenbuf[b * LCD_COLS]
276276+ push hl
277277+ ld de, #LCD_COLS
278278+ add hl, de ; hl += LCD_COLS
279279+ pop de ; de = screenbuf[b * LCD_COLS]
280280+ push bc
281281+ ld bc, #LCD_COLS
282282+ ldir
283283+ pop bc
284284+ inc b
285285+ ld a, b
286286+ cp #TEXT_ROWS - 1
287287+ jr nz, screenbuf_shift_loop
288288+screenattrs_shift:
289289+ ld b, #1
290290+screenattrs_shift_loop:
291291+ ld h, b
292292+ ld l, #0
293293+ call screenbuf_offset
294294+ ld de, #_screenattrs
295295+ add hl, de ; hl = screenattrs[b * LCD_COLS]
296296+ push hl
297297+ ld de, #LCD_COLS
298298+ add hl, de
299299+ pop de
300300+ push bc
301301+ ld bc, #LCD_COLS
302302+ ldir
303303+ pop bc
304304+ inc b
305305+ ld a, b
306306+ cp #TEXT_ROWS - 1
307307+ jr nz, screenattrs_shift_loop
308308+last_row_zero:
309309+ ld a, #TEXT_ROWS - 1
310310+ ld h, a
311311+ ld l, #0
312312+ call screenbuf_offset
313313+ push hl
314314+ ld de, #_screenbuf
315315+ add hl, de
316316+ ld d, #0
317317+ ld e, #LCD_COLS - 1
318318+ add hl, de
319319+ ld b, #LCD_COLS
320320+ ld a, (_putchar_sgr)
321321+last_row_zero_loop:
322322+ ld (hl), #' '
323323+ dec hl
324324+ djnz last_row_zero_loop
325325+last_row_attr_dirty:
326326+ ld b, #LCD_COLS - 1
327327+ pop hl ; screenattrs[LCD_COLS * TEXT_ROWS]
328328+ ld de, #_screenattrs
329329+ add hl, de
330330+ ld d, #0
331331+ ld e, #LCD_COLS
332332+ add hl, de
333333+last_row_attr_loop:
334334+ ld (hl), a
335335+ dec hl
336336+ djnz last_row_attr_loop
337337+scroll_lcd_out:
338338+ pop hl
339339+ pop de
340340+ pop bc
341341+ ei
342342+ ret
343343+344344+345345+; void scroll_lcd_half(void)
346346+; scroll current LCD module up by FONT_HEIGHT rows, minus statusbar and
347347+; zero out the last line of text (only to the LCD)
348348+_scroll_lcd_half::
349349+ push ix
350350+ ld ix, #0
351351+ add ix, sp
352352+ push bc
353353+ push de
354354+ push hl
355355+ ; alloc 2 bytes on the stack for local storage
356356+ push hl
357357+ ld a, #LCD_HEIGHT - (FONT_HEIGHT * 2) ; iterations of pixel row moves
358358+scroll_init:
359359+ ld -1(ix), a ; store iterations
360360+ ld b, #20 ; do 20 columns total
361361+scroll_lcd_column:
362362+ ld -2(ix), b ; store new column counter
363363+ ld a, b
364364+ sub #1 ; columns are 0-based
365365+ ld h, #0
366366+ ld l, a
367367+ push hl
368368+ call _lcd_cas
369369+ pop hl
370370+scroll_rows:
371371+ ld b, #0
372372+ ld c, -1(ix) ; bc = row counter
373373+ ld hl, #LCD_START + 8 ; start of next line
374374+ ld de, #LCD_START
375375+ ldir ; ld (de), (hl), bc-- until 0
376376+scroll_zerolast:
377377+ ld hl, #LCD_START
378378+ ld d, #0
379379+ ld e, -1(ix)
380380+ add hl, de
381381+ ld b, #FONT_HEIGHT
382382+scroll_zerolastloop: ; 8 times: zero hl, hl++
383383+ ld (hl), #0
384384+ inc hl
385385+ djnz scroll_zerolastloop
386386+ ld b, -2(ix)
387387+ djnz scroll_lcd_column ; column--, if not zero keep going
388388+ pop hl
389389+ pop de
390390+ pop bc
391391+ ld sp, ix
392392+ pop ix
393393+ ret
394394+395395+396396+; address of screenbuf or screenattrs offset for a row/col in hl, returns in hl
397397+screenbuf_offset:
398398+ push bc
399399+ push de
400400+ ; uses hl
401401+ ex de, hl
402402+ ld hl, #0
403403+ ld a, d ; row
404404+ cp #0
405405+ jr z, multiply_srow_out ; only add rows if > 0
406406+ ld bc, #LCD_COLS
407407+multiply_srow:
408408+ add hl, bc
409409+ dec a
410410+ cp #0
411411+ jr nz, multiply_srow
412412+multiply_srow_out:
413413+ ld d, #0 ; col in e
414414+ add hl, de ; hl = (row * LCD_COLS) + col
415415+ pop de
416416+ pop bc
417417+ ret ; hl
418418+419419+420420+; void stamp_char(unsigned int row, unsigned int col)
421421+; row at 4(ix), col at 6(ix)
422422+; TODO: store last col group, compare to -11(ix) and if same, don't bother
423423+; changing LCD_CAS and re-reading 4038 data
424424+_stamp_char::
425425+ push ix
426426+ ld ix, #0
427427+ add ix, sp
428428+ push bc
429429+ push de
430430+ push hl
431431+ ld hl, #-14 ; stack bytes for local storage
432432+ add hl, sp
433433+ ld sp, hl
434434+ in a, (#06)
435435+ ld -3(ix), a ; stack[-3] = slot4000 device
436436+ in a, (#05)
437437+ ld -4(ix), a ; stack[-4] = slot4000 page
438438+find_char:
439439+ ld h, 4(ix)
440440+ ld l, 6(ix)
441441+ call screenbuf_offset
442442+ push hl
443443+ ld de, #_screenbuf
444444+ add hl, de ; hl = screenbuf[(row * LCD_COLS) + col]
445445+ ld a, (hl)
446446+ ld -5(ix), a ; stack[-5] = character to stamp
447447+ pop hl
448448+ ld de, #_screenattrs
449449+ add hl, de ; hl = screenattrs[(row * LCD_COLS) + col]
450450+ ld a, (hl)
451451+ ld -6(ix), a ; stack[-6] = character attrs
452452+calc_font_data_base:
453453+ ld h, #0
454454+ ld l, -5(ix) ; char
455455+ add hl, hl ; hl = char * FONT_HEIGHT (8)
456456+ add hl, hl
457457+ add hl, hl
458458+ ld de, #font_data
459459+ add hl, de
460460+ ld -7(ix), l
461461+ ld -8(ix), h ; stack[-8,-7] = char font data base addr
462462+calc_char_cell_base:
463463+ ld h, #0
464464+ ld l, 4(ix) ; row
465465+ add hl, hl
466466+ add hl, hl
467467+ add hl, hl ; hl = row * FONT_HEIGHT (8)
468468+ ld de, #LCD_START
469469+ add hl, de ; hl = 4038 + (row * FONT_HEIGHT)
470470+ ld -9(ix), l
471471+ ld -10(ix), h ; stack[-10,-9] = lcd char cell base
472472+fetch_from_table:
473473+ ld a, 6(ix) ; col
474474+ ld hl, #cursorx_lookup_data
475475+ ld b, #0
476476+ ld c, a
477477+ add hl, bc
478478+ ld b, (hl)
479479+ ld a, b
480480+pluck_col_group:
481481+ and #0b11111000 ; upper 5 bits are col group
482482+ srl a
483483+ srl a
484484+ srl a
485485+ ld -11(ix), a ; stack[-11] = col group
486486+pluck_offset:
487487+ ld a, b
488488+ and #0b00000111 ; lower 3 bits are offset
489489+ ld -12(ix), a ; stack[-12] = offset
490490+ ld d, #FONT_HEIGHT ; for (row = FONT_HEIGHT; row >= 0; row--)
491491+next_char_row:
492492+ ld a, d
493493+ dec a
494494+ ld h, -8(ix) ; char font data base
495495+ ld l, -7(ix)
496496+ ld b, #0
497497+ ld c, a
498498+ add hl, bc
499499+ ld a, (hl) ; font_addr + (char * FONT_HEIGHT) + row
500500+ ld b, -6(ix)
501501+ bit #ATTR_BIT_REVERSE, b
502502+ jr nz, reverse
503503+ bit #ATTR_BIT_CURSOR, b
504504+ jr nz, reverse
505505+ jr not_reverse
506506+reverse:
507507+ cpl ; flip em
508508+ and #0b00011111 ; mask off bits not within FONT_WIDTH
509509+not_reverse:
510510+ ld -13(ix), a ; stack[-13] = working font data
511511+ ld a, -6(ix)
512512+ bit #ATTR_BIT_UNDERLINE, a
513513+ jr z, not_underline
514514+ ld a, d
515515+ cp #FONT_HEIGHT
516516+ jr nz, not_underline
517517+underline:
518518+ ld -13(ix), #0xff
519519+not_underline:
520520+ ld a, 6(ix) ; col
521521+ cp #LCD_COLS / 2 ; assume a char never spans both LCD sides
522522+ jr nc, rightside
523523+leftside:
524524+ ld a, #DEVICE_LCD_LEFT
525525+ jr swap_lcd
526526+rightside:
527527+ ld a, #DEVICE_LCD_RIGHT
528528+swap_lcd:
529529+ out (#06), a
530530+ ld e, #FONT_WIDTH ; for (col = FONT_WIDTH; col > 0; col--)
531531+next_char_col: ; inner loop, each col of each row
532532+ ld -14(ix), #0b00011111 ; font data mask that will get shifted
533533+determine_cas:
534534+ ld c, #0
535535+ ld b, -11(ix) ; col group
536536+ ld a, -12(ix) ; bit offset
537537+ add #FONT_WIDTH
538538+ sub e ; if offset+(5-col) is >= 8, advance col
539539+ cp #LCD_COL_GROUP_WIDTH
540540+ jr c, skip_advance ; if a >= 8, advance (dec b)
541541+ dec b
542542+ ld c, -12(ix) ; bit offset
543543+ ld a, #LCD_COL_GROUP_WIDTH
544544+ sub c
545545+ ld c, a ; c = number of right shifts
546546+skip_advance:
547547+do_lcd_cas:
548548+ ld h, #0
549549+ ld l, b
550550+ push hl
551551+ call _lcd_cas
552552+ pop hl
553553+ ; if this character doesn't fit entirely in one lcd column, we need to
554554+ ; span two of them and on the left one, shift font data and masks right
555555+ ; to remove right-most bits that will be on the next column
556556+prep_right_shift:
557557+ ld a, c
558558+ cp #0
559559+ jr z, prep_left_shift
560560+ ld b, c
561561+ ld c, -14(ix) ; matching mask 00011111
562562+ ld a, -13(ix) ; load font data like 00010101
563563+right_shift:
564564+ srl a ; shift font data right #b times
565565+ srl c ; and mask to match
566566+ djnz right_shift ; -> 10101000
567567+ ld -14(ix), c
568568+ jr done_left_shift
569569+ ; shift font data and masks to the left
570570+prep_left_shift:
571571+ ld c, -14(ix) ; mask
572572+ ld a, -12(ix) ; (bit offset) times, shift font data
573573+ cp #0
574574+ ld b, a
575575+ ld a, -13(ix) ; read new font data
576576+ jr z, done_left_shift
577577+left_shift:
578578+ sla a
579579+ sla c
580580+ djnz left_shift
581581+done_left_shift:
582582+ ld b, a
583583+ ld a, c
584584+ cpl
585585+ ld -14(ix), a ; invert mask
586586+ ld a, b
587587+read_lcd_data:
588588+ ld h, -10(ix)
589589+ ld l, -9(ix)
590590+ ld b, a
591591+ ld a, d
592592+ dec a
593593+ ld c, a
594594+ ld a, b
595595+ ld b, #0
596596+ add hl, bc ; hl = 4038 + (row * FONT_HEIGHT) + row - 1
597597+ ld b, a ; store new font data
598598+ ld a, (hl) ; read existing cell data
599599+ and -14(ix) ; mask off new char cell
600600+ or b ; combine data into cell
601601+ ld (hl), a
602602+ dec e
603603+ jp nz, next_char_col
604604+ dec d
605605+ jp nz, next_char_row
606606+stamp_char_out:
607607+ ld a, -3(ix) ; restore slot4000device
608608+ out (#06), a
609609+ ld a, -4(ix) ; restore slot4000page
610610+ out (#05), a
611611+ ld hl, #14 ; remove stack bytes
612612+ add hl, sp
613613+ ld sp, hl
614614+ pop hl
615615+ pop de
616616+ pop bc
617617+ ld sp, ix
618618+ pop ix
619619+ ret
620620+621621+622622+; void uncursor(void)
623623+; remove cursor attribute from old cursor position
624624+_uncursor::
625625+ push de
626626+ push hl
627627+ ld a, (_cursory)
628628+ ld h, a
629629+ ld a, (_cursorx)
630630+ ld l, a
631631+ call screenbuf_offset
632632+ ld de, #_screenattrs
633633+ add hl, de ; screenattrs[(cursory * TEXT_COLS) + cursorx]
634634+ ld a, (hl)
635635+ res #ATTR_BIT_CURSOR, a ; &= ~(ATTR_CURSOR)
636636+ set #ATTR_BIT_DIRTY, a ; |= (ATTR_DIRTY)
637637+ ld (hl), a
638638+ pop hl
639639+ pop de
640640+ ret
641641+642642+; void recursor(void)
643643+; force-set cursor attribute
644644+_recursor::
645645+ push de
646646+ push hl
647647+ ld a, (_cursory)
648648+ ld h, a
649649+ ld a, (_cursorx)
650650+ ld l, a
651651+ call screenbuf_offset
652652+ ld de, #_screenattrs
653653+ add hl, de ; screenattrs[(cursory * TEXT_COLS) + cursorx]
654654+ ld a, (hl)
655655+ or #(ATTR_CURSOR | ATTR_DIRTY)
656656+ ld (hl), a
657657+ pop hl
658658+ pop de
659659+ ret
660660+661661+662662+; int putchar(int c)
663663+_putchar::
664664+ push ix
665665+ ld ix, #0
666666+ add ix, sp ; char to print is at 4(ix)
667667+ push de
668668+ push hl
669669+ call _uncursor
670670+ ld a, 4(ix)
671671+ cp #'\b' ; backspace
672672+ jr nz, not_backspace
673673+backspace:
674674+ ld a, (_cursorx)
675675+ cp #0
676676+ jr nz, cursorx_not_zero
677677+ ld a, (_cursory)
678678+ cp #0
679679+ jp z, putchar_fastout ; cursorx/y at 0,0, nothing to do
680680+ dec a
681681+ ld (_cursory), a ; cursory--
682682+ ld a, #LCD_COLS - 2
683683+ ld (_cursorx), a
684684+ jp putchar_out
685685+cursorx_not_zero:
686686+ dec a
687687+ ld (_cursorx), a ; cursorx--;
688688+ jp putchar_out
689689+not_backspace:
690690+ cp #'\r'
691691+ jr nz, not_cr
692692+ xor a
693693+ ld (_cursorx), a ; cursorx = 0
694694+ jr not_crlf
695695+not_cr:
696696+ cp #'\n'
697697+ jr nz, not_crlf
698698+ xor a
699699+ ld (_cursorx), a ; cursorx = 0
700700+ ld a, (_cursory)
701701+ inc a
702702+ ld (_cursory), a ; cursory++
703703+not_crlf:
704704+ ld a, (_cursorx)
705705+ cp #LCD_COLS
706706+ jr c, not_longer_text_cols ; cursorx < TEXT_COLS
707707+ xor a
708708+ ld (_cursorx), a ; cursorx = 0
709709+ ld a, (_cursory)
710710+ inc a
711711+ ld (_cursory), a
712712+not_longer_text_cols:
713713+ ld a, (_cursory)
714714+ cp #TEXT_ROWS
715715+ jr c, scroll_out
716716+scroll_up_screen:
717717+ call _scroll_lcd
718718+ xor a
719719+ ld (_cursorx), a
720720+ ld a, #TEXT_ROWS - 1
721721+ ld (_cursory), a ; cursory = TEXT_ROWS - 1
722722+scroll_out:
723723+ ld a, 4(ix)
724724+ cp a, #'\r'
725725+ jr z, cr_or_lf
726726+ cp a, #'\n'
727727+ jr z, cr_or_lf
728728+ jr store_char_in_buf
729729+cr_or_lf:
730730+ ld a, (_putchar_quick)
731731+ cp #1
732732+ jr z, putchar_out
733733+ call _redraw_screen
734734+ jr putchar_fastout
735735+store_char_in_buf:
736736+ ld a, (_cursory)
737737+ ld h, a
738738+ ld a, (_cursorx)
739739+ ld l, a
740740+ call screenbuf_offset
741741+ push hl
742742+ ld de, #_screenbuf
743743+ add hl, de ; hl = screenbuf[(cursory * LCD_COLS) + cursorx]
744744+ ld a, 4(ix)
745745+ ld (hl), a ; store character
746746+ pop hl
747747+ ld de, #_screenattrs
748748+ add hl, de ; hl = screenattrs[(cursory * LCD_COLS) + cursorx]
749749+ ld a, (_putchar_sgr)
750750+ set #ATTR_BIT_DIRTY, a
751751+ ld (hl), a ; = putchar_sgr | ATTR_DIRTY
752752+advance_cursorx:
753753+ ld a, (_cursorx)
754754+ inc a
755755+ ld (_cursorx), a
756756+ cp #LCD_COLS ; if (cursorx >= LCD_COLS)
757757+ jr c, putchar_out
758758+ xor a
759759+ ld (_cursorx), a
760760+ ld a, (_cursory)
761761+ inc a
762762+ ld (_cursory), a
763763+check_cursory:
764764+ cp #TEXT_ROWS ; and if (cursory >= TEXT_ROWS)
765765+ jr c, putchar_out
766766+ call _scroll_lcd
767767+ ld a, #TEXT_ROWS - 1
768768+ ld (_cursory), a ; cursory = TEXT_ROWS - 1
769769+putchar_out:
770770+ ld a, (_cursory)
771771+ ld h, a
772772+ ld a, (_cursorx)
773773+ ld l, a
774774+ call screenbuf_offset
775775+ ld de, #_screenattrs
776776+ add hl, de ; hl = screenattrs[(cursory * LCD_COLS) + cursorx]
777777+ ld a, (hl) ; read existing attrs
778778+ set #ATTR_BIT_DIRTY, a
779779+ set #ATTR_BIT_CURSOR, a
780780+ ld (hl), a ; = putchar_sgr | ATTR_DIRTY | ATTR_CURSOR
781781+ ld a, (_putchar_quick)
782782+ cp #1
783783+ jr z, putchar_fastout
784784+ call _redraw_screen
785785+putchar_fastout:
786786+ pop hl
787787+ pop de
788788+ ld sp, ix
789789+ pop ix
790790+ ret
791791+792792+793793+; void putchar_attr(unsigned char row, unsigned char col, char c, char attr)
794794+; directly manipulates screenbuf/attrs without scrolling or length checks
795795+; row at 4(ix), col at 5(ix), c at 6(ix), attr at 7(ix)
796796+_putchar_attr::
797797+ push ix
798798+ ld ix, #0
799799+ add ix, sp
800800+ push de
801801+ push hl
802802+store_char:
803803+ ld h, 4(ix)
804804+ ld l, 5(ix)
805805+ call screenbuf_offset
806806+ push hl
807807+ ld de, #_screenbuf
808808+ add hl, de ; screenbuf[(row * TEXT_COLS) + col]
809809+ ld a, 6(ix)
810810+ ld (hl), a
811811+store_attrs:
812812+ pop hl
813813+ ld de, #_screenattrs
814814+ add hl, de ; screenattrs[(row * TEXT_COLS) + col]
815815+ ld a, 7(ix)
816816+ set #ATTR_BIT_DIRTY, a
817817+ ld (hl), a
818818+ pop hl
819819+ pop de
820820+ ld sp, ix
821821+ pop ix
822822+ ret
···11+#!/usr/bin/env ruby
22+#
33+# generate_cursorx_lookup
44+# generate cursorx lookup table using 5 bits for col group and 3 for offset
55+# msTERM
66+#
77+# Copyright (c) 2019 joshua stein <jcs@jcs.org>
88+#
99+# Permission to use, copy, modify, and distribute this software for any
1010+# purpose with or without fee is hereby granted, provided that the above
1111+# copyright notice and this permission notice appear in all copies.
1212+#
1313+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1414+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1515+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1616+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1717+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1818+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1919+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2020+#
2121+2222+# pixels: 01234567012345670123456801234567012345670123456701234567012345670
2323+# col group: | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 |
2424+# font cell: .....11111.....11111.....11111.....11111.....11111.....11111.....
2525+2626+File.open("#{__dir__}/../cursorx_lookup.inc", "w+") do |f|
2727+ f.puts "; AUTOMATICALLY GENERATED FILE - see tools/generate_cursorx_lookup.rb"
2828+2929+ pcol = 0
3030+ 64.times do |x|
3131+ col_group = 20 - (pcol / 8) - 1
3232+ off = pcol % 8
3333+3434+ v = sprintf("%05b%03b", col_group, off).to_i(2)
3535+3636+ f.puts "\t.db #0x#{sprintf("%02x", v)}\t\t\t; #{sprintf("%08b", v)} - col group #{col_group}, offset #{off}"
3737+3838+ pcol += 5
3939+4040+ if pcol == 160
4141+ pcol = 0
4242+ end
4343+ end
4444+end
+252
tools/generate_scancodes.rb
···11+#!/usr/bin/env ruby
22+#
33+# generate_scancodes
44+# generate scancode lookup tables
55+# msTERM
66+#
77+# Copyright (c) 2019 joshua stein <jcs@jcs.org>
88+#
99+# Permission to use, copy, modify, and distribute this software for any
1010+# purpose with or without fee is hereby granted, provided that the above
1111+# copyright notice and this permission notice appear in all copies.
1212+#
1313+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1414+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1515+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1616+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1717+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1818+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1919+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2020+#
2121+2222+META_KEY_BEGIN = 200
2323+2424+KEYS = [
2525+ :MAIN_MENU,
2626+ :BACK,
2727+ :PRINT,
2828+ :F1,
2929+ :F2,
3030+ :F3,
3131+ :F4,
3232+ :F5,
3333+ :POWER,
3434+ :SIZE,
3535+ :SPELLING,
3636+ :EMAIL,
3737+ :PAGE_UP,
3838+ :PAGE_DOWN,
3939+ :CAPS_LOCK,
4040+ :LEFT_SHIFT,
4141+ :RIGHT_SHIFT,
4242+ :FN,
4343+ :UP,
4444+ :DOWN,
4545+ :LEFT,
4646+ :RIGHT,
4747+]
4848+4949+META_KEY_NONE = 255
5050+5151+UPPERCASES = {
5252+ "`" => "~",
5353+ "1" => "!",
5454+ "2" => "@",
5555+ "3" => "#",
5656+ "4" => "$",
5757+ "5" => "%",
5858+ "6" => "^",
5959+ "7" => "&",
6060+ "8" => "*",
6161+ "9" => "(",
6262+ "0" => ")",
6363+ "-" => "_",
6464+ "=" => "+",
6565+ "\\" => "|",
6666+ "[" => "{",
6767+ "]" => "}",
6868+ ";" => ":",
6969+ "'" => "\"",
7070+ "," => "<",
7171+ "." => ">",
7272+ "/" => "?",
7373+}
7474+7575+CONTROLS = {
7676+ "a" => 1,
7777+ "b" => 2,
7878+ "c" => 3,
7979+ "d" => 4,
8080+ "e" => 5,
8181+ "f" => 6,
8282+ "g" => 7,
8383+ "h" => 8,
8484+ "i" => 9,
8585+ "j" => 10,
8686+ "k" => 11,
8787+ "l" => 12,
8888+ "m" => 13,
8989+ "n" => 14,
9090+ "o" => 15,
9191+ "p" => 16,
9292+ "q" => 17,
9393+ "r" => 18,
9494+ "s" => 19,
9595+ "t" => 20,
9696+ "u" => 21,
9797+ "v" => 22,
9898+ "w" => 23,
9999+ "x" => 24,
100100+ "y" => 25,
101101+ "z" => 26,
102102+ "3" => 27,
103103+ "[" => 27,
104104+ "4" => 28,
105105+ "\\" => 28,
106106+ "5" => 29,
107107+ "]" => 29,
108108+ "6" => 30,
109109+ "7" => 31,
110110+ "-" => 31,
111111+ "/" => 31,
112112+ "8" => 127,
113113+}
114114+115115+SCANCODES = {
116116+ 0 => :MAIN_MENU,
117117+ 1 => :BACK,
118118+ 2 => :PRINT,
119119+ 3 => :F1,
120120+ 4 => :F2,
121121+ 5 => :F3,
122122+ 6 => :F4,
123123+ 7 => :F5,
124124+ 15 => :POWER,
125125+ 19 => "@",
126126+ 20 => :SIZE,
127127+ 21 => :SPELLING,
128128+ 22 => :EMAIL,
129129+ 23 => :PAGE_UP,
130130+ 32 => "`",
131131+ 33 => "1",
132132+ 34 => "2",
133133+ 35 => "3",
134134+ 36 => "4",
135135+ 37 => "5",
136136+ 38 => "6",
137137+ 39 => "7",
138138+ 48 => "8",
139139+ 49 => "9",
140140+ 50 => "0",
141141+ 51 => "-",
142142+ 52 => "=",
143143+ 53 => "\b", # backspace
144144+ 54 => "\\",
145145+ 55 => :PAGE_DOWN,
146146+ 64 => "\t",
147147+ 65 => "q",
148148+ 66 => "w",
149149+ 67 => "e",
150150+ 68 => "r",
151151+ 69 => "t",
152152+ 70 => "y",
153153+ 71 => "u",
154154+ 80 => "i",
155155+ 81 => "o",
156156+ 82 => "p",
157157+ 83 => "[",
158158+ 84 => "]",
159159+ 85 => ";",
160160+ 86 => "'",
161161+ 87 => "\n",
162162+ 96 => :CAPS_LOCK,
163163+ 97 => "a",
164164+ 98 => "s",
165165+ 99 => "d",
166166+ 100 => "f",
167167+ 101 => "g",
168168+ 102 => "h",
169169+ 103 => "j",
170170+ 112 => "k",
171171+ 113 => "l",
172172+ 114 => ",",
173173+ 115 => ".",
174174+ 116 => "/",
175175+ 117 => :UP,
176176+ 118 => :DOWN,
177177+ 119 => :RIGHT,
178178+ 128 => :LEFT_SHIFT,
179179+ 129 => "z",
180180+ 130 => "x",
181181+ 131 => "c",
182182+ 132 => "v",
183183+ 133 => "b",
184184+ 134 => "n",
185185+ 135 => "m",
186186+ 144 => :FN,
187187+ 147 => " ",
188188+ 150 => :RIGHT_SHIFT,
189189+ 151 => :LEFT,
190190+}
191191+192192+File.open("#{__dir__}/../scancodes.inc", "w+") do |scf|
193193+ scf.puts "; AUTOMATICALLY GENERATED FILE - see tools/generate_scancodes.rb"
194194+ scf.puts "\t.equ\tMETA_KEY_BEGIN,\t#0d#{sprintf("%03d", META_KEY_BEGIN)}"
195195+ scf.puts "\t.equ\tMETA_KEY_NONE,\t#0d#{sprintf("%03d", META_KEY_NONE)}"
196196+ scf.puts
197197+198198+ 3.times do |x|
199199+ if x == 0
200200+ scf.puts "scancode_table:"
201201+ elsif x == 1
202202+ scf.puts
203203+ scf.puts "scancode_table_uppercase:"
204204+ elsif x == 2
205205+ scf.puts
206206+ scf.puts "scancode_table_control:"
207207+ end
208208+209209+ (0 .. SCANCODES.keys.last).each do |sc|
210210+ if k = SCANCODES[sc]
211211+ origk = k
212212+ if k.is_a?(Symbol)
213213+ k = META_KEY_BEGIN + KEYS.index(k)
214214+ elsif k.is_a?(String)
215215+ k = k.ord
216216+ end
217217+ raise if !k
218218+219219+ if x == 1
220220+ if u = UPPERCASES[origk]
221221+ k = u.ord
222222+ origk = u
223223+ elsif ("a" .. "z").include?(origk)
224224+ k = origk.upcase.ord
225225+ origk = origk.upcase
226226+ end
227227+ elsif x == 2
228228+ if u = CONTROLS[origk]
229229+ k = u
230230+ origk = u
231231+ end
232232+ end
233233+234234+ scf.puts "\t.db #0d#{sprintf("%03d", k)}\t\t; #{origk.inspect}"
235235+ else
236236+ scf.puts "\t.db #0d#{sprintf("%03d", META_KEY_NONE)}"
237237+ end
238238+ end
239239+ end
240240+end
241241+242242+File.open("#{__dir__}/../meta_keys.h", "w+") do |mkh|
243243+ mkh.puts "/* AUTOMATICALLY GENERATED FILE - see tools/generate_scancodes.rb */"
244244+ mkh.puts "#define\tMETA_KEY_BEGIN\t#{META_KEY_BEGIN}"
245245+246246+ KEYS.each_with_index do |k,x|
247247+ mkh.puts "#define\tKEY_#{k}\t#{k.length < 3 ? "\t" : ""}#{META_KEY_BEGIN + x}"
248248+ end
249249+250250+ mkh.puts
251251+ mkh.puts "#define\tMETA_KEY_NONE\t#{META_KEY_NONE}"
252252+end