···11+#
22+# MailStation example program
33+# Copyright (c) 2019-2021 joshua stein <jcs@jcs.org>
44+#
55+# Permission to use, copy, modify, and distribute this software for any
66+# purpose with or without fee is hereby granted, provided that the above
77+# copyright notice and this permission notice appear in all copies.
88+#
99+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+#
1717+1818+# the name of your program
1919+PROG= hello
2020+2121+# your c source files
2222+SRCS= main.c
2323+2424+# your asm source files
2525+ASM_SRCS=
2626+2727+# default to build running from ram
2828+LOC?= ram
2929+3030+3131+ASZ80?= sdasz80 -l -ff
3232+SDCC?= sdcc -mz80 --opt-code-size
3333+3434+SRCDIR?= ${.CURDIR}
3535+3636+OBJ?= ${SRCDIR}/obj
3737+3838+.if ${LOC:L} == "flash"
3939+BASE_ADDR= 0x4000
4040+.elif ${LOC:L} == "ram"
4141+BASE_ADDR= 0x8000
4242+.else
4343+.BEGIN:
4444+ @echo 'LOC must be "flash" or "ram"'
4545+ @exit 1
4646+.endif
4747+4848+all: checkobj $(PROG).bin
4949+5050+obj: .PHONY
5151+ mkdir $(OBJ)
5252+5353+# it would be nice to just make this ourselves but if it doesn't exist before
5454+# make is invoked, it screws things up
5555+checkobj: .PHONY
5656+ @if [ ! -d $(OBJ) ]; then \
5757+ echo "\"${MAKE} obj\" first"; \
5858+ exit 1; \
5959+ fi
6060+6161+clean:
6262+ rm -rf $(OBJ)/*
6363+6464+# assembly
6565+6666+ADDRS_INC= ${SRCDIR}/lib/addrs-${LOC}.inc
6767+6868+crt0.rel: lib/crt0.s
6969+ $(ASZ80) -o ${.TARGET} ${ADDRS_INC} $>
7070+7171+getchar.rel: lib/getchar.s
7272+ $(ASZ80) -o ${.TARGET} ${ADDRS_INC} $>
7373+7474+putchar.rel: lib/putchar.s lib/font/spleen-5x8.inc
7575+ $(ASZ80) -o ${.TARGET} ${ADDRS_INC} $(SRCDIR)/lib/putchar.s
7676+7777+wifi.rel: lib/wifi.s
7878+ $(ASZ80) -o ${.TARGET} ${ADDRS_INC} $>
7979+8080+# generated code
8181+8282+lib/font/spleen-5x8.inc: lib/font/spleen-5x8.hex
8383+ ruby $(SRCDIR)/lib/tools/hexfont2inc.rb $> > $(SRCDIR)/${.TARGET}
8484+8585+# all relocation files for final ihx
8686+REL_FILES= crt0.rel putchar.rel getchar.rel wifi.rel
8787+8888+.for S in $(SRCS)
8989+REL_FILES+= ${S:R}.rel
9090+${S:R}.rel: $(S)
9191+ $(SDCC) -c ${.TARGET} $(SRCDIR)/$(S)
9292+.endfor
9393+9494+.for S in $(ASM_SRCS)
9595+REL_FILES+= ${S:R}.rel
9696+${S:R}.rel: $(S)
9797+ $(ASZ80) -o ${.TARGET} ${ADDRS_INC} $>
9898+.endfor
9999+100100+# link
101101+102102+$(PROG).ihx: $(REL_FILES)
103103+ @SDCC="$(SDCC) --no-std-crt0" TARGET="$(.TARGET)" \
104104+ BASE_ADDR="$(BASE_ADDR)" CODE_OFF="$(CODE_OFF)" \
105105+ PROG_MAP="$(PROG).map" \
106106+ ruby $(SRCDIR)/lib/tools/relink_packed.rb $>
107107+108108+# convert to binary
109109+110110+$(PROG).bin: $(PROG).ihx
111111+ objcopy -Iihex -Obinary $> $@
112112+ @if [ `stat -f '%z' ${.TARGET}` -gt 16384 ]; then \
113113+ ls -l ${.TARGET}; \
114114+ echo "${.TARGET} overflows a ${LOC} page, must be <= 16384; increase DATA_SIZE"; \
115115+ exit 1; \
116116+ fi
117117+118118+# helpers
119119+120120+disasm: $(PROG).bin
121121+ z80dasm -al -g ${BASE_ADDR} $> > $(PROG).dasm
+62
README.md
···11+# MailStation Example Program
22+33+This is an example program to get you started writing code for the
44+[Cidco MailStation](https://jcs.org/2019/05/03/mailstation)
55+Z80 computer.
66+77+The code in `lib/` sets up the C run-time (`crt0`) and gives you a `putchar`
88+routine that draws on the screen with a 5x8 font.
99+1010+`main()` prints "Hello, World" and then loops forever echoing any keys typed on
1111+the keyboard out to the screen.
1212+This is where your code should go to do something more useful.
1313+1414+## Building
1515+1616+Install
1717+[SDCC](http://sdcc.sourceforge.net/),
1818+Ruby,
1919+and if on a non-BSD platform, a BSD Make (bmake).
2020+2121+Once installed, issue `make` (or `bmake`):
2222+2323+ $ make obj
2424+ $ make
2525+2626+This will produce a file `obj/hello.bin` for uploading to the MailStation.
2727+2828+By default, the linker sets up the program to be running from RAM (loaded via
2929+[WSLoader](https://jcs.org/wifistation/loader)) at `0x4000`.
3030+If loading to Data Flash, set `LOC` to `flash` (and clean first):
3131+3232+ $ make LOC=flash clean all
3333+3434+## Running
3535+3636+Run Loader/WSLoader on the MailStation and then upload `obj/hello.bin` to it
3737+via a parallel LapLink cable or a
3838+[WiFiStation](https://jcs.org/wifistation).
3939+4040+## Extending
4141+4242+If your code extends beyond `main.c`, add your source `.c` files to the `SRCS`
4343+variable in `Makefile`, and any assembly files to `ASM_SRCS`.
4444+4545+You'll probably also want to change the `PROG` variable to be something
4646+meaningful.
4747+4848+## License (ISC)
4949+5050+Copyright (c) 2019-2021 joshua stein <jcs@jcs.org>
5151+5252+Permission to use, copy, modify, and distribute this software for any
5353+purpose with or without fee is hereby granted, provided that the above
5454+copyright notice and this permission notice appear in all copies.
5555+5656+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
5757+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
5858+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
5959+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
6060+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
6161+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
6262+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+26
lib/addrs-flash.inc
···11+; vim:syntax=z8a:ts=8:sw=8
22+;
33+; Copyright (c) 2019-2021 joshua stein <jcs@jcs.org>
44+;
55+; Permission to use, copy, modify, and distribute this software for any
66+; purpose with or without fee is hereby granted, provided that the above
77+; copyright notice and this permission notice appear in all copies.
88+;
99+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+;
1717+1818+ ; When running from Data Flash Loader, we are loaded at 0x4000
1919+ ; and use slot 8 for swapping devices
2020+2121+ .equ RUN_ADDR, #0x4000
2222+ .equ RUN_DEVICE, #0x6
2323+ .equ RUN_PAGE, #0x5
2424+ .equ SLOT_ADDR, #0x8000
2525+ .equ SLOT_DEVICE, #0x8
2626+ .equ SLOT_PAGE, #0x7
+26
lib/addrs-ram.inc
···11+; vim:syntax=z8a:ts=8:sw=8
22+;
33+; Copyright (c) 2019-2021 joshua stein <jcs@jcs.org>
44+;
55+; Permission to use, copy, modify, and distribute this software for any
66+; purpose with or without fee is hereby granted, provided that the above
77+; copyright notice and this permission notice appear in all copies.
88+;
99+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+;
1717+1818+ ; When running from Loader/WSLoader, we are loaded at 0x8000
1919+ ; and use slot 4 for swapping devices
2020+2121+ .equ RUN_ADDR, #0x8000
2222+ .equ RUN_DEVICE, #0x8
2323+ .equ RUN_PAGE, #0x7
2424+ .equ SLOT_ADDR, #0x4000
2525+ .equ SLOT_DEVICE, #0x6
2626+ .equ SLOT_PAGE, #0x5
+422
lib/crt0.s
···11+; vim:syntax=z8a:ts=8
22+;
33+; MailStation example program
44+; Copyright (c) 2019-2021 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+ .module crt0
2020+2121+ .include "mailstation.inc"
2222+ .globl _main
2323+2424+ .area _HEADER (ABS)
2525+2626+ .org RUN_ADDR
2727+start:
2828+ jp boot
2929+3030+ .dw (icons)
3131+ .dw (caption)
3232+ .dw (dunno)
3333+3434+dunno:
3535+ .db #0
3636+xpos:
3737+ .dw #0
3838+ypos:
3939+ .dw #0
4040+caption:
4141+ .dw #0x0001 ; ?
4242+ .dw (endcap - caption - 6) ; number of chars
4343+ .dw #0x0006 ; offset to first char
4444+ .ascii "Example" ; the caption string
4545+endcap:
4646+4747+icons:
4848+ .dw (icon2 - icon1) ; size of icon1
4949+ .dw (icon1 - icons) ; offset to icon1
5050+ .dw (iconend - icon2) ; size of icon2
5151+ .dw (icon2 - icons) ; offset to icon2
5252+5353+icon1:
5454+ .dw #0x0022 ; icon width (34, 5 bytes per row)
5555+ .db #0x22 ; icon height (34)
5656+5757+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
5858+ .db #0x00, #0x00, #0x00, #0xe0, #0x03 ; .............................#####
5959+ .db #0x00, #0x00, #0x00, #0x20, #0x02 ; .............................#...#
6060+ .db #0x00, #0x00, #0x00, #0xa0, #0x02 ; .............................#.#.#
6161+ .db #0x00, #0x00, #0x00, #0xa0, #0x02 ; .............................#.#.#
6262+ .db #0x00, #0x00, #0x00, #0xa0, #0x02 ; .............................#.#.#
6363+ .db #0x00, #0x00, #0x00, #0xe0, #0x03 ; .............................#####
6464+ .db #0x00, #0x00, #0x00, #0x80, #0x00 ; ...............................#..
6565+ .db #0x00, #0xff, #0xff, #0x63, #0x00 ; ........##################...##...
6666+ .db #0x00, #0x01, #0x00, #0x12, #0x00 ; ........#................#..#.....
6767+ .db #0x00, #0x01, #0x00, #0x12, #0x00 ; ........#................#..#.....
6868+ .db #0x00, #0x01, #0x00, #0x0a, #0x00 ; ........#................#.#......
6969+ .db #0xe0, #0x01, #0x00, #0x1e, #0x00 ; .....####................####.....
7070+ .db #0xf0, #0x01, #0x00, #0x3e, #0x00 ; ....#####................#####....
7171+ .db #0xf0, #0x01, #0x00, #0x36, #0x00 ; ....#####................##.##....
7272+ .db #0xf8, #0xff, #0xff, #0x7f, #0x00 ; ...############################...
7373+ .db #0xa8, #0x92, #0x24, #0x55, #0x00 ; ...#.#.#.#..#..#..#..#..#.#.#.#...
7474+ .db #0xfc, #0xff, #0xff, #0xff, #0x00 ; ..##############################..
7575+ .db #0x54, #0x55, #0x55, #0x95, #0x00 ; ..#.#.#.#.#.#.#.#.#.#.#.#.#.#..#..
7676+ .db #0xfe, #0xff, #0xff, #0xff, #0x01 ; .################################.
7777+ .db #0xaa, #0xaa, #0xaa, #0x2a, #0x01 ; .#.#.#.#.#.#.#.#.#.#.#.#.#.#.#..#.
7878+ .db #0xfe, #0xff, #0xff, #0xff, #0x01 ; .################################.
7979+ .db #0x53, #0x55, #0x55, #0x15, #0x03 ; ##..#.#.#.#.#.#.#.#.#.#.#.#.#...##
8080+ .db #0xff, #0xff, #0xff, #0xff, #0x03 ; ##################################
8181+ .db #0x26, #0x00, #0x00, #0xa8, #0x01 ; .##..#.....................#.#.##.
8282+ .db #0xfc, #0xff, #0xff, #0xff, #0x00 ; ..##############################..
8383+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
8484+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
8585+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
8686+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
8787+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
8888+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
8989+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
9090+ .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
9191+9292+icon2:
9393+ ; not used
9494+ .dw #0x0000 ; width
9595+ .db #0x00 ; height
9696+iconend:
9797+9898+boot:
9999+ xor a
100100+ out (#0x0d), a ; put the cpu in its highest speed
101101+102102+ ; all of our code expects to run in ram so when we're running from
103103+ ; dataflash, we have to copy our page of flash into ram, then jump to
104104+ ; it
105105+106106+ ; swap in a page of ram
107107+ ld a, #DEVICE_RAM
108108+ out (#SLOT_DEVICE), a
109109+ out (#SLOT_PAGE), a
110110+111111+ ; copy ourselves into ram
112112+ ld de, #SLOT_ADDR
113113+ ld hl, #RUN_ADDR
114114+ ld bc, #0x4000
115115+ ldir ; ld (de), (hl), de++, hl++, bc--
116116+117117+ jp SLOT_ADDR + (hijump - start)
118118+119119+hijump:
120120+ ; PC is now in SLOT_ADDR, put our new RAM page into RUN_DEVICE/PAGE
121121+ out (#RUN_DEVICE), a
122122+ out (#RUN_PAGE), a
123123+124124+ ; then jump back there
125125+ jp RUN_ADDR + (lojump - start)
126126+127127+lojump:
128128+ call find_shadows
129129+ call _main ; main c code
130130+ jp _exit
131131+132132+; set location of port shadow variables depending on firmware version
133133+find_shadows:
134134+ ld a, (#0x0037) ; firmware major version
135135+ cp #0x1
136136+ jr z, ver_1
137137+ cp #0x2
138138+ jr z, ver_2
139139+ cp #0x3
140140+ jr z, ver_3
141141+unrecognized_firmware: ; we can't blink because that requires
142142+ jp 0x0 ; port and fw function addresses
143143+ver_1:
144144+ ld a, (#0x0036) ; firmware minor version
145145+ cp #0x73
146146+ jr z, ver_1_73
147147+ jr unrecognized_firmware
148148+ver_1_73: ; eMessage 1.73CID
149149+ ld hl, #p2shadow
150150+ ld (hl), #0xdb9f
151151+ ld hl, #p3shadow
152152+ ld (hl), #0xdba0 ; TODO: verify
153153+ ret
154154+ver_2:
155155+ ld a, (#0x0036) ; firmware minor version
156156+ cp #0x54
157157+ jr z, ver_2_54
158158+ jr unrecognized_firmware
159159+ver_2_54: ; MailStation 2.54
160160+ ld hl, #p2shadow
161161+ ld (hl), #0xdba2
162162+ ld hl, #p3shadow
163163+ ld (hl), #0xdba3
164164+ ld hl, #p28shadow
165165+ ld (hl), #0xdba0
166166+ ret
167167+ver_3:
168168+ ld a, (#0x0036) ; firmware minor version
169169+ cp #0x0d3
170170+ jr z, ver_3_03
171171+ jr unrecognized_firmware
172172+ver_3_03: ; MailStation 3.03
173173+ ld hl, #p2shadow
174174+ ld (hl), #0xdba5
175175+ ld hl, #p3shadow
176176+ ld (hl), #0xdba6
177177+ ret
178178+179179+ .area _DATA
180180+181181+; shadow locations
182182+p2shadow:
183183+ .dw #0xdba2
184184+p3shadow:
185185+ .dw #0xdba3
186186+p28shadow:
187187+ .dw #0xdba0
188188+delay_func:
189189+ jp 0x0a5c
190190+191191+_debug0::
192192+ .db #0
193193+_debug1::
194194+ .db #0
195195+_debug2::
196196+ .db #0
197197+_debug3::
198198+ .db #0
199199+_debug4::
200200+ .db #0
201201+202202+ .area _CODE
203203+204204+; exit handler, restart
205205+_exit::
206206+ call _reboot
207207+208208+_powerdown_mode::
209209+ call #0x0a6b ; firmware powerdownmode function
210210+211211+_reboot::
212212+ jp 0x0000
213213+214214+; new_mail(unsigned char on)
215215+; toggles 'new mail' light
216216+_new_mail::
217217+ di
218218+ push ix
219219+ ld ix, #0
220220+ add ix, sp
221221+ push hl
222222+ push af
223223+ ld a, 4(ix)
224224+ cp #0
225225+ ld hl, (p2shadow)
226226+ jr z, light_off
227227+light_on:
228228+ ld a, (hl)
229229+ set 4, a
230230+ jr write_p2
231231+light_off:
232232+ ld a, (hl)
233233+ res 4, a
234234+write_p2:
235235+ ld (hl), a
236236+ out (#0x02), a ; write p2shadow to port2
237237+ pop af
238238+ pop hl
239239+ pop ix
240240+ ei
241241+ ret
242242+243243+; delay(unsigned int millis)
244244+; call mailstation function that delays (stack) milliseconds
245245+_delay::
246246+ push ix
247247+ ld ix, #0
248248+ add ix, sp
249249+ push af
250250+ push bc
251251+ push hl
252252+ ld l, 4(ix)
253253+ ld h, 5(ix)
254254+ push hl
255255+ call delay_func
256256+ pop hl
257257+ pop hl
258258+ pop bc
259259+ pop af
260260+ pop ix
261261+ ret
262262+263263+; blink(unsigned int millis)
264264+; turn new mail LED on, wait millis, turn it off, wait millis
265265+_blink::
266266+ push ix
267267+ ld ix, #0
268268+ add ix, sp
269269+ push hl
270270+ ld l, #1
271271+ push hl
272272+ call _new_mail ; turn it on
273273+ pop hl
274274+ ld l, 4(ix)
275275+ ld h, 5(ix)
276276+ push hl
277277+ call _delay ; wait
278278+ pop hl
279279+ ld l, #0
280280+ push hl
281281+ call _new_mail ; turn it off
282282+ pop hl
283283+ ld l, 4(ix)
284284+ ld h, 5(ix)
285285+ push hl
286286+ call _delay ; wait
287287+ pop hl
288288+ pop af
289289+ pop ix
290290+ ret
291291+292292+; void lcd_sleep(void)
293293+; turn the LCD off
294294+_lcd_sleep::
295295+ di
296296+ push hl
297297+ ld hl, (p2shadow)
298298+ ld a, (hl)
299299+ and #0b01111111 ; LCD_ON - turn port2 bit 7 off
300300+ ld (hl), a
301301+ out (#0x02), a ; write p2shadow to port2
302302+ pop hl
303303+ ei
304304+ ret
305305+306306+307307+; void lcd_wake(void)
308308+; turn the LCD on
309309+_lcd_wake::
310310+ di
311311+ push hl
312312+ ld hl, (p2shadow)
313313+ ld a, (hl)
314314+ or #0b10000000 ; LCD_ON - turn port2 bit 7 on
315315+ ld (hl), a
316316+ out (#0x02), a ; write p2shadow to port2
317317+ pop hl
318318+ ei
319319+ ret
320320+321321+; unsigned char read_port(unsigned char port)
322322+; return a byte read from a port
323323+_read_port::
324324+ push ix
325325+ ld ix, #0
326326+ add ix, sp
327327+ push af
328328+ push bc
329329+ ld b, #0
330330+ ld c, 4(ix)
331331+ in l, (C)
332332+ ld h, #0
333333+ pop bc
334334+ pop af
335335+ pop ix
336336+ ret
337337+338338+339339+; 8-bit multiplication
340340+; de * a = hl
341341+mult8::
342342+ ld b, #8
343343+ ld hl, #0
344344+mult8_loop:
345345+ add hl, hl
346346+ rlca
347347+ jr nc, mult8_noadd
348348+ add hl, de
349349+mult8_noadd:
350350+ djnz mult8_loop
351351+mult8_out:
352352+ ret
353353+354354+; 16-bit multiplication
355355+; bc * de = hl
356356+mult16:
357357+ ld a, b
358358+ ld b, #16
359359+ ld hl, #0
360360+mult16_loop:
361361+ add hl, hl
362362+ sla c
363363+ rla
364364+ jr nc, mult16_noadd
365365+ add hl, de
366366+mult16_noadd:
367367+ djnz mult16_loop
368368+ ret
369369+370370+371371+; 8-bit division
372372+; divide e by c, store result in a and remainder in b
373373+div8:
374374+ xor a
375375+ ld b, #8
376376+div8_loop:
377377+ rl e
378378+ rla
379379+ sub c
380380+ jr nc, div8_noadd
381381+ add a, c
382382+div8_noadd:
383383+ djnz div8_loop
384384+ ld b,a
385385+ ld a,e
386386+ rla
387387+ cpl
388388+ ret
389389+390390+; 16-bit division
391391+; divide bc by de, store result in bc, remainder in hl
392392+div16:
393393+ ld hl, #0
394394+ ld a, b
395395+ ld b, #8
396396+div16_loop1:
397397+ rla
398398+ adc hl, hl
399399+ sbc hl, de
400400+ jr nc, div16_noadd1
401401+ add hl, de
402402+div16_noadd1:
403403+ djnz div16_loop1
404404+ rla
405405+ cpl
406406+ ld b, a
407407+ ld a, c
408408+ ld c, b
409409+ ld b, #8
410410+div16_loop2:
411411+ rla
412412+ adc hl, hl
413413+ sbc hl, de
414414+ jr nc, div16_noadd2
415415+ add hl, de
416416+div16_noadd2:
417417+ djnz div16_loop2
418418+ rla
419419+ cpl
420420+ ld b, c
421421+ ld c, a
422422+ ret
+65
lib/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
···11+; vim:syntax=z8a:ts=8
22+;
33+; MailStation example program
44+; Copyright (c) 2019-2021 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+ .module getchar
2020+2121+ .include "mailstation.inc"
2222+2323+ .area _DATA
2424+2525+ ; scancode_table holds three tables of ascii characters which
2626+ ; '_getchar' uses to determine which character to return, depending on
2727+ ; the scancode pressed and the state of the shift and caps lock keys.
2828+ .include "scancodes.inc"
2929+3030+keyboardbuffer:
3131+ .ds 2 ; scancode buffer for _getchar
3232+capslock:
3333+ .db #0
3434+3535+ .area _CODE
3636+3737+; unsigned char peekkey(void)
3838+; check for a scancode using the firmware, then look it up in the scancode
3939+; table (respecting the shift key and caps lock as control) and return the
4040+; ascii value of the key in the l register
4141+_peekkey::
4242+ ld de, #keyboardbuffer
4343+ push de
4444+ call get_keycode_from_buffer
4545+ pop de
4646+ ld a, (keyboardbuffer) ; check for caps lock first
4747+ cp #0x60
4848+ jr z, is_caps_lock
4949+ jr not_caps_lock
5050+is_caps_lock:
5151+ ld a, (keyboardbuffer + 1) ; check flags
5252+ bit 0, a ; set=pressed, reset=released
5353+ jp nz, caps_down
5454+ ld a, #0 ; caps lock released
5555+ ld (capslock), a
5656+ jr nokey
5757+caps_down:
5858+ ld a, #1
5959+ ld (capslock), a
6060+ jr nokey
6161+not_caps_lock:
6262+ ld a, (keyboardbuffer + 1) ; check flags
6363+ bit 0, a ; set=pressed, reset=released
6464+ jp z, nokey ; key was released, bail
6565+ bit 6, a ; when set, shift was held down
6666+ jr z, lowercase
6767+capital:
6868+ ld hl, #scancode_table_uppercase
6969+ jr char_offset
7070+lowercase:
7171+ ld a, (capslock)
7272+ cp #1
7373+ jr z, as_control
7474+ ld hl, #scancode_table
7575+ jr char_offset
7676+as_control:
7777+ ld hl, #scancode_table_control
7878+ jr char_offset
7979+char_offset:
8080+ push hl
8181+ ld hl, #50
8282+ push hl
8383+ call _delay
8484+ pop hl
8585+ pop hl
8686+ ld a, (keyboardbuffer)
8787+ ld b, #0
8888+ ld c, a
8989+ add hl, bc
9090+ ld a, (hl)
9191+ ld h, #0
9292+ ld l, a
9393+ ret
9494+nokey:
9595+ ld h, #0
9696+ ld l, #0
9797+ ret
9898+9999+100100+; unsigned char getkey(void)
101101+; peekkey() but loops until a key is available
102102+_getkey::
103103+ call _peekkey
104104+ ld a, l
105105+ cp #0
106106+ jp z, _getkey
107107+ ret
108108+109109+110110+; int getchar(void)
111111+; uses _getkey and filters out non-printables, returns in l register
112112+_getchar::
113113+ call _getkey
114114+ ld a, l
115115+ cp a, #META_KEY_BEGIN
116116+ jr nc, _getchar ; a >=
117117+ ret
+130
lib/mailstation.h
···11+/*
22+ * MailStation example program
33+ * Copyright (c) 2019-2021 joshua stein <jcs@jcs.org>
44+ *
55+ * Permission to use, copy, modify, and distribute this software for any
66+ * purpose with or without fee is hereby granted, provided that the above
77+ * copyright notice and this permission notice appear in all copies.
88+ *
99+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+ */
1717+1818+#ifndef _INCL_MAILSTATION
1919+#define _INCL_MAILSTATION
2020+2121+#include "meta_keys.h"
2222+2323+/* define some ports - see 0x1b2b */
2424+__sfr __at(0x01) portkeyboard;
2525+__sfr __at(0x02) port2;
2626+__sfr __at(0x05) slot4000page;
2727+__sfr __at(0x06) slot4000device;
2828+__sfr __at(0x07) slot8000page;
2929+__sfr __at(0x08) slot8000device;
3030+__sfr __at(0x09) portpowerstatus;
3131+__sfr __at(0x0d) portcpuclockrate;
3232+__sfr __at(0x10) rtcseconds;
3333+__sfr __at(0x11) rtc10seconds;
3434+__sfr __at(0x12) rtcminutes;
3535+__sfr __at(0x13) rtc10minutes;
3636+__sfr __at(0x14) rtchours;
3737+__sfr __at(0x15) rtc10hours;
3838+__sfr __at(0x16) rtcdayofweek;
3939+__sfr __at(0x17) rtcdays;
4040+__sfr __at(0x18) rtc10days;
4141+__sfr __at(0x19) rtcmonths;
4242+__sfr __at(0x1a) rtc10months;
4343+__sfr __at(0x1b) rtcyears;
4444+__sfr __at(0x1c) rtc10years;
4545+__sfr __at(0x28) port28;
4646+4747+/* LCD parameters (2 screens) */
4848+#define LCD_WIDTH (160 * 2) // 320
4949+#define LCD_HEIGHT 128
5050+#define LCD_COL_GROUPS 20
5151+#define LCD_COL_GROUP_WIDTH 8
5252+5353+#define FONT_WIDTH 5
5454+#define FONT_HEIGHT 8
5555+5656+/* columns of characters */
5757+#define LCD_COLS (LCD_WIDTH / FONT_WIDTH) // 64
5858+#define LCD_ROWS (LCD_HEIGHT / FONT_HEIGHT) // 16
5959+#define TEXT_COLS LCD_COLS // 64
6060+#define TEXT_ROWS (LCD_ROWS - 1) // 15
6161+6262+#define ATTR_CURSOR (1 << 0)
6363+#define ATTR_REVERSE (1 << 1)
6464+#define ATTR_BOLD (1 << 2)
6565+#define ATTR_UNDERLINE (1 << 3)
6666+6767+/* for printf */
6868+#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
6969+#define BYTE_TO_BINARY(byte) \
7070+ (byte & 0x80 ? '1' : '0'), \
7171+ (byte & 0x40 ? '1' : '0'), \
7272+ (byte & 0x20 ? '1' : '0'), \
7373+ (byte & 0x10 ? '1' : '0'), \
7474+ (byte & 0x08 ? '1' : '0'), \
7575+ (byte & 0x04 ? '1' : '0'), \
7676+ (byte & 0x02 ? '1' : '0'), \
7777+ (byte & 0x01 ? '1' : '0')
7878+7979+8080+/* for debugging access from asm */
8181+extern unsigned char debug0;
8282+extern unsigned char debug1;
8383+extern unsigned char debug2;
8484+extern unsigned char debug3;
8585+extern unsigned char debug4;
8686+8787+8888+/* crt0.s */
8989+extern void exit(void);
9090+extern void powerdown_mode(void);
9191+extern void new_mail(unsigned char on);
9292+extern void reboot(void);
9393+extern void delay(unsigned int millis);
9494+extern void blink(unsigned int millis);
9595+extern unsigned char read_port(unsigned char port);
9696+9797+9898+/* putchar.s */
9999+extern unsigned char cursorx;
100100+extern unsigned char cursory;
101101+extern unsigned char putchar_sgr;
102102+extern unsigned char putchar_quick;
103103+extern unsigned char *font_addr;
104104+extern void lcd_cas(unsigned char col);
105105+extern void lcd_sleep(void);
106106+extern void lcd_wake(void);
107107+extern void uncursor(void);
108108+extern void recursor(void);
109109+extern void clear_screen(void);
110110+extern void clear_screen_bufs(void);
111111+extern void redraw_screen(void);
112112+extern void scroll_lcd_half(void);
113113+extern void clear_lcd_half(void);
114114+extern void stamp_char(unsigned char row, unsigned char col);
115115+extern void putchar_attr(unsigned char row, unsigned char col, unsigned char c,
116116+ unsigned char attr);
117117+118118+119119+/* getchar.s */
120120+extern unsigned char getscancode(unsigned char *charbuffer);
121121+extern int getkey(void);
122122+extern int peekkey(void);
123123+124124+125125+/* wifi.s */
126126+void wifi_init(void);
127127+extern int wifi_write(char);
128128+extern int wifi_read(void);
129129+130130+#endif
+87
lib/mailstation.inc
···11+; vim:syntax=z8a:ts=8:sw=8
22+;
33+; MailStation example program
44+; Copyright (c) 2019-2021 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+ ; these are defined in addrs-*, either of which is linked into the
2020+ ; build based on where we're running from
2121+ .globl RUN_ADDR
2222+ .globl RUN_DEVICE
2323+ .globl RUN_PAGE
2424+ .globl SLOT_ADDR
2525+ .globl SLOT_DEVICE
2626+ .globl SLOT_PAGE
2727+2828+ ; per-firmware version port shadow variables
2929+ .globl p2shadow
3030+ .globl p3shadow
3131+ .globl p28shadow
3232+3333+ ; functions
3434+ .equ get_keycode_from_buffer,#0x0a9a
3535+ .equ lcd_buf_to_screen, #0x2473
3636+3737+ ; lcd bit storage buffer, copied with lcd_buf_to_screen
3838+ .equ lcd_buf, #0xc010
3939+ .equ lcd_buf_end, #lcd_buf + ((LCD_WIDTH * LCD_HEIGHT) / 8) - 1
4040+4141+ .equ _obuf, #0xf500
4242+ .equ _obuf_pos, #0xf704
4343+4444+ ; some constants from mailstion.h
4545+ .equ LCD_WIDTH, #160 * 2 ; 320
4646+ .equ LCD_HEIGHT, #128
4747+ .equ LCD_COL_GROUPS, #20
4848+ .equ LCD_COL_GROUP_WIDTH, #8
4949+ .equ FONT_WIDTH, #5
5050+ .equ FONT_HEIGHT, #8
5151+ .equ LCD_COLS, #LCD_WIDTH / FONT_WIDTH ; 64
5252+ .equ LCD_ROWS, #LCD_HEIGHT / FONT_HEIGHT ; 16
5353+ .equ TEXT_ROWS, #LCD_ROWS - 1 ; 15
5454+5555+ .equ DEVICE_RAM, #0x01
5656+ .equ DEVICE_LCD_LEFT, #0x02
5757+ .equ DEVICE_DATAFLASH, #0x03
5858+ .equ DEVICE_LCD_RIGHT, #0x04
5959+ .equ DEVICE_MODEM, #0x05
6060+6161+ ; addressing the LCD once it's loaded in SLOT_ADDR
6262+ .equ LCD_START, #SLOT_ADDR + 0x0038
6363+6464+ .equ ATTR_BIT_CURSOR, #0
6565+ .equ ATTR_BIT_REVERSE, #1
6666+ .equ ATTR_BIT_BOLD, #2
6767+ .equ ATTR_BIT_UNDERLINE, #3
6868+6969+ .equ ATTR_CURSOR, #(1 << ATTR_CURSOR)
7070+ .equ ATTR_REVERSE, #(1 << ATTR_REVERSE)
7171+ .equ ATTR_BOLD, #(1 << ATTR_BOLD)
7272+ .equ ATTR_UNDERLINE, #(1 << ATTR_UNDERLINE)
7373+7474+ .globl _saved_cursorx
7575+ .globl _saved_cursory
7676+ .globl _delay
7777+7878+ ; settings
7979+ .globl _obuf
8080+ .globl _obuf_pos
8181+8282+ ; debug variables
8383+ .globl _debug0
8484+ .globl _debug1
8585+ .globl _debug2
8686+ .globl _debug3
8787+ .globl _debug4
···11+; vim:syntax=z8a:ts=8
22+;
33+; putchar for 5x8 font
44+; Copyright (c) 2019-2021 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+ .module putchar
2020+2121+ .include "mailstation.inc"
2222+2323+ ; screen contents (characters) array in upper memory
2424+ .equ _screenbuf, #0xc000
2525+ .equ _screenbufend, #0xc3ff
2626+2727+ ; per-character attributes array in upper memory
2828+ .equ _screenattrs, #0xc400
2929+ .equ _screenattrsend, #0xc7ff
3030+3131+ .area _DATA
3232+3333+font_data::
3434+ .include "font/spleen-5x8.inc"
3535+3636+ ; lookup table for putchar
3737+ ; left-most 5 bits are col group for lcd_cas
3838+ ; last 3 bits are offset into col group
3939+cursorx_lookup_data::
4040+ .include "cursorx_lookup.inc"
4141+4242+_cursorx:: ; cursor x position, 0-indexed
4343+ .db #0
4444+_cursory:: ; cursor y position, 0-indexed
4545+ .db #0
4646+_saved_cursorx:: ; cursor x position, 0-indexed
4747+ .db #0
4848+_saved_cursory:: ; cursor y position, 0-indexed
4949+ .db #0
5050+_putchar_sgr:: ; current SGR for putchar()
5151+ .db #0
5252+5353+5454+ .area _CODE
5555+5656+; void lcd_cas(unsigned char col)
5757+; enable CAS, address the LCD column col (in h), and disable CAS
5858+_lcd_cas::
5959+ push ix
6060+ ld ix, #0
6161+ add ix, sp
6262+ push de
6363+ push hl
6464+ ld hl, (p2shadow)
6565+ ld a, (hl)
6666+ and #0b11110111 ; CAS(0) - turn port2 bit 3 off
6767+ ld (hl), a
6868+ out (#0x02), a ; write p2shadow to port2
6969+ ld de, #LCD_START
7070+ ld a, 4(ix)
7171+ ld (de), a ; write col argument
7272+ ld a, (hl)
7373+ or #0b00001000 ; CAS(1) - turn port2 bit 3 on
7474+ ld (hl), a
7575+ out (#0x02), a
7676+ pop hl
7777+ pop de
7878+ ld sp, ix
7979+ pop ix
8080+ ret
8181+8282+8383+; void clear_screen(void)
8484+_clear_screen::
8585+ di
8686+ push hl
8787+ in a, (#SLOT_DEVICE)
8888+ ld h, a
8989+ in a, (#SLOT_PAGE)
9090+ ld l, a
9191+ push hl
9292+ ld a, #DEVICE_LCD_RIGHT
9393+ out (#SLOT_DEVICE), a
9494+ call _clear_lcd_half
9595+ ld a, #DEVICE_LCD_LEFT
9696+ out (#SLOT_DEVICE), a
9797+ call _clear_lcd_half
9898+ pop hl
9999+ ld a, h
100100+ out (#SLOT_DEVICE), a
101101+ ld a, l
102102+ out (#SLOT_PAGE), a
103103+ pop hl
104104+ ei
105105+ ret
106106+107107+_clear_screen_bufs::
108108+ di
109109+ push bc
110110+ push de
111111+ push hl
112112+ xor a
113113+ ld (_cursorx), a
114114+ ld (_cursory), a
115115+ ld (_saved_cursorx), a
116116+ ld (_saved_cursory), a
117117+ ld (_putchar_sgr), a
118118+zero_screenbuf:
119119+ ld hl, #_screenbuf
120120+ ld de, #_screenbuf + 1
121121+ ld bc, #_screenbufend - _screenbuf
122122+ ld (hl), #' '
123123+ ldir
124124+zero_screenattrs:
125125+ ld hl, #_screenattrs
126126+ ld de, #_screenattrs + 1
127127+ ld bc, #_screenattrsend - _screenattrs
128128+ ld (hl), #0
129129+ ldir
130130+clear_screen_out:
131131+ pop hl
132132+ pop de
133133+ pop bc
134134+ ei
135135+ ret
136136+137137+138138+; void clear_lcd_half(void)
139139+; zero out the current LCD module (must already be in SLOT_DEVICE)
140140+; from v2.54 firmware at 0x2490
141141+_clear_lcd_half::
142142+ push bc
143143+ push de
144144+ ld b, #20 ; do 20 columns total
145145+clear_lcd_column:
146146+ ld h, #0
147147+ ld a, b
148148+ dec a ; columns are 0-based
149149+ ld l, a
150150+ push hl
151151+ call _lcd_cas
152152+ pop hl
153153+ push bc ; preserve our column counter
154154+ ld hl, #LCD_START
155155+ ld (hl), #0 ; zero out hl, then copy it to de
156156+ ld de, #LCD_START + 1 ; de will always be the next line
157157+ ld bc, #128 - 1 ; iterate (LCD_HEIGHT - 1) times
158158+ ldir ; ld (de), (hl), bc-- until 0
159159+ pop bc ; restore column counter
160160+ djnz clear_lcd_column ; column--, if not zero keep going
161161+clear_done:
162162+ pop de
163163+ pop bc
164164+ ret
165165+166166+167167+; void redraw_screen(void)
168168+_redraw_screen::
169169+ push bc
170170+ push de
171171+ push hl
172172+ ld b, #0
173173+redraw_rows:
174174+ ld d, b ; store rows in d
175175+ ld b, #0
176176+redraw_cols:
177177+ push bc ; XXX figure out what is corrupting
178178+ push de ; bc and de in stamp_char, these shouldn't be needed
179179+ push hl
180180+ ld h, #0 ; cols
181181+ ld l, b
182182+ push hl
183183+ ld h, #0 ; rows
184184+ ld l, d
185185+ push hl
186186+ call _stamp_char
187187+ pop hl
188188+ pop hl
189189+ pop hl
190190+ pop de
191191+ pop bc
192192+redraw_cols_next:
193193+ inc hl
194194+ inc b
195195+ ld a, b
196196+ cp #LCD_COLS
197197+ jr nz, redraw_cols
198198+ ld b, d
199199+ inc b
200200+ ld a, b
201201+ cp #LCD_ROWS
202202+ jr nz, redraw_rows
203203+redraw_screen_out:
204204+ pop hl
205205+ pop de
206206+ pop bc
207207+ ret
208208+209209+210210+; void scroll_lcd(void)
211211+; scroll entire screen up by FONT_HEIGHT rows, minus statusbar
212212+_scroll_lcd::
213213+ di
214214+ push bc
215215+ push de
216216+ push hl
217217+ in a, (#SLOT_DEVICE)
218218+ ld h, a
219219+ in a, (#SLOT_PAGE)
220220+ ld l, a
221221+ push hl
222222+ ld a, #DEVICE_LCD_LEFT
223223+ out (#SLOT_DEVICE), a
224224+ call _scroll_lcd_half
225225+ ld a, #DEVICE_LCD_RIGHT
226226+ out (#SLOT_DEVICE), a
227227+ call _scroll_lcd_half
228228+ pop hl
229229+ ld a, h
230230+ out (#SLOT_DEVICE), a
231231+ ld a, l
232232+ out (#SLOT_PAGE), a
233233+shift_bufs:
234234+ ld b, #0
235235+screenbuf_shift_loop:
236236+ ld h, b
237237+ ld l, #0
238238+ call screenbuf_offset
239239+ ld de, #_screenbuf
240240+ add hl, de ; hl = screenbuf[b * LCD_COLS]
241241+ push hl
242242+ ld de, #LCD_COLS
243243+ add hl, de ; hl += LCD_COLS
244244+ pop de ; de = screenbuf[b * LCD_COLS]
245245+ push bc
246246+ ld bc, #LCD_COLS
247247+ ldir ; ld (de), (hl), de++, hl++, bc--
248248+ pop bc
249249+ inc b
250250+ ld a, b
251251+ cp #TEXT_ROWS - 1
252252+ jr nz, screenbuf_shift_loop
253253+screenattrs_shift:
254254+ ld b, #0
255255+screenattrs_shift_loop:
256256+ ld h, b
257257+ ld l, #0
258258+ call screenbuf_offset
259259+ ld de, #_screenattrs
260260+ add hl, de ; hl = screenattrs[b * LCD_COLS]
261261+ push hl
262262+ ld de, #LCD_COLS
263263+ add hl, de
264264+ pop de
265265+ push bc
266266+ ld bc, #LCD_COLS
267267+ ldir
268268+ pop bc
269269+ inc b
270270+ ld a, b
271271+ cp #TEXT_ROWS - 1
272272+ jr nz, screenattrs_shift_loop
273273+last_row_zero:
274274+ ld a, #TEXT_ROWS - 1
275275+ ld h, a
276276+ ld l, #0
277277+ call screenbuf_offset
278278+ ld de, #_screenbuf
279279+ add hl, de
280280+ ld d, #0
281281+ ld e, #LCD_COLS - 1
282282+ add hl, de
283283+ ld b, #LCD_COLS
284284+ ld a, (_putchar_sgr)
285285+last_row_zero_loop:
286286+ ld (hl), #' '
287287+ dec hl
288288+ djnz last_row_zero_loop
289289+scroll_lcd_out:
290290+ pop hl
291291+ pop de
292292+ pop bc
293293+ ei
294294+ ret
295295+296296+297297+; void scroll_lcd_half(void)
298298+; scroll current LCD module up by FONT_HEIGHT rows, minus statusbar and
299299+; zero out the last line of text (only to the LCD)
300300+_scroll_lcd_half::
301301+ push ix
302302+ ld ix, #0
303303+ add ix, sp
304304+ push bc
305305+ push de
306306+ push hl
307307+ ; alloc 2 bytes on the stack for local storage
308308+ push hl
309309+ ld a, #LCD_HEIGHT - (FONT_HEIGHT * 2) ; iterations of pixel row moves
310310+scroll_init:
311311+ ld -1(ix), a ; store iterations
312312+ ld b, #20 ; do 20 columns total
313313+scroll_lcd_column:
314314+ ld -2(ix), b ; store new column counter
315315+ ld a, b
316316+ sub #1 ; columns are 0-based
317317+ ld h, #0
318318+ ld l, a
319319+ push hl
320320+ call _lcd_cas
321321+ pop hl
322322+scroll_rows:
323323+ ld b, #0
324324+ ld c, -1(ix) ; bc = row counter
325325+ ld hl, #LCD_START + 8 ; start of next line
326326+ ld de, #LCD_START
327327+ ldir ; ld (de), (hl), bc-- until 0
328328+scroll_zerolast:
329329+ ld hl, #LCD_START
330330+ ld d, #0
331331+ ld e, -1(ix)
332332+ add hl, de
333333+ ld b, #FONT_HEIGHT
334334+scroll_zerolastloop: ; 8 times: zero hl, hl++
335335+ ld (hl), #0
336336+ inc hl
337337+ djnz scroll_zerolastloop
338338+ ld b, -2(ix)
339339+ djnz scroll_lcd_column ; column--, if not zero keep going
340340+ pop hl
341341+ pop de
342342+ pop bc
343343+ ld sp, ix
344344+ pop ix
345345+ ret
346346+347347+348348+; address of screenbuf or screenattrs offset for a row/col in hl, returns in hl
349349+screenbuf_offset:
350350+ push bc
351351+ push de
352352+ ; uses hl
353353+ ex de, hl
354354+ ld hl, #0
355355+ ld a, d ; row
356356+ cp #0
357357+ jr z, multiply_srow_out ; only add rows if > 0
358358+ ld bc, #LCD_COLS
359359+multiply_srow:
360360+ add hl, bc
361361+ dec a
362362+ cp #0
363363+ jr nz, multiply_srow
364364+multiply_srow_out:
365365+ ld d, #0 ; col in e
366366+ add hl, de ; hl = (row * LCD_COLS) + col
367367+ pop de
368368+ pop bc
369369+ ret ; hl
370370+371371+372372+; void stamp_char(unsigned int row, unsigned int col)
373373+; row at 4(ix), col at 6(ix)
374374+_stamp_char::
375375+ push ix
376376+ ld ix, #0
377377+ add ix, sp
378378+ push bc
379379+ push de
380380+ push hl
381381+ ld hl, #-15 ; stack bytes for local storage
382382+ add hl, sp
383383+ ld sp, hl
384384+ in a, (#SLOT_DEVICE)
385385+ ld -3(ix), a ; stack[-3] = old slot device
386386+ in a, (#SLOT_PAGE)
387387+ ld -4(ix), a ; stack[-4] = old slot page
388388+find_char:
389389+ ld h, 4(ix)
390390+ ld l, 6(ix)
391391+ call screenbuf_offset
392392+ push hl
393393+ ld de, #_screenbuf
394394+ add hl, de ; hl = screenbuf[(row * LCD_COLS) + col]
395395+ ld a, (hl)
396396+ ld -5(ix), a ; stack[-5] = character to stamp
397397+ pop hl
398398+ ld de, #_screenattrs
399399+ add hl, de ; hl = screenattrs[(row * LCD_COLS) + col]
400400+ ld a, (hl)
401401+ ld -6(ix), a ; stack[-6] = character attrs
402402+calc_font_data_base:
403403+ ld h, #0
404404+ ld l, -5(ix) ; char
405405+ add hl, hl ; hl = char * FONT_HEIGHT (8)
406406+ add hl, hl
407407+ add hl, hl
408408+ ld de, #font_data
409409+ add hl, de
410410+ ld -7(ix), l
411411+ ld -8(ix), h ; stack[-8,-7] = char font data base addr
412412+calc_char_cell_base:
413413+ ld h, #0
414414+ ld l, 4(ix) ; row
415415+ add hl, hl
416416+ add hl, hl
417417+ add hl, hl ; hl = row * FONT_HEIGHT (8)
418418+ ld de, #LCD_START
419419+ add hl, de ; hl = 4038 + (row * FONT_HEIGHT)
420420+ ld -9(ix), l
421421+ ld -10(ix), h ; stack[-10,-9] = lcd char cell base
422422+fetch_from_table:
423423+ ld a, 6(ix) ; col
424424+ ld hl, #cursorx_lookup_data
425425+ ld b, #0
426426+ ld c, a
427427+ add hl, bc
428428+ ld b, (hl)
429429+ ld a, b
430430+pluck_col_group:
431431+ and #0b11111000 ; upper 5 bits are col group
432432+ srl a
433433+ srl a
434434+ srl a
435435+ ld -11(ix), a ; stack[-11] = col group
436436+pluck_offset:
437437+ ld a, b
438438+ and #0b00000111 ; lower 3 bits are offset
439439+ ld -12(ix), a ; stack[-12] = offset
440440+ ld -15(ix), #0 ; stack[-15] = previous lcd col
441441+ ld d, #FONT_HEIGHT ; for (row = FONT_HEIGHT; row >= 0; row--)
442442+next_char_row:
443443+ ld a, d
444444+ dec a
445445+ ld h, -8(ix) ; char font data base
446446+ ld l, -7(ix)
447447+ ld b, #0
448448+ ld c, a
449449+ add hl, bc
450450+ ld a, (hl) ; font_addr + (char * FONT_HEIGHT) + row
451451+ ld b, -6(ix)
452452+ bit #ATTR_BIT_REVERSE, b
453453+ jr nz, reverse
454454+ bit #ATTR_BIT_CURSOR, b
455455+ jr nz, reverse
456456+ jr not_reverse
457457+reverse:
458458+ cpl ; flip em
459459+ and #0b00011111 ; mask off bits not within FONT_WIDTH
460460+not_reverse:
461461+ ld -13(ix), a ; stack[-13] = working font data
462462+ ld a, -6(ix)
463463+ bit #ATTR_BIT_UNDERLINE, a
464464+ jr z, not_underline
465465+ ld a, d
466466+ cp #FONT_HEIGHT
467467+ jr nz, not_underline
468468+underline:
469469+ ld -13(ix), #0xff
470470+not_underline:
471471+ ld a, 6(ix) ; col
472472+ cp #LCD_COLS / 2 ; assume a char never spans both LCD sides
473473+ jr nc, rightside
474474+leftside:
475475+ ld a, #DEVICE_LCD_LEFT
476476+ jr swap_lcd
477477+rightside:
478478+ ld a, #DEVICE_LCD_RIGHT
479479+swap_lcd:
480480+ out (#SLOT_DEVICE), a
481481+ ld e, #FONT_WIDTH ; for (col = FONT_WIDTH; col > 0; col--)
482482+next_char_col: ; inner loop, each col of each row
483483+ ld -14(ix), #0b00011111 ; font data mask that will get shifted
484484+determine_cas:
485485+ ld c, #0
486486+ ld b, -11(ix) ; col group
487487+ ld a, -12(ix) ; bit offset
488488+ add #FONT_WIDTH
489489+ sub e ; if offset+(5-col) is >= 8, advance col
490490+ cp #LCD_COL_GROUP_WIDTH
491491+ jr c, skip_advance ; if a >= 8, advance (dec b)
492492+ dec b
493493+ ld c, -12(ix) ; bit offset
494494+ ld a, #LCD_COL_GROUP_WIDTH
495495+ sub c
496496+ ld c, a ; c = number of right shifts
497497+skip_advance:
498498+do_lcd_cas:
499499+ ld a, -15(ix) ; previous lcd cas
500500+ cp b
501501+ jr z, prep_right_shift
502502+ ld h, #0
503503+ ld l, b
504504+ push hl
505505+ call _lcd_cas
506506+ pop hl
507507+ ld -15(ix), b ; store lcd col for next round
508508+ ; if this character doesn't fit entirely in one lcd column, we need to
509509+ ; span two of them and on the left one, shift font data and masks right
510510+ ; to remove right-most bits that will be on the next column
511511+prep_right_shift:
512512+ ld a, c
513513+ cp #0
514514+ jr z, prep_left_shift
515515+ ld b, c
516516+ ld c, -14(ix) ; matching mask 00011111
517517+ ld a, -13(ix) ; load font data like 00010101
518518+right_shift:
519519+ srl a ; shift font data right #b times
520520+ srl c ; and mask to match
521521+ djnz right_shift ; -> 10101000
522522+ ld -14(ix), c
523523+ jr done_left_shift
524524+prep_left_shift:
525525+ ld c, -14(ix) ; mask
526526+ ld a, -12(ix) ; (bit offset) times, shift font data
527527+ cp #0
528528+ ld b, a
529529+ ld a, -13(ix) ; read new font data
530530+ jr z, done_left_shift
531531+left_shift:
532532+ sla a
533533+ sla c
534534+ djnz left_shift
535535+done_left_shift:
536536+ ld b, a
537537+ ld a, c
538538+ cpl
539539+ ld -14(ix), a ; store inverted mask
540540+ ld a, b
541541+read_lcd_data:
542542+ ld h, -10(ix)
543543+ ld l, -9(ix)
544544+ ld b, a
545545+ ld a, d
546546+ dec a
547547+ ld c, a
548548+ ld a, b
549549+ ld b, #0
550550+ add hl, bc ; hl = 4038 + (row * FONT_HEIGHT) + row - 1
551551+ ld b, a ; store new font data
552552+ ld a, (hl) ; read existing cell data
553553+ and -14(ix) ; mask off new char cell
554554+ or b ; combine data into cell
555555+ ld (hl), a
556556+ dec e
557557+ jp nz, next_char_col
558558+ dec d
559559+ jp nz, next_char_row
560560+stamp_char_out:
561561+ ld a, -3(ix) ; restore old slot device
562562+ out (#SLOT_DEVICE), a
563563+ ld a, -4(ix) ; restore old slot page
564564+ out (#SLOT_PAGE), a
565565+ ld hl, #15 ; remove stack bytes
566566+ add hl, sp
567567+ ld sp, hl
568568+ pop hl
569569+ pop de
570570+ pop bc
571571+ ld sp, ix
572572+ pop ix
573573+ ret
574574+575575+576576+; void uncursor(void)
577577+; remove cursor attribute from old cursor position
578578+_uncursor::
579579+ push de
580580+ push hl
581581+ ld a, (_cursory)
582582+ ld h, a
583583+ ld a, (_cursorx)
584584+ ld l, a
585585+ call screenbuf_offset
586586+ ld de, #_screenattrs
587587+ add hl, de ; screenattrs[(cursory * TEXT_COLS) + cursorx]
588588+ ld a, (hl)
589589+ res #ATTR_BIT_CURSOR, a ; &= ~(ATTR_CURSOR)
590590+ ld (hl), a
591591+ ld a, (_cursorx)
592592+ ld l, a
593593+ push hl
594594+ ld a, (_cursory)
595595+ ld l, a
596596+ push hl
597597+ call _stamp_char
598598+ pop hl
599599+ pop hl
600600+ pop hl
601601+ pop de
602602+ ret
603603+604604+; void recursor(void)
605605+; force-set cursor attribute
606606+_recursor::
607607+ push de
608608+ push hl
609609+ ld a, (_cursory)
610610+ ld h, a
611611+ ld a, (_cursorx)
612612+ ld l, a
613613+ call screenbuf_offset
614614+ ld de, #_screenattrs
615615+ add hl, de ; screenattrs[(cursory * TEXT_COLS) + cursorx]
616616+ ld a, (hl)
617617+ set #ATTR_BIT_CURSOR, a
618618+ ld (hl), a
619619+ pop hl
620620+ pop de
621621+ ret
622622+623623+624624+; int putchar(int c)
625625+_putchar::
626626+ push ix
627627+ ld ix, #0
628628+ add ix, sp ; char to print is at 4(ix)
629629+ push de
630630+ push hl
631631+ call _uncursor
632632+ ld a, 4(ix)
633633+ cp #'\b' ; backspace
634634+ jr nz, not_backspace
635635+backspace:
636636+ ld a, (_cursorx)
637637+ cp #0
638638+ jr nz, cursorx_not_zero
639639+ ld a, (_cursory)
640640+ cp #0
641641+ jp z, putchar_fastout ; cursorx/y at 0,0, nothing to do
642642+ dec a
643643+ ld (_cursory), a ; cursory--
644644+ ld a, #LCD_COLS - 2
645645+ ld (_cursorx), a
646646+ jp putchar_draw_cursor
647647+cursorx_not_zero:
648648+ dec a
649649+ ld (_cursorx), a ; cursorx--;
650650+ jp putchar_draw_cursor
651651+not_backspace:
652652+ cp #'\r'
653653+ jr nz, not_cr
654654+ xor a
655655+ ld (_cursorx), a ; cursorx = 0
656656+ jr not_crlf
657657+not_cr:
658658+ cp #'\n'
659659+ jr nz, not_crlf
660660+ xor a
661661+ ld (_cursorx), a ; cursorx = 0
662662+ ld a, (_cursory)
663663+ inc a
664664+ ld (_cursory), a ; cursory++
665665+not_crlf:
666666+ ld a, (_cursorx)
667667+ cp #LCD_COLS
668668+ jr c, not_longer_text_cols ; cursorx < TEXT_COLS
669669+ xor a
670670+ ld (_cursorx), a ; cursorx = 0
671671+ ld a, (_cursory)
672672+ inc a
673673+ ld (_cursory), a
674674+not_longer_text_cols:
675675+ ld a, (_cursory)
676676+ cp #TEXT_ROWS
677677+ jr c, scroll_out
678678+scroll_up_screen:
679679+ call _scroll_lcd
680680+ xor a
681681+ ld (_cursorx), a
682682+ ld a, #TEXT_ROWS - 1
683683+ ld (_cursory), a ; cursory = TEXT_ROWS - 1
684684+scroll_out:
685685+ ld a, 4(ix)
686686+ cp a, #'\r'
687687+ jr z, cr_or_lf
688688+ cp a, #'\n'
689689+ jr z, cr_or_lf
690690+ jr store_char_in_buf
691691+cr_or_lf:
692692+ jp putchar_draw_cursor
693693+store_char_in_buf:
694694+ ld a, (_cursory)
695695+ ld h, a
696696+ ld a, (_cursorx)
697697+ ld l, a
698698+ call screenbuf_offset
699699+ push hl
700700+ ld de, #_screenbuf
701701+ add hl, de ; hl = screenbuf[(cursory * LCD_COLS) + cursorx]
702702+ ld a, 4(ix)
703703+ ld (hl), a ; store character
704704+ pop hl
705705+ ld de, #_screenattrs
706706+ add hl, de ; hl = screenattrs[(cursory * LCD_COLS) + cursorx]
707707+ ld a, (_putchar_sgr)
708708+ ld (hl), a ; = putchar_sgr
709709+ ld a, (_cursorx)
710710+ ld l, a
711711+ push hl
712712+ ld a, (_cursory)
713713+ ld l, a
714714+ push hl
715715+ call _stamp_char
716716+ pop hl
717717+ pop hl
718718+advance_cursorx:
719719+ ld a, (_cursorx)
720720+ inc a
721721+ ld (_cursorx), a
722722+ cp #LCD_COLS ; if (cursorx >= LCD_COLS)
723723+ jr c, putchar_draw_cursor
724724+ xor a
725725+ ld (_cursorx), a
726726+ ld a, (_cursory)
727727+ inc a
728728+ ld (_cursory), a
729729+check_cursory:
730730+ cp #TEXT_ROWS ; and if (cursory >= TEXT_ROWS)
731731+ jr c, putchar_draw_cursor
732732+ call _scroll_lcd
733733+ ld a, #TEXT_ROWS - 1
734734+ ld (_cursory), a ; cursory = TEXT_ROWS - 1
735735+putchar_draw_cursor:
736736+ ld a, (_cursory)
737737+ ld h, a
738738+ ld a, (_cursorx)
739739+ ld l, a
740740+ call screenbuf_offset
741741+ ld de, #_screenattrs
742742+ add hl, de ; hl = screenattrs[(cursory * LCD_COLS) + cursorx]
743743+ ld a, (hl) ; read existing attrs
744744+ set #ATTR_BIT_CURSOR, a
745745+ ld (hl), a ; = putchar_sgr | ATTR_CURSOR
746746+ ld a, (_cursorx)
747747+ ld l, a
748748+ push hl
749749+ ld a, (_cursory)
750750+ ld l, a
751751+ push hl
752752+ call _stamp_char
753753+ pop hl
754754+ pop hl
755755+putchar_fastout:
756756+ pop hl
757757+ pop de
758758+ ld sp, ix
759759+ pop ix
760760+ ret
761761+762762+763763+; void putchar_attr(unsigned char row, unsigned char col, char c, char attr)
764764+; directly manipulates screenbuf/attrs without scrolling or length checks
765765+; row at 4(ix), col at 5(ix), c at 6(ix), attr at 7(ix)
766766+_putchar_attr::
767767+ push ix
768768+ ld ix, #0
769769+ add ix, sp
770770+ push de
771771+ push hl
772772+store_char:
773773+ ld h, 4(ix)
774774+ ld l, 5(ix)
775775+ call screenbuf_offset
776776+ push hl
777777+ ld de, #_screenbuf
778778+ add hl, de ; screenbuf[(row * TEXT_COLS) + col]
779779+ ld a, 6(ix)
780780+ ld (hl), a
781781+store_attrs:
782782+ pop hl
783783+ ld de, #_screenattrs
784784+ add hl, de ; screenattrs[(row * TEXT_COLS) + col]
785785+ ld a, 7(ix)
786786+ ld (hl), a
787787+ ld l, 5(ix)
788788+ push hl
789789+ ld l, 4(ix)
790790+ push hl
791791+ call _stamp_char
792792+ pop hl
793793+ pop hl
794794+ pop hl
795795+ pop de
796796+ ld sp, ix
797797+ pop ix
798798+ ret
···11+#!/usr/bin/env ruby
22+#
33+# generate cursorx lookup table using 5 bits for col group and 3 for offset
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+# pixels: 01234567012345670123456801234567012345670123456701234567012345670
2020+# col group: | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 |
2121+# font cell: .....11111.....11111.....11111.....11111.....11111.....11111.....
2222+2323+File.open("#{__dir__}/../cursorx_lookup.inc", "w+") do |f|
2424+ f.puts "; AUTOMATICALLY GENERATED FILE - see tools/generate_cursorx_lookup.rb"
2525+2626+ pcol = 0
2727+ 64.times do |x|
2828+ col_group = 20 - (pcol / 8) - 1
2929+ off = pcol % 8
3030+3131+ v = sprintf("%05b%03b", col_group, off).to_i(2)
3232+3333+ f.puts "\t.db #0x#{sprintf("%02x", v)}\t\t\t; #{sprintf("%08b", v)} - col group #{col_group}, offset #{off}"
3434+3535+ pcol += 5
3636+3737+ if pcol == 160
3838+ pcol = 0
3939+ end
4040+ end
4141+end
+249
lib/tools/generate_scancodes.rb
···11+#!/usr/bin/env ruby
22+#
33+# generate scancode lookup tables
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+META_KEY_BEGIN = 200
2020+2121+KEYS = [
2222+ :MAIN_MENU,
2323+ :BACK,
2424+ :PRINT,
2525+ :F1,
2626+ :F2,
2727+ :F3,
2828+ :F4,
2929+ :F5,
3030+ :POWER,
3131+ :SIZE,
3232+ :SPELLING,
3333+ :EMAIL,
3434+ :PAGE_UP,
3535+ :PAGE_DOWN,
3636+ :CAPS_LOCK,
3737+ :LEFT_SHIFT,
3838+ :RIGHT_SHIFT,
3939+ :FN,
4040+ :UP,
4141+ :DOWN,
4242+ :LEFT,
4343+ :RIGHT,
4444+]
4545+4646+META_KEY_NONE = 255
4747+4848+UPPERCASES = {
4949+ "`" => "~",
5050+ "1" => "!",
5151+ "2" => "@",
5252+ "3" => "#",
5353+ "4" => "$",
5454+ "5" => "%",
5555+ "6" => "^",
5656+ "7" => "&",
5757+ "8" => "*",
5858+ "9" => "(",
5959+ "0" => ")",
6060+ "-" => "_",
6161+ "=" => "+",
6262+ "\\" => "|",
6363+ "[" => "{",
6464+ "]" => "}",
6565+ ";" => ":",
6666+ "'" => "\"",
6767+ "," => "<",
6868+ "." => ">",
6969+ "/" => "?",
7070+}
7171+7272+CONTROLS = {
7373+ "a" => 1,
7474+ "b" => 2,
7575+ "c" => 3,
7676+ "d" => 4,
7777+ "e" => 5,
7878+ "f" => 6,
7979+ "g" => 7,
8080+ "h" => 8,
8181+ "i" => 9,
8282+ "j" => 10,
8383+ "k" => 11,
8484+ "l" => 12,
8585+ "m" => 13,
8686+ "n" => 14,
8787+ "o" => 15,
8888+ "p" => 16,
8989+ "q" => 17,
9090+ "r" => 18,
9191+ "s" => 19,
9292+ "t" => 20,
9393+ "u" => 21,
9494+ "v" => 22,
9595+ "w" => 23,
9696+ "x" => 24,
9797+ "y" => 25,
9898+ "z" => 26,
9999+ "3" => 27,
100100+ "[" => 27,
101101+ "4" => 28,
102102+ "\\" => 28,
103103+ "5" => 29,
104104+ "]" => 29,
105105+ "6" => 30,
106106+ "7" => 31,
107107+ "-" => 31,
108108+ "/" => 31,
109109+ "8" => 127,
110110+}
111111+112112+SCANCODES = {
113113+ 0 => :MAIN_MENU,
114114+ 1 => :BACK,
115115+ 2 => :PRINT,
116116+ 3 => :F1,
117117+ 4 => :F2,
118118+ 5 => :F3,
119119+ 6 => :F4,
120120+ 7 => :F5,
121121+ 15 => :POWER,
122122+ 19 => "@",
123123+ 20 => :SIZE,
124124+ 21 => :SPELLING,
125125+ 22 => :EMAIL,
126126+ 23 => :PAGE_UP,
127127+ 32 => "`",
128128+ 33 => "1",
129129+ 34 => "2",
130130+ 35 => "3",
131131+ 36 => "4",
132132+ 37 => "5",
133133+ 38 => "6",
134134+ 39 => "7",
135135+ 48 => "8",
136136+ 49 => "9",
137137+ 50 => "0",
138138+ 51 => "-",
139139+ 52 => "=",
140140+ 53 => "\b", # backspace
141141+ 54 => "\\",
142142+ 55 => :PAGE_DOWN,
143143+ 64 => "\t",
144144+ 65 => "q",
145145+ 66 => "w",
146146+ 67 => "e",
147147+ 68 => "r",
148148+ 69 => "t",
149149+ 70 => "y",
150150+ 71 => "u",
151151+ 80 => "i",
152152+ 81 => "o",
153153+ 82 => "p",
154154+ 83 => "[",
155155+ 84 => "]",
156156+ 85 => ";",
157157+ 86 => "'",
158158+ 87 => "\n",
159159+ 96 => :CAPS_LOCK,
160160+ 97 => "a",
161161+ 98 => "s",
162162+ 99 => "d",
163163+ 100 => "f",
164164+ 101 => "g",
165165+ 102 => "h",
166166+ 103 => "j",
167167+ 112 => "k",
168168+ 113 => "l",
169169+ 114 => ",",
170170+ 115 => ".",
171171+ 116 => "/",
172172+ 117 => :UP,
173173+ 118 => :DOWN,
174174+ 119 => :RIGHT,
175175+ 128 => :LEFT_SHIFT,
176176+ 129 => "z",
177177+ 130 => "x",
178178+ 131 => "c",
179179+ 132 => "v",
180180+ 133 => "b",
181181+ 134 => "n",
182182+ 135 => "m",
183183+ 144 => :FN,
184184+ 147 => " ",
185185+ 150 => :RIGHT_SHIFT,
186186+ 151 => :LEFT,
187187+}
188188+189189+File.open("#{__dir__}/../scancodes.inc", "w+") do |scf|
190190+ scf.puts "; AUTOMATICALLY GENERATED FILE - see tools/generate_scancodes.rb"
191191+ scf.puts "\t.equ\tMETA_KEY_BEGIN,\t#0d#{sprintf("%03d", META_KEY_BEGIN)}"
192192+ scf.puts "\t.equ\tMETA_KEY_NONE,\t#0d#{sprintf("%03d", META_KEY_NONE)}"
193193+ scf.puts
194194+195195+ 3.times do |x|
196196+ if x == 0
197197+ scf.puts "scancode_table:"
198198+ elsif x == 1
199199+ scf.puts
200200+ scf.puts "scancode_table_uppercase:"
201201+ elsif x == 2
202202+ scf.puts
203203+ scf.puts "scancode_table_control:"
204204+ end
205205+206206+ (0 .. SCANCODES.keys.last).each do |sc|
207207+ if k = SCANCODES[sc]
208208+ origk = k
209209+ if k.is_a?(Symbol)
210210+ k = META_KEY_BEGIN + KEYS.index(k)
211211+ elsif k.is_a?(String)
212212+ k = k.ord
213213+ end
214214+ raise if !k
215215+216216+ if x == 1
217217+ if u = UPPERCASES[origk]
218218+ k = u.ord
219219+ origk = u
220220+ elsif ("a" .. "z").include?(origk)
221221+ k = origk.upcase.ord
222222+ origk = origk.upcase
223223+ end
224224+ elsif x == 2
225225+ if u = CONTROLS[origk]
226226+ k = u
227227+ origk = u
228228+ end
229229+ end
230230+231231+ scf.puts "\t.db #0d#{sprintf("%03d", k)}\t\t; #{origk.inspect}"
232232+ else
233233+ scf.puts "\t.db #0d#{sprintf("%03d", META_KEY_NONE)}"
234234+ end
235235+ end
236236+ end
237237+end
238238+239239+File.open("#{__dir__}/../meta_keys.h", "w+") do |mkh|
240240+ mkh.puts "/* AUTOMATICALLY GENERATED FILE - see tools/generate_scancodes.rb */"
241241+ mkh.puts "#define\tMETA_KEY_BEGIN\t#{META_KEY_BEGIN}"
242242+243243+ KEYS.each_with_index do |k,x|
244244+ mkh.puts "#define\tKEY_#{k}\t#{k.length < 3 ? "\t" : ""}#{META_KEY_BEGIN + x}"
245245+ end
246246+247247+ mkh.puts
248248+ mkh.puts "#define\tMETA_KEY_NONE\t#{META_KEY_NONE}"
249249+end
+72
lib/tools/hexfont2inc.rb
···11+#
22+# convert a hex-exported bitmap font (like from gbdfed) to an asm include file
33+# Copyright (c) 2019 joshua stein <jcs@jcs.org>
44+#
55+# Permission to use, copy, modify, and distribute this software for any
66+# purpose with or without fee is hereby granted, provided that the above
77+# copyright notice and this permission notice appear in all copies.
88+#
99+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+#
1717+1818+chars = []
1919+char_size = 0
2020+all_bytes = []
2121+2222+if !ARGV[0]
2323+ puts "usage: #{$0} <hex file converted from bdf>"
2424+ exit 1
2525+end
2626+2727+File.open(ARGV[0], "r") do |f|
2828+ char = 0
2929+3030+ while f && !f.eof?
3131+ line = f.gets
3232+3333+ # 0023:A0E0A0E0A000
3434+ if !(m = line.match(/^(....):(.+)$/))
3535+ raise "unexpected format: #{line.inspect}"
3636+ end
3737+3838+ char = m[1].to_i(16)
3939+ char_size = (m[2].length / 2)
4040+4141+ # A0E0A0E0A000
4242+ # -> [ "A0", "e0", "A0", "e0", "A0", "00", "00" ]
4343+ bytes = m[2].scan(/(..)/).flatten
4444+4545+ # -> [ 0xa0, 0xe0, 0xa0, 0xe0, 0xa0, 0x00, 0x00 ]
4646+ bytes = bytes.map{|c| c.to_i(16) }
4747+4848+ # -> [ 101000000, 11100000, ... ]
4949+ # -> [ [ 1, 0, 1, 0, 0, 0, 0, 0 ], [ 1, 1, 1, 0, 0, 0, 0, 0 ], ... ]
5050+ bytes = bytes.map{|c| sprintf("%08b", c).split(//).map{|z| z.to_i } }
5151+5252+ # -> [ [ 0, 0, 0, 0, 0, 1, 0, 1 ], [ 0, 0, 0, 0, 0, 1, 1, 1 ], ... ]
5353+ bytes = bytes.map{|a| a.reverse }
5454+5555+ # -> [ 0x5, 0x7, ... ]
5656+ bytes = bytes.map{|a| a.join.to_i(2) }
5757+5858+ chars[char] = bytes
5959+ end
6060+end
6161+6262+(0 .. 255).each do |c|
6363+ if chars[c] && chars[c].any?
6464+ print ".db " << chars[c].map{|c| sprintf("#0x%02x", c) }.join(", ")
6565+ if c >= 32 && c <= 126
6666+ print "\t; #{sprintf("%.3d", c)} - #{c.chr}"
6767+ end
6868+ print "\n"
6969+ else
7070+ puts ".db " << char_size.times.map{|c| "#0x00" }.join(", ")
7171+ end
7272+end
+66
lib/tools/relink_packed.rb
···11+#/usr/bin/env ruby
22+#
33+# link, figure out header/code/data sizes, then relink with them packed tightly
44+# Copyright (c) 2021 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+def get_size(area)
2020+ File.open(ENV["PROG_MAP"], "r") do |f|
2121+ while f && !f.eof?
2222+ line = f.gets
2323+ # _HEADER0 00000000 000000F9 = 249. bytes (ABS,CON)
2424+ if m = line.match(/^#{area} .* (\d+)\. *bytes/)
2525+ return m[1].to_i
2626+ end
2727+ end
2828+ end
2929+3030+ raise "can't find #{area} in #{ENV["PROG_MAP"]}"
3131+end
3232+3333+def sdcc(code_start, data_start)
3434+ s = "#{ENV["SDCC"]} --code-loc #{sprintf("0x%04x", code_start)} " +
3535+ "--data-loc #{sprintf("0x%04x", data_start)} -o #{ENV["TARGET"]} " +
3636+ "#{ARGV.join(" ")}"
3737+ puts s
3838+ system(s)
3939+end
4040+4141+base_addr = ENV["BASE_ADDR"].to_i(16)
4242+header_size = 0x1000
4343+code_start = base_addr + header_size
4444+data_start = base_addr + 0x4000
4545+4646+# link once at a large offset data-loc
4747+sdcc(code_start, data_start)
4848+4949+header_size = get_size("_HEADER0")
5050+printf "header: %d bytes (0x%04x)\n", header_size, header_size
5151+5252+code_size = get_size("_CODE")
5353+printf "code: %d bytes (0x%04x)\n", code_size, code_size
5454+5555+data_size = get_size("_DATA")
5656+printf "data: %d bytes (0x%04x)\n", data_size, data_size
5757+5858+printf "total: %d bytes (0x%04x)\n", header_size + code_size + data_size,
5959+ header_size + code_size + data_size
6060+6161+code_start = base_addr + header_size
6262+data_start = code_start + code_size
6363+printf "relinking with base: 0x%04x code: 0x%04x data:0x%04x\n",
6464+ base_addr, code_start, data_start
6565+6666+sdcc(code_start, data_start)
+165
lib/wifi.s
···11+; vim:syntax=z8a:ts=8
22+;
33+; WiFiStation parallel port routines
44+; Copyright (c) 2019-2021 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+ .module wifi
2020+2121+ .include "mailstation.inc"
2222+2323+ .area _CODE
2424+2525+ .equ CONTROL_DIR, #0x0a
2626+ .equ CONTROL_DIR_OUT, #0xff
2727+ .equ CONTROL_DIR_IN, #0
2828+2929+ .equ CONTROL_PORT, #0x9
3030+ .equ CONTROL_STROBE_BIT, #0
3131+ .equ CONTROL_STROBE, #(1 << CONTROL_STROBE_BIT)
3232+ .equ CONTROL_LINEFEED_BIT, #1
3333+ .equ CONTROL_LINEFEED, #(1 << CONTROL_LINEFEED_BIT)
3434+ .equ CONTROL_INIT, #(1 << 2)
3535+ .equ CONTROL_SELECT, #(1 << 3)
3636+3737+ .equ DATA_DIR, #0x2c
3838+ .equ DATA_DIR_OUT, #0xff
3939+ .equ DATA_DIR_IN, #0
4040+ .equ DATA_PORT, #0x2d
4141+4242+ .equ STATUS_PORT, #0x21
4343+ .equ STATUS_BUSY, #(1 << 7)
4444+ .equ STATUS_ACK, #(1 << 6)
4545+4646+; void wifi_init(void);
4747+_wifi_init::
4848+ ; lower control lines
4949+ ld a, #CONTROL_DIR_OUT
5050+ out (#CONTROL_DIR), a
5151+ xor a
5252+ out (#CONTROL_PORT), a
5353+ ret
5454+5555+; at idle, lower all control lines
5656+; writer: reader:
5757+; raise strobe
5858+; see high strobe as high busy
5959+; raise linefeed
6060+; see high linefeed as high ack
6161+; write all data pins
6262+; lower strobe
6363+; see low strobe as low busy
6464+; read data
6565+; lower linefeed
6666+; see lower linefeed as high ack
6767+6868+; int wifi_write(char); -1 on error, 0 on success
6969+_wifi_write::
7070+ push ix
7171+ ld ix, #0
7272+ add ix, sp
7373+ push bc
7474+ push de
7575+ ld c, 4(ix) ; char to send
7676+ ld a, #DATA_DIR_OUT
7777+ out (#DATA_DIR), a ; we're sending out
7878+ ld a, #CONTROL_STROBE
7979+ out (#CONTROL_PORT), a ; raise strobe
8080+ ld de, #0xffff
8181+wait_for_ack:
8282+ in a, (#STATUS_PORT)
8383+ and #STATUS_ACK ; is ack high?
8484+ jr nz, got_ack ; yes, break
8585+ dec de ; no, de--
8686+ ld a, d
8787+ cp #0
8888+ jr nz, wait_for_ack
8989+ ld a, e
9090+ cp #0
9191+ jr nz, wait_for_ack
9292+ jr abort_send ; de == 0, fail
9393+got_ack:
9494+ ld a, c
9595+ out (#DATA_PORT), a ; write data
9696+ xor a
9797+ out (#CONTROL_PORT), a ; lower strobe
9898+ ld de, #0xffff
9999+wait_for_final_ack:
100100+ in a, (#STATUS_PORT)
101101+ and #STATUS_ACK ; is ack low?
102102+ jr z, got_final_ack ; yes, break
103103+ dec de ; no, de--
104104+ ld a, d
105105+ cp #0
106106+ jr nz, wait_for_final_ack
107107+ ld a, e
108108+ cp #0
109109+ jr nz, wait_for_final_ack
110110+got_final_ack:
111111+ pop de
112112+ pop bc
113113+ pop ix
114114+ ld hl, #0 ; return 0
115115+ ret
116116+abort_send:
117117+ xor a
118118+ out (#CONTROL_PORT), a ; lower strobe
119119+ pop de
120120+ pop bc
121121+ pop ix
122122+ ld hl, #-1 ; return -1
123123+ ret
124124+125125+126126+; int wifi_read(void); -1 on nothing read, >= 0 on success returning char
127127+_wifi_read::
128128+ push ix
129129+ ld ix, #0
130130+ add ix, sp
131131+ push de
132132+ ld hl, #-1 ; return -1 unless we read something
133133+ in a, (#STATUS_PORT)
134134+ and #STATUS_BUSY ; is busy high?
135135+ jr z, recv_done ; no, bail
136136+ and #STATUS_ACK ; but is ack high too? probably bogus
137137+ jr nz, recv_done
138138+ ld a, #DATA_DIR_IN
139139+ out (#DATA_DIR), a ; we're reading in
140140+ ld a, #CONTROL_LINEFEED ; raise linefeed
141141+ out (#CONTROL_PORT), a
142142+ ld de, #0xffff
143143+wait_for_busy_ack:
144144+ in a, (#STATUS_PORT)
145145+ and #STATUS_BUSY ; is busy high?
146146+ jr z, read_data ; no, break
147147+ dec de ; no, de--
148148+ ld a, d
149149+ cp #0
150150+ jr nz, wait_for_busy_ack
151151+ ld a, e
152152+ cp #0
153153+ jr nz, wait_for_busy_ack
154154+ jr recv_done ; de == 0, fail
155155+read_data:
156156+ in a, (#DATA_PORT)
157157+ ld h, #0
158158+ ld l, a
159159+raise_lf:
160160+ xor a
161161+ out (#CONTROL_PORT), a ; lower linefeed
162162+recv_done:
163163+ pop de
164164+ pop ix
165165+ ret
+93
main.c
···11+/*
22+ * MailStation example program
33+ * Copyright (c) 2019-2021 joshua stein <jcs@jcs.org>
44+ *
55+ * Permission to use, copy, modify, and distribute this software for any
66+ * purpose with or without fee is hereby granted, provided that the above
77+ * copyright notice and this permission notice appear in all copies.
88+ *
99+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+ */
1717+1818+#include <stdio.h>
1919+#include <string.h>
2020+#include <stdlib.h>
2121+2222+#include "lib/mailstation.h"
2323+2424+unsigned char lastkey;
2525+2626+void process_keyboard(void);
2727+void process_input(unsigned char b);
2828+2929+int
3030+main(void)
3131+{
3232+ /* ignore first peekkey() if it returns power button */
3333+ lastkey = KEY_POWER;
3434+3535+ clear_screen_bufs();
3636+ clear_screen();
3737+3838+ wifi_init();
3939+4040+ printf("Hello, World\n");
4141+4242+ for (;;) {
4343+ process_keyboard();
4444+ }
4545+}
4646+4747+void
4848+process_keyboard(void)
4949+{
5050+ unsigned char b;
5151+5252+ b = peekkey();
5353+5454+ /* this breaks key-repeat, but it's needed to debounce */
5555+ if (b == 0)
5656+ lastkey = 0;
5757+ else if (b == lastkey)
5858+ b = 0;
5959+ else
6060+ lastkey = b;
6161+6262+ if (b == 0)
6363+ return;
6464+6565+ switch (b) {
6666+ case KEY_POWER:
6767+#if 0
6868+ /* XXX: this triggers erroneously */
6969+ powerdown_mode();
7070+#endif
7171+ break;
7272+ case KEY_F1:
7373+ break;
7474+ case KEY_MAIN_MENU:
7575+ break;
7676+ case KEY_PAGE_UP:
7777+ break;
7878+ case KEY_PAGE_DOWN:
7979+ break;
8080+ case KEY_UP:
8181+ break;
8282+ case KEY_DOWN:
8383+ break;
8484+ case KEY_LEFT:
8585+ break;
8686+ case KEY_RIGHT:
8787+ break;
8888+ case KEY_SIZE:
8989+ break;
9090+ default:
9191+ putchar(b);
9292+ }
9393+}