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
/
BEEHIVE
/
COMMS
/
MEX4BEE.ARC
/
MXOBEE15.ASM
< prev
next >
Wrap
Assembly Source File
|
1990-09-20
|
40KB
|
1,441 lines
; Title 'MEX114 overlay for the Microbee version 1.51'
maclib z80
REV equ 15 ; overlay revision number
;MEX114 Microbee Overlay
; Version 1.0: May 31, 1986 adapted from PMMI overlay v2.2 by George Hansper
; " 1.1: June 21, 1986 revised by G.H. & Julian Loke
; " 1.2: 86.08.23 auto-dial added, rewrote I/O - G.H.
; " 1.3: 86.08.25 fixed small bug - G.H.
; " 1.4: 87.03.24 Made dialling TELCOM compatible, modified I/O - G.H.
; " 1.5: 87.07.11 Auto-setting of port-B interrupt vect, - G.H.
; " 1.51:87.08.20 Auto clearing of buffer during disconnect - S.C.
; Modified to assemble with DRI MAC.COM
; Added ESC-@ send break
; Made interrupt setting work on cassete out bit
; instead of speaker bit to stop the thing clicking
; Increased maximum wait allowed for carrier
; Made modem go OFFLINE if dialling aborted
; Added optional fast dialling
;
;------------------------ C O P Y R I G H T ---------------------------
; This overlay is hereby placed in the public domain.
; Any persons wishing to use the serial I/O routines contained herein
; for any purposes (commercial or otherwise), feel free to do so.
; This does not extend to the MEX set of programs, which have their
; own copyright
;----------------------------------------------------------------------
;---------------- Suggested Installation Procedure: -------------------
; You will need:
; (this overlay)
; the MEX set of programs (Sigm 218, MEX114 is in Sigm 214)
; Z80.LIB
; MAC.COM (dri)
; MLOAD.COM
;
; First, edit this overlay to suit your system:
; if you have a 2MHz bee:
; include the instuctions noted after the "receive:" label
; modify each entry to the "setbds:"/"setbdt:" table
; as per this example -
; db '30',0b0h,64,1
; db '45',0b0h,43,2
; becomes:
; db '30',0b0h,32,1
; db '45',0b0h,21,2
; (all delay constants are halved)
; except the delay for 75b, 0 becomes 128
; (do not change the special handler at the end of rec$interrupt)
; NOTE: the maximum baud rate attainable is now 9600 (not 19200)
; endif
; if your modem supports TELCOM-type dialling:
; set DIALON to DTR
; else
; set DIALON to 0 (as supplied)
; endif
; you may wish to edit the CLREOS routine if your console does not
; use ^W for clear to end-of-screen
;
; To construct a working copy of MEX, I used the following sequence:
;
; MAC MXOBEE15 $PZ -S
; MLOAD MEX=MEX,MXOBEE15
;----------------------------------------------------------------------
;------------------------ User Notes --------------------------------
; This overlay implements a full-featured MEX. Please refer to
; the MEX commands 'HELP','?' and 'SET ?' for user instructions.
; It is intended for use on non-smart modems, so the auto-baud rate
; selection only affects MEX, and not your modem (ie change your
; modem mode manually). The same goes for the 'SET FLIP' command.
; Some extra signals are required when dialling, (see below) but
; these are available on most non-smart modems.
; One non-standard variation has been included: specifying a baud-rate
; of 110 in the PHONE library will actually set send=75, receive=1200
; when the DIAL or CALL commands are used.
; Overlay version 1.5 does NOT need to be 'customized' to suit your
; operating system. (This was done by setting INTVCB in version 1.4
; and earlier.)
;----------------------------------------------------------------------
;------------------------ Notes on Auto-Dialling ----------------------
; The mode of dialling is configured by the label DIALON. In overlay version
; 1.5 this is stored at memory location 1B4h, (so you can easily change it)
; DIALON may be set to DTR or 0.
;
; In addition, the following two signals are essential:
; 1. the CTS input (RS232 pin 5) on the 'Bee is used to indicate the
; presence of carrier (HIGH=carrier present)
; 2. the CLK output (RS232 pin 24) on the 'Bee is used to place the
; the modem ONLINE ('off-hook') and OFFLINE ('on-hook')
; (HIGH=ONLINE)
;
; Method 1:
; DIALON = 0 ; (1B4h)=0
; The dialling pulses are simulated by switching the modem ONLINE
; and OFFLINE in the appropriate sequence.
; The dialling pulses will appear on pin 24 as follows:
; DTR pin 24 CLK PHONE LINE equivalent to
; 0 low on-hook 'break'
; 1 high off-hook 'make'
; Dialling in this manner does NOT meet the Telecom specification
; on dialling, but it works.
; The RS232 connector should be wired (more-or-less):
; BEE MODEM
; I/O Signal Pin Pin Signal I/O
; Out TDX 2 ---- 2 TDX In
; In RDX 3 ---- 3 RDX Out
; +5V RTS 4 ---- 4 RTS In
; In CTS (Used for DCD) 5 ---- 8 DCD (carrier detect) Out
; - GND 7 ---- 7 GND -
; Out CLK (Used for DTR) 24 ---- 20 DTR (on/off hook) In
;
; Make sure your modem does use the DCD and DTR pins as above.
; If not, use whatever pins perform these functions.
;
;Method 2:
; DIALON = DTR ; (1B4h)=4
; This method of dialling is equivalent to that used by TELCOM.
; The DTR output (pin 24 CLK) will be placed in a high
; resistance (Hi-Z) state for the duration of dialling.
; The hardware should detect this condition and place the modem
; in an 'off normal' state.
; The pulses will appear on the RSOUT output (pin 2 TDX):
; pin 24 CLK RSOUT pin 2 TDX PHONE LINE
; Hi-Z 1 low 'make'
; Hi-Z 0 high 'break'
; Except while dialling, CLK and CTS are used as described above.
;----------------------------------------------------------------------
; Equates used only by Microbee routines grouped together here.
;
; Z80 PIO equates:
CTLA equ 1 ;PIO port A control
DATAB equ 2 ; " " B data
CTLB equ 3 ; " " " control
DTR equ 4 ;DTR output on bit 2 (PIO port B)
RSOUT equ 20h ;RS-232 " " " 5
CTS equ 8 ;CTS input " " 3
RSIN equ 10h ;RS-232 " " " 4
CASOUT equ 02h ;Cassette output on bit 1 (only used for
;setting up the interrupts
XOFF equ 'S'-40h
XON equ 'Q'-40h
; Misc equates
NO equ 0
YES equ 0FFh
TPA equ 100h
CR equ 13
LF equ 10
BS equ 8
TAB equ 9
ESC equ 27
;User options... the following equates will change the character
;of your overlay.
DIALON equ 0 ;DTR ;bit used for 'off-normal' (dialling) state
FAST$DIAL equ YES ;YES = dial at twice normal rate
; MEX function codes:
;
MEX equ 0D00h ;address of the service processor
INMDM equ 255 ;get char from port to A, CY=no more in 100 ms
TIMER equ 254 ;delay 100ms * reg B
TMDINP equ 253 ;B=# secs to wait for char, cy=no char
CHEKCC equ 252 ;check for ^C from KBD, Z=present
SNDRDY equ 251 ;test for modem-send ready
RCVRDY equ 250 ;test for modem-receive ready
SNDCHR equ 249 ;send a character to the modem (after sndrdy)
RCVCHR equ 248 ;recv a char from modem (after rcvrdy)
LOOKUP equ 247 ;table search: see CMDTBL comments for info
PARSFN equ 246 ;parse filename from input stream
BDPARS equ 245 ;parse baud-rate from input stream
SBLANK equ 244 ;scan input stream to next non-blank
EVALA equ 243 ;evaluate numeric from input stream => hl
LKAHED equ 242 ;get nxt char w/o removing from input
GNC equ 241 ;get char from input, cy=1 if none
ILP equ 240 ;inline print
DECOUT equ 239 ;decimal output
PRBAUD equ 238 ;print baud rate
;
CONOUT equ 2 ;simulated BDOS function 2: console char out
PRINT equ 9 ;simulated BDOS function 9: print string
INBUF equ 10 ;input buffer, same structure as BDOS 10
org TPA ;we begin
ds 3 ;MEX has a JMP START here
; The following variables are located at the beginning of the program
; to facilitate modification without the need of re-assembly. They will
; be moved in MEX 2.0.
;
PMODEM: db NO ;yes=PMMI modem \ / These 2 locations are not
SMODEM: db NO ;yes=Smartmodem / \ referenced by MEX
TPULSE: db 'P' ;T=touch, P=pulse (not referenced by MEX)
CLOCK: db 34 ;clock speed x .1, up to 25.5 mhz.
MSPEED: db 5 ;sets display time for sending a file
;0=110 1=300 2=450 3=600 4=710
;5=1200 6=2400 7=4800 8=9600 9=19200
BYTDLY: db 0 ;default time to send character in
;terminal mode file transfer (0-9)
;0=0 delay, 1=10 ms, 5=50 ms, 9=90 ms
CRDLY: db 5 ;end-of-line delay after CRLF in terminal
;mode file transfer for slow BBS systems
;0=0 delay, 1=100 ms, 5=500 ms, 9=900 ms
COLUMS: db 5 ;number of directory columns
SETFL: db YES ;yes=user-defined SET command
SCRTST: db YES ;yes=if home cursor and clear screen
;routine at CLRSCRN
db 0 ;was once ACKNAK, now spare
BAKFLG: db NO ;yes=make .BAK file
CRCDFL: db YES ;yes=default to CRC checking
;no=default to Checksum checking
TOGCRC: db YES ;yes=allow toggling of Checksum to CRC
CVTBS: db NO ;yes=convert backspace to rub
TOGLBK: db YES ;yes=allow toggling of bksp to rub
ADDLF: db NO ;no=no LF after CR to send file in
;terminal mode (added by remote echo)
TOGLF: db YES ;yes=allow toggling of LF after CR
TRNLOG: db NO ;yes=allow transmission of logon
;write logon sequence at location LOGON
SAVCCP: db NO ;yes=do not overwrite CCP
LOCNXT: db NO ;yes=local cmd if EXTCHR precedes
;no=not local cmd if EXTCHR precedes
TOGLOC: db YES ;yes=allow toggling of LOCNXTCHR
LSTTST: db YES ;yes=allow toggling of printer on/off
;in terminal mode. Set to no if using
;the printer port for the modem
XOFTST: db YES ;yes=allow testing of XOFF from remote
;while sending a file in terminal mode
XONWT: db NO ;yes=wait for XON after sending CR while
;transmitting a file in terminal mode
TOGXOF: db YES ;yes=allow toggling of XOFF testing
IGNCTL: db YES ;yes=do not send control characters
;above CTL-M to CRT in terminal mode
;no=send any incoming CTL-char to CRT
EXTRA1: db 0 ;for future expansion
EXTRA2: db 0 ;for future expansion
BRKCHR: db '@'-40h ;^@ = Send a 300 ms. break tone
NOCONN: db 'N'-40h ;^N = Disconnect from phone line
LOGCHR: db 'L'-40h ;^L = Send logon
LSTCHR: db 'P'-40h ;^P = Toggle printer
UNSVCH: db 'R'-40h ;^R = Close input text buffer
TRNCHR: db 'T'-40h ;^T = Transmit file to remote
SAVCHR: db 'Y'-40h ;^Y = Open input text buffer
EXTCHR: db '^'-40h ;^^ = Send next character
db 0 ;not used
db 0 ; " "
;
; Low-level modem I/O routines: this will be replaced with
; a jump table in MEX 2.0 (you can insert jumps here to longer
; routines if you'd like ... I'd recommend NOT putting part of
; a routine in this area, then jumping to the rest of the routine
; in the non-fixed area; that will complicate the 2.0 conversion)
;
INCTL1: ret ;in modem control port, 10 bytes
db 0,0,0,0,0,0,0,0,0
OTDATA: jmp oDATA
db 0,0,0,0,0,0,0
INPORT: jmp iDATA
db 0,0,0,0,0,0,0
; Bit-test routines. These routines were used to mask & test the status of
; the byte returned by INTCTL1. The MASKx routines now also get the required
; status, as well as masking it - G.H.
;
MASKR: jmp rec$stat ;bit to test for receive ready
TESTR: cpi 1 ;value of receive bit when ready
ret
MASKS: jmp send$stat ;bit to test for send ready
TESTS: cpi CTS ;value of send bit when ready
ret
; Save space by using these 12 spare bytes for send$stat
; send$stat is not called until there is data to output (eg key pressed)
send$stat:
call ONLINE ; Set DTR before sending data
mvi a,CTS
; in DATAB ; modified so that terminal doesn't
; hang if there is no carrier (this
; condition could prevent disconnect)
ani CTS
ret
ds 12-($-send$stat) ; unused but necessary (MDM compat)
; Special modem function jump vectors
LOGON: ds 2 ;needed for MDM compat, not ref'd by MEX
DIALV: jmp PDIAL ;dial digit in A
DISCV: jmp OFFLINE ;disconnect the modem
GOODBV: jmp RESTORE ;called before exit to CP/M
INMODV: jmp INITMOD ;initialization. Called at cold-start
NEWBDV: jmp PBAUD ;set baud rate
NOPARV: jmp NOPAR ;set modem for no-parity
PARITV: jmp PARITY ;set modem parity
SETUPV: jmp SETCMD ;SET cmd: jump to a RET if you don't write SET
SPMENV: ds 3 ;not used with MEX
VERSNV: jmp SYSVER ;Overlay's voice in the sign-on message
BREAKV: jmp PBREAK ;send a break
; MDM compatible routines:
ILPRTV: ds 3 ;replace with MEX function 9
INBUFV: ds 3 ;replace with MEX function 10
ILCMPV: ds 3 ;replace with table lookup funct. 247
INMDMV: ds 3 ;replace with MEX function 255
NXSCRV: ds 3 ;not supported by MEX (returns w/no action)
TIMERV: ds 3 ;replace with MEX function 254
;
;
; Clear/screen and clear/end-of-screen. Each routine must use the
; full 9 bytes alloted (may be padded with nulls).
;
CLREOS:
call ILPRT ;Clear to end of screen code
db 01bh,'Y',0
ret
ds 2
CLS: mvi a,'Z'-40h ;CLS code
jmp TYPE
; ds 4
;------------------------------------------------------------
;
; *** END OF FIXED FORMAT AREA ***
;
;------------------------------------------------------------
; Microbee serial I/O routines - Data area
sendflag: db 0 ; NZ while sending data
xflag: db 1 ; indicates if automatic XOFF sent. Initialised
; to NZ so that X-ON sent on first logon
baud: db 10h ; Receive baud rate (1200 baud)
db 10h ; Send baud rate (1200 baud)
ndata: db 8 ; 8 data bits
npdata: db 8 ; (ndata) + parity bit (if req)
stopbits: db 1 ; 1 stop bits
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
ans$time: db 30 ; timeout for answer in seconds (max. 50 secs)
dial$abort: db 0 ; flag for ^C during dial
dialtype: db DIALON ; Dialling is hook-toggle if 0, TELCOM if DTR
;Simulated status ports:
; Before MEX will start any (terminal) send/rec, it must first read at least
; 3 rec-not-ready states.
; Furthermore, if more than ~280h (D4h chars) consecutive rec-ready states
; are read, MEX will only display every 3rd byte read from the buffer
; unless two not-ready states are given for every ready state!!!
; I have noted that once a char-ready state is sent, iDATA is called, but
; MASKr (ie rec$stat) is called twice before the character is actually
; displayed. The buffer is not read again (even if rec$stat returns
; ready states) until the third time rec$stat is called.
; In summary, for efficient, reliable operation rec$stat must return two
; not-ready states straight after returning a ready-state
; MDM730 does not suffer from this peculiarity
;
rec$stat: ; Check if data in buffer
lda statbyte ; 1=char-ready, 0=not-ready
ora a
jrnz rstat1
push b
push h
call bufftest ; Test for data in buffer, NZ=yes
pop h
pop b
rz ; Z = buffer empty, return A=0
mvi a,2 ; return ready-state
sta statbyte
dcr a ; data in buffer, return A=1
ret
rstat1: dcr a
sta statbyte ; return not-ready
xra a
ret
; iDATA also handles the internal automatic XOFF/XON on impending
; buffer overflow
iDATA: ;in modem data port
push b ;Reads input buffer
push h
call bufftest ; data in buffer? returns BC=(rs$rptr),HL=diff
jrz iDATA2 ; 0=empty, 1=one byte ... -1=full
mvi a,0FEh ; Unused bits of rs$rptr, rs$wptr
ora h
inr a
jrnz iDATA1
mov a,l
adi 20h
cc iDATAF ; Buffer nearly full - PANIC!!
iDATA1: inx b
res 1,b ; Determines buffer size (200H)
lxi h,rs$buff
dad b
mov a,m ; first read buffer
sbcd rs$rptr ; THEN update rptr
dcr h ; Set NZ flag
iDATA2: pop h
pop b
ret
bufftest: ; Test for data in buffer, NZ=yes
lhld rs$wptr
lbcd rs$rptr
xra a
dsbc bc
rnz ; Yes, data in buffer, return NZ
; Buffer empty
call rec$reti ; ei, in case disabled by buffer overflow
lda xflag
ora a
rz
mvi a,XON
call oDATA ; send XON if remote was XOFFed
xra a ; Set Z flag
sta xflag
ret
iDATAF: lda xflag ; Buffer (nearly) full
ora a
rnz ; XOFF already sent
dcr a
sta xflag
mvi a,XOFF
; jmp 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'= (npdata) for adjustment at write
oDATA: ;out modem data port
; out 9 ;turn off wait cycling (not needed on all bees)
push psw ; & let CPU run at full speed
push b ; wait-ing initiated by writing to screen RAM
exx
push b
push d
push h
exx
push d
push h
mov c,a ;store character in C
lda ndata ;number of data bits (send)
mov b,a ;store it in b
mov e,a ;and e
mvi a,1
send$1: rrcr c ;shift data into carry in one direction and
ralr l ;out the other (reverse the order of bits)
add a ; generate mask
djnz send$1
dcr a ; a = mask
ana l ; mask send data
jpe send$2
cmc ; carry = parity bit
send$2: lda bparity
dcr a
jrz send$nopar
dcr a
jrnz send$pe
cmc
send$pe:
dadc hl ; add parity bit to send data
inr e ; +1 to data count for parity
send$nopar:
mvi a,13
sub e ; no of data bits + parity (if req)
mov b,a
send$3: dad h
inx h
djnz send$3
lda stopbits
add e ; +stopbits
inr a ; +1 since e=1 -> finished sending
mov e,a ; send data counter
mov c,b ; rec data count = 0 ie rec is idle
lda baud+1 ; send delay
mov d,a ; send delay (for resetting)
adi 20 ; this will have more effect on the higher
; baud rates
mov b,a
djnz $-1 ; - these three instructions were added to give
mov b,d ; - an effectively longer stop pulse (gave
; problems with only one stop bit at 1200
; baud on file transfers)
mov b,d ; send delay counter
mvi a,DTR
sta sendflag ; NZ = sending
out DATAB ; output start bit (high)
jmp receive
; Timing loop
; Ideally, 176 clock cycles long
; Instructions used purely for timing labled '+'
rec$write: ; receive + 18 cycles
exx ; 4\
mvi a,9 ; 7 \
djnz rec$weq ; 8 | 13
sub l ; 4 | = 37
mov b,a ; 4 /
in DATAB ; 10/
jrz rec$noadj ; 7\ 12
ana e ; 4 \
sub e ; 4 |
mov a,d ; 4 | =12(9b) =35(8b) =52(7b)
rec$adj: rarr a ; 4 |
djnz rec$adj ; 8 / 13
mov d,a ; 4/
rec$noadj: lhld rs$rptr ; 16\
lbcd rs$wptr ; 20 \
inx b ; 6 |
res 1,b ; 8 | dictates buffer size (200H)
xra a ; 4 |
dsbc bc ; 15 |
jrz rec$full ; 7 |
sbcd rs$wptr ; 20 | = 196(ei) =177(no ei)
dcr a ; 4 | total = 286 (8b, ei)
rec$full: lxi h,rs$buff ; 10 | hl = buffer
dad b ; 11 |
mov m,d ; 7 | d' = data for receive
exx ; 4 |
mov c,a ; 4 | -1=ei, 0=don't ei
in DATAB ; 10 |
ani RSIN ; 7 |
add c ; 4 /
cm rec$ei ; 10/ 17+22
send:
djnz send$eq ; 8\ 13 b = delay counter
dcr e ; 4 \ e = data counter
jrz send$finish ; 7 | 12 e=1, finished send, pop HL,DE
dad h ; 11 |
mov a,h ; 4 | total = 63
ani RSOUT ; 7 |
ori DTR ; 7 |
out DATAB ; 11 /
mov b,d ; 4/ reset delay count
receive:
; push h ;+11\ 2MHz only
; dad h ;+11 | " "
; pop h ;+10/ " " (total = 32)
mov a,c ; 4\
dcr a ; 4 | c = data counter for receive
jz rec$write ; 10 | c=1, last bit
inr a ; 4 |
jm rec$test ; 10 | c=-1, wait for RSIN to go low
jrz rec$idle ; 7 | 12 c=0, idle
exx ; 4 |
djnz rec$eq ; 8 | 13 b'= delay counter (rec)
in DATAB ; 10 | receive and record data
ana e ; 4 |
sub e ; 4 | total = 113
rarr d ; 8 |
mov b,c ; 4 | reset delay counter
exx ; 4 |
dcr c ; 4 | dec data counter
jr rec$101 ; 12/ receive + 101
rec$idle: ; receive + 44 cycles
lda sendflag ; 13\ rec is only waiting for send to finish
ora a ; 4 |
jrz exit ; 7 | 12
nop ;+ 4 |
rec$72: ; receive + 72
push h ;+11 |
pop h ;+10 |
nop ;+ 4 |
rec$97: ; receive + 97
nop ;+ 4 |
rec$101: ; receive + 101
jr send ; 12/ total = 44 + 69 = 113
rec$weq: ; receive + 42 cycles
mov a,m ;+ 7 \
mov a,m ;+ 7 /
rec$eq: ; receive + 56 cycles
exx ; 4\
jr rec$72 ; 12/ receive + 72
; waiting forms a second closed loop, which is run when both sending and
; receiving have finished, but RSIN is still high. If RSIN remains high for
; more than 24ms (~2 bits @ 75 baud), an exit without ei will occur
waiting: ; waiting loop = 79 clk cycles
dcx b ; 6\
bit 2,b ; 8 | bc counts FFFF down to FBFF
jrz exit ; 7/ 12 timeout... exit without ei
rec$test: ; receive + 32 cycles
in DATAB ; 10 \
ani RSIN ; 7 | rec is testing RSIN
jrnz rec$high ; 7 | 12
mov c,a ; 4 | =0 receive = idle
call rec$reti ;17+18|
jr send ; 12 / total = 75 + 39 = 113 - 6
; Must have c=0 and b<>1 upon return to main loop
rec$high: ; receive + 61 cycles
lda sendflag ; 13\ rec is waiting for input to go low (to
ora a ; 4 | prevent unwanted interrupt)
jrz waiting ; 7 | 12 execute waiting loop
jr rec$97 ; 12/ total = 61 + 36 = receive + 97
rec$ei: inr c ; 4\
rec$reti: ; | rec$reti called by rec$stat
ei ; 4\|
reti ; 14/ 'interrupt serviced' signal for PIO
; (PIO won't re-interrupt till serviced)
send$finish: ; send + 24 (execute once only per send)
xra a ; 4\
sta sendflag ; 13 |
pop h ; 10 |
pop d ; 10 |
djnz receive ; 13/ sets b=FF, always jumps
; total = 50 + 24 = 63 + 11
send$eq: ; send + 13
lda sendflag ; 13\
ora a ; 4 |
jz send$idle ; 10 |
mov a,m ;+ 7 |
nop ;+ 4 |
jr receive ; 12/ total = 13 + 50 = 63
send$idle: ; send + 40
mvi b,0FFh ; 7\ stay on send$eq path
ora c ; 4 |
jrnz receive ; 7/ 12 total = 40 + 23 = 63
exit: ; send + 58
exx ; 4\
pop h ; 10 |
pop d ; 10 |
pop b ; 10 | = 68
exx ; 4 |
pop b ; 10 |
pop psw ; 10 |
ret ; 10/
; send + 126 = receive + 412 (no waiting)
rec$interrupt: ; 19\ cycles to respond to interrupt
out 9 ; 11 |
push psw ; 11 |
lda sendflag ; 13 | = 68
ora a ; 4 |
jnz rec$wsend ; 10/
push b ; 11\
mov b,a ; 4 | b = send delay count = 0 (disable send)
lda npdata ; 13 |
mov c,a ; 4 | c = rec data count
exx ; 4 |
push b ; 11 |
push d ; 11 |
push h ; 11 | = 126
mov l,a ; 4 | l' = (npdata) used in adjusting
lxi d,RSIN ; 10 | d' = data for rec
lda baud ; 13 |
mov c,a ; 4 | c' = rec delay (for resetting counter)
rar ; 4 | a' = c'/2 (carry = 0 from "or a")
add c ; 4 | = delay x 1.5 for 1st bit
mov b,a ; 4 | b' = rec delay counter
exx ; 4 |
jnz receive ; 10/ rec$interrupt + 194
; Special handlng of delay=0 (75 baud)
exx
mvi b,128
exx
inr c ; read extra data bit at start
jmp receive
rec$wsend: ; rec$interrupt + 68 cycles
lda npdata ; 13\
mov c,a ; 4 | c = rec data counter
exx ; 4 |
mov l,a ; 4 | l' = (npdata) for adjusting
lxi d,RSIN ; 10 | d' = data for rec
lda baud ; 13 | = 95
mov c,a ; 4 | c' = rec delay (for resetting counter)
rar ; 4 | a' = c'/2 (carry = 0 from "or a")
add c ; 4 | = delay x 1.5 for 1st bit
mov b,a ; 4 | b' = rec delay counter
jrz rec$75 ; 7 | 12
exx ; 4 |
pop psw ; 10 |
ret ; 10/ rec$interrupt + 163
rec$75: mvi b,128 ; Special handling for delay=0 (75b)
exx
inr c ; read extra data bit at start
pop psw
ret
; Modem initialization.
INITMOD:
lxi b,rec$interrupt
jr NEWVCB ; Set (interrupt vector) to bc, save old
; exit routine
RESTORE:
lbcd oldvcb
NEWVCB:
; saves current (interrupt vector) in (oldvcb),
; loads (interrupt vector) with BC.
; Upper half of address in 'I' reg, lower half determined as follows:
; Method: fill a whole memory page with interrupt vectors, so that
; each will execute a different number of 'inc a' instructions.
; The number of 'inc a' instructions executed will reflect the
; setting of the PIO interrupt vector.
push b
lxi d,0BFFh ; end of 'inc a' series
lxi h,0C00h ; start of table of interrupt vectors
mvi b,80h ; counter
mvi a,0C9h ; 'ret' instruction
VCBFILL:
stax d ; store interrupt routine instruction
mov m,e ; store (vector) lsb
inx h
mov m,d ; store (vector) msb
inx h
dcx d
mvi a,3Ch ; 'inc a' instruction
djnz VCBFILL
; Force an interrupt from port-B, MUST be from port-B, NOT port-A
mvi a,03h
out CTLA ; Disable port-A interrupts
in DATAB
ani 0FFh-CASOUT ; Cassette output-bit=0, used to force interrupt
out DATAB
lxi h,INITTABLE
lxi b,CTLB+256* 4
di ; wait until we're ready
outir ; initialize port-B for interrupt
im2
ori CASOUT ; Cassette output-bit=1
out DATAB ; (generates the interrupt)
ldai
mov d,a ; save interrupt page
mvi a,0Ch ; set new interrupt page
stai
xra a
call init$reti ; in case of 'unserviced' interrupt (else PIO
; won't interrupt)
; eg if buffer full, NO 'reti' is executed
ei ; let interrupt happen
hlt ; wait for interrupt
di ; (Note: ei performed by rec$stat->bufftest)
add a ; A reg contains value number of 'inc a'
; instructions executed
mov e,a
mov a,d
stai ; Restore interrupt page
outi
outi ; Initialize PIO for normal operation
mvi a,83h
out CTLA ; Re-enable port-A interrupts
pop b
xchg
mov e,m ; save old (vector) lsb
mov m,c ; store new (vector) lsb
inx h
mov d,m ; save old (vector) msb
mov m,b ; store new (vector) msb
sded oldvcb ; store old (vector)
init$reti:
reti ; 'interrupt serviced' signal for PIO
INITTABLE: ; Table used to initialize PIO (port B)
db 0ffh ; = mmxx1111 set port B mode (control: mm=11)
db 99h ; I/O mask: 1=in, 0=out
db 0B7h ; = eahm0111: e=1 enable interrupt,
; a=0 OR bits, h=1 bits high, m=1 mask follows
db 0FFh-CASOUT ; interrupt mask: 1=ignore, 0=monitor
;^ this was modified by Simeon from being the
;speaker bit. It is used to set up the ints.
;however it clicked the speaker every time
;the program was initialised
db 0f7h ; = eahm0111: e=1 enable interrupt,
; a=1 AND bits, h=1 bits high, m=1 mask follows
db 0ffh-RSIN-CTS ; interrupt mask: 1=ignore, 0=monitor
; Setup for odd/even parity (called before file-transfer)
PARITY:
; set no-parity (called after file-transfer)
NOPAR: ret
; send-break routine
PBREAK: ;Added by Simeon
;Break sends a signal that will cause a framing error at the remote
;end (on purpose). This is used to reset some computers. This
;routine will continue to receive while sending the break.
push psw
push b
exx
push b
push d
push h
exx
push d
push h
lxi h,00ffh ;HL holds the data and stop bits
mvi c,0
mvi d,00h ;this controls the length of time for the
;break tone condition (080h= approx 50ms)
mov b,d
mvi e,8
mvi a,DTR
sta sendflag
out DATAB
jmp receive
; Dialing routine
;
; A = ASCII digit to dial | 254 start dial | 255 end dial
; At end of dialing (A=255), return (in A)
; 0 carrier present
; 1 busy
; 2 timeout (no answer)
; 3 keyboard abort
; 4 error reported
;
PDIAL: di ; Stop noise, etc from interfering (ei done by
lxi h,dial$abort ; rec$stat->bufftest)
inr a
jrz DIALEND
inr a
jrz DIALSTART
bit 0,m ; (hl)=0 ok,3 abort
rnz
sui '9'+3 ; '9'-> -1, '0'-> -10
adi 10
jrnc DIALNIX
jrnz DIAL1
mvi a,10 ; convert 0 to 10
DIAL1: mov b,a
DIAL2: if dialon
xra a ; Dial 10 pps, make:break = 1:2
else
mvi a,RSOUT
endif
if fast$dial ; note the new option to allow you to dial
; faster if the exchange will take it - SC
mvi c,33
else
mvi c,66
endif ;fast$dial
call PULSE ; 'break' pulse (> 0.5 megaohm)
mvi a,DTR+RSOUT
if fast$dial
mvi c,16
else
mvi c,33
endif ;fast$dial
call PULSE ; 'make' pulse (< 0.5 ohm)
djnz DIAL2
DIALNIX:
if fast$dial
mvi b,8
else
mvi b,16 ; Delay between digits 800ms
endif ;fast$dial
;jmp DIALCK ; fall into DIALCK
; Check for abort, over period = b*50ms
; requires: hl=dial$abort, b=delay
; returns: a=(hl)
DIALCK: mvi c,CHEKCC
call MEX
jrnz DIALC1
mvi m,3
DIALC1: mov a,m
ora a
rnz
mvi c,50
call MSECS ; No regs affected
djnz DIALCK
ret
DIALSTART:
mov m,a ; (a=0)
call ONLINE
mvi b,60
call DIALCK ; wait 3 secs for dial tone
mvi a,0FFH ; Set port B to control mode - I/O mask follows
out CTLB
lda dialtype ; Activate off-normal contacts on modem by
xri 99H ; making DIALON an input = hi-impedance
out CTLB
ret
; RSOUT is kept high as I found this necessary for carrier detection
; Changed to become a DIALON option by Simeon
DIALEND:
mvi a,0FFH ; Set port B to control mode - I/O mask follows
out CTLB
mvi a,99H ; Make DIALON output again (return to normal)
out CTLB
if dialon
mvi a,DTR ; Modem online, RSOUT high (for carrier detect)
else
mvi a,DTR+RSOUT
endif
out DATAB
lda ans$time
mov e,a ; Multiply A by 5 (8 bit)
add a ; x 2
add a ; x 4
add e ; + 1
mov e,a ; e = (ans$time) x 5
mvi d,0
DIALE1:
mvi b,4
call DIALCK ; Delay 200ms, NZ = abort (ctrl-C)
jrnz OFFLINE ; Keyboard abort: connect modem, return 3
in DATAB ; Test carrier
ani CTS
jrnz DIALE2
mov d,a ; Carrier interrupted, reset count (a=0)
DIALE2: mvi a,5 ; require 1 second uninterrupted carrier
sub d
jrz ONLINE ; Carrier detect confirmed: connect, return 0
inr d
dcr e
jrnz DIALE1
call OFFLINE ; No answer: hangup phone, return 2
mvi b,40
call DIALCK ; do 2s delay
ori 2 ; no answer: return 2 (or 3 if aborted)
ret
; Put pulse on phone line (DTR in A, C ms long)
PULSE: out DATAB
; Delay C milliseconds
MSECS: push psw
push b
mov b,c
MSECS1: lda CLOCK
MSECS2: mvi c,5 ; 7
MSECS3: dcr c ; 4*5
jrnz MSECS3 ;12*4+7
dcr a ; 4
jrnz MSECS2 ; 12
djnz MSECS1
pop b
pop psw
ret
; connect modem to phone line
ONLINE:
mvi a,DTR+RSOUT ; DTR high, RSOUT low
out DATAB
mov a,m ; A=return code (0 or 3) if hl=dial$abort
ret
; disconnect the modem
OFFLINE:
mvi a,RSOUT ; DTR low, RSOUT low
out DATAB
di
push h
lhld rs$rptr
shld rs$wptr ;make the read and write pointers the same
pop h ;(necessary if disconnecting while still
; receiving so that the buffer is empty
; when started up next time)
ret
; Sign-on message
SYSVER: call ILPRT
SOMESG: db 'Microbee version',cr,lf,'Software port',0
; Newline on console
CRLF: call ILPRT
db CR,LF,0
ret
; type char in A on console
TYPE: push h ;save 'em
push d
push b
mov e,a ;align output character
mvi c,CONOUT ;print via MEX
call MEX
pop b
pop d
pop h
ret
;------------------------------------------------------------
;
; The remainder of this overlay implements a very versatile
; SET command
; Control is passed here after MEX parses a SET command.
;
SETCMD:
mvi c,SBLANK
call MEX
jc SETSHO ;jump if no arguments with SET
lxi d,CMDTBL
mvi c,LOOKUP
call MEX
push h
rnc ;execute set command
pop h
call ILPRT
db TAB,'SET command error - type "SET ?" for help',CR,LF,0
ret
; SET command table ... note that tables are constructed of command-
; name (terminated by high bit=1) followed by word-data-value returned
; in HL by MEX service processor LOOKUP. Table must be terminated by
; a binary zero.
;
; Note that LOOKUP attempts to find the next item in the input stream
; in the table passed to it in HL ... if found, the table data item is
; returned in HL; if not found, LOOKUP returns carry set.
CMDTBL: db '?'+80h ; set ?
dw STHELP ;
db 'HEL','P'+80h ; set help
dw STHELP
db 'BAU','D'+80h ; set baud
dw STBAUD ;
db 'B'+80h ; set baud
dw STBAUD ;
db 'SEN','D'+80h ; set send baud rate
dw SETSND
db 'S'+80h
dw SETSND
db 'RECEIV','E'+80h ; set receive baud rate
dw SETREC
db 'R'+80h
dw SETREC
db 'DAT','A'+80h ; set no of data bits
dw SETDATA
db 'D'+80h
dw SETDATA
db 'STO','P'+80h ; set stop bits
dw SETSTOP
db 'PARIT','Y'+80h ; set parity
dw SETPAR
db 'P'+80h
dw SETPAR
db 'FLI','P'+80H ; swap send/receive
dw SETFLIP
db 'F'+80h
dw SETFLIP
db 'WAI','T'+80H
dw SETWAIT ; set wait time for answer
db 'W'+80H
dw SETWAIT
db 0 ; <<=== table terminator
; SET <no-args>: print current statistics
;
SETSHO: lxi h,SHOTBL ;get table of SHOW subroutines
SETSLP: mov e,m ;get table address
inx h
mov d,m
inx h
mov a,d ;end of table?
ora e
rz ;exit if so
push h ;save table pointer
xchg ;adrs to HL
call GOHL ;do it
call CRLF
pop h ;it's done
jr SETSLP
GOHL: pchl
; table of SHOW subroutines, last address executed first
SHOTBL: dw BDSHOW
dw SHODATA
dw SHOSTOP
dw SHOPAR
dw SHODCD
dw 0 ;<<== table terminator
; SET ? processor
;
STHELP:
mvi a,25
call EQUALA
call ILPRT
db ' Microbee SET command ',0
mvi a,25
call EQUALA
call ILPRT
db LF
db CR,LF, 'SET BAUD <value> or SET B <value>'
db CR,LF, 'SET SEND <value> or SET S <value>'
db CR,LF, 'SET RECEIVE <value> or '
db 'SET R <value>'
db CR,LF, 'SET FLIP or SET F ... swaps baud rates: send <=> receive'
db CR,LF,LF,' baud rates allowed are:'
db CR,LF, ' 75, 110, 134, 150, 300, 450, 600'
db CR,LF, ' 710, 1200, 1800, 2400, 4800, 9600, 19200'
db CR,LF,LF,'SET DATA <value> or SET D <value>'
db CR,LF, ' ... DATA bits sent/received: 1 <= value <= 8'
db CR,LF,LF,'SET PARITY <c> or SET P <c>'
db CR,LF, ' ... <c>: N = no parity, E = even parity, O = odd parity'
db CR,LF,LF,'SET STOP ... toggles number of stop bits: 1 <=> 2'
db CR,LF,LF,'SET WAIT <value> ... set timeout for answer: 1 to 25 secs'
db CR,LF,0
mvi a,74
;jmp EQUALA ;fall into EQUALA
;Type '=' x A reg (scrounging bytes)
EQUALA: push b
mov b,a ;store value in b
mvi a,'='
EQUAL1: call TYPE
djnz EQUAL1
pop b
ret
; Show carrier detect status
SHODCD: in DATAB
ani CTS ; (actually DCD)
jrnz SHODC1
call ILPRT
db 'NO ',0
SHODC1: call ILPRT
db 'Carrier is present',0
ret
; Automatic SET BAUD through phone library
; Note: specifying 110 in phone library sets 1200/75
PBAUD:
call MTOBAUD ; get baud rate corresponding to mspeed
push h
ora a ; mspeed=0 <=> 110 baud
mvi a,5 ; mspeed=5 <=> 1200 baud
cz MTOBAUD ; get 1200 baud in L, if mspeed is 110
mov a,l
sta baud ; set receive baud rate
pop h
jr PBAUDSET
; SET BAUD processor
STBAUD: lxi d,setbdt
call setbd1
mov a,h
sta MSPEED
mov h,l
shld baud
jr BDSHOW
SETSND: lxi d,setbds ; set sending baud rate
call setbd1
PBAUDSET:
mov a,h
sta MSPEED
mov a,l
sta baud+1
jr BDSHOW
SETREC: lxi d,setbdt ; set receiving baud rate
call setbd1
mov a,l
sta baud
jr BDSHOW
SETFLIP: ; Swap send<=>receive baud rates
lhld baud
mov a,h
mov h,l
mov l,a
shld baud
lxi d,setbdt-1 ; make MSPEED correspond to send baud rate
setfl1: inx d
call NEXTBD
jrc BDSHOW
ldax d
inx d
cmp h
jrnz setfl1
ldax d
sta MSPEED
BDSHOW: call ILPRT ;display baud
db 'Baud rate, send = ',0
lda baud+1
call bdsho1 ;display baud rate + ' '
call ILPRT
db 'receive = ',0
lda baud
bdsho1:
mov c,a
lxi d,setbds
bdsho2: mov h,d
mov l,e
call NEXTBD
jrc bdsho5
ldax d
inx d
inx d
cmp c
jrnz bdsho2
bdsho4: mov a,m
ani 7fh
call TYPE
bit 7,m
inx h
jrz bdsho4
bdsho5: call ILPRT
db ' ',0
ret
setbd1: mvi c,SBLANK
call MEX
jrc setbd2
mvi c,LOOKUP
call MEX
rnc
call ILPRT
db TAB,'Invalid baud rate',CR,LF,0
setbd2: pop psw
jr BDSHOW
NEXTBD: ldax d
inx d
ora a
rm
jrnz NEXTBD ; Z = End of table (should not occur)
stc
ret ; error, return carry set
MTOBAUD: ; convert mspeed in A to baud
mov h,a ; A = MSPEED
lxi d,setbdt-1
MTOB1: inx d
call NEXTBD
rc ; Error, mspeed not in table
ldax d
mov l,a ; store baud in l
inx d
ldax d
cmp h
jrnz MTOB1
ret ; Exit with A=H=mspeed, L=baud
; Look-up table for SET BAUD format: 'nnn',baudrate,MSPEED
setbds: ; could use any delay for sending,
; but 170 is maximum for receiving
; ie do not use: 170 < delay <=255
setbdt:
db '7','5'+80h,0,0 ; Special case, see rec$interrupt
db '11',0b0h,170,0
db '13','4'+80h,143,0
db '15',0b0h,128,0
db '30',0b0h,64,1
db '45',0b0h,43,2
db '60',0b0h,32,3
db '71',0b0h,27,4
db '120',0b0h,16,5
db '180',0b0h,11,5
db '240',0b0h,8,6
db '480',0b0h,4,7
db '960',0b0h,2,8
db '1920',0b0h,1,9
db 0
; Set no. of stop bits, toggles 1<=>2
SETSTOP: lda stopbits
dcr a
jrnz ststp1
mvi a,2
ststp1: sta stopbits
SHOSTOP: lda stopbits
call DECA ; Display A, ILPRT
db ' stop bits',0
ret
SETDATA: mvi c,SBLANK
call MEX
mvi c,GNC
call MEX
jrc SHODATA
sui '0'
jrz setdte
cpi 9
jrnc setdte
sta ndata
SHODATA: lda ndata
call DECA ; Display A, ILPRT
db ' data bits',0
GET$NPDATA: ; sets (npdata)
lda bparity
mov b,a
lda ndata
dcr b
jrz setdt1 ; parity = none
inr a ; parity = odd,even
setdt1: sta npdata ; = no of data bits + parity bit
ret
setdte: call ERROR
db '1 <= n <= 8',CR,LF,0
jr SHODATA
;Set parity 1=none, 2=odd else even:
SETPAR: mvi c,SBLANK
call MEX
mvi c,GNC
call MEX
jrc SHOPAR
mvi c,1
ani 5fh
cpi 'N'
jrz setpr1 ; none = 1
inr c
cpi 'O'
jrz setpr1 ; odd = 2
inr c ; even = 3
cpi 'E'
jrnz SETPER
setpr1: mov a,c
sta bparity
call GET$NPDATA ; (npdata) = (ndata) + parity bit (if present)
SHOPAR: call ILPRT ; Show parity status:
db 'parity: ',0
lda bparity
mov b,a
djnz SHOPR1 ; b = 1?
call ILPRT ; yes
db 'none',0
ret
SHOPR1: djnz SHOPR2 ; no, b = 2?
call ILPRT ; yes
db 'odd',0
ret
SHOPR2: call ILPRT ; no
db 'even',0
ret
SETPER: call ERROR
db 'N, O, or E',CR,LF,0
jr SHOPAR
SETWAIT:
mvi c,SBLANK
call MEX
jrc SHOWAIT ; No <value> to parse
mvi c,EVALA
call MEX
mov a,l
cpi 26
jrnc SETWERR
ora a
jrz SETWERR
sta ans$time
SHOWAIT:
lda ans$time
call DECA ; Display A in decimal, then ILPRT
db ' s wait',0
ret
SETWERR: call ERROR
db '1-25 secs',CR,LF,0
jr SHOWAIT
; Display A reg in decimal, then print in-line text
DECA: mov l,a
mvi h,0
mvi c,DECOUT
call MEX
jr ILPRT
; Print Error Message (ILPRT fashion)
ERROR: call ILPRT
db TAB,'Invalid - must be ',0
; Print in-line message
ILPRT: mvi c,ILP ;get function code
jmp MEX ;go do it
; Buffer for incoming data
rs$buff equ 0B00h ;Code must end before 0B00h
;------------------------------------------------------------
;
; End of Microbee MEX modem overlay
;
;------------------------------------------------------------
end ; Overlay must end before 0D00h
all MEX
mvi c,GNC
call MEX
jrc SHOPAR
mvi c,1
ani 5fh
cpi 'N'
jrz setpr1 ; none = 1
inr