TITLE 'Commodore Modem Overlay V1.0 19 Nov 85' ; $*MACRO ; have MAC only display CODE not MACRO expansion ; ; This overlay was created for the C128 ; by Von Ertwine 19 Nov 85 ; ; SYSTEM CONSTANTS ; mdm$tp equ 0104h ; 7 (1670 if set) ; 6 (1650 or 1660 if set) ; 5 (1660 if set) ; 4 (supports CD if clear) ; 3 (set if 1200 baud is supported) ; 2 (set if 600 baud is supported) ; 1 (set if 300 baud is supported) ; 0 (set if 110 baud is supported) TPULSE EQU 0105h ;TONE/PULSE FLAG IN MODEM OVERLAY MSPEED EQU 0107h ; current baud rate MDM$ST EQU 012Ah ; normal status call SHOWBD EQU 012Eh ; show current baud rate out$mdm equ 0134h mask$s equ 014eh test$s equ 0151h DIALV EQU 0162h ;LOCATION OF DIAL VECTOR IN OVERLAY DISCV EQU 0165h ;LOCATION OF DISCONNECT VECTOR IN OVERLAY NEWBDV EQU 016Eh ;change baud rate (value in A) DIALOC EQU 0800h ;DIALING CODE GOES HERE MEX EQU 0D00h ;"CALL MEX" SMINIT equ 0d55h ; modem init vector ssetv equ 0d57h ; sset cmd vector smexit equ 0d59h ; modem exit vector page ; ; FOLLOWING ARE FUNCTION CODES FOR THE MEX SERVICE CALL PROCESSOR ; INMDM EQU 255 ;RETURN CHAR FROM MDM IN A, CY=NO CHR IN 100MS 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 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 ; ; eom equ 00H bell equ 07H ;bell lf equ 0AH ;linefeed cr equ 0DH ;carriage return esc equ 1BH ;escape ; no equ 0 yes equ not(no) page XxD$config: equ 0fd4Eh ; bit 7 0=no parity 1=parity ; bit 6 0=mark/space 1=odd/even ; bit 5 0=space/even 1=mark/odd ; bit 1 0=1 stop bit 1=2 stop bits ; bit 0 0=7 data bits 1=8 data bits RS232$status: equ XxD$config+1 ; bit 7 0=no data 1=send data ; bit 6 1=sending data now ; bit 5 1=recv queue active ; bit 4 1=parity error ; bit 3 1=framing error ; bit 2 not used ; bit 1 receiving data now ; bit 0 data byte ready xmit$data: equ RS232$status+1 recv$data: equ xmit$data+1 modrcvb equ 01H ; bit to test for receive modrcvr equ 01H ; value when receive ready modsndb: equ 80H ; bit to test for send modsndr: equ 00H ; value when send ready data$port$a equ 0DD00h ; bit 2 TxD Data (output) data$port$b equ data$port$a+1 data$port$b$dir equ data$port$b+2 ; bit 7 Data Set Ready (input) ; bit 6 Clear to Send (input) ; bit 5 Place Phone On Hook (active low output) ; bit 4 Carrier Detect (active low input) ; bit 3 Ring Indicator (active low input) ; bit 2 Data Terminal Ready (active hi output) ; bit 1 Request to Send (active hi output) ; bit 0 Received Data (input) page bit macro ?N,?R db 0cbh,?N*8+?R+40h endm setb macro ?N,?R db 0cbh,?N*8+?R+0C0h endm res macro ?N,?R db 0cbh,?N*8+?R+80h endm @chk macro ?DD if (?DD GT 7Fh) and (?DD LT 0FF80h) 'Displacement Error' endif endm jr macro ?N db 18h,?N-$-1 @chk ?N-$ endm jrz macro ?N db 28h,?N-$-1 @chk ?N-$ endm jrnz macro ?N db 20h,?N-$-1 @chk ?N-$ endm jrc macro ?N db 38h,?N-$-1 @chk ?N-$ endm jrnc macro ?N db 30h,?N-$-1 @chk ?N-$ endm inp macro ?R db 0EDh,?R*8+40h endm outp macro ?R db 0EDh,?R*8+41h endm exx macro db 0d9h endm page ; ; ; org mdm$st jmp mdm$stat ; default as 1670 and 1011A status ; org dialv jmp dial ; point system to dialer routine ; org discv jmp discon ; point disconect to modem disconect ; org SMINIT dw init$mod ; install modem init vector ; org ssetv dw do$sset ; install SSET comand vector ; ; ; org dialoc do$sset: mvi c,sblank call mex ; any arguments? jrc disp$options lxi d,opt$tbl mvi c,lookup call mex jrnc ipchl disp$options: call initmod mvi c,ilp call mex db cr,lf,'Select 1011A, 1650, 1670, 1660 or' db ' 1660CD (if Carrier Detect is supported)' db cr,lf,eom ret ipchl: pchl page opt$tbl: db '1011','A'+80h dw set$1011 db '165','0'+80h dw set$1650 db '1660C','D'+80h dw set$1660cd db '166','0'+80h dw set$1660 db '167','0'+80h dw set$1670 db 0 set$1670: mvi a,10001010b db 21h ; lxi h,(mvi a,0Fh) set$1011: mvi a,00001111b db 21h ; lxi h,(mvi a,42h) set$1650: mvi a,01000010b db 21h ; lxi h,(mvi a,72h) set$1660: mvi a,01110010b db 21h ; lxi h,(mvi a,62h) set$1660cd: mvi a,01100010b sta mdm$tp page mvi c,sblank call mex ; any arguments? jrc no$parm ani 01011111b ; make upper case cpi 'T' ; user select Tone? jrz save$T$P ; yes, go set it cpi 'P' ; no, user select Pluse jrnz no$parm ; no, use old parameter save$T$P: ; yes, select Pluse sta TPULSE ; change to PLUSE or TONE no$parm: lda mspeed cpi 0 ; 110 baud jrz ck$110 cpi 1 ; 300 baud jrz ck$300 cpi 3 ; 600 baud jrz ck$600 cpi 5 ; 1200 baud jrnz change$baud mvi a,00001000b db 21h ; lxi h,mvi a,4 ck$600: mvi a,00000100b db 21h ; lxi h,mvi a,2 ck$300: mvi a,00000010b db 21h ; lxi h,mvi a,1 ck$110: mvi a,00000001b lxi h,mdm$tp ; point to allowed rates ana m ; is baud rate allowed for jrnz initmod ; ..this modem ? yes, go do it page ; ; ; change$baud: ; no, adjust to lowest allowed rate mvi a,-1 try$next: inr a sta mspeed call NEWBDV ; change the baud rate lda mspeed ; ..(Cy set if bad rate) jrc try$next lxi h,showbd ; tell user the new baud rate push h ; ..after we are done ; ; C128 initialization -- set baudrate. ; INITMOD: mvi c,ilp call mex db cr,lf,'Current Device type is: ',eom call show$device shld MDM$ST+1 mvi c,ilp call mex db cr,lf,eom lxi b,data$port$b$dir lda mdm$tp ; get current modem type ani 40h ; 1650, 1660 modem? jrz init$no$dial mvi a,26h ; set data dir to output outp a lda on$hook$flag ; =FF if on hook ora a cnz phone$on$hook ; hang-up the phone (only if on hook) mvi c,low(data$port$b) inp a init$no$dial: ori 6 ; set DTR and CTS (active hi) outp a ret page ; ; ; show$device: mvi c,ilp lxi h,mdm$stat lda mdm$tp ; get current type bit 7,a jrz test$1660$50 lda tpulse sta msg$tpulse call mex db '1670 ' msg$tpulse: db 'x',eom ret ; test$1660$50: bit 6,a jrnz test$1650 call mex db '1011A - RS232 interface',eom ret ; test$1650: lxi h,mdm$stat$1650$60 bit 5,a jrnz show$1660 call mex db '1650',eom ret ; show$1660: bit 4,a jrnz show$1660$no$CD call mex db '1660 with CD',eom ret ; show$1660$no$CD: call mex db '1660 with-out CD',eom ret page ; ; FOLLOWING ROUTINE DISCONNECTS THE MODEM USING SMARTMODEM ; CODES. ALL REGISTERS ARE AVAILABLE FOR THIS FUNCTION. ; NOTHING RETURNED TO CALLER. ; DISCON: lda mdm$tp ani 80h jrz phone$on$hook ; go place phone on hook if not 1670 call wait$2$sec ; 1670, send hayes disconnect seq. call mdm$snd$msg db '+++',eom call wait$2$sec call mdm$snd$msg db 'ATH',cr,eom ; not needed by 1670 but req for hayes ret wait$2$sec: mvi b,20 mvi c,timer jmp mex ; delay 2 seconds page ; ; Place phone on hook (This routine is used if 1650 or 1660) ; phone$on$hook: mvi a,yes sta on$hook$flag lxi b,data$port$b lda mdm$tp ani 20h ; bit 5=1 if 1660, =0 if 1650 jrz type$1650 type$1660: inp a ori 20h ; set, phone on hook (1660) jr on$hook$cont type$1650: inp a ani not(20h) ; set, phone on hook (1650) on$hook$cont: outp a ret page ; ; This is the DIAL routine called by MEX to dial a digit. The digit ; to be dialed is passed in the A register. Note that two special ; codes must be intercepted as non-digits: 254 (start dial sequence) ; and 255 (end-dial sequence). Mex will always call DIAL with 254 ; in the accumulator prior to dialing a number. Mex will also call ; dial with 255 in A as an indication that dialing is complete. Thus, ; the overlay may use these values to "block" the number, holding it ; in a buffer until it is completely assembled (in fact, that's the ; scheme employed here for the Smartmodem). ; ; After the 254-start-dial sequence, MEX will call the overlay with ; digits, one-at-a-time. MEX will make no assumptions about the dig- ; its, and will send each to the DIAL routine un-inspected (some modems, ; like the Smartmodem, allow special non-numeric characters in the ; phone number, and MEX may make no assumptions about these). ; ; After receiving the end-dial sequence (255) the overlay must take ; whatever end-of-dial actions are necessary *including* waiting for ; carrier at the distant end. The overlay should monitor the keyboard ; during this wait (using the MEX keystat service call), and return ; an exit code to MEX in the A register, as follows: ; ; 0 - Carrier detected, connection established ; 1 - Far end busy (only for modems that can detect this condition) ; 2 - No answer (or timed out waiting for modem response) ; 3 - Keyboard abort (^C only: all others should be ignored) ; 4 - Error reported by modem ; ; ; ; The overlay should not loop forever in the carrier-wait routine, but ; instead use either the overlay timer vector, or the INMDMV (timed 100 ; ms character wait) service call routine. ; ; The DIAL routine is free to use any of the registers, but must return ; the above code after an end-dial sequence ; next$dial$state equ $+1 dial: jmp dial$idle page ; ; ; dial$idle: cpi -2 ; start dial sequence? mvi a,4 ; dial error code (valid if last state) rnz ; exit if not (this is the start state) lda mdm$tp ; get the modem type byte bit 7,a ; is device a Commodore 1670 ? jrz dl$tst$1650$60 ; no, go test 1650 and 1660 ; ; user has a Hayes compadable modem (1670) ; lda tpulse ; GET OVERLAY'S TOUCH-TONE FLAG sta ATDx ; PUT INTO STRING call mdm$snd$msg ; db 'ATD' ATDx: db 'T ',eom call set$next$state ; ; at this point we should recieve numbers to dial or exit code (-1) ; cpi -1 jnz mdm$snd$A ; send it (do not change states) call mdm$snd$CR ; send CR to modem to end input mvi b,-1 call strip$mdm ; remove any modem input call result ; returns with result is A push psw ; save result call mdm$snd$CR ; send CR to the modem pop psw ; ..will abort if error else ; ..just send CR to host. set$start$state: lxi h,dial$idle push h set$next$state: pop h shld next$dial$state ret page ; ; User has selected the 1011A or EQV. device. No dialing. ; dial$1011a: mvi a,4 ; device error code (1011A ret ; ..can not dial) ; ; ; dl$tst$1650$60: bit 6,a ; test if commodore 1650 or 1660 modem jrz dial$1011A ; if not 1650 or 1660 must be 1011A ; ; User has selected Commodore 1650 or 1660 modem ; call phone$on$hook xri 20h ; toggle on/off hook bit outp a ; take phone off hook call wait$2$sec call set$next$state ; ; ; cpi -1 jrz done$1650$60 cpi ',' jrz delay$1$sec cpi '-' rz sui '0' jrc send$responce jrnz not$10 mvi a,10 not$10: cpi 10+1 mov d,a jrnc send$responce page ; ; dial number in D ; dial$digit: lxi b,data$port$b ; point to CIA inp a ; get phone off hook value mov e,a dial$digit$cont: xri 20h ; toggle dial port bit outp a ; output phone on hook value mvi a,6 call delay ; delay 0.06 seconds outp e ; phone back off hook mvi a,4 call delay ; delay 0.04 seconds mov a,e dcr d jrnz dial$digit$cont delay$1$sec: mvi a,100 ; delay 1 second ; ; delay for the number of 0.01 seconds in A ; 20,455 T states per 10 mSecond ; 10+7ah+7ahl+4ahl+12ahl-5ah+4ah+12ah-5a+4a+12a-5+10+4+10=23ahl+18ah+11a+29 ; if H=19, L=46 then ; # T states = 20455a+33 delay: di ;4 push h ;10 delay$more: mvi h,19 ;7ah outer$delay: mvi l,46 ;7ahl inter$delay: dcr l ;4ahl jrnz inter$delay ;12ah(l-1)+7ah = 12ahl-5ah dcr h ;4ah jrnz outer$delay ;12a(h-1)+7a = 12ah-5a dcr a ;4a jrnz delay$more ;12(a-1)+7 = 12a-5 pop h ;10 ei ;4 ret ;10 page ; ; ; done$1650$60: lda mdm$tp cma ani 10h ; bit 4=0 if it supports CD jrz send$responce ; send A=0 (connected) lxi h,45000 wait$carrier: lxi b,data$port$b inp a ani 10h ; test carrier detect jrz send$responce ; send A=0 (connected) call test$abort ; user request abort ? jrz send$responce ; yes, send A=3 (abort code) dcx h mov a,h ora l jrnz wait$carrier mvi a,2 ; send - NO CARRIER code send$responce: lxi h,on$hook$flag mvi m,no jmp set$start$state page ; ; THE FOLLOWING LOOP WAITS FOR A RESULT FROM THE MODEM (UP TO ; 60 SECONDS: YOU MAY CHANGE THIS VALUE IN THE FOLLOWING LINE). ; NOTE THAT THE SMARTMODEM HAS AN INTERNAL 30 SECOND TIMEOUT WHILE ; FOR A CARRIER ON THE OTHER END. YOU CAN CHANGE BY PLAYING WITH THE ; S7 VARIABLE (I.E. SEND THE SMARTMODEM "AT S7=20" TO LOWER THE 30 SECOND ; WAIT TO 20 SECONDS). ; result: mvi c,60 ; wait 60 seconds for a responce result$loop: push b ; save current time lxi b,1*256+tmdinp ; wait for up to 1 second for input call mex ; any modem responce yet? pop b ; recover current time jrnc test$responce ; yes, go test it call test$abort ; no, user wish to abort rz ; yes, exit (A=3) dcr c ; no, 60 second over yet? jrnz result$loop ; no, test again mvi a,2 ; yes, return timeout code ret ; ; ; test$responce: call test$code ; check responce Cy=1 if unknown mov a,b ; place responce code in A push psw ; save responce and Cy flag mvi b,lf ; remove characters from modem until call strip$mdm ; ..none in 100MS or a LF pop psw ; recover responce and Cy flag jrc result ; start over if bad responce ret page ; ; ; test$abort: push h push b ; save count (in C) mvi c,chekcc call mex ; user type a ^C ? pop b pop h mvi a,3 ; return abort code (only ret ; ..used if abort) ; ; ; strip$mdm: mvi c,inmdm ; get modem character (Cy=1 if push b ; ..none in 100Ms) call mex pop b rc ; return if Modem is empty cmp b ; test for end character rz ; found user char, return jr strip$mdm ; get next character ; ; check modems responce against legal ones, return carry set if ; not a legal responce, else return code for responce ; test$code: ani 7FH ; strip parity bit mvi b,0 ; start with connect code cpi 'C' ; Connect? rz ; yes, ret cpi '1' ; # code for Connect? rz ; yes, ret cpi '5' ; # code for Connect 1200? rz ; yes, ret mvi b,2 ; no connect code cpi 'N' ; No Connect? rz ; yes, ret cpi '3' ; # code for No Connect? rz ; yes, ret mvi b,4 ; modem error code cpi 'E' ; Error ? rz ; yes, ret cpi '4' ; #code for Error? rz ; yes, ret stc ; set carry if none of above ret page ; ; send string following CALL to the modem (to EOM) ; mdm$snd$msg: xthl call mdm$snd$HL xthl ret ; ; send string pointed to by HL to modem (ended with a zero (EOM)) ; mdm$snd$HL: mov a,m ; FETCH NEXT CHARACTER ora a ; END? rz ; DONE IF SO call mdm$snd$A inx h jr mdm$snd$HL ; ; send a CR to the modem ; mdm$snd$CR: mvi a,cr ; ; send character in A to modem ; mdm$snd$A: push psw ; save char to send mdm$snd$bzy: mvi c,snd$rdy ; test if modem is ready for a byte call mex ; jrnz mdm$snd$bzy ; if not ready loop back pop psw ; recover char to send jmp out$mdm page ; ; ; mdm$stat$1650$60: lda on$hook$flag ora a jrz read$status ; return modem status if off hook ; ; test if modem is ringing ; exx ; save HLDEBC lxi b,data$port$b inp a bit 3,a ; ring indicator bit (active low) jrnz ring$cont ; exit if not ringing ; ; ringing, take phone off hook and wait for CD (if used) ; xri 20h ; toggle on/off hook bit outp a ; take off hook mvi a,no ; set phone off hook sta on$hook$flag lda mdm$tp ; bit 4=0 if supports CD ani 10h ; =10h if no support jrz ring$cont mvi l,0 carrier$yet: dcr l jrz ring$cont inp a ani 10h ; test if carrier present jrz ring$cont ; call delay ; delay .16 seconds jr carrier$yet ; will wait .16*255 = 40.8 seconds page ; ; ; read$status: lda mdm$tp ; bit 4=0 if supports CD ani 10h jrnz mdm$stat ; exit if CD is not supported ; ; make sure that the carrier is still present (hang up if not) ; exx ; save HLDEBC lxi b,data$port$B inp a ani 10h ; test if carrier present cnz phone$on$hook ; lost carrier, hang up the phone ring$cont: exx ; restore HLDEBC ; ; ; mdm$stat: lda RS232$status ret ; ; data area ; on$hook$flag: db yes ; yes=0FFh=on hook, no=00=off hook end