-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathZ80SBCLoad.asm
193 lines (173 loc) · 5.01 KB
/
Z80SBCLoad.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
; 10/25/18 TinyLoad for protoZ80SBC, Z80SBCLoad
; Derived from TinyLoad for Z280
; default I/O location is 0xF8 for status, 0xF9 for data
; status bits are D[0] for transmit empty, D[1] for receive ready. D[7..2] are 0xF8 which is high 6 bits
; of the interrupt vector
; write 0x4 to 0xf8 to enable transmit empty interrupt
; write 0x0 to 0xf8 to disable transmit empty interrupt
; serial port initially configured in boot strap mode. This is the boot strap code
; clear all memory to zero
; file load program fit in 256 bytes
; File is intel HEX format
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Z80SBCLoad, Copyright (C) 2018 Hui-chien Shen
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RxData equ 0f9h ; CPLD UART receive register
TxData equ 0f9h ; CPLD UART transmit register
RxStat equ 0f8h ; CPLD UART transmitter status/control register
TxStat equ 0f8h ; CPLD UART receiver status/control register
nop
LD SP,0FFFFH ; initialize stack pointer to top of memory
ld hl,SignOn$ ; print the sign on message
call strout
xor a ; clear reg A
ld hl,0ffffh ; clear memory, starting from the top
clrmem:
ld (hl),a ; clear memory
dec hl
cp h ; reaching 100h?
jr nz,clrmem
main:
call cinq
cp ':' ; intel load file starts with :
jr z,fileload
cp 'G' ; execute
jr nz,main
call cout ; echo back 'G'
ld a,' ' ; put out a space
call cout
call GETHEX ; get starting address
ld h,a
call GETHEX
ld l,a
jp (hl)
fileload:
; call cout ; echo back valid input character
call GETHEX ; get two ASCII char (byte count) into hex byte in reg A
ld d,a ; save byte count to reg D
ld b,a ; initialize the checksum
call GETHEX ; get MSB of address
ld h,a ; HL points to memory to be loaded
add a,b ; accumulating checksum
ld b,a ; checksum is kept in reg B
call GETHEX ; get LSB of address
ld l,a
add a,b ; accumulating checksum
ld b,a ; checksum is kept in reg B
call GETHEX ; get the record type, 0 is data, 1 is end
cp 0
jr z,filesave
cp 1 ; end of file transfer?
jp nz,unknown ; if not, print a 'U'
; end of the file load
call GETHEX ; flush the line, get the last byte
ld a,'X' ; mark the end with 'X'
call cout
ld a,10 ; carriage return and line feed
call cout
ld a,13
call cout
jp main
; jp 200h
; the assumption is the data is good and will be saved to the destination memory
filesave:
add a,b ; accumulating checksum of record type
ld b,a ; checksum is kept in reg B
filesav1:
call GETHEX ; get a byte
ld (hl),a ; save to destination
add a,b ; accumulating checksum
ld b,a ; checksum is kept in reg B
inc hl
dec d
jr nz,filesav1
call GETHEX ; get the checksum
neg a ; 2's complement
cp b ; compare to checksum accumulated in reg B
jr nz,badload ; checksum not match, put '?'
ld a,'.' ; checksum match, put '.'
filesav2:
call cout
jp main ; repeat until record end
badload:
ld a,'?' ; checksum not match, put '?'
jr filesav2
unknown:
ld a,'U' ; put out a 'U' and wait for next record
call cout
jp main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;GETHEX -- Get byte from console as hex
;
;pre: none
;post: A register contains byte from hex input
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GETHEX:
push de ; save register
CALL cinq
CALL ASCHEX
RLCA
RLCA
RLCA
RLCA
LD D,A
CALL cinq
CALL ASCHEX
OR D
pop de ;restore register
RET
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ASCHEX -- Convert ASCII coded hex to nybble
;
;pre: A register contains ASCII coded nybble
;post: A register contains nybble
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ASCHEX: SUB 30h
CP 0Ah
RET M
AND 5Fh
SUB 07h
RET
; string output
; output string pointed by HL until null terminator
strout:
ld a,(hl) ;get next character
cp 0 ;null terminator?
ret z ;return if null terminator
call cout ; put out a character
inc hl ; next char in the string
jr strout
; output char in reg A
cout:
push af ; save reg
cout1:
in a,(0f8h) ; read status
and 1 ; TxEmpty is D[0]
jr z,cout1 ; wait for TxEmpty asserted
pop af
out (0f9h),a ; put out character
ret
; input char into reg A
cin:
in a,(0f8h) ;read status
and 2 ;data ready flag is in D[1]
jr z,cin
in a,(0f9h) ; read data
call cout ; echo back
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CINQ -- Get a char from the console and no echo
;
;pre: console device is initialized
;post: received char is in A register
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cinq:
in a,(0f8h) ;read status
and 2 ;data ready flag is in D[1]
jr z,cinq
in a,(0f9h) ; read data
ret
RET
SignOn$: db 0ah,0dh,"Z80SBC Loader v0.4",0ah,0dh
db "G xxxx to run",0ah,0dh,0
db 0,0,0,0,0,0,0,0,0,0,0,0,0 ; pad to exactly 256 byte