···11+; vim:syntax=z8a:ts=8
22+;
33+; dataflash loader
44+;
55+; works as a "stage 2" loader, so type in loader.asm and then run Loader from
66+; the Yahoo! menu, then use sendload to upload this compiled binary, then
77+; use sendload to upload your actual binary to be written to the dataflash at
88+; DATAFLASH_PAGE
99+;
1010+; Copyright (c) 2019 joshua stein <jcs@jcs.org>
1111+;
1212+; Permission to use, copy, modify, and distribute this software for any
1313+; purpose with or without fee is hereby granted, provided that the above
1414+; copyright notice and this permission notice appear in all copies.
1515+;
1616+; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1717+; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1818+; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1919+; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2020+; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2121+; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2222+; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2323+;
2424+2525+ .module dataflashloader
2626+2727+ ; which page (Yahoo! menu slot) to save an uploaded program to
2828+ ; page 0 is 0x0000, page 1 0x4000, page 2, 0x8000, etc.
2929+ .equ DATAFLASH_PAGE, #0
3030+3131+3232+ ; when running from Loader, we are loaded at 0x8000 and use slot 4
3333+ .equ SLOT_ADDR, #0x4000
3434+ .equ RUN_ADDR, #0x8000
3535+ .equ SLOT_DEVICE, #0x6
3636+ .equ SLOT_PAGE, #0x5
3737+3838+ ; where we'll buffer the 256 bytes we receive before writing to flash
3939+ .equ RAM_ADDR, #0xd000
4040+4141+ .equ SDP_LOCK, #SLOT_ADDR + 0x040a
4242+ .equ SDP_UNLOCK, #SLOT_ADDR + 0x041a
4343+4444+ ; lpt
4545+ .equ CONTROL, #0x2c
4646+ .equ DATA, #0x2d
4747+ .equ STATUS, #0x21
4848+4949+ .equ LPT_BUSY_IN, #0x40
5050+ .equ LPT_BUSY_OUT, #0x08
5151+ .equ LPT_STROBE_IN, #0x80
5252+ .equ LPT_STROBE_OUT, #0x10
5353+ .equ LPT_TRIB_MASK, #0x07
5454+ .equ LPT_DIB_MASK, #0x03
5555+5656+5757+ .area _DATA
5858+ .area _HEADER (ABS)
5959+6060+ .org #RUN_ADDR
6161+6262+ jp eventhandler
6363+6464+ .dw (icons)
6565+ .dw (caption)
6666+ .dw (dunno)
6767+dunno:
6868+ .db #0
6969+zip:
7070+ .dw #0
7171+zilch:
7272+ .dw #0
7373+caption:
7474+ .dw #0x0001
7575+ .dw (endcap - caption - 6) ; num of chars
7676+ .dw #0x0006 ; offset to first char
7777+ .ascii "FlashLoader" ; the caption string
7878+endcap:
7979+8080+icons:
8181+ .dw #0x0 ; size icon0
8282+ .dw (icon0 - icons) ; offset to icon0
8383+ .dw #0 ; size icon1
8484+ .dw (icon1 - icons) ; offset to icon1 (0x00b5)
8585+icon0:
8686+ .dw #0x0 ; icon width
8787+ .db #0x0 ; icon height
8888+8989+icon1:
9090+ .dw #0 ; icon width
9191+ .db #0 ; icon height
9292+9393+eventhandler:
9494+ push ix
9595+ ld ix, #0
9696+ add ix, sp
9797+ push bc
9898+ push de
9999+ push hl
100100+ ld hl, #-8 ; stack bytes for local storage
101101+ add hl, sp
102102+ ld sp, hl
103103+ in a, (#SLOT_DEVICE)
104104+ ld -3(ix), a ; stack[-3] = slot 8 device
105105+ in a, (#SLOT_PAGE)
106106+ ld -4(ix), a ; stack[-4] = slot 8 device
107107+ ld a, #3 ; slot 8 device = dataflash
108108+ out (#SLOT_DEVICE), a
109109+ xor a ; slot 8 page = 0
110110+ out (#SLOT_PAGE), a
111111+ ld hl, #SLOT_ADDR
112112+ ld -5(ix), h
113113+ ld -6(ix), l ; stack[-5,-6] = data flash location
114114+get_size:
115115+ call getbyte ; low byte of total bytes to download
116116+ ld -8(ix), a
117117+ call getbyte ; high byte of total bytes to download
118118+ ld -7(ix), a
119119+ cp #0x40 ; we can't write more than 0x3fff
120120+ jr c, size_ok
121121+size_too_big:
122122+ jp 0x0000
123123+size_ok:
124124+ di ; prevent things from touching RAM
125125+ call sdp
126126+ ld a, (#SDP_UNLOCK)
127127+read_chunk_into_ram:
128128+ ; read 256 bytes at a time into ram, erase the target flash sector,
129129+ ; then program it with those bytes
130130+ ld b, -7(ix)
131131+ ld c, -8(ix)
132132+ ld hl, #RAM_ADDR
133133+ingest_loop:
134134+ call getbyte
135135+ ld (hl), a
136136+ inc hl
137137+ dec bc
138138+ ld a, l
139139+ cp #0
140140+ jr z, done_ingesting ; on 256-byte boundary
141141+ ld a, b
142142+ cp #0
143143+ jr nz, ingest_loop ; bc != 0, keep reading input
144144+ ld a, c
145145+ cp #0
146146+ jr nz, ingest_loop ; bc != 0, keep reading input
147147+done_ingesting:
148148+ ld -7(ix), b ; update bytes remaining to fetch on
149149+ ld -8(ix), c ; next iteration
150150+move_into_flash:
151151+ ld a, #3 ; slot 8 device = dataflash
152152+ out (#SLOT_DEVICE), a
153153+ ld a, #DATAFLASH_PAGE
154154+ out (#SLOT_PAGE), a
155155+ ld de, #RAM_ADDR
156156+ ld h, -5(ix) ; data flash write location
157157+ ld l, -6(ix)
158158+sector_erase:
159159+ ld (hl), #0x20 ; 28SF040 Sector-Erase Setup
160160+ ld (hl), #0xd0 ; 28SF040 Execute
161161+sector_erase_wait:
162162+ ld a, (hl) ; wait until End-of-Write
163163+ ld b, a
164164+ ld a, (hl)
165165+ cp b
166166+ jr nz, sector_erase_wait
167167+byte_program_loop:
168168+ ld a, (de)
169169+ ld (hl), #0x10 ; 28SF040 Byte-Program Setup
170170+ ld (hl), a ; 28SF040 Execute
171171+byte_program:
172172+ ld a, (hl)
173173+ ld b, a
174174+ ld a, (hl) ; End-of-Write by reading it
175175+ cp b
176176+ jr nz, byte_program ; read until writing succeeds
177177+ inc hl ; next flash byte
178178+ inc de ; next RAM byte
179179+ ld a, l
180180+ cp #0
181181+ jr nz, byte_program_loop
182182+sector_done:
183183+ ld -5(ix), h ; update data flash write location
184184+ ld -6(ix), l
185185+ ld a, -7(ix)
186186+ cp #0
187187+ jp nz, read_chunk_into_ram ; more data to transfer
188188+ ld a, -8(ix)
189189+ cp #0
190190+ jp nz, read_chunk_into_ram ; more data to transfer
191191+ ; all done
192192+flash_out:
193193+ ld a, #3 ; slot 8 device = dataflash
194194+ out (#SLOT_DEVICE), a
195195+ xor a ; slot 8 page = 0
196196+ out (#SLOT_PAGE), a
197197+ call sdp
198198+ ld a, (#SDP_LOCK)
199199+bail:
200200+ ld a, -3(ix) ; restore slot 8 device
201201+ out (#SLOT_DEVICE), a
202202+ ld a, -4(ix) ; restore slot 8 page
203203+ out (#SLOT_PAGE), a
204204+ ld hl, #8 ; remove stack bytes
205205+ add hl, sp
206206+ ld sp, hl
207207+ pop hl
208208+ pop de
209209+ pop bc
210210+ ld sp, ix
211211+ pop ix
212212+ ei
213213+ jp 0x0000
214214+215215+216216+sdp:
217217+ ld a, (#SLOT_ADDR + 0x1823) ; 28SF040 Software Data Protection
218218+ ld a, (#SLOT_ADDR + 0x1820)
219219+ ld a, (#SLOT_ADDR + 0x1822)
220220+ ld a, (#SLOT_ADDR + 0x0418)
221221+ ld a, (#SLOT_ADDR + 0x041b)
222222+ ld a, (#SLOT_ADDR + 0x0419)
223223+ ret
224224+ ; caller needs to read final SDP_LOCK or SDP_UNLOCK address
225225+226226+227227+; loop until we get a byte, return it in a
228228+getbyte:
229229+ push hl
230230+getbyte_loop:
231231+ call _lptrecv
232232+ ld a, h
233233+ cp #0
234234+ jp nz, getbyte_loop
235235+ ld a, l
236236+ pop hl
237237+ ret
238238+239239+240240+; receive a tribble byte from host, return h=1 l=0 on error, else h=0, l=(byte)
241241+lptrecv_tribble:
242242+ push bc
243243+ ld hl, #0 ; h will contain error, l result
244244+ xor a
245245+ out (DATA), a ; drop busy/ack, wait for strobe
246246+ ld b, #0xff ; try a bunch before bailing
247247+wait_for_strobe:
248248+ in a, (STATUS)
249249+ and #LPT_STROBE_IN ; inb(STATUS) & stbin
250250+ jr nz, got_strobe
251251+ djnz wait_for_strobe
252252+strobe_failed:
253253+ ld h, #1
254254+ ld l, #0
255255+ jr lptrecv_tribble_out
256256+got_strobe:
257257+ in a, (STATUS)
258258+ sra a
259259+ sra a
260260+ sra a
261261+ and #LPT_TRIB_MASK ; & tribmask
262262+ ld l, a
263263+ ld a, #LPT_BUSY_OUT ; raise busy/ack
264264+ out (DATA), a
265265+ ld b, #0xff ; retry 255 times
266266+wait_for_unstrobe:
267267+ in a, (STATUS)
268268+ and #LPT_STROBE_IN ; inb(STATUS) & stbin
269269+ jr z, lptrecv_tribble_out
270270+ djnz wait_for_unstrobe
271271+ ; if we never get unstrobe, that's ok
272272+lptrecv_tribble_out:
273273+ ld a, #LPT_BUSY_OUT ; raise busy/ack
274274+ out (DATA), a
275275+ pop bc
276276+ ret
277277+278278+279279+; unsigned char lptrecv(void)
280280+; receive a full byte from host in three parts
281281+; return h=1 l=0 on error, otherwise h=0, l=(byte)
282282+_lptrecv::
283283+ push bc
284284+ call lptrecv_tribble
285285+ ld a, h
286286+ cp #1
287287+ jr z, lptrecv_err ; bail early if h has an error
288288+ ld b, l
289289+ call lptrecv_tribble
290290+ ld a, h
291291+ cp #1
292292+ jr z, lptrecv_err
293293+ ld a, l
294294+ sla a
295295+ sla a
296296+ sla a
297297+ add b
298298+ ld b, a ; += tribble << 3
299299+ call lptrecv_tribble
300300+ ld a, h
301301+ cp #1
302302+ jr z, lptrecv_err
303303+ ld a, l
304304+ and #LPT_DIB_MASK ; dibmask
305305+ sla a
306306+ sla a
307307+ sla a
308308+ sla a
309309+ sla a
310310+ sla a
311311+ add b ; += (tribble & dibmask) << 6
312312+ ld h, #0
313313+ ld l, a
314314+lptrecv_out:
315315+ pop bc
316316+ ret
317317+lptrecv_err:
318318+ pop bc
319319+ ld hl, #0x0100
320320+ ret