TITLE 'Interrupt-driven MEX overlay for SSM IO5 v1.0' ; MXO-I510.ASM -- SSM IO5 overlay for MEX 86/11/22 VERSION equ 10 ;version number ; NOTE 1: This overlay is designed for use with either MEXPLUS or MEX. ; Edit this file for your preferences, then follow the 'TO USE' ; example shown below. ; ; NOTE 2: The SSM IO-5 board supports two Intel 8251s plus 3 parallel ports. ; Extensive configuration options are supported by the many jumpers on ; this board, but only a few of them relate to software. These are ; supported by the SET command, as follows: ; SET BASE sets base port number of board ; SET PORT selects which serial port (A or B) ; SET DATA select position of data port (0 or 1); the ; status port will be at the other address ; SET RST selects restart vector to use ; Other SET commands supported: ; SET DIAL select pulse/touch dialing ; TO USE: First edit this file filling in answers for your own equipment, ; then assemble with ASM.COM or equivalent assembler. Finally, ; depending on the version of Mex, create an executable module: ; ; For MEX (v1.1x), use MLOAD (v2.3 or later) to overlay the base ; .COM file: ; A>MLOAD MEX.COM=MEX114.COM,[MXO-SMxx,]MXO-I5xx ; where MXO-SMxx is an optional modem overlay. ; ; For MEXPLUS (v1.2 or greater), use MLOAD to create an overlay ; file (.OVR), boot MEXPLUS, and use its builtin LOAD command to ; install the overlay: ; A>MLOAD MXO-MMxx.OVR=MXO-I5xx.HEX ; A>MEXPLUS ; [MEX] A0>>LOAD MXO-I5XX.OVR ; ; = = = = = = = = = = = = = = = = = = ; ; 86.11.22 - v1.0: created first version, based on - Roger Burrows ; MXO-S511.ASM ; ; = = = = = = = = = = = = = = = = = = YES equ 0FFH NO equ 0 BELL equ 07H ;bell TAB equ 09H ;tab LF equ 0AH ;linefeed CR equ 0DH ;carriage return ESC equ 1BH ;escape ; Change the following information to match your equipment DEFBASE equ 090H ;Default base address for IO5 board DEFPORT equ 'B' ;Default serial port (A or B) DEFDATA equ 0 ;Default data port (0=even, 1=odd) DEFRST equ 7 ;Default restart code (1 thru 7) ; End of information to change DEFSTAT equ 1-DEFDATA ;Default status port DATA equ DEFBASE+(DEFPORT-'A')*2+DEFDATA ;8251 data port STATUS equ DEFBASE+(DEFPORT-'A')*2+DEFSTAT ;8251 status port TXRDY equ 01H ; Transmitter Empty MDSNDB equ TXRDY ; Bit to test for Send MDSNDR equ TXRDY ; & value when ready RXRDY equ 02H ; Receiver Ready MDRCVB equ RXRDY ; Bit to test for Receive MDRCVR equ RXRDY ; & value when ready COMMAND equ STATUS ;8251 command port TXEN equ 01H ; Transmit Enable DTR equ 02H ; Data Terminal Ready RXEN equ 04H ; Receive Enable SBRK equ 08H ; Send Break ER equ 10H ; Error Reset RTS equ 20H ; Request To Send IR equ 40H ; Internal Reset STDCMD equ TXEN+DTR+RXEN+RTS ;standard command combination STDMODE equ 4EH ;Standard Mode Value (see 8251 spec): ; bits: 8 data, 1 stop, no parity ; baud rate factor = 16x ; 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 ;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 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 100h ;we begin ds 3 ;for the "JMP START" instruction PMODEM: db NO ;yes=PMMI modem \ / These 2 locations are not SMODEM: db NO ;yes=Smartmodem / \ referenced by MEX TPULSE: db 'T' ;T=touch, P=pulse (referenced by MXO-SMxx) CLOCK: db 40 ;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 5 ;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 YES ;yes=allow transmission of logon ;write logon sequence at location LOGON SAVCCP: db YES ;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 NO ;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 NO ;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 ds 2 ;used for PMMI (not referenced by MEX) INSTAT: ;in modem status port jmp STATIN ;(can't fit code into 10 bytes) ; Default values for BASE, PORT, DATA, RST (here to save space) db 'DFT' ;eyecatcher BASEVAL:db DEFBASE PORTVAL:db DEFPORT DATAVAL:db DEFDATA RSTVAL: db DEFRST OTDATA: di ;out modem data port DA1: out DATA mvi a,STDCMD ;have to turn TXEN back on call OUTCMD ; to transmit data ei ret INDATA: ;in modem data port jmp DATAIN ;(can't fit code into 10 bytes) ; Buffer pointers & overrun counter (here to save space) db 'BU' ;eyecatcher INTPTR: dw BUFFER ;next spot where interrupt routine stores data MEXPTR: dw BUFFER ;next spot where mex gets data from OVERUN: db 0 ;number of buffer overruns MASKR: ani MDRCVB ;bit to test for receive ready ret TESTR: cpi MDRCVR ;value of receive bit when ready ret MASKS: ani MDSNDB ;bit to test for send ready ret TESTS: cpi MDSNDR ;value of send bit when ready ret DCDTST: jmp DCDVEC ;data carrier detect (MEXPLUS) RNGDET: jmp RNGVEC ;ring detect (MEXPLUS) db 0,0,0,0,0 ;reserved SMDISC: ds 3 ;smartmodem disconnect (MEXPLUS) DIALV: ds 3 ;dial digit in A DISCV: jmp PDISC ;disconnect modem (with DTR) GOODBV: jmp GOODBY ;call just before exit to CP/M INMODV: jmp INITMD ;called on entry from CP/M NEWBDV: jmp DUMMY ;set new baud rate NOPARV: jmp DUMMY ;set modem for no parity PARITV: jmp DUMMY ;set modem for parity SETUPV: jmp SETCMD ;process SET cmd ds 3 ;reserved, not used by MEX VERSNV: jmp SYSVER ;sign-on message from overlay BREAKV: jmp PBREAK ;send a break ; Do not change the following six lines ; (jump vectors, not supported in MEX 2.0) ds 3 ;replace with MEX function 9 ds 3 ;replace with MEX function 10 ds 3 ;replace with MEX function 247 ds 3 ;replace with MEX function 255 ds 3 ;not supported by MEX ds 3 ;replace with MEX function 254 ; Routines to clear screen & clear to end of screen. If using ; these routines, set SCRTST to YES at 010AH (above). CLREOS: mvi c,PRINT lxi d,TCLEOS call MEX ret CLS: mvi c,PRINT lxi d,TCLSCR call MEX ret ; End of fixed area ... from here to 1FFH is reserved (MEXPLUS) org 200H ;.......... start of free-format user routines .......... ; Return data carrier detect status (MEXPLUS) DCDVEC: ;not supported - no DCD on 8251s ;(drops through into RNGVEC) ; Return ring indicator status (MEXPLUS) RNGVEC: mvi a,254 ;not supported - no RI on 8251s DUMMY: ret ; Get input status by combining hardware & buffer status STATIN: push h ;save regs ST1: in STATUS ani 255-MDRCVR ;turn off real input status mov h,a ;and save across s/rtn call call COMPTR ;compare pointers mov a,h ;restore status pop h ; & regs rz ;equal, return any o/p status ori MDRCVR ;else set i/p waiting ret ; Get input data (from buffer) DATAIN: call COMPTR ;buffer empty ? mvi a,BELL ;yes - return with a bell (mex shouldn't rz ; be calling us anyway) push h ;save regs push d lhld MEXPTR ;get data ptr mov d,m ;get the byte call INCPTR ;up the pointer shld MEXPTR ; & save mov a,d ;put byte where mex wants it pop d ;restore regs pop h ret ;*************** keep the next few routines together *************** ; Send break to remote PBREAK: call FLUSH ;flush buffer first mvi a,STDCMD+SBRK ;set break jmp ODO ;go output, delay, then output ; Disconnect modem PDISC: call FLUSH ;flush buffer first mvi a,STDCMD-DTR ;turn off DTR ; Output A to command port, Delay 300 millisecs, then Output normal command ODO: call OUTCMD mvi b,3 ;delay 300 milliseconds mvi c,TIMER call MEX ; Output command to resume normal status NORMST: mvi a,STDCMD ;resuming normal service ... ; Output to command port OUTCMD: ;output to command port CO1: out COMMAND ; Dummy routine ret ;*************** end of 'drop-through' routines *************** ; Enter from CP/M INITMD: call FIXRST ;set up restart vector call FIXIO ;fix up port references ret ; Exit to CP/M GOODBY: di ;disable interrupts xra a ;turn off everything (DTR, RTS etc) jmp OUTCMD ;which returns ; Issue signon message SYSVER: mvi c,ILP call MEX db 'Version ',VERSION/10+'0','.',VERSION MOD 10+'0' db ' for SSM IO5 board at base address ',0 lda BASEVAL ;print base port number call PRTHEX call CRLF mvi c,ILP call MEX db 'Serial port ',0 mvi c,CONOUT lda PORTVAL ;print serial port (A or B) mov e,a call MEX mvi c,ILP ;show data port as even or odd call MEX db ', data port is ',0 lda DATAVAL lxi d,EVEN ;print even/odd ora a ;is it even ? jz SYSVR1 ;yes lxi d,ODD ;else odd SYSVR1: mvi c,PRINT call MEX mvi c,ILP call MEX db ', restart code ',0 lhld RSTVAL ;print restart code mvi h,0 mvi c,DECOUT call MEX mvi c,ILP call MEX db ', ',0 lda TPULSE lxi d,PLSMSG ;print pulse/touch cpi 'P' ;is it pulse ? jz SYSVR2 ;yes lxi d,TCHMSG ;else touch SYSVR2: mvi c,PRINT call MEX mvi c,ILP call MEX db ' dialing',0 ret ; Handle SET command SETCMD: mvi c,SBLANK ;any arguments? call MEX jc STSHOW ;if not, go print out values mvi c,LOOKUP lxi d,CMDTBL ;parse command call MEX jc SETERR ;can't find it pchl ;else go there SETERR: mvi c,PRINT lxi d,SETEMS ;print error jmp MEX ;which returns SETEMS: db 'SET command error',CR,LF,CR,LF,'$' CMDTBL: db '?'+80h ; "set ?" dw STHELP db 'BAS','E'+80h ; "set base" dw STBASE db 'DAT','A'+80h ; "set data" dw STDATA db 'DIA','L'+80h ; "set dial" dw STDIAL db 'RS','T'+80h ; "set rst" dw STRST db 'POR','T'+80h ; "set port" dw STPORT db 0 ; SET : print current statistics STSHOW: lxi h,SHOTBL ;get table of SHOW subroutines STSHLP: 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 mvi c,CHEKCC ;check for console abort call MEX pop h ;it's done jnz STSHLP ;continue if no abort ; Enter here to do cr+lf CRLF: mvi c,PRINT lxi d,CRLFMS jmp MEX ;which returns GOHL: pchl ; table of SHOW subroutines SHOTBL: dw SHOWBA dw SHOWPO dw SHOWDT dw SHOWRS dw SHOWDL dw 0 ; SET ? processor STHELP: mvi c,PRINT lxi d,HLPMSG jmp MEX ;which returns ; The help message HLPMSG: db CR,LF,'SET command options:' db CR,LF,' SET BASE ... set new base port (00,10 ... F0)' db CR,LF,' SET DATA <0|1> ... set data port address (0=even,1=odd)' db CR,LF,' SET DIAL ... set Pulse or Touch dialing' db CR,LF,' SET PORT ... set serial port id' db CR,LF,' SET RST ... set restart code (1 thru 7)' db CR,LF CRLFMS: db CR,LF,'$' ; SET BASE command processor STBASE: mvi c,SBLANK ;skip blanks call MEX jc SETERR ;nothing else lxi h,0 ;use HL to build port # STBAS1: push h ;save across MEX mvi c,GNC ;get char from input, cy=1 if none call MEX pop h ;restore jc STBAS2 call HEXCON ;convert to hex nybble in A jc SETERR ;not hex dad h ;shift L left by 4 bits dad h dad h dad h ora l ;or with new nybble mov l,a ;back to L mov a,h ;anything in H yet ? ora a jnz SETERR ;yes - too big jmp STBAS1 ;else go round again STBAS2: mov a,l ;reg L contains base port # ani 0fh ;check boundary alignment jnz SETERR ;no good mov a,l ;ok - save for display sta BASEVAL call FIXIO ;fix up in/out with BASEVAL,PORTVAL,DATAVAL SHOWBA: mvi c,ILP call MEX db 'Base address set to ',0 lda BASEVAL call PRTHEX jmp CRLF ;which returns ; SET DATA processor STDATA: mvi c,SBLANK ;skip blanks call MEX jc SETERR ;nothing else mvi c,EVALA ;get numeric from input call MEX mov a,h ora a ;> 256 ? jnz SETERR ;yes, error mov a,l cpi 2 ;check it jnc SETERR ;invalid sta DATAVAL ;store it call FIXIO ;fix up in/out with BASEVAL,PORTVAL,DATAVAL SHOWDT: mvi c,ILP call MEX db 'Data port set to ',0 lda DATAVAL ;get data setting lxi d,EVEN ;assume even ora a ;is it ? jz SHOWT2 ;yes lxi d,ODD ;else odd SHOWT2: mvi c,PRINT call MEX jmp CRLF ;which returns ; SET DIAL processor STDIAL: mvi c,SBLANK ;skip blanks call MEX jc SETERR ;nothing else lxi d,DIALTB ;parse cmd mvi c,LOOKUP call MEX jc SETERR mov a,l ;get upper case character sta TPULSE ;store it for MXO-SMxx SHOWDL: mvi c,ILP ;display status call MEX db 'Dialing type set to ',0 lda TPULSE ;get pulse/touch setting lxi d,PLSMSG ;assume pulse cpi 'P' ;is it ? jz SHOWD2 ;yes lxi d,TCHMSG ;else touch SHOWD2: mvi c,PRINT call MEX jmp CRLF ;which returns ; Dial table - saves us worrying about upper/lower case DIALTB: db 'P'+80H ;pulse dial db 'P',0 db 'T'+80H ;touch dial db 'T',0 db 0 ; SET PORT processor STPORT: mvi c,SBLANK ;skip blanks call MEX jc SETERR ;nothing else lxi d,PORTTB ;parse cmd mvi c,LOOKUP call MEX jc SETERR mov a,l ;get upper case character sta PORTVAL ;a-ok, save it call FIXIO ;fix up in/out with BASEVAL,PORTVAL,DATAVAL SHOWPO: mvi c,ILP ;display status call MEX db 'Port set to ',0 mvi c,CONOUT lda PORTVAL mov e,a call MEX ;display it jmp CRLF ;which returns ; SET RST processor STRST: mvi c,SBLANK ;skip blanks call MEX jc SETERR ;nothing else mvi c,EVALA ;get numeric from input call MEX mov a,h ora a ;> 256 ? jnz SETERR ;yes, error mov a,l ora a ;zero isn't allowed jz SETERR cpi 8 ;max of 7 jnc SETERR ;invalid sta RSTVAL ;store it call FIXRST ;fix up RST vector SHOWRS: mvi c,ILP call MEX db 'Restart code set to ',0 lhld RSTVAL ;get value in hl mvi h,0 mvi c,DECOUT call MEX jmp CRLF ;which returns ; Port table - saves us worrying about upper/lower case PORTTB: db 'A'+80H db 'A',0 db 'B'+80H db 'B',0 db 0 ;.......... subroutines used by the above routines .......... ; Convert ascii char in A to hex nybble HEXCON: cpi '0' ;< '0' ? rc ;error - return with carry set cpi '9'+1 ;<= '9' ? jc HEXNUM ;yes - must be 0-9 cpi 'A' ;< 'A' ? rc ;error cpi 'F'+1 ;<= 'F' ? jc HEXALF ;yes - convert to hex nybble cpi 'a' ;< 'a' ? rc ;error cpi 'f'+1 ;<= 'f' ? jnc HEXERR ;no - error sui 'a'-'A' ;convert lower case a-f to upper HEXALF: stc adi 9 ;convert upper case A-F to 3ah-3fh HEXNUM: ani 0fh ;clear high-order nybble ret HEXERR: stc ret ; Print contents of A as hex, followed by 'h' PRTHEX: push psw ;save A rrc ;right justify one hex digit rrc rrc rrc call PRTNYB ;print first hex digit pop psw call PRTNYB ;print second hex digit mvi c,CONOUT mvi e,'h' jmp MEX ;print trailing 'h' & return ; Print hex nybble PRTNYB: ani 0fh ;print one hex digit adi 90h ;trust me, this works daa aci 40h daa ;now ascii mvi c,CONOUT ;print it mov e,a jmp MEX ;which returns ; Update in & out instructions with new BASEVAL, PORTVAL, DATAVAL FIXIO: lda PORTVAL ;get port id sui 'A' ; make it 0 or 1 add a ; then 0 or 2 (offset to 1st reg for A or B) mov b,a ; in reg b lda BASEVAL ;get base address ora b mov b,a ;b = address of even port lda DATAVAL ;get data port offset ora b ;a = address of data port sta DA1+1 sta DA2+1 xri 01h ;now a = address of status/command port sta ST1+1 sta ST2+1 sta CO1+1 ;now go reinitialise ports mvi b,3 ;send 3 nulls xra a ; to force to command state FIXIO2: call OUTCMD dcr b jnz FIXIO2 mvi a,IR ;reset to mode level call OUTCMD mvi a,STDMODE ;set standard mode call OUTCMD mvi a,STDCMD+ER ;clear errors call OUTCMD jmp NORMST ;which returns ; Initialise restart vector & interrupts FIXRST: di ;safety first lxi d,INTRPT ;de->interrupt routine lhld RSTVAL ;hl = restart code mvi h,0 dad h dad h dad h ;* 8 = address of restart vector mvi a,JMP ;set up vector mov m,a inx h ;hl->a(interrupt routine) xchg ;hl->interrupt rtn, de->a(interrupt rtn) mov a,l stax d ;store address inx d mov a,h stax d ei ;ok, interrupt away ret ; Compare buffer pointers ; return with z flag set => pointers equal COMPTR: push h ;save regs push d lhld INTPTR xchg ;de->next place for input data lhld MEXPTR ;hl->next char for mex mov a,h cmp d ;high-order bytes the same ? jnz COMPTX ;no - definitely not equal mov a,l ;yes - it all depends on the low-order cmp e COMPTX: pop d ;restore regs pop h ret ;with z/nz set ; Increment buffer pointer ; enter with pointer in hl ; return with hl pointing to next byte of buffer (wrapped if nec) INCPTR: inx h ;increment mov a,h cpi HIGH BUFEND ;reached end of buffer ? rnz ;no, return mov a,l ;maybe cpi LOW BUFEND ;really end ? rnz ;no, return lxi h,BUFFER ;yes - time to wrap instead ret ; Flush buffer FLUSH: lhld INTPTR ;set MEXPTR equal to INTPTR shld MEXPTR ret ; Miscellaneous messages EVEN: db 'even address','$' ODD: db 'odd address','$' PLSMSG: db 'pulse','$' TCHMSG: db 'touch','$' ; Terminal-dependent control sequences TCLEOS: db 17H,'$' ;clear to end-of-screen TCLSCR: db 18H,'$' ;clear screen ;.......... interrupt-related items .......... ; Interrupt handler INTRPT: push psw ;minimum status save to start with ST2: in STATUS ;get status ani RXRDY ;receive data ? jnz INTRCV ;yes, go get it mvi a,STDCMD-TXEN ;else we got a useless TXRDY interrupt call OUTCMD ; so turn off TXEN jmp INTXIT ; & exit INTRCV: push h ;save more status DA2: in DATA lhld INTPTR ;where we want to store data mov m,a ;store it call INCPTR ;up to next shld INTPTR call COMPTR ;caught up to MEXPTR ? jnz INTOK ;no lxi h,OVERUN ;oops - overrun inr m INTOK: pop h ;restore status INTXIT: pop psw ei ;enable interrupts again ret ; Circular buffer for interrupt routine BUFFER equ $ ;at 1200 baud, 100h bytes fills in 2 secs BUFEND equ 0b00h ;don't club Smartmodem overlay BUFSIZ equ BUFEND-BUFFER ;for interest ;..... ; ; Note: This overlay must terminate prior to 0B00H (with Smartmodem overlay) ; 0D00H (without Smartmodem overlay) ;..... END