-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathint32k.asm
395 lines (322 loc) · 16.3 KB
/
int32k.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
;==============================================================================
;
; The rework to support MS Basic HLOAD, RESET, MEEK, MOKE,
; and the Z80 instruction tuning are copyright (C) 2020-23 Phillip Stevens
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;
; ACIA 6850 interrupt driven serial I/O to run modified NASCOM Basic 4.7.
; Full input and output buffering with incoming data hardware handshaking.
; Handshake shows full before the buffer is totally filled to allow run-on
; from the sender. Transmit and receive are interrupt driven.
; 115200 baud, 8n2
;
; feilipu, August 2020
;
;==============================================================================
;
; The updates to the original BASIC within this file are copyright Grant Searle
;
; You have permission to use this for NON COMMERCIAL USE ONLY
; If you wish to use it elsewhere, please include an acknowledgement to myself.
;
; http://searle.wales/
;
;==============================================================================
;
; NASCOM ROM BASIC Ver 4.7, (C) 1978 Microsoft
; Scanned from source published in 80-BUS NEWS from Vol 2, Issue 3
; (May-June 1983) to Vol 3, Issue 3 (May-June 1984)
; Adapted for the freeware Zilog Macro Assembler 2.10 to produce
; the original ROM code (checksum A934H). PA
;
;==============================================================================
.global RST_00
.global RST_08
.global RST_10
.global INT_INT
.global PRINT
RST_00 = INIT
RST_08 = TXA
RST_10 = RXA
INT_INT = acia_int
; ACIA 68B50 Register Mnemonics
SER_CTRL_ADDR = $80 ; Address of Control Register (write only)
SER_STATUS_ADDR = $80 ; Address of Status Register (read only)
SER_DATA_ADDR = $81 ; Address of Data Register
SER_RESET = $03 ; Master Reset (issue before any other Control word)
SER_CLK_DIV_64 = $02 ; Divide the Clock by 64 (default value)
SER_CLK_DIV_16 = $01 ; Divide the Clock by 16
SER_CLK_DIV_01 = $00 ; Divide the Clock by 1
SER_8O1 = $1C ; 8 Bits Odd Parity 1 Stop Bit
SER_8E1 = $18 ; 8 Bits Even Parity 1 Stop Bit
SER_8N1 = $14 ; 8 Bits No Parity 1 Stop Bit
SER_8N2 = $10 ; 8 Bits No Parity 2 Stop Bits
SER_7O1 = $0C ; 7 Bits Odd Parity 1 Stop Bit
SER_7E1 = $08 ; 7 Bits Even Parity 1 Stop Bit
SER_7O2 = $04 ; 7 Bits Odd Parity 2 Stop Bits
SER_7E2 = $00 ; 7 Bits Even Parity 2 Stop Bits
SER_TDI_BRK = $60 ; _RTS low, Transmitting Interrupt Disabled, BRK on Tx
SER_TDI_RTS1 = $40 ; _RTS high, Transmitting Interrupt Disabled
SER_TEI_RTS0 = $20 ; _RTS low, Transmitting Interrupt Enabled
SER_TEI_RTS0_N = ~ SER_TEI_RTS0
SER_TDI_RTS0 = $00 ; _RTS low, Transmitting Interrupt Disabled
SER_TEI_MASK = ~ $60 ; Mask for the Tx Interrupt & RTS bits
SER_REI = $80 ; Receive Interrupt Enabled
SER_IRQ = $80 ; IRQ (Either Transmitted or Received Byte)
SER_PE = $40 ; Parity Error (Received Byte)
SER_OVRN = $20 ; Overrun (Received Byte
SER_FE = $10 ; Framing Error (Received Byte)
SER_CTS = $08 ; Clear To Send
SER_DCD = $04 ; Data Carrier Detect
SER_TDRE = $02 ; Transmit Data Register Empty
SER_RDRF = $01 ; Receive Data Register Full
ACIA_CONFIG_INITIAL = SER_TDI_RTS0|SER_8N2|SER_CLK_DIV_64
ACIA_CONFIG_DEFAULT = SER_REI|SER_TDI_RTS0|SER_8N2|SER_CLK_DIV_64
.section .acia_in, "rx" ; ORG $0070
;==============================================================================
;
; CODE SECTION
;
;------------------------------------------------------------------------------
acia_int:
push af
push hl
in a,(SER_STATUS_ADDR) ; get the status of the ACIA
rrca ; check whether a byte has been received, via SER_RDRF
jr NC,acia_tx_send ; if not, go check for bytes to transmit
acia_rx_get:
in a,(SER_DATA_ADDR) ; Get the received byte from the ACIA
ld l,a ; Move Rx byte to l
ld a,(serRxBufUsed) ; Get the number of bytes in the Rx buffer
cp SER_RX_BUFSIZE-1 ; check whether there is space in the buffer
jr NC,acia_tx_check ; buffer full, check if we can send something
ld a,l ; get Rx byte from l
ld hl,(serRxInPtr) ; get the pointer to where we poke
ld (hl),a ; write the Rx byte to the serRxInPtr address
inc l ; move the Rx pointer low byte along, 0xFF rollover
ld (serRxInPtr),hl ; write where the next byte should be poked
ld hl,serRxBufUsed
inc (hl) ; atomically increment Rx buffer count
ld a,(serRxBufUsed) ; get the current Rx count
cp SER_RX_FULLSIZE ; compare the count with the preferred full size
jp NZ,acia_tx_check ; leave the RTS low, and check for Rx/Tx possibility
ld a,(serControl) ; get the ACIA control echo byte
and SER_TEI_MASK ; mask out the Tx interrupt bits
or SER_TDI_RTS1 ; set RTS high, and disable Tx Interrupt
ld (serControl),a ; write the ACIA control echo byte back
out (SER_CTRL_ADDR),a ; set the ACIA CTRL register
acia_tx_check:
in a,(SER_STATUS_ADDR) ; get the status of the ACIA
rrca ; check whether a byte has been received, via SER_RDRF
jr C,acia_rx_get ; another byte received, go get it
acia_tx_send:
rrca ; check whether a byte can be transmitted, via SER_TDRE
jr NC,acia_txa_end ; if not, we're done for now
ld a,(serTxBufUsed) ; get the number of bytes in the Tx buffer
or a ; check whether it is zero
jp Z,acia_tei_clear ; if the count is zero, then disable the Tx Interrupt
ld hl,(serTxOutPtr) ; get the pointer to place where we pop the Tx byte
ld a,(hl) ; get the Tx byte
out (SER_DATA_ADDR),a ; output the Tx byte to the ACIA
inc l ; move the Tx pointer, just low byte, along
ld a,SER_TX_BUFSIZE-1 ; load the buffer size, (n^2)-1
and l ; range check
or serTxBuf&0xFF ; locate base
ld l,a ; return the low byte to l
ld (serTxOutPtr),hl ; write where the next byte should be popped
ld hl,serTxBufUsed
dec (hl) ; atomically decrement current Tx count
jr NZ,acia_txa_end ; if we've more Tx bytes to send, we're done for now
acia_tei_clear:
ld a,(serControl) ; get the ACIA control echo byte
and SER_TEI_RTS0_N ; mask out (disable) the Tx Interrupt
ld (serControl),a ; write the ACIA control byte back
out (SER_CTRL_ADDR),a ; set the ACIA CTRL register
acia_txa_end:
pop hl
pop af
ei
reti
.section .acia_rx, "rx" ; ORG $00D8
;------------------------------------------------------------------------------
; SECTION acia_rxa_chk ; ORG $00D8
;
; .RXA_CHK ; insert directly into JumP table
; ld a,(serRxBufUsed)
; ret
;------------------------------------------------------------------------------
RXA:
ld a,(serRxBufUsed) ; get the number of bytes in the Rx buffer
or a ; see if there are zero bytes available
jr Z,RXA ; wait, if there are no bytes available
cp SER_RX_EMPTYSIZE ; compare the count with the preferred empty size
jp NZ,rxa_get_byte ; if the buffer is too full, don't change the RTS
di ; critical section begin
ld a,(serControl) ; get the ACIA control echo byte
and SER_TEI_MASK ; mask out the Tx interrupt bits
or SER_TDI_RTS0 ; set RTS low.
ld (serControl),a ; write the ACIA control echo byte back
ei ; critical section end
out (SER_CTRL_ADDR),a ; set the ACIA CTRL register
rxa_get_byte:
push hl ; store HL so we don't clobber it
ld hl,(serRxOutPtr) ; get the pointer to place where we pop the Rx byte
ld a,(hl) ; get the Rx byte
inc l ; move the Rx pointer low byte along
ld (serRxOutPtr),hl ; write where the next byte should be popped
ld hl,serRxBufUsed
dec (hl) ; atomically decrement Rx count
pop hl ; recover HL
ret ; char ready in A
;------------------------------------------------------------------------------
.section .acia_tx, "rx" ; ORG $0100
TXA: ; output a character in A via ACIA:
push hl ; store HL so we don't clobber it
ld l,a ; store Tx character
ld a,(serTxBufUsed) ; Get the number of bytes in the Tx buffer
or a ; check whether the buffer is empty
jr NZ,txa_buffer_out ; buffer not empty, so abandon immediate Tx
in a,(SER_STATUS_ADDR) ; get the status of the ACIA
and SER_TDRE ; check whether a byte can be transmitted
jr Z,txa_buffer_out ; if not, so abandon immediate Tx
ld a,l ; Retrieve Tx character for immediate Tx
out (SER_DATA_ADDR),a ; immediately output the Tx byte to the ACIA
pop hl ; recover HL
ret ; and just complete
txa_buffer_out:
ld a,(serTxBufUsed) ; get the number of bytes in the Tx buffer
cp SER_TX_BUFSIZE-1 ; check whether there is space in the buffer
jr NC,txa_buffer_out ; buffer full, so wait till it has space
ld a,l ; retrieve Tx character
ld hl,(serTxInPtr) ; get the pointer to where we poke
ld (hl),a ; write the Tx byte to the serTxInPtr
inc l ; move the Tx pointer, just low byte along
ld a,SER_TX_BUFSIZE-1 ; load the buffer size, (n^2)-1
and l ; range check
or serTxBuf&0xFF ; locate base
ld l,a ; return the low byte to l
ld (serTxInPtr),hl ; write where the next byte should be poked
ld hl,serTxBufUsed
inc (hl) ; atomic increment of Tx count
pop hl ; recover HL
ld a,(serControl) ; get the ACIA control echo byte
and SER_TEI_RTS0 ; test whether ACIA interrupt is set
ret NZ ; if so then just return
di ; critical section begin
ld a,(serControl) ; get the ACIA control echo byte again
and SER_TEI_MASK ; mask out the Tx interrupt bits
or SER_TEI_RTS0 ; set RTS low. if the TEI was not set, it will work again
ld (serControl),a ; write the ACIA control echo byte back
out (SER_CTRL_ADDR),a ; set the ACIA CTRL register
ei ; critical section end
ret
;------------------------------------------------------------------------------
.section .init, "rx" ; ORG $0148
MEM_ERR:
LD L,A ; preserve the error byte
DEFB 01H ; skip "LD L,BEL"
INIT:
LD L,BEL ; prepare a BEL, to indicate normal boot
LD A,SER_RESET ; master RESET the ACIA
OUT (SER_CTRL_ADDR),A
LD A,ACIA_CONFIG_INITIAL
; load the initial ACIA configuration
; 8n2 at 115200 baud
; receive interrupt disabled
; transmit interrupt disabled
OUT (SER_CTRL_ADDR),A ; output to the ACIA control byte
LD A,L ; get byte to transmit
OUT (SER_DATA_ADDR),A ; send it
LD A,(basicStarted) ; save BASIC STARTED flag
LD HL,RAMSTART ; do a short memory sanity check
LD (HL),0FFH ; set all bits
LD DE,RAMSTART+1
LD BC,WRKSPC_OFF
LDIR ; set all bytes
DEC HL ; revert final increment
DEC DE
EX DE,HL ; swap load direction
INC (HL) ; increment to 0, check all bits of all bytes were set
JR NZ,MEM_ERR ; if not output errored byte and do it again
LD BC,WRKSPC_OFF
LDDR ; clear all bits of all bytes
LD (basicStarted),A ; restore BASIC STARTED flag
LD A,(HL)
OR A ; check if all bits of all bytes were cleared
JR NZ,MEM_ERR ; if not output errored byte and do it again
LD SP,TEMPSTACK ; set up a temporary stack
LD HL,VECTOR_PROTO ; establish Z80 RST Vector Table
LD DE,VECTOR_BASE
LD BC,VECTOR_SIZE
LDIR
LD HL,serRxBuf ; initialise Rx Buffer
LD (serRxInPtr),HL
LD (serRxOutPtr),HL
LD HL,serTxBuf ; initialise Tx Buffer
LD (serTxInPtr),HL
LD (serTxOutPtr),HL
XOR A ; zero the RXA & TXA Buffer Counts
LD (serRxBufUsed),A
LD (serTxBufUsed),A
LD A,ACIA_CONFIG_DEFAULT
; load the default ACIA configuration
; 8n2 at 115200 baud
; receive interrupt enabled
; transmit interrupt disabled
LD (serControl),A ; write the ACIA control byte echo
OUT (SER_CTRL_ADDR),A ; output to the ACIA control byte
IM 1 ; interrupt mode 1
EI ; enable interrupts
START:
LD HL,SIGNON1 ; sign-on message
CALL PRINT ; output string
LD A,(basicStarted) ; check the BASIC STARTED flag
CP 'Y' ; to see if this is power-up
JP NZ,COLDSTART ; if not BASIC started then always do cold start
LD HL,SIGNON2 ; cold/warm message
CALL PRINT ; output string
CORW:
RST 10H
AND 11011111B ; lower to uppercase
CP 'C'
JP NZ,CHECKWARM
RST 08H
LD A,CR
RST 08H
LD A,LF
RST 08H
COLDSTART:
LD A,'Y' ; set the BASIC STARTED flag
LD (basicStarted),A
JP $0240 ; <<<< Start Basic COLD
CHECKWARM:
CP 'W'
JP NZ,CORW
RST 08H
LD A,CR
RST 08H
LD A,LF
RST 08H
WARMSTART:
JP $0243 ; <<<< Start Basic WARM
PRINT:
LD A,(HL) ; get character
OR A ; is it $00 ?
RET Z ; then RETurn on terminator
CALL TXA ; output character in A
INC HL ; next Character
JP PRINT ; continue until $00
.section .init_st, "rx" ; ORG $01F0
;==============================================================================
;
; STRINGS
;
;==============================================================================
SIGNON1:
.ascii "\r\nRC2014 - MS Basic Loader\r\n"
.asciz "z88dk - feilipu\r\n"
SIGNON2:
.asciz "\r\nCold | Warm start (C|W) ? "