Example program for the Cidco MailStation Z80 computer
1; vim:syntax=z8a:ts=8
2;
3; MailStation example program
4; Copyright (c) 2019-2021 joshua stein <jcs@jcs.org>
5;
6; Permission to use, copy, modify, and distribute this software for any
7; purpose with or without fee is hereby granted, provided that the above
8; copyright notice and this permission notice appear in all copies.
9;
10; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17;
18
19 .module crt0
20
21 .include "mailstation.inc"
22 .globl _main
23 .globl l__INITIALIZER
24 .globl s__INITIALIZER
25 .globl s__INITIALIZED
26
27 .area _HEADER (ABS)
28
29 .org RUN_ADDR
30start:
31 jp boot
32
33 .dw (icons)
34 .dw (caption)
35 .dw (dunno)
36
37dunno:
38 .db #0
39xpos:
40 .dw #0
41ypos:
42 .dw #0
43caption:
44 .dw #0x0001 ; ?
45 .dw (endcap - caption - 6) ; number of chars
46 .dw #0x0006 ; offset to first char
47 .ascii "Example" ; the caption string
48endcap:
49
50icons:
51 .dw (icon2 - icon1) ; size of icon1
52 .dw (icon1 - icons) ; offset to icon1
53 .dw (iconend - icon2) ; size of icon2
54 .dw (icon2 - icons) ; offset to icon2
55
56icon1:
57 .dw #0x0022 ; icon width (34, 5 bytes per row)
58 .db #0x22 ; icon height (34)
59
60 .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
61 .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
62 .db #0x00, #0xc0, #0x0f, #0x00, #0x00 ; ..............######..............
63 .db #0x00, #0x30, #0x30, #0x00, #0x00 ; ............##......##............
64 .db #0x00, #0x0c, #0xc0, #0x00, #0x00 ; ..........##..........##..........
65 .db #0x00, #0x02, #0x00, #0x01, #0x00 ; .........#..............#.........
66 .db #0x00, #0x01, #0x00, #0x02, #0x00 ; ........#................#........
67 .db #0x80, #0x00, #0x00, #0x04, #0x00 ; .......#..................#.......
68 .db #0x40, #0x00, #0x00, #0x08, #0x00 ; ......#....................#......
69 .db #0x20, #0x00, #0x00, #0x10, #0x00 ; .....#......................#.....
70 .db #0x10, #0x00, #0x00, #0x20, #0x00 ; ....#........................#....
71 .db #0x10, #0x00, #0x00, #0x20, #0x00 ; ....#........................#....
72 .db #0x08, #0x30, #0x30, #0x40, #0x00 ; ...#........##......##........#...
73 .db #0x08, #0x78, #0x78, #0x40, #0x00 ; ...#.......####....####.......#...
74 .db #0x08, #0x78, #0x78, #0x40, #0x00 ; ...#.......####....####.......#...
75 .db #0x04, #0x30, #0x30, #0x80, #0x00 ; ..#.........##......##.........#..
76 .db #0x04, #0x00, #0x00, #0x80, #0x00 ; ..#............................#..
77 .db #0x04, #0x00, #0x00, #0x80, #0x00 ; ..#............................#..
78 .db #0x04, #0x00, #0x00, #0x80, #0x00 ; ..#............................#..
79 .db #0x08, #0x00, #0x00, #0x40, #0x00 ; ...#..........................#...
80 .db #0x08, #0x00, #0x00, #0x40, #0x00 ; ...#..........................#...
81 .db #0x08, #0x04, #0x80, #0x40, #0x00 ; ...#......#............#......#...
82 .db #0x10, #0x04, #0x80, #0x20, #0x00 ; ....#.....#............#.....#....
83 .db #0x10, #0x08, #0x40, #0x20, #0x00 ; ....#......#..........#......#....
84 .db #0x20, #0x70, #0x38, #0x10, #0x00 ; .....#......###....###......#.....
85 .db #0x40, #0x80, #0x07, #0x08, #0x00 ; ......#........####........#......
86 .db #0x80, #0x00, #0x00, #0x04, #0x00 ; .......#..................#.......
87 .db #0x00, #0x01, #0x00, #0x02, #0x00 ; ........#................#........
88 .db #0x00, #0x02, #0x00, #0x01, #0x00 ; .........#..............#.........
89 .db #0x00, #0x0c, #0xc0, #0x00, #0x00 ; ..........##..........##..........
90 .db #0x00, #0x30, #0x30, #0x00, #0x00 ; ............##......##............
91 .db #0x00, #0xc0, #0x0f, #0x00, #0x00 ; ..............######..............
92 .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
93 .db #0x00, #0x00, #0x00, #0x00, #0x00 ; ..................................
94
95icon2:
96 ; not used
97 .dw #0x0000 ; width
98 .db #0x00 ; height
99iconend:
100
101boot:
102 xor a
103 out (#0x0d), a ; put the cpu in its highest speed
104
105 ; all of our code expects to run in ram so when we're running from
106 ; dataflash, we have to copy our page of flash into ram, then jump to
107 ; it
108
109 ; swap in a page of ram
110 ld a, #DEVICE_RAM
111 out (#SLOT_DEVICE), a
112 out (#SLOT_PAGE), a
113
114 ; copy ourselves into ram
115 ld de, #SLOT_ADDR
116 ld hl, #RUN_ADDR
117 ld bc, #0x4000
118 ldir ; ld (de), (hl), de++, hl++, bc--
119
120 jp SLOT_ADDR + (hijump - start)
121
122hijump:
123 ; PC is now in SLOT_ADDR, put our new RAM page into RUN_DEVICE/PAGE
124 out (#RUN_DEVICE), a
125 out (#RUN_PAGE), a
126
127 ; then jump back there
128 jp RUN_ADDR + (lojump - start)
129
130lojump:
131 call gsinit
132 call find_shadows
133 call _main ; main c code
134 jp _exit
135
136
137 .area _DATA
138
139; shadow locations
140p2shadow:
141 .dw #0xdba2
142p3shadow:
143 .dw #0xdba3
144p28shadow:
145 .dw #0xdba0
146delay_func:
147 jp 0x0a5c
148
149_debug0::
150 .db #0
151_debug1::
152 .db #0
153_debug2::
154 .db #0
155_debug3::
156 .db #0
157_debug4::
158 .db #0
159
160
161 .area _GSINIT
162gsinit:
163 ld bc, #l__INITIALIZER
164 ld a, b
165 or a, c
166 jr z, gsinit_next
167 ld de, #s__INITIALIZED
168 ld hl, #s__INITIALIZER
169 ldir
170gsinit_next:
171
172 .area _GSFINAL
173 ret
174
175
176 .area _CODE
177
178; set location of port shadow variables depending on firmware version
179find_shadows:
180 ld a, (#0x0037) ; firmware major version
181 cp #0x1
182 jr z, ver_1
183 cp #0x2
184 jr z, ver_2
185 cp #0x3
186 jr z, ver_3
187unrecognized_firmware: ; we can't blink because that requires
188 jp 0x0 ; port and fw function addresses
189ver_1:
190 ld a, (#0x0036) ; firmware minor version
191 cp #0x73
192 jr z, ver_1_73
193 jr unrecognized_firmware
194ver_1_73: ; eMessage 1.73CID
195 ld hl, #p2shadow
196 ld (hl), #0xdb9f
197 ld hl, #p3shadow
198 ld (hl), #0xdba0 ; TODO: verify
199 ret
200ver_2:
201 ld a, (#0x0036) ; firmware minor version
202 cp #0x54
203 jr z, ver_2_54
204 jr unrecognized_firmware
205ver_2_54: ; MailStation 2.54
206 ld hl, #p2shadow
207 ld (hl), #0xdba2
208 ld hl, #p3shadow
209 ld (hl), #0xdba3
210 ld hl, #p28shadow
211 ld (hl), #0xdba0
212 ret
213ver_3:
214 ld a, (#0x0036) ; firmware minor version
215 cp #0x0d3
216 jr z, ver_3_03
217 jr unrecognized_firmware
218ver_3_03: ; MailStation 3.03
219 ld hl, #p2shadow
220 ld (hl), #0xdba5
221 ld hl, #p3shadow
222 ld (hl), #0xdba6
223 ret
224
225; exit handler, restart
226_exit::
227 call _reboot
228
229_powerdown::
230 ; TODO: figure out what else needs to be set before shutting down to
231 ; prevent it going to the "Reset System Data" screen on next startup
232 ld hl, (#p28shadow)
233 ld a, (hl)
234 set 1, a ; 74c74 pin4
235 set 0, a ; modem reset
236 ld (hl), a
237 di
238 out (#0x28), a
239 halt
240 ret
241
242_reboot::
243 jp 0x0000
244
245; new_mail(unsigned char on)
246; toggles 'new mail' light
247_new_mail::
248 di
249 push ix
250 ld ix, #0
251 add ix, sp
252 push hl
253 push af
254 ld a, 4(ix)
255 cp #0
256 ld hl, (p2shadow)
257 jr z, light_off
258light_on:
259 ld a, (hl)
260 set 4, a
261 jr write_p2
262light_off:
263 ld a, (hl)
264 res 4, a
265write_p2:
266 ld (hl), a
267 out (#0x02), a ; write p2shadow to port2
268 pop af
269 pop hl
270 pop ix
271 ei
272 ret
273
274; delay(unsigned int millis)
275; call mailstation function that delays (stack) milliseconds
276_delay::
277 push ix
278 ld ix, #0
279 add ix, sp
280 push af
281 push bc
282 push hl
283 ld l, 4(ix)
284 ld h, 5(ix)
285 push hl
286 call delay_func
287 pop hl
288 pop hl
289 pop bc
290 pop af
291 pop ix
292 ret
293
294; blink(unsigned int millis)
295; turn new mail LED on, wait millis, turn it off, wait millis
296_blink::
297 push ix
298 ld ix, #0
299 add ix, sp
300 push hl
301 ld l, #1
302 push hl
303 call _new_mail ; turn it on
304 pop hl
305 ld l, 4(ix)
306 ld h, 5(ix)
307 push hl
308 call _delay ; wait
309 pop hl
310 ld l, #0
311 push hl
312 call _new_mail ; turn it off
313 pop hl
314 ld l, 4(ix)
315 ld h, 5(ix)
316 push hl
317 call _delay ; wait
318 pop hl
319 pop af
320 pop ix
321 ret
322
323; void lcd_sleep(void)
324; turn the LCD off
325_lcd_sleep::
326 di
327 push hl
328 ld hl, (p2shadow)
329 ld a, (hl)
330 and #0b01111111 ; LCD_ON - turn port2 bit 7 off
331 ld (hl), a
332 out (#0x02), a ; write p2shadow to port2
333 pop hl
334 ei
335 ret
336
337
338; void lcd_wake(void)
339; turn the LCD on
340_lcd_wake::
341 di
342 push hl
343 ld hl, (p2shadow)
344 ld a, (hl)
345 or #0b10000000 ; LCD_ON - turn port2 bit 7 on
346 ld (hl), a
347 out (#0x02), a ; write p2shadow to port2
348 pop hl
349 ei
350 ret
351
352; unsigned char read_port(unsigned char port)
353; return a byte read from a port
354_read_port::
355 push ix
356 ld ix, #0
357 add ix, sp
358 push af
359 push bc
360 ld b, #0
361 ld c, 4(ix)
362 in l, (C)
363 ld h, #0
364 pop bc
365 pop af
366 pop ix
367 ret
368
369
370; 8-bit multiplication
371; de * a = hl
372mult8::
373 ld b, #8
374 ld hl, #0
375mult8_loop:
376 add hl, hl
377 rlca
378 jr nc, mult8_noadd
379 add hl, de
380mult8_noadd:
381 djnz mult8_loop
382mult8_out:
383 ret
384
385; 16-bit multiplication
386; bc * de = hl
387mult16:
388 ld a, b
389 ld b, #16
390 ld hl, #0
391mult16_loop:
392 add hl, hl
393 sla c
394 rla
395 jr nc, mult16_noadd
396 add hl, de
397mult16_noadd:
398 djnz mult16_loop
399 ret
400
401
402; 8-bit division
403; divide e by c, store result in a and remainder in b
404div8:
405 xor a
406 ld b, #8
407div8_loop:
408 rl e
409 rla
410 sub c
411 jr nc, div8_noadd
412 add a, c
413div8_noadd:
414 djnz div8_loop
415 ld b,a
416 ld a,e
417 rla
418 cpl
419 ret
420
421; 16-bit division
422; divide bc by de, store result in bc, remainder in hl
423div16:
424 ld hl, #0
425 ld a, b
426 ld b, #8
427div16_loop1:
428 rla
429 adc hl, hl
430 sbc hl, de
431 jr nc, div16_noadd1
432 add hl, de
433div16_noadd1:
434 djnz div16_loop1
435 rla
436 cpl
437 ld b, a
438 ld a, c
439 ld c, b
440 ld b, #8
441div16_loop2:
442 rla
443 adc hl, hl
444 sbc hl, de
445 jr nc, div16_noadd2
446 add hl, de
447div16_noadd2:
448 djnz div16_loop2
449 rla
450 cpl
451 ld b, c
452 ld c, a
453 ret