I talked with Ewan at MicroBee, and he handed me source code to handle the MicroBee's bit-banged RS232 port:
It's a mini-terminal, but hopefully the relevant bits can be extracted.
Code: Select all
;************************************************************************
;** **
;** Simple terminal for the microbee. **
;** (or how to use the rs232 routines) **
;** **
;*******************************************************(C)EJW****19/7/89
; the program initialises itself for 1200 baud, if 2400 or
; 4800 baud are required, call the relevant init subroutines
; (init_2400 and init_4800)
; It is set to 8 data bits , no parity
.z80
aseg
org 0100h
bdos equ 5
;initialise program bits....
call look_col
call cls ;clear the screen
call rs_init ;initialise the rs232 queue and interupts ..
call init_screen ;sign on and select baud rate
term_loop:
call rs_in ;see if any data available at rs232 input queue
jr nz,nochar ;nz means no char available
call conout ;give character to screen
nochar: call conin ;look for char from keyboard
jr z,nokey ;z means no key available
call rs8_out ;give char (in A) to rs232 output.
nokey: jr term_loop ;jump back to start the terminal loop again
look_col:
ld a,64
out (8),a
ld hl,0f800h
ld a,(hl)
ld (col_sto),a
ld a,0
out (8),a
ret ;this saves current color for use in term.
cls: ld a,26
call conout ;clear screen and home cursor
ret
conin: push bc
push de
push hl
ld e,255
ld c,6 ;BDOS function 6 direct console I/O
call bdos
pop hl
pop de
pop bc
cp 0
ret
conout: push af
push bc
push de
push hl
ld e,a
ld c,2 ;BDOS function 2 screen output
call bdos
pop hl
pop de
pop bc
pop af
ret
init_screen:
ld a,64
out (8),a
ld hl,0f800h
ld de,0f801h
ld bc,1919
ld a,15
ld (hl),a
ldir ;set color to white on grey
ld a,0
out (8),a
ld a,10
ld b,6
downlp: call conout
djnz downlp
call spaceout
ld hl,mess1
call prnmes
ld hl,mess2
call prnmes
ld hl,mess3
call prnmes
ld hl,mess4
call prnmes
ld hl,mess5
call prnmes
ld hl,mess6
call prnmes
ld hl,mess1
call prnmes
ld hl,l1200
ld (simult),hl ;initialise for 1200/2400
wait_lp:
call conin
jr z,wait_lp
cp '1'
jp z,go_term_loop
cp '2'
jp z,init_2400
cp '3'
jp z,init_4800
cp 27
jp z,wboot
jp wait_lp
wboot: call cls
ld c,0
jp bdos
spaceout:
ld a,32
ld b,16
space_loop:
call conout
djnz space_loop
ret
init_2400:
ld a,13
ld (brpat),a ;set l1200 to 2400 baud
ld hl,1*256+33 ;change delay for rec routine
ld (semi_del),hl
ld hl,1*256+85
ld (full_del),hl
jp go_term_loop
init_4800:
ld hl,1*256+15
ld (semi_del),hl
ld hl,1*256+42
ld (full_del),hl
ld hl,l4800
ld (simult),hl
jp go_term_loop
go_term_loop:
call cls
ld a,64
out (8),a
ld hl,0f800h
ld de,0f801h
ld bc,1919
ld a,(col_sto)
ld (hl),a
ldir ;restore original color
ld a,0
out (8),a
jp term_loop
prnmes:
ld a,27
call conout
ld a,')'
call conout
ld b,32
prnlp: ld a,(hl)
call conout
inc hl
djnz prnlp
ld a,27
call conout
ld a,'('
call conout
ld a,10
call conout
ld a,13
call conout
call spaceout
ret
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;:: R S - 2 3 2 R O U T I N E S ::
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;These routines implement full-duplex communication.
nstrt equ 1 ;number of start bits (always 1)
ndata equ 8 ;number of data bits (7 or 8)
npar equ 0 ;number of parity bits
par_typ equ 1 ;parity type (1=none,2=odd,3=even)
nstop equ 1 ;number of stop bits
baud_rt equ 1 ;baud rate (1=1200,2=2400,3=4800)
;Bits of interest on PIO B data port
cass_in equ 0
cass_out equ 1
ser_clk equ 2
ser_cts equ 3
ser_in equ 4
ser_out equ 5
spker equ 6
net_bit equ 7
piob_data equ 2
piob_ctl equ 3
q_start equ 0a000h
q_size equ 01000h
piob_vec equ 0df4ah
vwait_off equ 9
;Disk version has bit 7 as output for network
norm_mask equ (1 shl cass_in) or (1 shl ser_cts) or (1 shl ser_in)
;Character to use when received parity is illegal
par_nok equ 7fh
;Equates from the master program
;q_start start of RS-232 queue storage
;q_size RS-232 queue size
;**************************************************
;** Output a character to the RS-232 port **
;**************************************************
RS8_out:
;Output a character to the RS-232 port
push af ;save regs
push bc
push de
push hl
push ix
exx
push af
push bc
push de
exx
ld l,0 ;start bit
ld b,8 ;number of data bits
; res 7,a ;kill top bit of character to send
eight8: rrca ;shift lsb of char into C (parity remains same)
adc hl,hl ;now put into lsb of HL
djnz eight8 ;and loop for number of valid data bits
; or a ;set P flag (and clear C)
; jp pe,send_par8 ;if parity is even already, jump
; scf ;set C to one for new parity
send_par8:
; adc hl,hl ;install the parity bit
ld b,13-nstrt-ndata-npar ;get starting bit position
align8: scf ;fill up rest of image with ones
adc hl,hl
djnz align8
ld d,nstrt+ndata+npar+nstop
;total number of bits to transmit
ld e,4 ;number of loops between bits
exx
ld de,0 ;not receiving at the moment
exx
in a,(piob_data) ;get current data of port
ld b,a ;and save
res ser_out,b ;set RS-232 output high (spacing)
; set 3,b ;so automodem (hayes ) can transmit
ld ix,(simult) ;address of timing routine
out (vwait_off),a ;stop video waiting
di ;no interrupts at this point
call jp_sim ;do the output/input
ei ;interrupts can occur now
; call key_update ;and scan the keyboard
; xor a ;clear eol counter as most keypresses
; ld (eol_cnt),a ;cause lots of characters to come back
exx
pop de
pop bc
pop af
exx
pop ix
pop hl
pop de
pop bc
pop af
ret
jp_sim: jp (ix)
l1200:
;1200 baud output routine
call txrx12 ;input/send a bit
;*********************************
db 03eh
brpat: db 34 ;ld a,34 ;adjusted for no rx
l12001: dec a
jr nz,l12001
or a ;4
or a ;4
ld a,0 ;7
ld a,0 ;7
;************************************************
;34 T-cycles
ld a,d ;4 get number of transmitted bits to go
or e ;4
exx ;4
or d ;4
or e ;4
exx ;4
jp nz,l1200 ;10 if not zero, jump
ret
l4800:
call txrx12
ld a,d
ld a,d
ld a,d
ld a,d
ld a,d
ld a,d
;************************************************
;34 T-cycles
ld a,d ;4 get number of transmitted bits to go
or e ;4
exx ;4
or d ;4
or e ;4
exx ;4
jr nz,l4800 ;7 if not zero, jump
ret
txrx12:
;353 t-cycles
;Transmit section
ld a,d ;4 finished transmitting?
or e ;4
jr z,ndly1_hay ; jump if we have
;7
ld a,e ;4 ready to send a bit?
or a ;4
jr nz,ndly2_hay ; jump if not
;7
add hl,hl ;11 shift transmit image by one bit
ld a,h ;4 create PIO data
and 1 shl ser_out ;7
or b ;4
out (piob_data),a ;11 change the output bit
dec d ;4 dec number of bits transmitted
ld e,3 ;7
nret1_hay:
; dec e ;4 dec counter for time between bits
ret
;Receive section
exx ;4 switch to regs for receive
ld a,d ;4 receive in progress?
or e ;4
jp z,start_det_hay ; if not, look for a start bit
;7
ld a,e ;4 time to recive a bit?
or a ;4
jp nz,ndly3_hay ; jump if not
;7
in a,(piob_data) ;11 input the bit
and 1 shl ser_in ;7
sub 1 ;7
rr c ;8
dec d ;4 dec number of bits to receive
ld e,4 ;7
jp nz,ndly4_hay ; jump if not end of received char
;7
call stor_rs ;147 store char in queue
ld de,0*100h+3 ;10 wait for end of character
ld a,0 ;7
or a ;4
nret3_hay:
dec e ;4 dec counter for time between bits
exx ;4 switch regs back
ret ;10
start_det_hay:
;start bit detection
;259 t-cycles
in a,(piob_data) ;11
bit ser_in,a ;8
jr z,mark_hay ; jump if no start bit
;7
ld de,9*100h+1 ;10 set number of bits to receive
; (start + data + parity- start bit gets
; dumped )
ld a,0 ;7
nretm_hay:
ld a,5 ;7
startb1_hay:
bit 0,(ix+1) ;20*5=100
dec a ;4*5=20
jr nz,startb1_hay ;12*4+7=55
bit 0,(ix+1) ;20
exx ;4
ret ;10
mark_hay:
;12
jp nretm_hay ;12
ndly1_hay:
;70 t-cycles
;12
ld a,0 ;7
ld a,0 ;7
ld a,0 ;7
ld a,0 ;7
ld a,0 ;7
ld a,0 ;7
or a ;4
or a
jp nret1_hay ;12
ndly2_hay:
;55 t-cycles
;12
or a ;4
or a ;4
or a ;4
or a ;4
or a ;4
or a ;4
dec e ;4
ld a,0 ;7
jp nret1_hay ;12
ndly3_hay:
;226 t-cycles
;12
ld a,5 ;7
ndly31_hay:
bit 0,(ix+1) ;20*5=100
dec a ;4*5=20
jr nz,ndly31_hay ;12*4+7=55
bit 0,(ix+1) ;20
jp nret3_hay ;12
ndly4_hay:
;175 t-cycles
;12
ld a,4 ;7
ndly41_hay:
bit 0,(ix+1) ;20*4=80
dec a ;4*4=16
jr nz,ndly41_hay ;12*4+7=43
ld a,0 ;7
jp nret3_hay ;10
;**********************************************************
;** Get the next character out of the RS-232 queue **
;**********************************************************
; return char in A and Z, or NZ if no char available from q
RS_in:
call RS_inst ;get the RS-232 input port status
ret nz ;return if not avail
push de
push hl
di
ld hl,(r_ptr) ;get pointer out of queue
xor a ;set Z
ld e,(hl) ;get the character
inc hl
ld a,h
and 0fh
or 0f0h and high q_start
ld h,a
ld (r_ptr),hl
ei
; ld a,e
; or a ;see if parity is valid
; jp pe,parity_ok ;jump if is OK
; ld e,par_nok+80h ;else get illegal parity char + flag
parity_ok:
; res 7,e ;kill parity bit
xor a
ld a,e
pop hl
pop de
ret
RS_inst:
;Return NZ if if no character available, or Z if avail
push de ;save regs
push hl
di
ld de,(w_ptr) ;get pointer into the queue
ld hl,(r_ptr) ;get pointer out of queue
or a
sbc hl,de ;see if pointing to same byte
ld a,0 ;if not the same, we have to return Z
jr nz,RS_ex ;if this is so, jump
dec a ;if the same, return NZ
RS_ex:
or a
ei
pop hl ;else return
pop de
ret
;********************************************
;** RS-232 receive interrupt routine **
;********************************************
;interrupt routine which receives an asynchronous character.
;Rotate in only the number of actual data bits,
;if there is a parity bit specified, then wait one bit time
;extra using full_del
;(if this is a false alarm, then ignore it)
int_rtn:
out (vwait_off),a ;kill wait states IMMEDIATELY!!!!
push af ;and all the regs
push bc
push de
push hl
in a,(piob_data) ;get the data
and 1 shl ser_in
jr z,abort ;if a false alarm (??), ignore it
;Adjust semi_del appropriately to give exactly one half bit time
ld hl,(semi_del) ;wait for half a bit time
llp1: dec l
jr nz,llp1
dec h
jr nz,llp1
ld e,8 ;get number of data bits + parity into E
data_lp:
;Adjust full_del to give exactly one bit time
ld hl,(full_del) ;get full bit delay time
llp2: dec l
jr nz,llp2
dec h
jr nz,llp2
in a,(piob_data) ;get the data
and 1 shl ser_in
sub 1
rr c ;into C
dec e ;dec number of bits
jr nz,data_lp ;loop till we get the lot
ld hl,(full_del) ;delay for a full bit time for parity bit
llp3: dec l
jr nz,llp3
dec h
jr nz,llp3
call stor_rs ;store char in C in queue
xor a ;and clear EOL counter
ld (eol_cnt),a
abort:
pop hl
pop de
pop bc
pop af
ei
noint:
reti
stor_rs:
;given char to save in c, store it on the queue
;(all paths take equal time - 147 t-cycles)
ld hl,(w_ptr) ;16 get pointer into the queue
ld (hl),c ;7 put the character onto the queue
ld de,(r_ptr) ;20 get pointer out of the queue
inc hl ;6
ld a,h ;4
and 0fh ;7
or 0f0h and high q_start ;7
ld h,a ;4
or a ;7
push hl ;11
sbc hl,de ;15 see if the same
pop hl ;10
jr z,stor_rs1 ; jump if they are
;7
ld (w_ptr),hl ;16 and re-save it
ret ;10 and return
stor_rs1:
;12
ld a,b ;4
ld a,0 ;7
ret ;10
dly_milli:
;Delay DE milliseconds
push bc
dly_lp:
ld b,0
djnz $
dec de
ld a,d
or e
jr nz,dly_lp
pop bc
ret
rs_init:
;Initialize for the RS-232 routines
di ;no interrupts
call disk_type
jr z,k56_1
ld a,4 ;get PIO B interrupt vector address
rst 28h
inc hl
inc hl
ld e,(hl) ;get old vector
inc hl
ld d,(hl)
ld (old_vec),de ;save old vector
ld de,int_rtn ;now install the new vector
ld (hl),d
dec hl
ld (hl),e
jr k128_1
k56_1: ld hl,(piob_vec) ;get current vector
ld (old_vec),hl ;save it
ld hl,int_rtn ;get new vector
ld (piob_vec),hl ;and install it
k128_1:
call noint ;do a RETI
call disk_type
jr z,k56_2
ld a,l
jr k128_2
k56_2:
ld a,low piob_vec ;interrupt vector
k128_2:
out (piob_ctl),a
ld a,10110111b ;wait for a 1,enable ints and mask follows
out (piob_ctl),a
ld a,not (1 shl ser_in) ;interrupt mask
out (piob_ctl),a
;Initialize the RS-232 queue
ld hl,q_start
ld (r_ptr),hl
ld (w_ptr),hl
ld hl,1*256+65
ld (semi_del),hl
ld hl,1*256+171
ld (full_del),hl
ld hl,l1200 ;address of the routine
ld (simult),hl
ei
ret
disk_type:
;Return Z if on CIAB/56k/workstation else return NZ
ld a,(2)
cp 0d6h
ret
simult: ds 2
w_ptr: ds 2
r_ptr: ds 2
old_vec: ds 2
eol_cnt: ds 1
full_del: ds 2
semi_del: ds 2
col_sto: db 0
mess1: db ' '
mess2: db ' Microbee Simple Terminal. '
mess3: db ' Press (1) - 1200 Bd '
mess4: db ' (2) - 2400 Bd '
mess5: db ' (3) - 4800 Bd '
mess6: db ' or [ESC] for CP/M '
end