Tools for working with Cidco Mailstations
1; vim:syntax=z8a:ts=8
2;
3; dataflash loader
4;
5; works as a "stage 2" loader, so type in loader.asm and then run Loader from
6; the Yahoo! menu, then use sendload to upload this compiled binary, then
7; use sendload to upload your actual binary to be written to the dataflash at
8; DATAFLASH_PAGE
9;
10; Copyright (c) 2019 joshua stein <jcs@jcs.org>
11;
12; Permission to use, copy, modify, and distribute this software for any
13; purpose with or without fee is hereby granted, provided that the above
14; copyright notice and this permission notice appear in all copies.
15;
16; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23;
24
25 .module dataflashloader
26
27 ; which page (Yahoo! menu slot) to save an uploaded program to
28 ; page 0 is 0x0000, page 1 0x4000, page 2, 0x8000, etc.
29 .equ DATAFLASH_PAGE, #0
30
31
32 ; when running from Loader, we are loaded at 0x8000 and use slot 4
33 .equ SLOT_ADDR, #0x4000
34 .equ RUN_ADDR, #0x8000
35 .equ SLOT_DEVICE, #0x6
36 .equ SLOT_PAGE, #0x5
37
38 ; where we'll buffer the 256 bytes we receive before writing to flash
39 .equ RAM_ADDR, #0xd000
40
41 .equ SDP_LOCK, #SLOT_ADDR + 0x040a
42 .equ SDP_UNLOCK, #SLOT_ADDR + 0x041a
43
44 ; lpt
45 .equ CONTROL, #0x2c
46 .equ DATA, #0x2d
47 .equ STATUS, #0x21
48
49 .equ LPT_BUSY_IN, #0x40
50 .equ LPT_BUSY_OUT, #0x08
51 .equ LPT_STROBE_IN, #0x80
52 .equ LPT_STROBE_OUT, #0x10
53 .equ LPT_TRIB_MASK, #0x07
54 .equ LPT_DIB_MASK, #0x03
55
56
57 .area _DATA
58 .area _HEADER (ABS)
59
60 .org #RUN_ADDR
61
62 jp eventhandler
63
64 .dw (icons)
65 .dw (caption)
66 .dw (dunno)
67dunno:
68 .db #0
69zip:
70 .dw #0
71zilch:
72 .dw #0
73caption:
74 .dw #0x0001
75 .dw (endcap - caption - 6) ; num of chars
76 .dw #0x0006 ; offset to first char
77 .ascii "FlashLoader" ; the caption string
78endcap:
79
80icons:
81 .dw #0x0 ; size icon0
82 .dw (icon0 - icons) ; offset to icon0
83 .dw #0 ; size icon1
84 .dw (icon1 - icons) ; offset to icon1 (0x00b5)
85icon0:
86 .dw #0x0 ; icon width
87 .db #0x0 ; icon height
88
89icon1:
90 .dw #0 ; icon width
91 .db #0 ; icon height
92
93eventhandler:
94 push ix
95 ld ix, #0
96 add ix, sp
97 push bc
98 push de
99 push hl
100 ld hl, #-8 ; stack bytes for local storage
101 add hl, sp
102 ld sp, hl
103 in a, (#SLOT_DEVICE)
104 ld -3(ix), a ; stack[-3] = slot 8 device
105 in a, (#SLOT_PAGE)
106 ld -4(ix), a ; stack[-4] = slot 8 device
107 ld a, #3 ; slot 8 device = dataflash
108 out (#SLOT_DEVICE), a
109 xor a ; slot 8 page = 0
110 out (#SLOT_PAGE), a
111 ld hl, #SLOT_ADDR
112 ld -5(ix), h
113 ld -6(ix), l ; stack[-5,-6] = data flash location
114get_size:
115 call getbyte ; low byte of total bytes to download
116 ld -8(ix), a
117 call getbyte ; high byte of total bytes to download
118 ld -7(ix), a
119 cp #0x40 ; we can't write more than 0x3fff
120 jr c, size_ok
121size_too_big:
122 jp 0x0000
123size_ok:
124 di ; prevent things from touching RAM
125 call sdp
126 ld a, (#SDP_UNLOCK)
127read_chunk_into_ram:
128 ; read 256 bytes at a time into ram, erase the target flash sector,
129 ; then program it with those bytes
130 ld b, -7(ix)
131 ld c, -8(ix)
132 ld hl, #RAM_ADDR
133ingest_loop:
134 call getbyte
135 ld (hl), a
136 inc hl
137 dec bc
138 ld a, l
139 cp #0
140 jr z, done_ingesting ; on 256-byte boundary
141 ld a, b
142 cp #0
143 jr nz, ingest_loop ; bc != 0, keep reading input
144 ld a, c
145 cp #0
146 jr nz, ingest_loop ; bc != 0, keep reading input
147done_ingesting:
148 ld -7(ix), b ; update bytes remaining to fetch on
149 ld -8(ix), c ; next iteration
150move_into_flash:
151 ld a, #3 ; slot 8 device = dataflash
152 out (#SLOT_DEVICE), a
153 ld a, #DATAFLASH_PAGE
154 out (#SLOT_PAGE), a
155 ld de, #RAM_ADDR
156 ld h, -5(ix) ; data flash write location
157 ld l, -6(ix)
158sector_erase:
159 ld (hl), #0x20 ; 28SF040 Sector-Erase Setup
160 ld (hl), #0xd0 ; 28SF040 Execute
161sector_erase_wait:
162 ld a, (hl) ; wait until End-of-Write
163 ld b, a
164 ld a, (hl)
165 cp b
166 jr nz, sector_erase_wait
167byte_program_loop:
168 ld a, (de)
169 ld (hl), #0x10 ; 28SF040 Byte-Program Setup
170 ld (hl), a ; 28SF040 Execute
171byte_program:
172 ld a, (hl)
173 ld b, a
174 ld a, (hl) ; End-of-Write by reading it
175 cp b
176 jr nz, byte_program ; read until writing succeeds
177 inc hl ; next flash byte
178 inc de ; next RAM byte
179 ld a, l
180 cp #0
181 jr nz, byte_program_loop
182sector_done:
183 ld -5(ix), h ; update data flash write location
184 ld -6(ix), l
185 ld a, -7(ix)
186 cp #0
187 jp nz, read_chunk_into_ram ; more data to transfer
188 ld a, -8(ix)
189 cp #0
190 jp nz, read_chunk_into_ram ; more data to transfer
191 ; all done
192flash_out:
193 ld a, #3 ; slot 8 device = dataflash
194 out (#SLOT_DEVICE), a
195 xor a ; slot 8 page = 0
196 out (#SLOT_PAGE), a
197 call sdp
198 ld a, (#SDP_LOCK)
199bail:
200 ld a, -3(ix) ; restore slot 8 device
201 out (#SLOT_DEVICE), a
202 ld a, -4(ix) ; restore slot 8 page
203 out (#SLOT_PAGE), a
204 ld hl, #8 ; remove stack bytes
205 add hl, sp
206 ld sp, hl
207 pop hl
208 pop de
209 pop bc
210 ld sp, ix
211 pop ix
212 ei
213 jp 0x0000
214
215
216sdp:
217 ld a, (#SLOT_ADDR + 0x1823) ; 28SF040 Software Data Protection
218 ld a, (#SLOT_ADDR + 0x1820)
219 ld a, (#SLOT_ADDR + 0x1822)
220 ld a, (#SLOT_ADDR + 0x0418)
221 ld a, (#SLOT_ADDR + 0x041b)
222 ld a, (#SLOT_ADDR + 0x0419)
223 ret
224 ; caller needs to read final SDP_LOCK or SDP_UNLOCK address
225
226
227; loop until we get a byte, return it in a
228getbyte:
229 push hl
230getbyte_loop:
231 call _lptrecv
232 ld a, h
233 cp #0
234 jp nz, getbyte_loop
235 ld a, l
236 pop hl
237 ret
238
239
240; receive a tribble byte from host, return h=1 l=0 on error, else h=0, l=(byte)
241lptrecv_tribble:
242 push bc
243 ld hl, #0 ; h will contain error, l result
244 xor a
245 out (DATA), a ; drop busy/ack, wait for strobe
246 ld b, #0xff ; try a bunch before bailing
247wait_for_strobe:
248 in a, (STATUS)
249 and #LPT_STROBE_IN ; inb(STATUS) & stbin
250 jr nz, got_strobe
251 djnz wait_for_strobe
252strobe_failed:
253 ld h, #1
254 ld l, #0
255 jr lptrecv_tribble_out
256got_strobe:
257 in a, (STATUS)
258 sra a
259 sra a
260 sra a
261 and #LPT_TRIB_MASK ; & tribmask
262 ld l, a
263 ld a, #LPT_BUSY_OUT ; raise busy/ack
264 out (DATA), a
265 ld b, #0xff ; retry 255 times
266wait_for_unstrobe:
267 in a, (STATUS)
268 and #LPT_STROBE_IN ; inb(STATUS) & stbin
269 jr z, lptrecv_tribble_out
270 djnz wait_for_unstrobe
271 ; if we never get unstrobe, that's ok
272lptrecv_tribble_out:
273 ld a, #LPT_BUSY_OUT ; raise busy/ack
274 out (DATA), a
275 pop bc
276 ret
277
278
279; unsigned char lptrecv(void)
280; receive a full byte from host in three parts
281; return h=1 l=0 on error, otherwise h=0, l=(byte)
282_lptrecv::
283 push bc
284 call lptrecv_tribble
285 ld a, h
286 cp #1
287 jr z, lptrecv_err ; bail early if h has an error
288 ld b, l
289 call lptrecv_tribble
290 ld a, h
291 cp #1
292 jr z, lptrecv_err
293 ld a, l
294 sla a
295 sla a
296 sla a
297 add b
298 ld b, a ; += tribble << 3
299 call lptrecv_tribble
300 ld a, h
301 cp #1
302 jr z, lptrecv_err
303 ld a, l
304 and #LPT_DIB_MASK ; dibmask
305 sla a
306 sla a
307 sla a
308 sla a
309 sla a
310 sla a
311 add b ; += (tribble & dibmask) << 6
312 ld h, #0
313 ld l, a
314lptrecv_out:
315 pop bc
316 ret
317lptrecv_err:
318 pop bc
319 ld hl, #0x0100
320 ret