home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
MBUG
/
MBUG167.ARC
/
ZMP-BEE.LBR
/
RSXMAIN.IZC
/
RSXMAIN.INC
Wrap
Text File
|
1979-12-31
|
14KB
|
520 lines
; Microbee Serial I/O RSX -- Ron Murray, 12/11/88
;
; Part of the RSX system. C.B. Falconer, 85/11/18
;
; This file should be named "RSXMAIN.INC". It is the actual
; RSX code to be installed. It has available to it the routines
; TSTR (like DOS string call, but registers preserved)
; DOS (like BDOS call, but register A specified the function,
; and the bc and de registers are preserved)
; GOBDOS (Just like the original BDOS call).
; note that DOS and GOBDOS do not use the RSX code.
; DO NOT MAKE A SEPARATE dseg.
;
; Equates used only by Microbee routines grouped together here.
;
; Z80 PIO equates:
DATAB equ 2 ;PIO port B data
CTLB equ 3 ; " " " control
DTR equ 4 ;DTR output on bit 2
RSOUT equ 20h ;RS-232 " " " 5
CTS equ 8 ;CTS input " " 3
RSIN equ 10h ;RS-232 " " " 4
XOFF equ 'S'-40h
XON equ 'Q'-40h
; End of Microbee equates
stksz equ 48 ; REQUIRED equate. Customize to system.
;
; Message for example only, not required
exumsg: db 'RSX executed',cr,lf,'$'
;
; This routine MUST be supplied. It may be only a "ret".
; This is executed on each warmboot (or DOS call #0)
; a,f,b,c,d,e,h,l (allowed)
boothk: ret
;
; This routine MUST be supplied. It may be only a "ret".
; This is executed each time CPM is rebooted or the disk
; system is reset by BDOS function 13.
; a,f,b,c,d,e,h,l (allowed)
bthook:
ret
; ld de,btmsg
; jp tstr ; and exit
;
; message for example only. Not required
;btmsg: db cr,lf,'Disks reset$'
;
; This message must be supplied. It can be empty (consisting of
; only the terminal $). End it with a "$" character. This message
; is displayed whenever the CPM system reboots, provided that this
; is the last RSX installed.
actmsg: db cr,lf,'Modem RSX active$'
;
; This message must also be supplied. It is output following the
; actmsg above. This combination allows one portion to be
; dynamically modified.
altid: db '$'
;
; This message must also be supplied. It is output when the RSX
; is actually removed, either by running the program again, or
; by some other program signalling removal. Terminate with '$'.
; NOTE that the actual removal is performed on the reboot following
; the termination, and only if this is the last RSX in the chain.
donemsg:
db cr,lf,'Modem RSX removed$'
;
; This routine MUST be supplied
; The actual code for the application goes here. The stack has
; been switched, and a return value (if any) should be in (hl).
; Note that the (de) parameter can never be 0 or 0ffffh on entry.
; If this system is really an i/o driver (such as a foreign disk
; reader/writer) then the RSX itself never need do anything, and
; this code can be left alone (except that a DOS call is available
; to trigger the message below). Otherwise the registers are set
; as if receiving a BDOS call, and appropriate action can be taken.
; If this application need only modify, and/or conditionally
; intercept a normal BDOS call, it can exit to the real BDOS by
; a "jmp gobdos". In this case ensure the bc and de values are
; suitable. We never get here for de = 0 or 0ffffh.
; Return address of rec_interrupt if de=0001.
rsx: ld a,e
dec a ; whenever de=0001
or d ; then
ld hl,addtab ; return table of addresses
ret z ; (de) was 1, data pointer enquiry
ld a,c ; get function
cp @rsx ; get status?
jp z,get_stat ; yes
cp @rsxx ; Data out?
jr z,put_data ; yes
cp @rsxy ; data in?
jr z,get_data ; yes
ret ; should never get here
;Get data from modem port
get_data:
call iDATA ; get into A
ld l,a ; get into hl
ld h,0
ret
;Send data to modem port
put_data:
ld a,e ; get it into A
call oDATA ; send it
ret
;Get simulated status reg. Return with bit 3 = Receive Data Register Full
;(1 == full), bit 4 = Transmit Data Register Empty (1 == empty).
; Similar to a 6551 (Revenge!)
get_stat:
call send_stat ; get send status
ld e,0 ; clear result
cp CTS ; ready?
jr nz,getst1
set 4,e ; yes, set the bit
getst1:
push de
call rec_stat ; get receive status
pop de ; restore send stat
cp 1 ; rx ready?
jr nz,getst2
set 3,e ; yes, set the bit
getst2:
ld a,e ; status to A
ld l,a ; and hl
ld h,0
ret
;Simulated status ports:
send_stat:
in a,(DATAB)
and CTS
ret
rec_stat: ; Check if data in buffer:
ld a,(statbyte) ; 1=char-ready, 0=not-ready
or a
jr nz,rstat1
push bc
push hl
ld hl,(rs_wptr)
ld bc,(rs_rptr)
sbc hl,bc
jr z,iDATAE ; Buffer empty
pop hl
pop bc
ld a,2 ; return ready-state
ld (statbyte),a
dec a
ret
rstat1: dec a
ld (statbyte),a ; return not-ready
xor a
ret
; iDATA also handles the internal automatic XOFF/XON on impending
; buffer overflow
iDATA: ;in modem data port
push bc ;Reads input buffer
push hl
ld bc,(rs_rptr)
ld hl,(rs_wptr)
xor a
sbc hl,bc
jr z,iDATAE ; 0=empty, 1=one byte ... -1=full
inc h
jr nz,iDATA1
ld a,l
add a,20h
call c,iDATAF ; Buffer nearly full - PANIC!!
iDATA1: inc bc
res 1,b ; Determines buffer size (200H)
ld hl,rs_buff
add hl,bc
ld a,(hl) ; first read buffer
ld (rs_rptr),bc ; THEN update rptr
dec h ; Set NZ flag
pop hl
pop bc
ret
iDATAE: pop hl ; Buffer empty
pop bc
call rec_reti ; ei, in case disabled by buffer overflow
ld a,(xflag)
or a
ret z
ld a,XON
call oDATA ; send XON if remote was XOFFed
xor a ; Set Z flag
ld (xflag),a
ret
iDATAF: ld a,(xflag) ; Buffer (nearly) full
or a
ret nz ; XOFF already sent
dec a
ld (xflag),a
ld a,XOFF
; jp oDATA ; Send XOFF character
; Serial I/O routine
; register assignments: A=G.P.
; Normal register set:
; B = send delay count C = receive data count
; never 1 for rec-only 1..n if receiving, 0 if rec idle
; -1 if waiting for stop-bit
; D = send delay E = data count, send
; H = send data L = send data
; DE,HL not used on receive only
;
; Alternate register set:
; not used on send only
; B'= rec delay count C'= rec delay
; D'= rec data E'= RSIN
; L'= (ndatap) for adjustment at write
oDATA: ;out modem data port
out (9),a ;turn off wait cycling (not needed on all bees)
push af ; & let CPU run at full speed
push bc ; wait-ing initiated by writing to screen RAM
exx
push bc
push de
push hl
exx
push de
push hl
ld c,a
ld a,(ndata)
ld b,a
ld e,a ; no of data bits (send)
ld a,1
send_1: rrc c
rl l ; reverse order of bits
add a,a ; generate mask
djnz send_1
dec a ; a = mask
and l ; mask send data
jp pe,send_2
ccf ; carry = parity bit
send_2: ld a,(bparity)
dec a
jr z,send_nopar
dec a
jr nz,send_pe
ccf
send_pe:
adc hl,hl ; add parity bit to send data
inc e ; +1 to data count for parity
send_nopar:
ld a,13
sub e ; no of data bits + parity (if req)
ld b,a
send_3: add hl,hl
inc hl
djnz send_3
ld a,(stopbits)
add a,e ; +stopbits
inc a ; +1 since d=1 -> finished sending
ld e,a ; send data counter
ld c,b ; rec data count = 0
ld a,(baud+1) ; send delay
ld d,a ; send delay (for resetting)
ld b,a ; send delay counter
send_cts: in a,(DATAB)
and CTS
jr z,send_cts
ld a,DTR
ld (sendflag),a ; NZ = sending
out (DATAB),a ; output start bit (high)
jp receive
; Timing loop
; Ideally, 176 clock cycles long
; Instructions used purely for timing labled '+'
rec_write: ; receive + 21 cycles
exx ; 4\
ld a,9 ; 7 \
djnz rec_weq ; 8 | 13
sub l ; 4 | = 37
ld b,a ; 4 /
in a,(DATAB) ; 10/
jr z,rec_noadj ; 7\ 12
and e ; 4 \
sub e ; 4 |
ld a,d ; 4 | =12(9b) =35(8b) =52(7b)
rec_adj: rr a ; 4 |
djnz rec_adj ; 8 / 13
ld d,a ; 4/
rec_noadj: ld hl,(rs_rptr) ; 16\
ld bc,(rs_wptr) ; 20 \
inc bc ; 6 |
res 1,b ; 8 | dictates buffer size (200H)
xor a ; 4 |
sbc hl,bc ; 15 |
jr z,rec_full ; 7 |
ld (rs_wptr),bc ; 20 | = 196(ei) =167(no ei)
dec a ; 4 | total = 289 (8b, ei)
rec_full: ld hl,rs_buff ; 10 | hl = buffer
add hl,bc ; 11 |
ld (hl),d ; 7 | d' = data for receive
exx ; 4 |
ld c,a ; 4 | -1=ei, 0=don't ei
in a,(DATAB) ; 10 |
and RSIN ; 7 |
add a,c ; 4 /
call m,rec_ei ; 10/ 17+22
send:
djnz send_eq ; 8\ 13 b = delay counter
dec e ; 4 \ e = data counter
jr z,send_finish ; 7 | 12 e=1, finished send, pop HL,DE
add hl,hl ; 11 |
ld a,h ; 4 | total = 63
and RSOUT ; 7 |
or DTR ; 7 |
out (DATAB),a ; 11 /
ld b,d ; 4/ reset delay count
receive:
; push hl ;+11\ 2MHz only
; add hl,hl ;+11 | " "
; pop hl ;+10/ " " (total = 32)
ld a,-1 ; 7\
add a,c ; 4 | c = data counter for receive
jp z,rec_write ; 10 | c=1, last bit
jr nc,rec_idle ; 7 | 12 c=0, idle
jp m,rec_test ; 10 | c=-1, wait for RSIN to go low
exx ; 4 |
djnz rec_eq ; 8 | 13 b'= delay counter (rec)
in a,(DATAB) ; 10 | receive and record data
and RSIN ; 7 |
sub RSIN ; 7 | total = 113
rr d ; 8 |
ld b,c ; 4 | reset delay counter
exx ; 4 |
dec c ; 4 | dec data counter
ld a,(hl) ;+ 7 |
jr send ; 12/
rec_idle: ; receive + 33 cycles
ld a,(hl) ;+ 7\ rec is only waiting for send to finish
ld a,(hl) ;+ 7 |
jr rec_59 ; 12/ receive + 59
rec_weq: ; receive + 45 cycles
jp rec_eq ;+10 >
rec_eq: ; receive + 55 cycles
exx ; 4\
rec_59: ; receive + 59
nop ;+ 4 |
ex (sp),hl ;+19 |
ex (sp),hl ;+19 |
jr send ; 12/ total = 55 + 58 = 113
rec_test: ; receive + 38 cycles
in a,(DATAB) ; 10 \
and RSIN ; 7 | rec is testing RSIN
jr nz,rec_wait ; 7 | 12
call rec_ei ;17+22|
jr send ; 12 / total = 75 + 38 = 113
rec_wait: ; receive + 67 cycles
ld a,(sendflag) ; 13\ rec is waiting for input to go low (to
or a ; 4 | prevent unwanted interrupt)
jp z,rec_test ; 10 | short waiting loop
ld a,(hl) ;+ 7 |
jr send ; 12/ total = 67 + 46 = 113
rec_ei: inc c ; 4\
rec_reti: ; | rec_reti called by rec_stat
ei ; 4 |
reti ; 14/
send_finish: ; send + 24 (execute once only per send)
xor a ; 4\
ld (sendflag),a ; 13 |
pop hl ; 10 |
pop de ; 10 |
jp receive ; 10/ total = 47 + 24 = 63 + 8
send_eq: ; send + 13
ld a,(sendflag) ; 13\
or a ; 4 |
jp z,send_idle ; 10 |
ld a,(hl) ;+ 7 |
nop ;+ 4 |
jr receive ; 12/ total = 13 + 50 = 63
send_idle: ; send + 40
ld b,0FFh ; 7\ stay on send_eq path
or c ; 4 |
jr nz,receive ; 7/ 12 total = 40 + 23 = 63
exit: ; send + 58
exx ; 4\
pop hl ; 10 |
pop de ; 10 |
pop bc ; 10 | = 68
exx ; 4 |
pop bc ; 10 |
pop af ; 10 |
ret ; 10/
; send + 126 = receive + 415 (min) via write
rec_interrupt: ; 19\ cycles to respond to interrupt
out (9),a ; 11 |
push af ; 11 |
ld a,(sendflag) ; 13 | = 68
or a ; 4 |
jp nz,rec_wsend ; 10/
push bc ; 11\
ld b,a ; 4 | b = send delay count = 0 (disable send)
ld a,(npdata) ; 13 |
ld c,a ; 4 | c = rec data count
exx ; 4 |
push bc ; 11 |
push de ; 11 |
push hl ; 11 | = 126
ld l,a ; 4 | l' = (npdata) used in adjusting
ld de,RSIN ; 10 | d' = data for rec
ld a,(baud) ; 13 |
ld c,a ; 4 | c' = rec delay (for resetting counter)
rra ; 4 | a' = c'/2 (carry = 0 from "or a")
add a,c ; 4 | = delay x 1.5 for 1st bit
ld b,a ; 4 | b' = rec delay counter
exx ; 4 |
jp nz,receive ; 10/ rec_interrupt + 194
; Special handlng of delay=0 (75 baud)
exx
ld b,128
exx
inc c ; read extra data bit at start
jp receive
rec_wsend: ; rec_interrupt + 68 cycles
ld a,(npdata) ; 13\
ld c,a ; 4 | c = rec data counter
exx ; 4 |
ld l,a ; 4 | l' = (npdata) for adjusting
ld de,RSIN ; 10 | d' = data for rec
ld a,(baud) ; 13 | = 95
ld c,a ; 4 | c' = rec delay (for resetting counter)
rra ; 4 | a' = c'/2 (carry = 0 from "or a")
add a,c ; 4 | = delay x 1.5 for 1st bit
ld b,a ; 4 | b' = rec delay counter
jr z,rec_75 ; 7 | 12
exx ; 4 |
pop af ; 10 |
ret ; 10/ rec_interrupt + 163
rec_75: ld b,128 ; Special handling for delay=0 (75b)
exx
inc c ; read extra data bit at start
pop af
ret
;
; ------------------- Initialized storage --------------------------
;
; This initialized byte is required
active: db true ; Non zero if active. allows 1..255
;
; ==================================================================
; **** Put initialized application storage past this point ****
; ==================================================================
;
;
;Table of addresses: address of this returned by hl when de=1
addtab:
dw rec_interrupt
dw bparity
dw stopbits
dw ndata
dw npdata
dw baud
; Microbee serial I/O routines - Data area
sendflag: db 0 ; NZ while sending data
xflag: db 0 ; indicates if automatic XOFF sent
baud: dw 4040h ; 300 baud
ndata: db 8 ; 8 data bits
npdata: db 8 ; (ndata) + parity bit (if req)
stopbits: db 1 ; 1 stop bit
bparity: db 1 ; 1=none, 2=odd, else even
rs_rptr: dw 0 ; read pointer into buffer
rs_wptr: dw 0 ; write " " "
oldvcb: dw 0 ; Storage for old interrupt vector
statbyte: db 0 ; counter for 0's returned by rec_stat after 1
; ------------------ End initialized storage -----------------------
;
; ------------------- Uninitialized storage ------------------------
;
; Required equate. Nothing from here on but "DS" statements.
segsiz equ $-@base ; Relocation bit table starts here
;
; ==================================================================
; **** Put uninitialized application storage past this point ****
; ==================================================================
;
; NOTE - If more that 32 bytes of stack are required see RSXMAST.Z80
; Buffer for incoming data
rs_buff: ds 200h
;
; -------------- End application uninitialized storage -------------