.title K11DIA dial command for kermit-11 .ident /t2.40/ ; 18-Oct-85 20:06:09 Brian Nelson ; ; 04-Dec-85 11:22:32 BDN Add optional linked list structure to ; define modem responses, will be useful ; when adding a SET DIAL command. ; 06-Dec-85 11:53:59 BDN Added extra field in DEFINE macro and ; support code to call external routines ; for dialing, needed for PRO/TMS. ; 09-Jan-86 17:48:17 RTM Added support for the RIXON Intelligent ; (RTM01) autodial modem (R212A). ; ; 13-Feb-86 10:10:08 BDN Put the DF112 and DF224 responses into ; linked lists. ; 23-Sep-86 14:10:30 BDN Vadic 4224 modems ; 23-Sep-86 14:10:42 BDN Added format effector %M for pulse/tone ; if a SET PHONE PULSE/TONE was done. Also ; added %B for 'blind' dialing, enabled if ; SET PHONE BLIND ; 30-Sep-86 13:05:53 BDN Added CTS2424, Also SHO DIAL ; 09-Feb-89 00:00:00 JCH Complete definition of DF224 modem. ; ; Copyright (C) 1985 1986 Change Software, Inc. ; ; ; Its about time for a DIAL command. Would be really nice to use ; on my PRO/350 and the XK: port. Of limitted utility on the 44 ; as I have to enter the GANDALF PACX to access the VADIC 212PAR ; modems. ; ; As noted in a number of comments, the timer support for RSTS/E ; is abbysmal, the only thing you can do is SLEEP n, where 'n' is ; in seconds. Even worse is the fact that various asynchronous ; events can prematurely cancel the sleep, like a delimiter typed ; at a terminal allocated to the job. Also, you can NOT do a mark ; time on RSTS/E, though version 9 does have the groundwork layed ; for adding timer AST's in that v9 supports asych i/o, thus the ; needed executive data structures are mostly present. ; ; I do not mean to knock RSTS/E really, RSTS has a great number ; of features that make interfacing with the terminal driver much ; simpler than what one finds on RSX and RT11, while at the same ; time introducing its own peculiarities. RSX is by far the worst ; as I often find grossly dissimiliar behavior between RSX sites. ; ; As a final reflection, if I were to grade RSTS, RT and RSX on ; simplicity and stability of terminal driver interfacing, RT11 ; would have to come first (both MT and XC/XL versions), followed ; by RSTS and bringing up the rear, RSX (I've been burned on both ; RSX and RSTS when new versions came out, though being a RSTS/E ; field test site tends to help). .if ndf, K11INC .ift .include /IN:K11MAC.MAC/ .endc .enabl lc .nlist ; It would be nice to be able to do this in a macro call but the ; PDP-11 assembler does not allow continuation lines. So what we ; will instead do is define each arg as a word offset from a base ; address of a list for that modem. ; In many cases we could fit all the info on one line, but macros ; dragged out that way are unreadable. It would be really nice to ; be able to say: ; ; moddef type = <VA212PAR> - , ; dial.time = 30. - , ; wake.string = ..... and so on. ; ; .macro moddef dial.t,wake.s,wake.r,wake.p,dmod.s,dmod.p,dial.s,dial.r ; ; Losely based on CKUDIAL.C ; ; All times for delays are in TICKS (1/60 second intervals) ; ; dial.time Time the modem may use to dial a number ; wake.string Get modems attention, like in VADIC 212 ^E<CR> ; wake.rate Delay in milliseconds modem may need ; wake.prompt Modems command mode prompt ; dmod.string String needed to get it to dial a number ; dmod.prompt Dial mode prompt ; dial.string The string for dial, includes a %s for number position ; dial.rate delay between sending numbers over, in TICKS ; wake.ack as in va212, 'HELLO:I'M READY' ; dial.ack string to wait for to confirm number ; dial.echo ; ; WARNING: If any of these values are CHANGED, and there is absolutely ; NO reason to do so, you must check K11ST0.MAC where a few of these ; are also defined for the SET DIAL command. Anything new that's need- ; ed in the modem database is always added on to the END of the list. ; While it is freely acknowledged that there are now a couple of ob- ; solete fields, it doesn't cost that much. Leave the structure alone. mod.next =: 0 ; next modem in list mod.str =: 2 ; address of name of modem mod.val =: 4 ; numeric value for dispatching dial.time =: 6 ; value of dial time wake.string =: 10 ; address of wakeup string wake.rate =: 12 ; value of delay wake.prompt =: 14 ; address of wakeup prompt dmod.string =: 16 ; address of dial dial string dmod.prompt =: 20 ; address of prompt returned for dial dial.string =: 22 ; address of formatting string for dial dial.rate =: 24 ; value of delay wake.ack =: 26 ; address of wakeup response dial.ack =: 30 ; 1st = waitfor, 2nd to confirm number dial.online =: 32 ; online ack string dial.blind =: 34 ; blind ack dial.wait =: 36 ; String for PAUSE characters dial.confirm =: 40 ; string to confirm number for dialing dial.go =: 42 ; ie, va212 returns "DIALING\n" res.bin =: 44 ; if <>, returns status with \n ; otherwise a binary response (DF03) dial.echo =: 46 ; if <>, numbers are echoed immediately mod.comment =: 50 ; res.head =: 52 ; ext.dial =: 54 ; if ne, address of external dialer dial.xabort =: 56 ; /45/ To abort call from modem dial.idle =: 60 ; /45/ Place modem in IDLE state dial.pulse =: 62 ; /45/ Switch to pulse dialing dial.nopulse =: 64 ; /45/ Switch to tone dialing def.guard =: 66 ; /45/ last thing (unused) .psect modinf ,ro,d,lcl,rel,con .enabl lc .macro modval val,offset .save .psect modinf . = $$current + offset .word val .restore .endm modval .macro modstr s,offset .save .psect string $$addr = . $$ = 0 .irpc ch,<s> ch1 = ''ch .if eq, ch1 - '^ .ift $$ = 100 .iff .if ne ,$$ .if ge , <ch1!40> - <'a!40> .iif le, <ch1!40> - <'z!40>, ch1 = ch1&137 .endc .endc .byte ch1-$$ $$ = 0 .endc .endr .byte 0 .restore .save .psect modinf . = $$current + offset .word $$addr .restore .endm modstr .sbttl inialize subfields of modem info structure .macro dial$time val modval val,dial.time .endm dial$time .macro wake$rate val modval val,wake.rate .endm wake$rate .macro dial$rate val modval val,dial.rate .endm dial$rate .macro wake$string s modstr <s>,wake.string .endm wake$string .macro wake$prompt s modstr <s>,wake.prompt .endm wake$prompt .macro dmod$string s modstr <s>,dmod.string .endm dmod$string .macro dmod$prompt s modstr <s>,dmod.prompt .endm dmod$prompt .macro dial$string s modstr <s>,dial.string .endm dial$string .macro mod$comment s modstr <s>,mod.com .endm mod$comment .macro dial$ack s modstr <s>,dial.ack .endm dial$ack .macro dial$confirm s modstr <s>,dial.confirm .endm dial$confirm .macro dial$online s modstr <s>,dial.online .endm dial$online .macro dial$blind s modstr <s>,dial.blind .endm dial$blind .macro dial$wait s modstr <s>,dial.wait .endm dial$wait .macro dial$go s modstr <s>,dial.go .endm dial$go .macro dial$echo v modval v,dial.echo .endm dial$echo .macro dial$pulse s ; /45/ String to force PULSE dialing modstr <s>,dial.pulse ; /45/ ... .endm dial$pulse ; /45/ ... .macro dial$nopulse s ; /45/ String to force tone dialing modstr <s>,dial.nopulse ; /45/ ... .endm dial$nopulse ; /45/ ... .macro dial$xabort s ; /45/ Use this field if we detect modstr <s>,dial.xabort ; /45/ a control C abort and need .endm dial$xabort ; /45/ to get the modem to stop dialing .macro dial$idle s ; /45/ Switch from command mode to modstr <s>,dial.idle ; /45/ to IDLE mode .endm dial$idle ; /45/ .macro ext$dial v modval v,ext.dial .endm ext$dial .macro res$bin v modval v,res.bin .endm res$bin .macro wake$ack s modstr <s>,wake.ack .endm wake$ack $$prev = modinf $$seq = 1 .save .psect usermd ,rw,d,gbl,rel,con null2: .byte 0 null: .byte 0 .psect string ,ro,d,lcl,rel,con .psect rwdata ,rw,d,lcl,rel,con bell: .byte 'G&37,'G&37,0 .even modtyp: .word 0 buffer: .blkb 200 ttbuff: .blkb 200 .restore .macro define lab ,s ,user .if nb ,user ; if we are defining the 'user' .ift ; modem we need to be in a global .save ; psect to be located in the root .psect usermd ,rw,d,gbl,rel,con ; put a reference to this psect .iff ; in k11dat $modtail== . ; to manually set link to 'usermd' .endc ; .iif ndf, $$prev ,$$prev = modinf $$current = . ; save current pc $res = 0 $reslast= 0 lab: .word 0 ; link to next please .iif nb, user, .globl lab ; must be global if USER mode .save ; save current psect .psect string ; switch to string psect $$ = . ; save it's current PC .asciz /s/ ; insert the modem type string .even ; always useful for word addressing .restore ; pop the previous psect (modinf) .word $$ ; and insert address of the string $'lab = $$seq ; define sequence value .word $$seq ; store value in structure $$seq = $$seq + 1 ; sequence number .word 30. ; default for dial time .word null ; default for wakeup .word 0 ; default for delay .word null ; default for command mode prompt .word null ; default for dial command .word null ; default for dial prompt .word null ; default for dial formatting .word 0 ; default for dial delay .word null ; default for wakeup ack .word null2 ; default for ack string .word null ; default for dial.online .word null ; default for dial.blind .word null ; default for dial.wait .word null ; default for dial.confirm .word null ; default for dial.go .word 0 ; 0 normal, else 1 for binary (res.bin) .word 0 ; default for dial.echo .word null ; default for MOD$COMMENT .word 0 ; res.head .word 0 ; possible external dialer address .word null ; /45/ Default for XABORT string .word null ; /45/ Default for IDLE string .word null ; /45/ Default for PULSE dialing .word null ; /45/ Default for TONE dialing .word 0 ; guard word $$end = . $$size = $$end - $$current .assume $$size eq <def.guard+2> .if b ,user ; we don't want psect errors from .ift ; MAC for the USER modem type. . = $$prev .word lab . = $$end $$prev = lab .iff .restore ; for USER type, return to old psect .endc ; Also, the USER modem MUST be last .endm define .sbttl create a linked list, header in res.head ; Added edit /39/ Brian Nelson 4-DEC-1985 09:46 ; ; Create a linked list of possible modem responses. The ; advantages of this is that we could use a SET DIAL command ; to create a modem data structure at run time, and that ; defining modems is simplified. As a note, there is no ; requirement to use this structure. It will be used for the ; DEFAULT entry in the 'GETSTS' routine. ; ; ; Format is: res.head(current_modem) --> first entry ; ; ; Entry format: .word next (or zero for tail) ; .word class (-1 fail, 0 info, 1 sucess) ; .asciz /response_string/ CONNECT = 1 INFO = 0 FAILED = -1 .save .psect resdat ,rw,d,lcl,rel,con resdat: .restore .macro mod$res s,class .save ; save current psect context .psect resdat ,rw,d,lcl,rel,con $res1 = . ; save current pc in 'resdat' .word 0 ; link to next is zero .if b, class ; if class not specified, then .word 0 ; make it zero .iff ; else insert it .word class ; response class .endc ; if b, class .asciz /s/ ; the actual text .even ; must do .restore ; get last psect .if eq ,$res ; is this first time for new .ift ; type ? modval $res1,res.head ; yes, stuff the link header .iff ; not the first time .save ; save current psect context .psect resdat ; and a new one $respc = . ; save the current pc . = $reslast ; backup to link word from previous .word $res1 ; insert address of new entry . = $respc ; restore correct pc .restore ; restore old psect context .endc ; end $reslast = $res1 ; lastlink = current_entry $res = 1 ; not the first time anymore .endm ; thats it .list .sbttl finally, we can define the modem .psect modinf ,ro,d,lcl,rel,con ; All xxxx$rate values are in TICKS (1/60 second) ; WARNING: The definition for USER_DEFINED MUST be last !!!!!!! ; WARNING: Before accessing any data here from outside of this ; module (overlay), insure the overlay is loaded, as in calling ; FINDMODEM with the modem type string .asciz pointed to by R5. modinf:: define v2pa ,<VA212PA> ; stand alone VA212PA dial$time 35. wake$string <^E^M> wake$rate 10 ; wait 8 ticks (8/60 second) wake$ack <HELLO:I'M READY> wake$prompt <*> dmod$string <D^M> dmod$prompt <?> dial$string <%s^M> dial$rate 10 dial$ack <^M> dial$confirm <^M> dial$go <DIALING> dial$echo 1 dial$xabort <^M> dial$idle <I^M> dial$wait <K> mod$comment <Stand alone VADIC VA212> mod$res <ON LINE> ,CONNECT mod$res <ONLINE> ,CONNECT mod$res <BUSY> ,FAILED mod$res <FAILED CALL> ,FAILED mod$res <NO DIAL> ,FAILED mod$res <VOICE> ,FAILED mod$res <TIME OUT> ,FAILED mod$res <RINGING> ,INFO define v2par ,<VA212PAR> ; rack mounted VA212PAR dial$time 35. wake$string <^E^M> wake$rate 10 wake$ack <HELLO:I'M READY> wake$prompt <*> dmod$string <D^M> dmod$prompt <?> dial$string <%s^M> dial$rate 10 dial$go <DIALING> dial$echo 1 dial$xabort <^M> dial$idle <I^M> dial$wait <K> mod$comment <Rack mounted VADIC VA212PAR> mod$res <ON LINE> ,CONNECT mod$res <ONLINE> ,CONNECT mod$res <BUSY> ,FAILED mod$res <FAILED CALL> ,FAILED mod$res <NO DIAL> ,FAILED mod$res <VOICE> ,FAILED mod$res <TIME OUT> ,FAILED mod$res <RINGING> ,INFO define vadic ,<VADIC> ; stand alone VA212PA dial$time 35. wake$string <^E^M> wake$rate 10 wake$ack <HELLO:I'M READY> wake$prompt <*> dmod$string <D^M> dmod$prompt <?> dial$string <%s^M> dial$rate 10 dial$ack <^M> dial$confirm <^M> dial$go <DIALING> dial$echo 1 dial$xabort <^M> dial$idle <I^M> dial$wait <K> mod$comment <Generic VADIC with autodial> mod$res <ON LINE> ,CONNECT mod$res <ONLINE> ,CONNECT mod$res <BUSY> ,FAILED mod$res <FAILED CALL> ,FAILED mod$res <NO DIAL> ,FAILED mod$res <VOICE> ,FAILED mod$res <TIME OUT> ,FAILED mod$res <RINGING> ,INFO define cts24 ,<CTS2424> ; CTS (Fabritek) 2424 autodial dial$time 35. wake$string <^T^WAT^Q^M> ; Just in case add ^Q<cr> in also wake$rate 10 wake$ack <Modem Ready> dial$string <D%M%B%S^M> dial$rate 10 dial$xabort <^T^W> dial$idle <^T^W> dial$wait <+> dial$pulse <P> dial$nopulse <T> dial$blind <&> dial$echo 1 mod$comment <CTS/Fabri-Tek 2424AD V.22bis Autodialier> mod$res <Ring> ,INFO mod$res <Busy> ,FAILED mod$res <Dead Line> ,FAILED mod$res <Disconnect> ,FAILED mod$res <Modem Ready> ,INFO mod$res <No Answer> ,FAILED mod$res <No Dialtone> ,FAILED mod$res <No Tone> ,FAILED mod$res <On Line> ,CONNECT mod$res <On Line Originate>,CONNECT mod$res <Redialing> ,INFO define va42 ,<VA4224> ; Rack mount 2400 baud dial$time 35. wake$string <^E^M> wake$rate 10 wake$ack <HELLO:I'M READY> wake$prompt <*> dial$string <D%M%S%B^M> dial$rate 10 dial$go <DIALING> dial$echo 1 dial$xabort <^M> dial$idle <I^M> dial$wait <K> dial$pulse <P> dial$nopulse <T> dial$blind <B> mod$comment <Vadic 4224 CCITT V.22bis autodial> mod$res <ON LINE> ,CONNECT mod$res <ONLINE> ,CONNECT mod$res <BUSY> ,FAILED mod$res <FAILED CALL> ,FAILED mod$res <NO DIAL> ,FAILED mod$res <VOICE> ,FAILED mod$res <TIME OUT> ,FAILED mod$res <RINGING> ,INFO mod$res <ON LINE 300> ,CONNECT mod$res <ON LINE 1200> ,CONNECT mod$res <ON LINE 2400> ,CONNECT mod$res <ERROR CONTROL> ,INFO mod$res <NO ERROR CONTROL>,INFO define df03 ,<DF03> ; dec DF03 dial$time 30. wake$string <^A^B> dial$string <%s> dial$echo 1 res$bin 1 ; responds in binary (no crlf) mod$comment <DEC DF03AC Autodial modem> define df100 ,<DF100> ; a DF112 dial$time 30. wake$string <^B> wake$rate 0 wake$ack <Ready> wake$prompt <Ready> dial$string <%s#> ; /45/ dial$rate 10 dial$xabort <^M> dial$echo 1 mod$comment <DEC Standalone DF112> mod$res <Attached> ,CONNECT mod$res <Busy> ,FAILED mod$res <Disconnected> ,FAILED mod$res <Error> ,FAILED mod$res <No answer> ,FAILED mod$res <No dial tone> ,FAILED mod$res <Speed:> ,FAILED define df200 ,<DF200> ; a DF2xx ? dial$time 30. wake$string <^B> wake$rate 0 wake$ack <Ready> wake$prompt <Ready> dial$string <%b%m%s!> ; /60/ dial$rate 0 dial$xabort <^B> ; /60/ dial$echo 1 dial$pulse <P> ; /60/ dial$nopulse <T> ; /60/ dial$blind <^A> ; /60/ mod$comment <DEC Standalone DF224> mod$res <Attached> ,CONNECT mod$res <Busy> ,FAILED mod$res <Disconnected> ,FAILED mod$res <Error> ,FAILED mod$res <No answer> ,FAILED mod$res <No dial tone> ,FAILED mod$res <Speed:> ,FAILED define hayes ,<HAYES> ; actually, its a VOLKSMODEM 12 dial$time 35. ; that we are testing with. It's wake$string <ATZ V1^M> ; supposed to be compatible. wake$rate 0 wake$ack <OK> wake$prompt <OK> dmod$string <> dmod$prompt <> dial$string <AT D %s^M> dial$rate 0 dial$echo 1 define microcom,<MICROCOM> ; dial$time 35. wake$string <4445^MSE2^MS1C0^MSCE ON^M> wake$rate 10 wake$ack <!> wake$prompt <!> dial$string <D%s^M> dial$rate 10 dial$echo 1 mod$res <CONNECT> ,CONNECT mod$res <NO CONNECT> ,FAILED mod$res <!> ,FAILED mod$comment <MicroCom SX1200> ; The following entry was added by edit (RTM01). define r212a ,<R212A> ; RIXON R212A Intelligent modem. dial$time 60. ; Long timeout for overseas. wake$string <^M^M> wake$rate 10 ; wait 8 ticks (8/60 second) wake$ack <RIXON R212A INTELLIGENT MODEM> wake$prompt <$> dmod$string <K> dmod$prompt <NUMBER:> dial$string <%s^M> dial$rate 10 ; dial$ack <^M> ; dial$confirm <^M> dial$go <DIALING:> dial$echo 1 mod$comment <RIXON R212A Intelligent Modem> mod$res <ON LINE> ,CONNECT mod$res <ON-LINE> ,CONNECT mod$res <NO ANSWER> ,FAILED mod$res <DEAD LINE> ,FAILED mod$res <BUSY> ,FAILED mod$res <END D> ,INFO mod$res <RINGING> ,INFO define protms ,<PROTMS> ; this one has to use QIOS for dial ext$dial tmsdial ; we will have to catch this one in ; a not so clean manner. Waiting for ; Steve Kovey's code for support. .sbttl define user defined modem structure ; This one <USER_DEFINED> MUST be the last one in the list. The link ; must be inserted at run time via a call to FINDMODEM with a known ; type, like CALLS FINDMO,<#vadid>. ; NONE of the field macros used above will work for this since the ; psect it's in is global. ; Setting up the fields is fairly simple, for example, setting the ; wakeup string would be: ; ; mov #usermd ,r4 ; strcpy #udiawake,argbuf ; mov #udiawake,wake.string(r4) ; stuff wakeup string addr ; ; And setting a reponse string, assuming CONNECT is success ; ; mov #usermd ,r4 ; address of data structure ; mov #contxt ,res.header(r4) ; and stuff the list in ; ; contxt: .word faitxt ; link to next ; .word 1 ; > 0 for indicating connect ; .asciz /CONNECT/ ; the string itself ; .even ; please ; faitxt: .word 0 ; no link to next ; .word -1 ; flag call failed ; .asciz /NOCONNECT/ ; the text if it fails ; .even ; please ; ; Where udiawake was a area in the root, defined in K11DAT.MAC ; Keep in mind that the code which looks for a response will NOT ; exit until (1) nothing happens for 30 seconds or (2) it finds ; a match in the list with a non-zero status. define usermd ,<USER_DEFINED>,GLOBAL ; This MUST be the last one ; No more definitions allowed past here .sbttl local macros to make life simpler .macro lowcase s mov s ,r0 call lowcas .endm lowcase .macro sleep time calls suspend ,<time,#0> .endm sleep .macro tsleep ticks calls suspend ,<#0,ticks> .endm tsleep .macro ttputstr s mov r1 ,-(sp) mov s ,r1 call doput mov (sp)+ ,r1 .endm ttputstr .macro ttputc c clr -(sp) bisb c ,(sp) mov sp ,r0 calls binwri ,<r0,#1,#lun.ti> tst (sp)+ .endm ttputc .macro ttgetc wait ; lun.ti is the remote lun .if b ,wait .ift calls xbinread,<#lun.ti,#0> .iff calls xbinread,<#lun.ti,wait> .endc .endm ttgetc .macro waitfor s ,time,fail .if b ,time .ift clr -(sp) .iff mov time ,-(sp) .endc mov s ,-(sp) call waitfor .if nb, fail tst r0 beq fail .endc .endm waitfor .macro waitc c ,time clr -(sp) mov sp ,r0 movb c ,(r0) .if b ,time .ift clr -(sp) .iff mov time ,-(sp) .endc mov r0 ,-(sp) call waitfor tst (sp)+ .endm waitc .macro didweget s,pat,status,ret,?b .save .psect string $$ = . .asciz /pat/ .restore mov #$$ ,-(sp) mov s ,-(sp) call didweget tst r0 beq b mov status ,ret b: .endm didweget remmod = ter$cc .sbttl REDIAL command .psect $code .enabl lsb c$redi::mov argbuf ,r3 ; /40/ argbuf address mov phnum ,r2 ; /40/ address of phone number mov #1 ,r4 ; /40/ default repeat count tstb @r3 ; /40/ Any repeat count passed ? beq 5$ ; /40/ No calls l$val ,<r3> ; /40/ retry count mov r1 ,r4 ; /40/ save repeat count tst r0 ; /40/ success ? beq 5$ ; /40/ yes message <Invalid number!>,cr br 90$ ; /40/ exit 5$: tstb @r2 ; /40/ Any real phone number today? bne 10$ ; /40/ yes message <No previous number!>,cr br 90$ ; /40/ exit if no DIAL command 10$: message <Using: > ; /40/ echo the number we will use print r2 ; /40/ message ; /40/ crlf strcpy r3 ,r2 ; /40/ copy old phone num to 'argbuf' 20$: tst cccnt ; /40/ control C abort ? bne 90$ ; /40/ Yes, exit this loop NOW mov r4 ,-(sp) ; /40/ save looping counter call c$dial ; /40/ no, try dialing mov (sp)+ ,r4 ; /40/ restore looping counter tst r0 ; /40/ success ? beq 100$ ; /40/ yes sob r4 ,20$ ; /40/ No, try again please 90$: mov #-1 ,r0 ; /40/ failure inc status ; /45/ Set global flag also 100$: return .dsabl lsb set$mo::strcpy #modem ,argbuf call findmodem tst r0 bne 100$ mov #-1 ,r0 inc status ; /45/ Global flag also return 100$: clr r0 return .sbttl the DIAL command .psect $code .enabl lsb c$dial::strcpy phnum ,argbuf ; /40/ save the phone number tstb ttdial ; check for a real terminal name bne 10$ ; ok message <Please use the SET LINE command first>,cr br 90$ ; exit 10$: tstb modem ; check for a SET MODEM command bne 20$ ; ok tst proflg ; /39/ is this a pro/350 ? beq 15$ ; /39/ no, fatal error cmpb ttdial ,#'X&137 ; /39/ check for XTn: bne 15$ ; /39/ no cmpb ttdial+1,#'T&137 ; /39/ if so, must be TMS on P/OS bne 15$ ; /39/ no, so no modem type fatal strcpy #modem ,protms+mod.str ; /39/ stuff 'PROTMS' in please br 20$ ; /39/ lookup the modem now 15$: message <Please use the SET MODEM command first>,cr br 90$ 20$: call findnumber ; /45/ Perhaps a SET PHO NUM ? bcs 90$ ; /51/ call findmodem ; do we know about this modem ? mov r0 ,modtype ; save the address of descriptor beq 90$ ; no luck, exit with failure mov ext.dial(r0),r1 ; /39/ need external routine to dial? beq 30$ ; /39/ no jsr pc ,@r1 ; /39/ yes, call it then br 120$ ; /39/ and exit 30$: mov dial.string(r0),r0 ; /39/ get dial formatting string addr tstb @r0 ; /39/ anything there ? bne 40$ ; /39/ yes message <A dial formatting string has not been defined>,cr br 90$ ; /39/ exit 40$: calls ttyini ,<#ttname,#lun.ti,#remmod> tst r0 ; did we get a successful open on line? bne 80$ ; no, emit some sort of error message call tidias ; /45/ Set characteristics (TC.DLU) call getatn ; try to get modems attention tst r0 ; did getting modems attn succeed? beq 90$ ; no call dodial ; actually try to dial the number now mov r0 ,-(sp) ; /45/ Save status return call tidiar ; /45/ Reset characteristics mov (sp)+ ,r0 ; /45/ Restore status return tst r0 ; returns 1 for success, zero else beq 90$ ; failure br 100$ ; success, return(0) re normal k11 80$: direrr r0 ; emit an error message and exit 90$: mov #-1 ,r0 br 110$ 100$: clr r0 110$: call 200$ 120$: tst r0 ; /45/ Errors ? beq 130$ ; /45/ No inc status ; /45/ Yes, set into global flag 130$: return 200$: mov r0 ,-(sp) calls ttyfin ,<#ttname,#lun.ti> ; close the terminal up now mov (sp)+ ,r0 return .dsabl lsb .sbttl match type of modem .enabl lsb ; FINDMODEM ; ; Passed: nothing, assumes MODEM name is in global modem:: ; ; Return: r0 address of structure for the modem ; r0 = 0 if the modem type could not be found ; ; Remark: It might be better to call PRINTM to display messages ; to better control the case if we got spawned under RSX ; or RSTS so we could avoid I/O and instead return some ; status info. findmo::tst $modtail ; /39/ Is the usre mode defined? bne 5$ ; /39/ Yes mov #usermd ,$modtail ; /39/ no, insert it please 5$: mov #modinf ,r3 ; get address of head of list 10$: strcmp #modem ,mod.str(r3) ; check for a match please tst r0 ; a match beq 80$ ; yes mov mod.next(r3),r3 ; no, try the next one please bne 10$ ; keep going message <This modem type is unknown. The known modem types>,cr message <are:>,cr message mov #modinf ,r3 ; now print the list of modems 20$: print mod.str(r3) ; first one strlen mod.str(r3) ; /39/ length of modem type sub #40 ,r0 ; /39/ tab over please neg r0 ; /39/ make it > 0 30$: print #200$ ; /39/ a space sob r0 ,30$ ; /39/ next please print mod.com(r3) ; /39/ dump comment string message ; cr/lf mov mod.next(r3),r3 ; next one in list bne 20$ ; ok br 90$ ; exit with failure 80$: mov r3 ,r0 ; return address of structure br 100$ ; bye 90$: clr r0 ; failure 100$: return ; bye .save .psect $Pdata ,d 200$: .byte 40,0 .restore global <modem> .dsabl lsb .sbttl Look for defined phone numbers .enabl lsb ; Added /45/ 20-Feb-86 13:00:50 BDN ; ; Check through linked list for phone number definitions findnu: mov r3 ,-(sp) ; Save this mov r2 ,-(sp) ; Save this one also cmpb @argbuf ,#'0 ; Only if not number in first pos blo 5$ ; Ok cmpb @argbuf ,#'9 ; Well ? bhi 5$ ; No clc ; Success, exit jmp 100$ ; A number, exit please 5$: mov #pnhead ,r2 ; Get listhead ;- calls tlog ,<argbuf> ; /47/ Check for logical names ; Loop a little 10$: mov (r2) ,r2 ; Get next entry beq 90$ ; Nothing there mov r2 ,r3 ; Get address of text add #2 ,r3 ; +2 for data mov #ntemp ,r0 ; Store temp here 20$: cmpb (r3) ,#40 ; All done looking ? beq 30$ ; Yes movb (r3)+ ,(r0)+ ; No, copy more of the NAME bne 20$ ; All done 30$: clrb (r0) ; Insure .asciz please strcmp #ntemp ,argbuf ; Check for a match please tst r0 ; Find a match ? bne 10$ ; No, check the next one ; 40$: inc r3 ; Point to number field tstb (r3) ; Anything really there? beq 100$ ; No, exit please message <Using: > ; Print the translation out strcpy argbuf ,r3 ; Copy it over print argbuf ; ... message ; .... clc ; Success br 100$ ; Exit 90$: print #npr ; /51/ Dump a prompt mov #ttbuff ,r2 ; /51/ Set address up calls kbread ,<r2> ; /51/ Read the response bicb #40 ,r2 ; /51/ Cvt to lower case cmpb @r2 ,#15 ; /51/ A CR beq 95$ ; /51/ Ok tstb @r2 ; /51/ Null ? beq 95$ ; /51/ Yes, success cmpb @r2 ,#'Y&137 ; /51/ A 'YES' beq 95$ ; /51/ Y, success sec ; /51/ No, failure br 100$ ; /51/ Exit 95$: clc ; /51/ Success 100$: mov (sp)+ ,r2 ; Pop and mov (sp)+ ,r3 ; pop again, then... return ; Exit .dsabl lsb .save .psect string .even ntemp: .blkb 200 npr: .asciz /No translation found for number. Continue <Yes> ? / .even .restore global <pnhead> .sbttl GETATN wake the modem up .enabl lsb ; Input: MODTYPE address of modem descriptor ; Return: R0 zero on failure, else success ; ; In case the modem is already awake (at command prompt mode) ; we will dump the string, wait for the ackknowledgment and ; then loop until we are sure there is no more data. For ex- ; ample, the VA212 will send back a INVALID COMMAND\n* string ; for the ^E and one also for the <CR>. We could, of course, ; first send the modem it's IDLE command, but there are times ; when that may be undesirable, like when the modem is being ; accessed via some sort of LAN or PABX. getatn::save <r1,r2,r3,r4> ; perhaps save something mov modtype ,r4 ; get address of structure call eatjunk ; trash stuff that may be waiting mov #3 ,r3 ; loop a little to get modem going mov wake.string(r4),r2 ; Get address of wakeup string 10$: ttputc (r2)+ ; We may need to insert delays tstb @r2 ; for the faster modems. beq 15$ ; All done? tsleep wake.rate(r4) ; Not done, pause if need be br 10$ ; And do some more 15$: waitfor wake.prompt(r4),#3,20$ ; now wait for an ack br 25$ ; success 20$: call eatjunk ; trash stuff that may be waiting mov wake.string(r4),r2 ; reset wakeup string sob r3 ,10$ ; next please br 90$ ; a complete failure, exit 25$: message <Modem in command mode>,cr 30$: call eatjunk mov #1 ,r0 ; return(success) br 100$ ; and exit 90$: message <Failed to get modems attention>,cr clr r0 100$: unsave <r4,r3,r2,r1> return .dsabl lsb .sbttl actually dial the number .enabl lsb ; Note that the waitfor routine, called by macro of same name will ; exit immediatly if the string to compare against is NULL, thus it ; won't hurt anything to call it for modems that may not respond. ; One other problem is in inserting delays into dumping strings to ; the modem in that the smallest interval RSTS/E can delay is one ; second. As always in Kermit-11, we must try to allow for code to ; function under all execs, not just RT or just RSTS or ..... dodial: save <r2,r3,r4,r5> ; save registers that we might zap mov #buffer ,r3 ; and a pointer to it also mov modtype ,r4 ; get address of modem descriptor mov #1 ,r2 ; since RSTSE can't sleep < 1 second mov #cccnt ,r5 ; control C int flag call fmtstr ; format the dialing string tst dialmode ; /45/ Should we force PULSE or TONE? beq 5$ ; /45/ Nothing bmi 4$ ; /45/ Tone ttputs dial.pulse(r4) ; /45/ See if user selected PULSE br 5$ ; /45/ Now get the thing to dial 4$: ttputs dial.nopulse(r4) ; /45/ Force TONE dialing 5$: ttputs dmod.string(r4) ; dump the prompt for dialing out waitfor dmod.prompt(r4),#4,80$ ; wait for a response call eatjunk ; let things settle a bit 10$: tstb (r3) ; end of it all ? beq 40$ ; yes tst @r5 ; user interupt happen ? bne 90$ ; yes, exit now ttputc (r3)+ ; no, dump one character out tst dial.echo(r4) ; does it echo characters after each beq 20$ ; character ? ttgetc #1 ; yes, just wait a second for the echo 20$: dec r2 ; time to take a short nap ? bne 10$ ; no tst dial.rate(r4) ; do we REALLY need to suspend ? beq 30$ ; no tsleep dial.rate(r4) ; yes, do it (RSTS of course, can't 30$: mov #1 ,r2 ; sleep < 1 but RSX & RT can). br 10$ ; may want to insert delays here 40$: mov #buffer ,r3 ; restore buffer pointer waitfor dial.ack(r4),#4 ; wait for number being echoed perhaps sleep #1 ; another kludge (mostly RSTS again) tst @r5 ; control C or other abort? bne 90$ ; yes ttputs dial.conf(r4) ; stuff a confirm (ok if null) waitfor dial.go(r4),#5,100$ ; maybe wait for any confirm that its message <Modem dialing>,cr ; dialing the number? call getsts ; get final result of the call please tst r0 ; success ? bne 110$ ; yes br 100$ ; no, dump failure message 80$: strcpy #buffer,#nodprompt ; copy message for no dial prompt br 100$ ; and dump message 90$: strcpy #buffer,#abort ; user interupt message 100$: tst cccnt ; /45/ Did this die from a control C beq 105$ ; /45/ No ttputs dial.xabort(r4) ; /45/ Yes, get the modem to STOP 105$: ttputs dial.idle(r4) ; /45/ Reset into IDLE state message <Call failed > ; oops print #buffer ; why it did message ; <CR><LF> clr r0 ; failure br 120$ ; exit 110$: message <Connection made, use the CONNECT command to access remote> print #bell ; /50/ Beep call getsys ; /48/ Don't wait unless rsx flavor cmpb r0 ,#SY$MPL ; /48/ M+ ? beq 115$ ; /48/ Yes, wait a moment cmpb r0 ,#SY$11M ; /48/ RSX11M 4.x ? bne 116$ ; /48/ No, don't suspend 115$: sleep #2 ; /45/ Wait a moment 116$: message ; successful call mov #1 ,r0 ; exit 120$: unsave <r5,r4,r3,r2> ; and registers return ; and exit .dsabl lsb .sbttl GETSTS Get final result of dial ; Passed: R4 --> structure describing modem ; ; Return: R0 1 for success, else zero ; ; This is the messy part as we have to anticipate response from ; whatever modem type we are trying to use. ; Much of the attempts to do timeouts is made very difficult by ; the fact that out of P/OS, RT11, RSX11M/m+ and RSTS/E, !ONLY! ; RSTS is unable to schedule time based AST's (mrkt$s and so on) ; Timer AST's would greatly help out here. ; ; Define dispatch table to take special actions for each modem type dispatch basedsp=moddsp,baseval=modlst,default=.default ;- /39/ dispatch $V2PA ,.v2pa ; standalone VA212PA ;- /39/ dispatch $V2PAR ,.v2par ; rack mount VA212PAR ;- /39/ dispatch $VADIC ,.vadic ; most vadics (same as 212PA) dispatch $HAYES ,.hayes ; HAYES (really volksmodem12) ;- /45/ dispatch $DF100 ,.df100 ; DEC DF112 ;- /45/ dispatch $DF200 ,.df200 ; DEC DF2xx series dispatch $DF03 ,.df03 ; DEC DF03 dispatch .save .psect .psect string defmsg: .asciz / No connection/ exring: .asciz / Excessive RINGING... messages returned/ abort: .asciz / Operation aborted under user interupt/ nodpro: .asciz / No response or invalid response to dial command/ .restore CONNECT = 1 FAILED = -1 status = 0 nrings = 2 temp = 4 locsiz = 10 getsts::tst diatmo ; /46/ Timeout set? bne 5$ ; /46/ Yes ..diat == . + 2 ; /46/ Patchable mov #30. ,diatmo ; /46/ No, set it up 5$: sub #locsiz ,sp ; allocate local r/w data mov sp ,r5 ; and a pointer mov #10 ,nrings(r5) ; for VA212 PA, excessive ringing clr temp(r5) ; temp clr (r5) ; status = 0 .assume status eq 0 ; assertion mov #buffer ,r3 ; a buffer pointer strcpy r3 ,#defmsg ; copy a message over 10$: tst (r5) ; while ( status == 0 ) bne 90$ ; exit with result mov #buffer ,r3 ; a buffer pointer 20$: ttgetc diatmo ; wait for a status return tst r0 ; successful ? beq 30$ ; yes mov #FAILED ,(r5) ; no br 70$ ; exit loop 30$: tstb r1 ; /51/ Never suffer imbedded NULLS beq 20$ ; /51/ Ignore if such bicb #^C177 ,r1 ; insure a real seven bit character movb r1 ,(r3)+ ; copy the byte over clrb (r3) ; insure we stay .asciz tst res.bin(r4) ; exit loop on single character I/O? bne 40$ ; yes cmpb r1 ,#CR ; carriage return finally found ? beq 40$ ; yes cmpb r1 ,#LF ; no, but a line feed will do nicely bne 20$ ; not a <LF>, try some more input then 40$: scan mod.val(r4),#modlst ; do a crude case statement asl r0 ; word indexing jsr pc ,@moddsp(r0) ; simple 70$: br 10$ ; next please 90$: mov #1 ,r0 ; assume success tst (r5) ; failure ? bpl 100$ ; no clr r0 ; return(0) for failure 100$: add #locsiz ,sp ; pop temps and exit return ; exit global <diatmo> ; /46/ .sbttl dispatch routines for specific modem (vadic, hayes) .enabl lsb ; The DEFAULT entry will read possible responses, and their ; message classes, from a linked list whose first address is ; in RES.HEADER(modem_address). Each list entry has a link to ; the next one (or a zero), a message class (lt zero if fail, ; == zero for ringing message, and gt zero for SUCCESS ). If ; a modem has no entries at all, the routine will return a ; failed call. While this method can tend to add size to this ; module as similiar modems must have separate list, it is a ; bit more general, while allowing us at some future time to ; provide a SET DIAL command to dynamically create the modem ; data structure for a previously undefined modem type. Added ; edit 2.39, 04-DEC-1985 10:17, Brian Nelson. ; ; We do not need a real definition for modem type DEFAULT as ; it is not possible to ever get this far if the type is un- ; defined. ; ; Default: .default: save <r3> ; /39/ save temp register mov res.head(r4),r3 ; /39/ get listhead of responses beq 90$ ; /39/ No responses defined ? 10$: mov r3 ,-(sp) ; /39/ get address of ascii text add #4 ,(sp) ; /39/ 4 byte offset for text mov #buffer ,-(sp) ; /39/ Stuff address of response call didweget ; /39/ check for the response tst r0 ; /39/ find the text in the list? bne 20$ ; /39/ yes, get the response class mov (r3) ,r3 ; /39/ no, get the next in the list bne 10$ ; /39/ something is left br 100$ ; /39/ nothing is left, continue... 20$: mov 2(r3) ,(r5) ; /39/ response class bne 100$ ; /39/ either success or failure PRINT #buffer ; /54/ MESSAGE ; /54/ dec nrings(r5) ; /39/ Choose a point to cut it off bne 100$ ; /39/ after a lot of ringing messages. strcpy #buffer,#exrings ; /39/ copy an informative message. Stop ttputs wake.string(r4) ; /39/ the modem from trying too hard waitfor wake.prompt(r4),#2 ; /39/ eat any possible response please 90$: mov #FAILED ,(r5) ; /39/ exit 100$: unsave <r3> ; /39/ pop r3 and exit return ; /39/ bye .dsabl lsb .sbttl Special cases .enabl lsb ; Case HAYES: .hayes: didweget #buffer,<CONNECT>,#CONNECTED,(r5) didweget #buffer,<NO CARRIER>,#FAILED,(r5) didweget #buffer,<BUSY>,#FAILED,(r5) didweget #buffer,<ERROR>,#FAILED,(r5) didweget #buffer,<RING>,#1,r0 ; macro clears r0 if string not found tst r0 ; beq 10$ ; and ring and ring and ring forever it message <Remote phone is ringing>,cr dec nrings(r5) ; would seem. so chose a point to cut bne 10$ ; it off at some point strcpy #buffer,#exrings ; copy an informative message and stop ttputs wake.string(r4) ; the modem from trying too hard waitfor wake.prompt(r4),#2 ; eat the OK please mov #FAILED ,(r5) ; exit 10$: return .dsabl lsb ; Case DF03: .df03: didweget #buffer,<A>,#CONNECTED,(r5) didweget #buffer,<B>,#FAILED,(r5) return .sbttl format dial string (sprintf works a lot better) ; Passed: R3 address of buffer to place result ; ARGBUF the number to insert fmtstr: mov r3 ,-(sp) mov dial.string(r4),r2 ; point to the dial formatting string 10$: tstb (r2) ; done ? beq 50$ ; yes cmpb (r2) ,#'% ; look for a format effector bne 30$ ; no mov argbuf ,r0 ; Assume phone number insertion cmpb 1(r2) ,#'s!40 ; %s formatting ? beq 20$ ; no cmpb 1(r2) ,#'s&137 ; %S formatting ? beq 20$ ; Yes mov dial.wait(r4),r0 ; No, assume pause string formatting cmpb 1(r2) ,#'p!40 ; %P for pause string? beq 20$ ; Yes cmpb 1(r2) ,#'p&137 ; check both cases beq 20$ ; No ; cmpb 1(r2) ,#'M&137 ; /54/ Mode (ie PULSE, TONE) beq 15$ ; /54/ Found it cmpb 1(r2) ,#'m!40 ; /54/ ... bne 16$ ; /54/ Nope 15$: mov dial.pulse(r4),r0 ; /54/ Assume PULSE dialing tst pulse ; /54/ Ever SET PHONE PULSE/TONE? beq 25$ ; /54/ No, ignore string bmi 20$ ; /54/ SET PHONE PULSE mov dial.nopulse(r4),r0 ; /54/ SET PHONE TONE br 20$ ; 16$: cmpb 1(r2) ,#'B&137 ; /54/ SET PHONE BLIND? beq 17$ ; /54/ Is this the formatting char? cmpb 1(r2) ,#'b!40 ; /54/ No, perhaps lower case? bne 30$ ; /54/ No, copy data then 17$: tst blind ; /54/ Ever do the SET PHONE BLIND? beq 25$ ; /54/ No, ignore string mov dial.blind(r4),r0 ; /54/ Yes, insert the data then. ; 20$: movb (r0)+ ,(r3)+ ; copy a byte please bne 20$ ; not a null, next please dec r3 ; fix current pointer up 25$: inc r2 ; skip over the '%' please br 40$ ; next please 30$: movb (r2) ,(r3)+ ; not formatting, copy character 40$: inc r2 ; skip over current character now br 10$ ; next 50$: clrb (r3) ; insure dial string is .asciz mov (sp)+ ,r3 ; restore pointer and exit return .sbttl misc util routines .enabl lsb ; WAITFOR(s,timeout) ; ; Wait for the character string in S ; ; Passed: 2(sp) string ; 4(sp) timeout ; Return: 1 success ; 0 timeout waitfor:save <r2,r3,r4> mov 2+6(sp) ,r4 ; string we are looking to get mov 2+10(sp),r3 ; timeout in seconds 10$: movb (r4)+ ,r2 ; next character to await beq 30$ ; success, we can leave now 20$: ttgetc r3 ; wait at most r3 seconds please tst r0 ; success ? bne 90$ ; no (i/o routines use r0 for err code) bicb #^C177 ,r1 ; remove parity always tstb r1 ; /51/ Ignore imbedded NULLS beq 20$ ; /51/ .... cmpb r1 ,r2 ; did we get the correct character? bne 20$ ; no, keep waiting then br 10$ ; found it, check for next character 30$: mov #1 ,r0 ; success br 100$ ; exit 90$: clr r0 ; failure, return(0) 100$: unsave <r4,r3,r2> mov (sp) ,4(sp) ; move return address up and exit cmp (sp)+ ,(sp)+ ; fix stack return ; exit .dsabl lsb didwege:save <r1,r2,r3> mov 2+10(sp),r3 ; string to look for mov 2+6(sp) ,r2 ; string to find it in lowcase r3 ; /45/ Lowercase it in buffer strlen r3 ; string length of pattern mov r0 ,r1 ; save it lowcase r2 ; /45/ Lowercase it in buffer strlen r2 ; length of source string calls instr ,<r2,r0,r3,r1> ; simple (use funct we already have) unsave <r3,r2,r1> ; pop temps and exit mov (sp) ,4(sp) ; move return address up and exit cmp (sp)+ ,(sp)+ ; fix stack return ; exit eatjunk:save <r0,r1> 10$: ttgetc #1 tst r0 beq 10$ unsave <r1,r0> return ; Added /45/ lowcas: mov r0 ,-(sp) ; Save starting address 10$: tstb (r0) ; End of the line yet? beq 100$ ; Yes, exit then cmpb (r0) ,#'A&137 ; A big letter? blo 20$ ; No cmpb (r0) ,#'Z&137 ; A big letter? bhi 20$ ; No bisb #40 ,(r0) ; Yes, make it lower 20$: inc r0 ; Next please br 10$ ; and check it 100$: mov (sp)+ ,r0 ; Return starting address return ; Exit doput: strlen r1 tst r0 beq 10$ calls binwri ,<r1,r0,#lun.ti> 10$: return .sbttl Show Modem/Dial .enabl lsb shomod:: shodia::call findmodem ; Do we know about this modem ? mov r0 ,r4 ; Save the address of descriptor bne 5$ ; No luck, exit with failure MESSAGE <No modem type has been SET>,CR return ; 5$: mov #slist ,r3 ; Info list start tst ext.dial(r4) ; Is this a built-in, like PRO/TMS? beq 10$ ; No MESSAGE <This is an internal modem>,CR jmp 100$ ; Exit ; 10$: movb (r3)+ ,r1 ; Type of data (1=String,0=Number) movb (r3)+ ,r2 ; Offset, zero implies end. beq 60$ ; All done add r4 ,r2 ; Move to the correct offset PRINT r3 ; Dump the header 20$: tstb (r3)+ ; Now look for the end of it bne 20$ ; Not yet tstb r1 ; Data type, zero is numeric bne 30$ ; Ascii DECOUT (r2) ; Dump the data br 50$ ; Next please 30$: mov (r2) ,r0 ; Get the string data address beq 40$ ; Nothing there tstb @r0 ; Again beq 40$ ; Nothing has been set call unfmts ; Convert and print it PRINT r0 ; Do it br 50$ ; Next 40$: MESSAGE <The option has not been SET> 50$: MESSAGE ; br 10$ ; Next ; 60$: mov res.head(r4),r3 ; Response header beq 100$ ; Nothing present MESSAGE ; MESSAGE <Modem message Message Class>,CR MESSAGE ; 65$: mov r3 ,r2 ; Get the response address beq 100$ ; All done add #4 ,r2 ; Offset is four STRCPY #buffer ,r2 ; Copy it STRLEN #buffer ; Length sub #24. ,r0 ; Amount to stuff in neg r0 ; Make it positive mov r0 ,r5 ; Save it in a safe place ble 75$ ; Avoid this please 70$: STRCAT #buffer ,#200$ ; Do it sob r5 ,70$ ; A few times 75$: PRINT #buffer ; Dump the text tst 2(r3) ; What is the message class? bmi 85$ ; If < 0, failure to connect beq 80$ ; If == 0 just informative MESSAGE <Successfull connect>,CR; br 90$ ; Next please 80$: MESSAGE <Informative message>,CR; br 90$ ; Next please 85$: MESSAGE <Failure to connect>,CR; 90$: mov (r3) ,r3 ; Pick up link to next br 65$ ; Next please ; 100$: return .Save .psect $Pdata,d 200$: .asciz / / .macro DES Type,Offset,Title .if B, Type .byte 0,0 .iff .byte Type,Offset .asciz @Title@ .endc .endm DES slist: DES 1,mod.comment,<Modem type: > DES 1,wake.string,<Wakeup string: > DES 1,wake.ack ,<Response to wakeup: > DES 1,dial.string,<Format for dialing: > DES 0,dial.rate, <Delay for dialing: > DES .Even .Restore .end