Title MEX Overlay for the Kaypro Computer Version 5.0 ; rev equ 50 ; Overlay revision level ;SEE .HIS AND .INF FILES FOR FURTHER INFO... ; Misc equates ; no equ 0 yes equ 0ffh ; tpa equ 100h ; ctrl equ 01fh cr equ ctrl and 'M' ; Carriage return lf equ ctrl and 'J' ; Line feed tab equ ctrl and 'I' ; Horizontal tab bell equ ctrl and 'G' ; Console bell ; ;------------------------------------------------------------ ; ; Customization equates. Here is where you can select ; options for this overlay, dependent on the type of external ; modem you have, etc. ; ;------------------------------------------------------------ ; mexplus equ YES ;no ; If YES, leaves out carrier loss code, and adds dcdvec and rngvec ; routines for MexPlus. Changes the way screen functions are handled. ; NOTE: Not tested as of version 5.0. ; extrn equ YES ;no ; If YES, defaults to External modem, but internal modem still available ; via SET cmd if intrn is set to yes. ; intrn equ NO ;yes ; If NO, all internal modem code is left out. External is default. ; Saves over 1K in the overlay. ; nodtr equ no ;YES ; If YES, define a disconnect routine in your overlay, and this overlay ; will use it instead of dropping DTR. Saves some code in this overlay. ; If NO, do NOT define disconnect in modem overlay, or internal modem ; disconnect will not work! Equivalent label is usually called DISC in ; Smartmodem overlay: set NODTR like DISC. ; ; If this character comes in the modem, ignore it. ; Cleans up dirty phone lines, esp. with 1200 or 2400 baud modems. Passes ; NULL to MEX in place of the char. Set to 0 to disable this function. ; Disabled during file transfers. ; badch1 equ 07Fh badch2 equ '{' ; Another character to ignore. badch3 equ '}' ; Set this to another char that bothers you. ; defbd equ 5 ; Set to MSPEED code for default baud rate for external modem. ; 0=110, 1=300, 3=600, 5=1200, 6=2400, 7=4800, 8=9600, 9=19200. ; (450 and 710 are not supported by the Kaypro hardware.) ; dildly equ 10 ; Set to 10 * seconds wanted for delay when ',' is encountered in ; dial stream. Equivalent to Smartmodem's S8. Internal modem only. ; dialwt equ 0 ; Set to 10 * seconds wanted for wait beyond 2 second minimum for dial ; tone after going off hook. Similar to Smartmodem's S6. Internal only. ; dcdwt equ 8 ; Set to tenths of second to wait before declaring carrier gone. If ; lines are noisy, set this higher, but not too high. Similar to ; Smartmodem's S10, minus S9. Affects both internal and external. ; Has no effect when MEXPLUS is YES. ; dcdrsp equ 6 ; Set to tenths of second to wait before declaring carrier present when ; attempting connection. Equivalent to Smartmodem's S9. Internal Only. ; tone equ yes ; Set to NO to have pulse dial be the default. ; ansdel equ 30 ; Default value for SET DELAY. Internal modem only, on dial and auto-answer. ; ebits equ 3 ; Default word length (# of bits) for external modem. ibits equ 3 ; Same, for internal. Affects default: can be SET. ; 0 = 5 bits, 1 = 7 bits, 2 = 6 bits, 3 = 8 bits ; esbits equ 1 ; Default number of stop bits on external modem. isbits equ 1 ; Default number of stop bits on internal modem. ; Only affects default: still can be reset via SET. ; 1 = 1 stop bit, 2 = 1.5 stop bits, 3 = 2 stop bits. ; eprty equ 0 ; Default parity setting on external modem. iprty equ 0 ; Same, only for internal if selected. ; Only affects default: still can be reset via SET. ; 0 = NONE, 1 = ODD, 3 = EVEN ; ;------------------------------------------------------------ ; ; Kaypro port definitions ; intport equ 0dh ; Base internal port intct1 equ intport+2 ; Internal modem control port intdat equ intport ; Internal modem data port piodat equ 21h ; Internal modem pio data port pioct1 equ piodat+2 ; Internal modem pio control port ; export equ 04h ; Base external port extct1 equ export+2 ; External modem status port extdat equ export ; External modem data port baudrp equ 00h ; External modem baud rate port ; ; Kaypro bit definitions ; ; SIO status register ; (Read register 0) ; mdrcvb equ 00000001b ; Modem receive bit (DAV) mdrcvr equ 00000001b ; Modem receive ready mdsndb equ 00000100b ; Modem send bit mdsndr equ 00000100b ; Modem send ready bit ; dcdbit equ 00001000b ; Data carrier detect bit ctsbit equ 00100000b ; Clear to send bit rngbit equ ctsbit ; Ring detect on internal modem ; ; (Write register 5) ; txon equ 00001000b ; Tx enable bit dtrbit equ 00000010b ; Data terminal ready bit sqtbit equ dtrbit ; DTR off squelches internal modem rtsbit equ 10000000b ; Ready to send bit ansbit equ rtsbit ; RTS on sets answer mode on internal orgmsk equ 01111111b ; RTS off sets originate mode on internal ; ; ; PIO control register ; onhook equ 01000000b ; Bit 6 set means modem on hook plsbit equ 00010000b ; Bit 4 set means pulse dial digrdy equ 10000000b ; Digit present to dialer chip dignot equ 01111111b ; Invert digit present to end dial sequence ; ; Misc. Kaypro definitions ; eoschr equ 017h ; Clear to end of screen code clschr equ 01Ah ; Clear whole screen (cls) code ; ; Z80 Opcode definitions. ; outc equ 079edh ; out (c),a for use in a dw inpc equ 078edh ; in (c),a for use in a dw ldix equ 021ddh ; ld ix,nnnn for use in a dw ldaix equ 07eddh ; ld a,(ix+n) for use in a dw orix equ 0b6ddh ; or (ix+n) for use in a dw andix equ 0a6ddh ; and a,(ix+n) for use in a dw ldixa equ 077ddh ; ld (ix+n),a for use in a dw ldbix equ 046ddh ; ld b,(ix+n) for use in a dw ldcix equ 04eddh ; ld c,(ix+n) for use in a dw ldixb equ 070ddh ; ld (ix+n),b for use in a dw ldixc equ 071ddh ; ld (ix+n),c for use in a dw ; ; Take care when using these, because obviously no range checking is ; done. Format: db jr,label-$-1 ; (if forward) ; db jr,(label-$-1) mod 256 ; (if backward) ; djnz equ 10h ; djnz nnnn for use in a db jr equ 18h ; jr nnnn for use in a db jrnz equ 20h ; jr nz,nnnn for use in a db jrz equ 28h ; jr z,nnnn for use in a db jrnc equ 30h ; jr nc,nnnn for use in a db jrc equ 38h ; jr c,nnnn for use in a db ; ;------------------------------------------------------------ ; ; MEX Service Processor ; 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 ; Set cmd table search. 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 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 prntbl equ 237 ; Print table prid equ 236 ; Print [mex] id onoff equ 235 ; Parse on/off fm input strm a=0 or 1 (c=err) ; 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 kstat equ 11 ; Get keyboard status kbdin equ 01 ; Keyboard input ; dconio equ 6 ; BDOS Direct Console I/O function # dconin equ 0ffh ; BDOS DCONIO flag for input ; ;------------------------------------------------------------ ; ; Actual code begins here. Below here are a number of ; equates which you can customize to suit your tastes. ; ;------------------------------------------------------------ ; org tpa ; We begin ; db 0c3h ; JMP instruc that MexPlus will check ds 2 ; MEX has a JMP START here ; ds 2 ; Not used by MEX ; if tone tpulse: db 'T' ; T=touch, P=pulse (Used by this overlay) endif if not tone tpulse: db 'P' ; T=touch, P=pulse endif ; clock: db 40 ; Clock speed x .1, up to 25.5 mhz. 106h mspeed: ; 107h if extrn or (not intrn) db defbd ; 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 endif ; if intrn and (not extrn) db 1 ; Internal modem always 300 baud. endif ; bytdly: db 5 ; Default time to send character in 108h ; 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 term 109h ; 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 10ah setfl: db yes ; Yes=user-defined SET command 10bh scrtst: db yes ; Yes=if home cursor and clear screen 10ch ; Routine at CLRSCRN db 0 ; Was once ACKNAK, now spare 10dh bakflg: db no ; yes Yes=make .BAK file 10eh crcdfl: db yes ; Yes=default to CRC checking 10fh ; No=default to Checksum checking togcrc: db yes ; Yes=allow toggling of Checksum to CRC 110h cvtbs: db no ; Yes=convert backspace to rub 111h toglbk: db yes ; Yes=allow toggling of bksp to rub 112h addlf: db no ; No=no LF after CR to send file in 113h ; Terminal mode (added by remote echo) toglf: db yes ; Yes=allow toggling of LF after CR 114h trnlog: db no ; Yes=allow transmission of logon 115h ; Write logon sequence at location LOGON savccp: db no ; yes Yes=do not overwrite CCP 116h locnxt: db yes ; Yes=local cmd if EXTCHR precedes 117h ; No=not local cmd if EXTCHR precedes togloc: db yes ; Yes=allow toggling of LOCNXTCHR 118h lsttst: db yes ; Yes=allow toggling of printer on/off 119h ; In terminal mode. Set to no if using ; The printer port for the modem xoftst: db no ; Yes=allow testing of XOFF from remote 11ah ; While sending a file in terminal mode xonwt: db no ; Yes=wait for XON after sending CR 11bh ; while Xmitting a file in terminal mode togxof: db yes ; Yes=allow toggling of XOFF testing 11ch ignctl: db no ; Yes=do not send control characters 11dh ; Above CTL-M to CRT in terminal mode ; No=send any incoming CTL-char to CRT extra1: db 0 ; For future expansion 11eh extra2: db 0 ; For future expansion 11fh brkchr: db 'B'-40h ; ^B = Send a 300 ms. break tone 120h ; Normally ^@, but later Kaypro's can't get it from the keyboard. noconn: db 'N'-40h ; ^N = Disconnect from phone line 121h logchr: db 'L'-40h ; ^L = Send logon 122h lstchr: db 'P'-40h ; ^P = Toggle printer 123h unsvch: db 'R'-40h ; ^R = Close input text buffer 124h trnchr: db 'T'-40h ; ^T = Transmit file to remote 125h savchr: db 'Y'-40h ; ^Y = Open input text buffer 126h extchr: db '^'-40h ; ^^ = Send next character 127h ; ds 2 ; Not used 128h ; ; Low-level modem I/O routines. ; inctl1: jmp inctrl ; In modem control port 12ah db 0,0,0,0,0,0,0 ; Spares if needed for non-PMMI ; otdata: jmp outdta ; Output char to modem. 134h db 0,0,0,0,0,0,0 ; Fill to required 10 bytes ; inport: jmp indata ; Input char from modem. 13eh db 0,0,0,0,0,0,0 ; Fill out to required 10 bytes ; ; Bit-test routines. ; maskr: ani mdrcvb ; Bit to test for receive ready 148h ret testr: cpi mdrcvr ; Value of receive bit when ready 14bh ret masks: ani mdsndb ; Bit to test for send ready 14eh ret tests: cpi mdsndr ; Value of send bit when ready 151h ret ; ; Two new MexPlus entry points... ; dcdtst: jmp dcdvec ; Data carrier detect 154h rngdet: jmp rngvec ; Ring detect 157h ; db 0,0,0,0,0 ; reserved space, assumed 0 ; smdisc: ds 3 ; Software disconnect for smartmodem 15fh ; dialv: jmp dial ; Dial a digit routine 162h discv: jmp discon ; Regular disconnect vector 165h goodbv: jmp goodbye ; Called before exit to CP/M 168h inmodv: jmp nitmod ; Initialization. Called at cold-start 16bh newbdv: jmp pbaud ; Set baud rate 16eh noparv: jmp filton ; Set for no-parity (end of file trans) 171h paritv: jmp filtoff ; Set parity (begin of file transfer) 174h setupv: jmp setcmd ; SET cmd: jump to a RET if not impl. 177h spmenv: ds 3 ; Not used with MEX 17ah versnv: jmp sysver ; Overlay's voice in the sign-on msg 17dh breakv: jmp sbreak ; Send a break 180h ; ; MDM calls supported in MEX 1.0 but not recommended for use. ; ilprtv: ds 3 ; Replace with MEX function 9 183h inbufv: ds 3 ; Replace with MEX function 10 186h ilcmpv: ds 3 ; Replace with table lookup funct. 247 189h inmdmv: ds 3 ; Replace with MEX function 255 18ch nxscrv: ds 3 ; Not supported by MEX (just returns) 18fh timerv: ds 3 ; Replace with MEX function 254 192h ; if not mexplus ; Use old MEX cls and clreos routines ; clreos: mvi e,eoschr ; Clear to end of screen routine 195h mvi c,conout call mex ret db 0 ; Fill out to required 9 bytes ; cls: mvi e,clschr ; Clear whole screen routine 19eh mvi c,conout call mex ret db 0 ; Fill out to required 9 bytes ; endif ; not mexplus ; ;------------------------------------------------------------ ; ; End of fixed area. Above here, be very careful of changing ; the length of things. Below here, total length just needs ; to be under 0B00, to leave room for external modem overlay. ; Equates above have been structured such that you should not ; have to change anything below here. ; ;------------------------------------------------------------ ; if mexplus org 0200h ; MexPlus overlays start here endif ; ; Input control/status port ; inctrl: if not mexplus ; Get rid of DCD logic, just return status call inctl ; Get control byte push psw ; Save status byte we just grabbed ani dcdbit ; Test DCD, while we got it cz dcdoff1 ; If down, might want to check status. pop psw ; Get full status byte back ret endif ; not mexplus ; ; ; INCTL used for internal overlay control calls, so won't end up ; in an infinite loop through dcdoff1. ; INCTRL calls fall right into here for MexPlus. ; inctl: lda ctlprt ; Get the appropriate port # inct2: push b ; save caller's BC mov c,a ; Port # in C mvi a,10h ; Register 0, reset dw outc ; out (c),a dw inpc ; in a,(c) pop b ; return BC ret ; ; ; DCDVEC returns NZ if DCD is up, Z otherwise. ; Used in various places to see if carrier still present. ; In MEXPLUS, returns A = 0 for no carrier ; 255 for carrier ; 254 for don't know ; dcdvec: lda ctlprt dcdvc1: call inct2 ; Get current control word ani dcdbit ; Test DCD bit ; if mexplus rz ; MexPlus return conventions ori 0ffh endif ; mexplus ; ret ; ; if not mexplus ; Implement carrier loss logic for MEX ; MexPlus does this itself. (?) ; ; DCDOFF will warn user if DCD drops during a connect session. ; DCDFLG insures that this message only printed once. ; Entry: Nothing. ; Exit: A trashed. ; dcdoff: call dcdvec ; Carrier up? rnz ; Yup, OK, just return. dcdoff1: lda dcdflg ; Check DCDFLG. Have we told user yet? ora a rz ; Already flagged, ignore it. push h push d push b mvi b,dcdwt ; Give it some time to stabilize call mtime call dcdvec ; Carrier back? db jrnz,dcdxit-$-1 xra a ; Carrier is really gone sta dcdflg ; Zap flag so this only happens once call milp ; And tell user db cr,lf,bell,'++ Carrier Lost ++',cr,lf,0 call discon ; hang up the phone dcdxit: pop b pop d pop h rngvec: ret ; endif ; not mexplus ; if mexplus ; ; RNGVEC returns ring-detect status for MexPlus ; A = 0 for not ringing, 255 for ring detected, 254 for don't know ; No way to read RS-232 RI (Ring Indicator) line on Kaypro, so return ; don't know. CAN get ring detect on internal, but don't know ; how MexPlus is going to use it, so I will avoid it. ; rngvec: mvi a,254 ret ; endif ; mexplus ; ; ; Output data to modem ; outdta: push b ; Save MEX's BC push psw ; Save char to be output lda datprt ; Get the appropriate port # mov c,a ; Port # in C pop psw ; Get char to be output back dw outc pop b ; Give MEX it's BC back ret ; ; ; Input data from modem ; indata: lda datprt ; Get the appropriate port # push b ; Save MEX's BC mov c,a ; Port # in C dw inpc ; if badch1 or badch2 or badch3 strip: ani 07fh ; Strip parity bit so filter will work endif ; if badch1 bdch1: cpi badch1 ; Throw away a typical garbage char db jrz,izap-$-1 endif ; if badch2 bdch2: cpi badch2 ; Throw away a typical garbage char db jrz,izap-$-1 endif ; if badch3 bdch3: cpi badch3 ; Throw away a typical garbage char db jrz,izap-$-1 endif ; if badch1 or badch2 or badch3 db jr,nozap-$-1 izap: xra a endif ; nozap: pop b ; Give MEX's BC back ret ; ; ; FILTOFF is called before a file transfer. ; Shuts off bad character filter on input. ; filtoff: if badch1 or badch2 or badch3 xra a endif ; if badch1 sta bdch1+1 ; Pass badch1 endif ; if badch2 sta bdch2+1 ; Pass badch2 endif ; if badch3 sta bdch3+1 ; Pass badch3 endif ; if badch1 or badch2 or badch3 cma ; A=0ffh sta strip+1 ; Turn off parity strip ret endif ; ; ; FILTON is called after a file transfer. Will undo what filtoff ; does, to get back to regular text transmissions. ; filton: if badch1 mvi a,badch1 sta bdch1+1 ; Strip badch1 now endif ; if badch2 mvi a,badch2 sta bdch2+1 ; Strip badch2 now endif ; if badch3 mvi a,badch3 sta bdch3+1 ; Strip badch3 now endif ; if badch1 or badch2 or badch3 mvi a,07fh sta strip+1 ; Turn on parity stripping endif ; ret ; If totally off, both filtoff and filton ; come here. ; ; ; Print out the overlay version ; sysver: call milp db lf db 'KAYPRO VIII Overlay, V' db rev/10+'0' db '.' db rev mod 10+'0' db '-' ; if intrn db 'I/' ; Flag Internal code when present endif ; db 'E' ; External code always present ; if mexplus db '+' ; Flag MexPlus version endif ; mexplus ; db cr,lf,lf db 0 ; if intrn lda intregs+1 ; Is internal modem active? ora a db jrz,sysv1-$-1 ; Nope, skip fireworks. call intm ; Select it, print it's name and bell call crlf endif ; sysv1: lda extregs+1 ; Is external modem active? ora a rz ; Nope, just return to MEX call extm ; Select it, print it's name and bell jmp crlf ; down line, return from there ; ; ; Disconnect Routine ; discon: if intrn call mdmsel db jrnz,ndisc-$-1 ; If external, do disconnect differently ; ; Disconnect internal modem, by ordering dialer 'on hook' ; call onhk ; Hang up phone call mdmsq ; Squelch modem mvi b,10 call mtime ; Let things settle down for a sec call dcdvec sta noint ; If DCD is up NOW, no internal modem present ora a rz ; If zero, all OK, just return call extm ; If not, select external jmp crlf endif ; intrn ; ; Disconnect external modem either by passing call to the ; modem overlay, or by dropping DTR for a second. ; ndisc: if nodtr jmp dummy ; This vector is fixed by NITMOD endif ; if not nodtr ; ; Disconnect external by dropping DTR for one second. Will ; not work with some modems like Anchors--if yours is one of ; these, be sure a disconnect is defined in your modem overlay, ; and set NODTR to YES. ; mvi b,5 ; Get current reg 5 dw ldaix db 5 ani 01111101b ; Turn off DTR and RTS call outctl mvi b,10 ; wait for one second call mtime mvi b,5 ; Get current reg 5 dw ldaix db 5 ori 10000010b ; Turn DTR, RTS back on jmp outctl ; (return from there) endif ; not nodtr ; if nodtr and (not intrn) ret ; outside in case both nodtr and not intrn endif ; ; ; SBREAK sends a break to the remote. ; sbreak: call mdmsel ; Get ptr to regs in IX mvi b,5 ; Select register 5 mvi a,00010000b ; Enable break bit mask or'd in dw orix db 5 call outctl ; mvi b,3 ; Delay 300 ms. call mtime ; mvi b,5 ; Select register 5 mvi a,11101111b ; Disable break bit mask and'd in dw andix db 5 jmp outctl ; Tell SIO, return from there ; ; ; NITMOD is called by MEX on entry, just before the signon msg is printed. ; Initialize RS-232 port for external modem (Zilog SIO & CTC) ; And the dialer (TMS99531 & PIO) and modem (TMS99532 & SIO) for internal. ; nitmod: if nodtr lda discv+2 ; Check current discon vect cpi discon/256 ; Ours? db jrz,nitck1-$-1 ; skip fix... lhld discv+1 ; Get current discon shld ndisc+1 ; Put in our conditional jump lxi h,discon ; Get our discon shld discv+1 ; Make it MEX's endif ; nodtr ; xra a sta extregs+1 ; Default external dcd off ; if intrn nitck1: sta intregs+1 ; Default internal dcd off sta noint ; Give internal modem a shot at it endif ; lda dialv+2 ; Check current dial vect cpi dial/256 ; Ours? db jrz,nitmd2-$-1 ; skip fix... lhld dialv+1 ; Get current dial shld ndial1+1 ; Fix our jumps to external dial shld ndial2+1 lxi h,dial ; Get our dial shld dialv+1 ; Make it MEX's ; nitmd2: if intrn mvi a,intct1 ; See if internal modem has carrier up. call dcdvc1 ; I.e.: returning from CP/M. db jrz,nitint-$-1 ; Nope, init the modem. sta intregs+1 ; Yup. Store DCD in internal's regs mvi a,0f0h ; Flag SIO params changed, but not baud sta intregs+2 db jr,nitext-$-1 ; Go init the external one ; ; Initialize Internal RS-232 and PIO port ; nitint: dw ldix ; Get IX=internal register table dw intregs mvi c,intct1 ; Get internal modem ctl/status port in C call nitsio ; Set up defaults, using port in C, regs at IX. ; mvi a,00001111b ; PIO Mode control: mode 0 (byte output) out pioct1 mvi a,07h ; PIO Interrupt control: disabled. out pioct1 ; mvi a,01011111b ; Prevent diagnostics (single tone/fast pulse) ; on dialer by sending binary 15. call digout ; Output to dialer. ; and fall through to external init ; nitext: endif ; intrn ; ; ; Initialize External RS-232 and Smartmodems ; (Set up the SIO, set baud rate, return) ; mvi a,extct1 ; See if external modem has carrier up. call dcdvc1 ; I.e.: returning from CP/M. db jrz,nitex1-$-1 ; Nope, init the modem. sta extregs+1 ; Yup, save DCD in external's regs ; if not intrn sta dcdflg ; Also current DCD flag if ext is only modem endif ; mvi a,0ffh ; Flag both baud and SIO params as wrong sta extregs+2 db jr,nitxit-$-1 ; And finish up ; nitex1: dw ldix ; Get IX=external register table dw extregs mvi c,extct1 ; Get external modem ctl/status port in C call nitsio ; Set up defaults, using port in C, regs at IX lda exmbd ; Get default baud rate call telctc ; Tell CTC what baud ; nitxit: if intrn call mdmsel ; Which modem is selected on boot? dw ldaix db 1 ; Get that modem's DCD flag sta dcdflg ; Make it the current one endif ; ret ; ; ; GOODBYE routines are called by MEX prior to exit to CP/M ; (currently not used in this overlay) ; goodbye: ; Do NOT reset any modem stats, et. al. ret ; MEX should leave modem AS IS when exiting. ; ; ; Initialize the Zilog SIO chip for either modem ; Entry: C = SIO control port # to talk to. ; IX = ptr to register table for this modem. ; Exit: B = trashed. ; nitsio: mvi b,0 ; Get REG. 0 dw ldaix db 0 call outc1 ; Give SIO command byte ; mvi b,03h ; Get REG. 3 dw ldaix db 3 call outc1 ; Receiver logic byte ; inr b ; Get REG. 4 dw ldaix db 4 call outc1 ; Receive/transmit control byte ; inr b ; Get REG. 5 dw ldaix db 5 call outc1 ; Transmitter logic byte ; dw ldaix ; Get return from CP/M flag db 2 ani 0fh ; Strip off SIO nybble dw ldixa db 2 ; And store it. SIO values now correct ; ret ; ; ; Output register value to SIO. Not req'd by MEX, but decreases ; code size in overlay. ; Entry: B = SIO register number ; A = value ; Exit: C is trashed. ; outctl: push psw ; Save value for later lda ctlprt ; Get port # mov c,a ; in C pop psw ; Get value back ; ; Alternate entry: supply your own port # in C. ; outc1: push psw ; Save value mov a,b dw outc ; Tell SIO which register pop psw dw outc ; Give SIO the value ret ; ; ; Set command processor ; setcmd: mvi c,sblank ; Any arguments? call mex db jrc,setsho-$-1 ; If not, display current values lxi d,cmdtbl mvi c,lookup call mex ; Parse the argument db jrc,seterr-$-1 ; If carry, print error message pchl ; If not, jump to found routine ; seterr: call milp ; Print error in set command db bell,'++ SET Error ++',cr,lf,0 ret ; if intrn setbad: call milp ; Command not applicable to current modem db bell,'++ SET Not Valid For ',0 call modemsh call milp db ' ++',cr,lf,0 ret endif ; ; ; "SET (no args)" -- print current stats ; setsho: call crlf call modemsh ; which modem? (show active status, too) call crlf call tpshow ; tone/pulse call crlf call crlf ; if intrn call dlshow ; answer delay call crlf call crlf endif ; call bdshow ; baud rate call crlf call shbits ; length call crlf call shprty ; parity call crlf call shstop ; stopbits call crlf jmp crlf ; and return ; ; Argument table ; cmdtbl: db '?'+80h ; SET ? (shows help) dw sethelp ; if intrn db 'INTERNA','L'+80h ; SET INTERNAL (modem) dw intm db 'EXTERNA','L'+80h ; SET EXTERNAL (modem) dw extm endif ; db 'TON','E'+80h ; SET TONE (dialing) dw sttone db 'PULS','E'+80h ; SET PULSE (dialing) dw stpulse ; if intrn db 'ANSWE','R'+80h ; SET ANSWER (mode) dw ans db 'MAN','O'+80h ; SET MANO (manual originate mode) dw manualo db 'MAN','A'+80h ; SET MANA (manual answer mode) dw manuala endif ; if intrn db 'DELA','Y'+80h ; SET DELAY dw delay endif ; db 'BAU','D'+80h ; SET BAUD dw stbaud db 'LENGT','H'+80h ; SET LENGTH dw stbits db 'PARIT','Y'+80h ; SET PARITY dw stprty db 'STOPBIT','S'+80h ; SET STOPBITS dw ststop ; db 0 ; <<== Table terminator ; ; "SET ?" processor ; sethelp: call milp db lf,'-- Modes --',cr,lf ; if intrn db 'INTERNAL',tab,tab endif ; db 'TONE',cr,lf ; if intrn db 'EXTERNAL',tab,tab endif ; db 'PULSE',cr,lf,lf ; if intrn db '-- Actions --',cr,lf db 'ANSWER',tab,tab,'Await Call',cr,lf db 'MANO',tab,tab,'Manual Originate',cr,lf db 'MANA',tab,tab,'Manual Answer',cr,lf,lf endif ; db '-- Values --',cr,lf ; if intrn db 'DELAY ',tab,'Carrier Wait: 1 - 255 secs',cr,lf,lf endif ; db 'BAUD ',tab,'110, 300, 600, 1200, 2400, 4800, ' db '9600, or 19200',cr,lf db 'LENGTH ',tab,'5, 6, 7 or 8',cr,lf db 'PARITY ',tab,'NONE, EVEN or ODD',cr,lf db 'STOPBITS ',tab,'1, 1.5 or 2',cr,lf,lf,0 ; ret ; ; if intrn ; ; "SET EXTERNAL" and "SET INTERNAL" processor ; intm: lda noint ; Is internal actually not present? ora a jnz seterr ; Yup. Don't allow switch mov b,a ; A is already 0 db jr,stmdm-$-1 endif ; extm: if intrn mvi b,0ffh stmdm: lxi h,extmdm ; Get ptr to current mdm flag mov a,m cmp b ; Compare to mode requested. db jrz,modemsh-$-1 ; If the same, don't change mspeed just show mov a,b ; Otherwise, get new modem mov m,a ; Make it the current one ora a db jrnz,stmdm1-$-1 ; External selected ; ; Internal selected ; lda mspeed ; Get the external modem speed value (modem ; overlay could change it to do auto-stepdown). inx h mov m,a ; Save value in EXMBD (for switching & cloning) inx h mvi a,intct1 ; Get internal port number mov m,a ; Make it the current CTLPRT inx h mvi a,intdat ; Get internal port number mov m,a ; Make it the current DATPRT inx h mov a,m ; Get current DCD status sta extregs+1 ; Save it in ext modem's DCDFLG lda intregs+1 ; Get internal's DCDFLG mov m,a ; Make it the current DCDFLG mvi a,1 ; Mspeed=1 always for internal (300 baud) db jr,stmdm2-$-1 ; ; External selected ; stmdm1: inx h mov b,m ; Get current EXMBD inx h mvi a,extct1 ; Get external port number mov m,a ; Make it the current CTLPRT inx h mvi a,extdat ; Get external port number mov m,a ; Make it the current DATPRT inx h mov a,m ; Get DCDFLG sta intregs+1 ; Save it in internal modem's DCDFLG lda extregs+1 ; Get external's DCDFLG mov m,a ; Make it the current DCDFLG mov a,b ; Get exmbd value back in a ; stmdm2: sta mspeed ; Tell MEX what speed we are at ; And fall through to modemsh... endif ; intrn ; ; ; Show current modem selected ; modemsh: if intrn call mdmsel ; Which modem is it, in case called directly db jrz,mdmsh1-$-1 call milp ; Show external db 'EX',0 db jr,mdmsh2-$-1 mdmsh1: call milp ; Show internal db 'IN',0 mdmsh2: call milp ; Finish up db 'TERNAL Modem',0 endif ; intrn ; call dcdoff ; Check DCD, in case it dropped lately lda dcdflg ; Get current value ora a rz ; Zero? don't flag it ; if intrn call milp db ' ',bell,'(*)',0 ; Print flag for active line endif ; intrn ; if not intrn call milp ; At least flag DCD present on extrn db 'Carrier Present',bell,0 endif ; not intrn ; ret ; endif ; intrn ; ; ; "SET TONE" and "SET PULSE" processor ; ; Change default dialing mode. External modem ; overlay must use TPULSE at 105h for this to affect ; it. Can still be overridden temporarily by using ; 'T' and/or 'P' in dial strings. ; sttone: mvi b,'T' ; TPULSE flag: touch tone mode ; if intrn call sitone ; set internal to touch tone endif ; db jr,sdial1-$-1 stpulse: mvi b,'P' ; TPULSE flag: pulse mode ; if intrn call sipulse ; set internal to pulse endif ; sdial1: mov a,b ; Retrieve TPULSE flag and store it sta tpulse ; And fall through to tpshow... ; ; ; Show current Pulse/Tone setting. ; tpshow: if intrn lda dialwd ; If internal, check dialwd ani plsbit endif ; intrn ; if not intrn lda tpulse ; If not, check tpulse cpi 'T' endif ; not intrn ; db jrz,tptone-$-1 call milp db 'PULS',0 db jr,tpxit-$-1 tptone: call milp db 'TON',0 tpxit: call milp db 'E Dial',0 ret ; if intrn ; ; Internal modem tone/pulse flag set. ; These routines also called by the dialer. ; sitone: mvi a,onhook ; on hook, tone dial db jr,siret-$-1 ; sipulse: mvi a,onhook or plsbit ; on hook, pulse dial siret: sta dialwd ret ; ; ; "SET ANSWER" processor ; ; Attempts to emulate Smartmodem auto-answer on ; internal modem. ; ans: call mdmsel ; Which modem? jnz setbad ; External. Nope, can't do this... mvi a,0ffh sta ansflg ; Set answer flag for other routines ; And fall through to WFANS ; ; WFANS is the internal modem auto-answer function. Since the ; modem is not truly auto-answer, the program must continually poll ; the port for detection of a ring, then 'answer', i.e., go off hook ; and turn on carrier. The ENDIAL routine then waits for a response ; from the caller (a carrier tone); the routine waits for the number ; of seconds specified by the SET DELAY command. CTS is ring detect. ; call milp db clschr,'Auto-Answer: ^C Aborts',cr,lf,0 wfans: mvi c,chekcc ; User abort? call mex db jrz,endans-$-1 ; If so, quit waiting... call inctrl ; Have we got a ring? ani rngbit ; CTS will go up if we do db jrz,(wfans-$-1) mod 256 ; If not, loop until abort or ring. ; call off ; Go off hook and turn modem on call milp db bell,'Answering Call',cr,lf,0 xra a sta digit ; Zap out possible 'R' flag for endial jmp endial ; Wait for caller's carrier ; endans: call crlf call milp db 'Aborted',bell db cr,lf,0 ret ; ; ; ; "SET MANO" and "SET MANA" processor ; ; Manually go off hook in Originate or Answer mode. ; Only used on internal: returns error if external selected. ; (Formerly "MANUALO" and "MANUALA", but these ; were a bit difficult to type. Time is of ; the essence when presenting carrier...) ; manualo: call mdmsel jnz setbad ; No good for external modem xra a ; A=0 (originate mode) sta ansflg db jr,off-$-1 ; Turn modem on manuala: call mdmsel jnz setbad ; No good for external modem mana1: mvi a,0ffh ; A=0FFH (answer mode) sta ansflg ; and fall through to off ; ; ; OFF takes modem off hook, waits required 2 seconds, and ; activates the modem. ; off: call offhk ; Go off hook mvi b,20 ; Wait 2 seconds (req'd by telephone call mtime ; company when answering phone) jmp mdmon ; Now turn modem on and return from there ; ; ; OFFHK takes modem off hook ; Bit 6 controls switch hook 1=on, 0=off. ; Bit 4 is set to pulse, so dialer won't interfere with modem. ; offhk: mvi a,plsbit ; off hook, pulse out piodat ret ; ; ; ONHK puts modem on hook. ; onhk: mvi a,onhook or plsbit ; on hook, pulse out piodat ret ; ; ; "SET DELAY" processor ; ; Sets delay used when waiting for carrier to appear ; on dialing out and on answering phone. Only affects ; internal: returns error when used with external. ; delay: call mdmsel jnz setbad ; Nope, no good for external modem mvi c,sblank call mex db jrc,dlshow-$-1 ; If blank, show current value mvi c,evala call mex mov a,h ora a jnz seterr ; No greater than 255, please mov a,l sta ndelay ; store new delay dlshow: call milp db 'Answer DELAY',tab,0 lda ndelay mov l,a mvi h,0 mvi c,decout ; MEX prints value call mex call milp db ' secs',0 ret ; endif ; intrn ; ; ; "SET BAUD" processor ; ; Set current baud rate. Returns error if used ; with internal modem, which is always 300 baud. ; stbaud: mvi c,sblank ; If input blank, show current value call mex db jrc,bdshow-$-1 ; if intrn call mdmsel ; Can't change from 300 on internal jz setbad endif ; mvi c,bdpars ; parse a baudrate call mex db jrc,stbd1-$-1 ; bad one call pbaud ; Try to set it stbd1: jc seterr ; Might be unsupported ; And fall through to bdshow ; ; Show current baud rate ; bdshow: call milp db 'BAUD Rate',tab,0 lda mspeed mvi c,prbaud ; Let MEX print it call mex call mdmsel ; Which modem we on? dw ldaix ; Get returned from CP/M flag db 2 ani 0fh ; Get baud rate changed nybble rz ; No change jmp prflg ; Yes. Print flag (inside SIOCHG routine). ; ; ; This routine sets baud rate passed as MSPEED code in A. ; Returns CY=1 if baud rate not supported. ; pbaud: push h ; Don't alter regs--MEX or modem overlay push d ; might call this through NEWBDV push b mov b,a ; Save data being passed in ; if intrn call mdmsel ; Which modem? db jrz,pbxit-$-1 ; If internal, just return. endif ; mov a,b ; Get mspeed code back call telctc db jrc,pbxit-$-1 ; If error, don't change variables sta mspeed ; Set it for MEX's use sta exmbd ; Set it in overlay (for switching, cloning) ora a ; Clear carry (error flag) pbxit: pop b pop d pop h ret ; ; ; TELCTC takes an MSPEED code in A, sends appropriate ; value to CTC for external modem. Separated out from ; pbaud so the initialization routine can call it separately. ; telctc: mov e,a ; MSPEED code to DE mvi d,0 lxi h,baudtb ; offset into baudrate table dad d mov a,m ; get code ora a ; 0 means unsupported by hardware stc ; prep carry in case unuupported db jrz,telxit-$-1 ; exit if bad cmc ; turn carry off if here--we are ok push psw ; not bad, so set the baud rate mvi a,47h out baudrp ; Kick the CTC pop psw out baudrp ; Give it the speed value lda extregs+2 ; Get current return from CP/M flag ani 0f0h ; Strip out 'baud rate changed' nybble sta extregs+2 ; Store new value mov a,e ; Get MSPEED code back telxit: ret ; ; Table of CTC rate values used by above routine. ; baudtb: db 02h ; 110 db 05h ; 300 db 0 ; 450 (not supported) db 06h ; 600 db 0 ; 710 (not supported) db 07h ; 1200 db 0ah ; 2400 db 0ch ; 4800 db 0eh ; 9600 db 0fh ; 19200 ; ; ; "SET LENGTH" processor: set bits per character ; ; The number of bits per character is controlled for ; the receiver circuit by bits 6 and 7 of the byte ; sent to the SIO write-register 3 and for the trans- ; mitter circuit by bits 5 and 6 of the byte sent to ; the SIO write-register 5. The assumption has been ; made here that both transmission and reception will ; be carried on at the same number of bits per charac- ; ter. The bit configurations are: ; ; Bit 6 Bit 5 (Register 3) ; BPC Bit 7 Bit 6 (Register 5) ; 5 0 0 ; 6 1 0 ; 7 0 1 ; 8 1 1 ; stbits: mvi c,sblank ; Check for bits/char call mex db jrc,shbits-$-1 ; If none, print current value lxi d,bittbl ; Check for proper syntax mvi c,lookup call mex jc seterr ; No match, print error call mdmsel ; Get current modem's regs dw ldbix ; Get REG 3 db 3 dw ldcix ; Get REG 5 db 5 pchl ; Jump to routine address from lookup ; bit5: mov a,b ; Reg 3 ani 03fh ; Reset bits 6 & 7 mov b,a mov a,c ; Reg 5 ani 09fh ; Reset bits 5 & 6 db jr,stbts1-$-1 ; bit6: mov a,b ; Reg 3 ani 0bfh ; Reset bit 6 ori 080h ; Set bit 7 mov b,a mov a,c ; Reg 5 ani 0dfh ; Reset bit 5 ori 040h ; Set bit 6 db jr,stbts1-$-1 ; bit7: mov a,b ; Reg 3 ori 040h ; Set bit 6 ani 07fh ; Reset bit 7 mov b,a mov a,c ; Reg 5 ori 020h ; Set bit 5 ani 0bfh ; Reset bit 6 db jr,stbts1-$-1 ; bit8: mov a,b ; Reg 3 ori 0C0h ; Set bit 6 & 7 mov b,a mov a,c ; Reg 5 ori 060h ; Set bit 5 & 6 ; stbts1: dw ldixa ; Store Reg 5 db 5 dw ldixb ; Store Reg 3 db 3 lda ctlprt ; Get current control port mov c,a ; in C call nitsio ; Tell the SIO the change ; and fall through to shbits ; ; Show current bits per character ; shbits: call milp ; Display bits/char db 'LENGTH',tab,tab,0 call mdmsel ; Which modem we using? dw ldaix ; Get Reg 5 db 5 ani 060h ; Extract bits 5, 6 for examination ora a ; If bits 5,6 = 0 then 5 bpc db jrnz,shbts1-$-1 ; Nope, go try others. call milp db '5',0 db jr,shbtsx-$-1 ; shbts1: cpi 040h ; Test bits 5, 6 = 10 db jrnz,shbts2-$-1 ; If not, go try others. call milp ; If so, 6 bits. db '6',0 db jr,shbtsx-$-1 ; shbts2: cpi 020h ; Test bits 5, 6 = 01 db jrnz,shbts3-$-1 ; If not, must be 8 bits call milp ; Yup, 7 bits db '7',0 db jr,shbtsx-$-1 ; shbts3: call milp ; 8 bits per char db '8',0 ; shbtsx: call milp db ' bits',0 jmp siochg ; And show if value is current ; ; SET LENGTH command table ; bittbl: db '5'+80h ; "set length 5" dw bit5 db '6'+80h ; "set length 6" dw bit6 db '7'+80h ; "set length 7" dw bit7 db '8'+80h ; "set length 8" dw bit8 db 0 ; <<== end of bpc table ; ; ; "SET PARITY" processor: reset transmit/receive parity ; ; Parity is controlled by bits 0 and 1 of ; the byte sent to the SIO write-register ; 4 as follows: ; ; Parity Bit 1 Bit 0 ; Off - 0 ; Odd 0 1 ; Even 1 1 ; stprty: mvi c,sblank ; Check for parity code call mex db jrc,shprty-$-1 ; If none, print current parity lxi d,partbl ; Check for proper syntax mvi c,lookup call mex jc seterr ; No match, print error call mdmsel ; Which modem? dw ldaix ; Get this modem's register 4 db 4 pchl ; Jump to routine address from lookup ; proff: ani 0fch ; Reset bits 0 & 1 db jr,partb1-$-1 preven: ori 003h ; Set bits 0 & 1 db jr,partb1-$-1 prodd: ori 001h ; Set bit 0 ani 0fdh ; Reset bit 1 ; partb1: dw ldixa ; Put A in Reg 4 db 4 lda ctlprt ; Get current control port mov c,a ; in C call nitsio ; SIO might be interested in this ; And fall through to shprty... ; ; Show parity selected. ; shprty: call milp ; Display parity db 'PARITY',tab,tab,0 call mdmsel ; Get current modem's regs dw ldaix ; Get Reg 4 db 4 ; ani 003h ; Extract bits 0,1 ora a ; If bits off then parity off db jrnz,shprt1-$-1 call milp db 'OFF',0 db jr,siochg-$-1 ; shprt1: cpi 1 ; If bits 0,1 = 01, then odd db jrnz,shprt2-$-1 call milp db 'ODD',0 db jr,siochg-$-1 ; shprt2: call milp db 'EVEN',0 ; And fall through to siochg ; ; SIOCHG flags if SIO has not been set since return from ; CP/M (meaning current settings might not be accurate). ; Called from SHPRTY (above), and SHSTOP and SHBITS. ; siochg: dw ldaix ; Get current return from CP/M flag db 2 ani 0f0h ; Isolate SIO change nybble rz ; Not changed, do nothing prflg: call milp db tab,'(?)',0 ; Flag possibly wrong SIO value ret ; ; SET PARITY command table ; partbl: db 'OF','F'+80h ; "set parity off" dw proff db 'EVE','N'+80h ; "set parity even" dw preven db 'OD','D'+80h ; "set parity odd" dw prodd db 0 ; <<== end of parity table ; ; ; "SET STOPBITS" processor: reset number of stop bits ; ; The number of stop bits is controlled by bits ; 2 and 3 of the byte sent to the SIO write- ; register 4, as follows: ; ; Stop bits Bit 3 Bit 2 ; 1 0 1 ; 1.5 1 0 ; 2 1 1 ; ; ststop: mvi c,sblank ; Check for stop bits call mex db jrc,shstop-$-1 ; If none, print current value lxi d,stptbl ; Check for proper syntax mvi c,lookup call mex jc seterr ; No match, print error. call mdmsel ; Get current modem's regs dw ldaix ; Get Reg 4 db 4 pchl ; Jump to routine address from lookup ; stop01: ani 0f7h ; Reset bit 3 ori 004h ; Set bit 2 db jr,ststp1-$-1 stop02: ori 00ch ; Set bits 2 and 3 db jr,ststp1-$-1 stop15: ori 008h ; Set bit 3 ani 0fbh ; Reset bit 2 ; ststp1: dw ldixa ; Store Reg 4 db 4 lda ctlprt ; Get current port # mov c,a ; in C call nitsio ; Tell the SIO what's going on ; and fall through to showst ; ; Show current stop bits ; shstop: call milp ; Display stop-bits db 'STOPBITS',tab,0 call mdmsel ; Which modem? (for folks who call us direct) dw ldaix ; Get Reg 4 db 4 ani 00Ch ; Extract bits 2, 3 for examination cpi 4 ; If 2 on, 3 off, one stop db jrnz,shst15-$-1 ; No, go check for 1.5 or 2 call milp db '1',0 db jr,shstxt-$-1 shst15: cpi 008h ; IF 3 on, 2 off, 1.5 stops db jrnz,shst20-$-1 ; Nope, go do 2. call milp db '1.5',0 db jr,shstxt-$-1 shst20: call milp ; Must be 2 stop bits db '2',0 ; shstxt: jmp siochg ; And show if this value is current one ; ; SET STOPBITS command table ; stptbl: db '1'+80h ; "set stopbits 1" dw stop01 db '2'+80h ; "set stopbits 2" dw stop02 db '1.','5'+80h ; "set stopbits 1.5" dw stop15 db 0 ; <<== End of stop-bits table ; ; ; ; Internal Modem Dialing routine ; ; This routine hands off to the Modem overlay's dialer ; if one was installed (NITMOD fixes the appropriate vectors: ; this may or may not be kosher under MexPlus, untested). ; ; Supports ',' in dial string to insert delay, ; and 'T' or 'P' in dial string to switch to tone or pulse. ; Supports '*' and '#' codes when Tone dialing. ; Exclamation mark does flash, ';' returns to command mode ; with phone off hook (for really long dial strings or to ; use MEX simply as a phone dialing program) and ; 'R' initiates 'reverse' call: dial out, but use answer mode. ; (';' and 'R' must be last char in string. Mutually exclusive). ; ; Also allows user to hit any key to abort current dial ; without aborting other dial commands lined up, along ; with ^C for full abort. ; dial: if intrn mov b,a ; Save number call mdmsel ; External modem in use? mov a,b ; Get number jnz exdial ; Pass calls to MXM overlay via exdial routine lxi h,dialwd ; Set up ptr to dialwd (saves space) cpi 254 jz stdial ; Start dial command... cpi 255 jz endial ; End dial command... sta digit ; Save digit for Endial next pass through cpi ',' ; Smartmodem pause command? db jrnz,ckcar-$-1 mvi b,dildly ; Delay to simulate smartmodem jmp mtime ; return from MEX ; ckcar: cpi 'T' db jrnz,ckcar1-$-1 jmp sitone ; Set DIALWD to tone and return from there ; ckcar1: cpi 'P' db jrnz,ckcar2-$-1 jmp sipulse ; Set DIALWD to pulse and return from there ; ckcar2: cpi '!' db jrnz,ckcar3-$-1 call onhk ; Flash (transfer call, etc): Go on hook mvi b,5 ; Wait 1/2 second call mtime jmp offhk ; Go off hook. Return from there. ; ckcar3: mov c,a ; Save digit mov a,m ; Get dialwd ani plsbit ; Tone dial? mov a,c db jrnz,ckcar5-$-1 ; Nope, skip special tone-only digits ; cpi '*' db jrnz,ckcar4-$-1 mvi c,10 ; Dialer wants 10 decimal to dial '*' db jr,dialit-$-1 ; ckcar4: cpi '#' db jrnz,ckcar5-$-1 mvi c,11 ; Dialer wants 11 decimal to dial '#' db jr,dialit-$-1 ; ckcar5: cpi '9'+1 ; Digits are 0-9 rnc ; Too big... sui '0' rc ; Too small... mov c,a ; Save it for dialer dialit: mov a,m ; Get DIALWD: Tone/Pulse ani 00111111b ; Off hook mask ora c ; Or digit with Tone/Pulse & off hook mask call digout ; Give this digit to dialer chip ; ; Instead of using interrupts we will just delay appropriate ; amount and assume dialer did it's job. This guards against ; crashing 2.2u1 bios, and for folks who use SET INTERNAL when ; no modem is there (would wait forever for dialer interrupt). ; ani plsbit ; Tone or pulse? mvi b,2 ; Tone takes 140 ms total, round up to 200 db jrz,dial2-$-1 ; mov a,c ; Get digit to calculate Pulse delay ora a db jrnz,dial1-$-1 ; If not 0, just go add IDP and do it mvi a,10 ; It was 0: make it 10 for 10 pulses dial1: adi 11 ; Add 800 ms inter-digit pause + lotsa slop mov b,a dial2: jmp mtime ; Delay, and return from there ; ; ; Digout sends one digit to dialer chip. ; Digit is binary, in low nybble of output byte. ; Bit 7 is toggled to tell dialer data is ready. (Data Present line) ; On/Off hook (bit 6) and Pulse/Tone (bit 4) should be set as desired. ; digout: out piodat ; Hand it to the dialer nop ; Slight delay (150 ns required) nop ori digrdy ; Digit ready mask out piodat nop ; Slight delay (300 ns required) nop nop ani dignot ; Digit not ready mask out piodat ret ; ; ; Comes here at beginning of dial. ; stdial: lda tpulse ; Get Tone/pulse flag cpi 'T' db jrnz,stdial1-$-1 call sitone ; Fix DIALWD in case changed during last dial db jr,stdial2-$-1 stdial1: call sipulse ; Fix DIALWD in case changed during last dial stdial2: xra a ; Set to originate mode sta ansflg call mdmsq ; Turn off modem call offhk ; Go off-hook mvi b,20+dialwt ; Give dial-tone at least 2 sec to appear jmp mtime ; return from MEX ; ; ; ENDIAL waits for a response from the modem, whether in originate ; or answer mode. In either case, carrier detection is awaited ; for the length of time specified in the SET DELAY command (value ; stored in NDELAY). A modest attempt is made to simulate smartmodem ; operation in the answer mode. ; endial: call offhk ; Take off hook (needed when dialing out) lda digit ; cpi 'R' ; Reverse? (Answer mode dial?) db jrnz,endd1-$-1 call mana1 ; Present carrier now and avoid the rush db jr,endd2-$-1 ; endd1: cpi ';' ; Return to command mode? db jrnz,endd2-$-1 mvi a,0ffh ; send MEX bogus return code ret ; endd2: lda ndelay ; Get delay value mov b,a ; in B wtlp: push b ; Save it for later mvi b,10 ; Wait one second call mtime mvi c,kstat ; Was a key hit? call mex ora a pop b ; Get delay back db jrz,nokey-$-1 ; No key, keep waiting mvi c,dconio ; Get key directly (no console echo) mvi e,dconin ; MEX passes BDOS calls on through. call mex cpi ctrl and 'C' ; Was it abort char? db jrz,ctrc-$-1 ; Yup, disconnect & return abort code db jr,ddone-$-1 ; Nope, just pretend to time out. ; nokey: call dcdvec ; If no key, is DCD up? db jrnz,gotc-$-1 ; Got a carrier, so answer db djnz,(wtlp-$-1) mod 256 ; ; Timed out, disconnect. ; ddone: call discon ; Disconnect modem from phone mvi a,2 ; Return timeout code to MEX ret ; ; Got a carrier ; gotc: push b ; Preserve time count mvi b,dcdrsp ; Wait a sec for carrier to assert call mtime call dcdvec ; Still there? pop b db jrz,(wtlp-$-1) mod 256 ; Nope, keep waiting sta dcdflg ; Flag DCD so if it drops later, we know lda ansflg ; Mode: answer or originate? ora a ; If answering, modem is already on db jrz,turnon-$-1 ; If originating, send a carrier ; lda digit cpi 'R' ; Answer, but are we dialing? db jrz,conect-$-1 ; Yup, skip msg 'cause MEX will print one call milp db cr,lf,bell,'Carrier Detected',cr,lf,0 ; Got it...tell user. conect: xra a ret ; turnon: call mdmon ; DCD=1, connect, turn on modem xra a ; Return connect code (A=0) to MEX ret ; ctrc: call discon ; Disconnect modem from phone mvi a,3 ; Return Abort code to MEX ret ; endif ; intrn ; ; ; Pass calls to external modem's dialer through here. ; If intrn is off, and no external overlay is installed, ; ALL dialer calls come through here. ; exdial: cpi 255 ; endial command? ndial1: jnz dummy ; nope, just jump. (Vector fixed by NITMOD:) ndial2: call dummy ; call & return. (Vector fixed by NITMOD:) ora a rnz ; not connect code, just return to MEX mvi a,0ffh sta dcdflg ; Flag DCD for connect status xra a ret ; return connect to MEX ; dummy: mvi a,4 ; Uninstalled external modem. ret ; return error code to MEX. ; ; if intrn ; ; Turn off internal modem ; RTS controls squelch (drop RTS to squelch modem) ; DTR controls answer/orig. (DTR up = answer) ; mdmsq: mvi c,intct1 ; Internal port mvi b,5 ; Register 5 lda ansflg ora a ; Answer or originate? lda intregs+5 ; Get internal reg 5 db jrnz,msq1-$-1 ; ani orgmsk ; Orig mode,Tx squelch on ori txon db jr,msq2-$-1 ; msq1: ori ansbit or txon ; Ans mode, Tx squelch on ; msq2: jmp outc1 ; Tell SIO, return from there ; ; ; Turn on internal modem ; RTS controls squelch (drop RTS to squelch modem) ; DTR controls answer/orig. (DTR up = answer) ; mdmon: mvi c,intct1 ; Internal port mvi b,5 ; Register 5 lda ansflg ora a lda intregs+5 ; Get internal reg 5 db jrnz,mon1-$-1 ; ani orgmsk ; Orig mode, Tx squelch off ori txon or sqtbit db jr,mon2-$-1 ; mon1: ori ansbit or txon or sqtbit ; Ans mode, Tx squelch off ; mon2: jmp outc1 ; Tell SIO, return from there ; endif ; intrn ; ; ; General utility routines ; milp: mvi c,ilp ; Inline print string jmp mex ; mtime: mvi c,timer ; MEX timer, delay B * 100 msec jmp mex ; crlf: call milp ; Print return and linefeed db cr,lf,0 ret ; ; MDMSEL is called to interrogate which modem is in use. ; Returns: IX=current modems register table. ; Z if internal, NZ if external ; A register is destroyed. ; Called even if only using external, to set IX pointer. ; mdmsel: if intrn dw ldix ; Default to internal reg table dw intregs lda extmdm ; Which modem are we using? ora a jz mdsel1 endif ; intrn ; dw ldix ; Get extregs ptr dw extregs ; if not intrn mvi a,0ffh ; Force 'external' reply from routine ora a ; for any callers who might be using it. endif ; not intrn ; mdsel1: ret ; ; Screen function calls for MexPlus ; if mexplus cls equ 140fh ; Clear screen entrev equ 1415h ; Enter reverse video exrev equ 1418h ; Exit reverse video endif ; if not mexplus entrev: exrev: ret ; Just return endif ; ;========================================================================== ; Data Area ; ; Miscellaneous Default Data ; Note: EXTMDM, EXMBD, CTLPRT, DATPRT and DCDFLG must appear in that ; order or the SET EXTERNAL / SET INTERNAL routine will barf. ; extmdm: if extrn or (not intrn) db 0ffh ; Default modem is external endif ; if intrn and (not extrn) db 0 ; Default modem is internal endif ; exmbd: db defbd ; External modem speed. ; ctlprt: if extrn or (not intrn) db extct1 ; Default control port is external one. endif ; if intrn and (not extrn) db intct1 ; Default control port is internal one. endif ; datprt: if extrn or (not intrn) db extdat ; Default data port is external one. endif ; if intrn and (not extrn) db intdat ; Default data port is internal one. endif ; if not mexplus dcdflg: db 0 ; Flag for dcdoff. Leave this zero. endif ; if intrn and tone dialwd: db 01000000b ; Tone dial (bit 4 reset) on hook endif ; if intrn and (not tone) dialwd: db 01010000b ; Pulse dial (bit 4 set) on hook endif ; if intrn digit: db 0 ; DIAL stores 'R' or ';' here for ENDIAL ansflg: db 0 ; 0: originate, 0FFH: answer ndelay: db ansdel ; Seconds to wait for carrier noint: db 0 ; DISCON sets this if DCD stays up on int mdm ; after hang up. If so, modem does not exist. endif ; ; ; Default Z80 SIO params. Kept in table form so they can ; be indirectly referenced via Z80's IX register. ; Note that we never need Registers 1 and 2, so we use ; those locations for other storage. This way the table ; indices are more straightforward. ; ; Values for internal modem. ; if intrn intregs: db 00011000b ; Reg 0: Reset Channel A db 0 ; DCDFLG for this modem db 0 ; Non-zero: returned from CP/M db (ibits*64) or 01h ; Reg 3: Recieve params. db (isbits*4) or iprty or 040h ; Reg 4: Misc params. db (ibits*32) or 08h ; Reg 5: Transmit params. ; endif ; ; Values for external modem. ; extregs: db 00011000b ; Reg 0: Reset Channel A db 0 ; DCDFLG for this modem db 0 ; Non-zero: returned from CP/M db (ebits*64) or 01h ; Reg 3: Recieve params. db (esbits*4) or eprty or 040h ; Reg 4: Misc params. db (ebits*32) or 08ah ; Reg 5: Transmit params. ; endmxo equ $ ; ; ; The following code will insure that we don't exceed the maximum size ; allowed for MEX overlays. If we do, an error will occur in assembly, ; and you will see the comment below. Code stolen from MXM-240x.ASM. ; gap equ 0AFFh-endmxo oops equ (gap and 8000h) shr 15 ; if oops lda undef1 ; ERROR! Overlay past 0B00H! lda undef2 ; You must shorten before use! endif ; end