title mini modem program ; MINIMOD - a subset modem program for MSDOS. ; =========================================== ; ; Dave Moore, ; QCOM Pty Ltd ; May 84. ; ; Thió  prograí  ió  copyright®  (Yes¬  reallù  - itó  iî ;          source!©  Permissioî ió granteä tï uså it¬  buô noô foò ;          profit¬  provideä  thaô thió noticå ió retaineä  alonç ;          witè  thå starô uð messagå iî thå prograí  itself¬  anä ;          provideä thaô anù modifieä versionó oæ thió prograí arå ;          returneä  tï  thå publiã domaiî bù submissioî  tï  thió ;          system. ; ; ; will perform terminal mode, christensen up and down load and ; a few others ; ; ; SYSTEM CONDITIONALS hp150 equ 1 ;delete if not hp150 handle equ 1 ;delete if use CP/M compatible auxilary reads/writes ; ; hp150 is defined for hp150 only ; for this machine, handle should also be defined ; ; handle should be defined if you want to use reads and writes ; from the devices handle rather than from by using AUX read and write ; on most machines, either method should work ; ; The program has been used to connect an HP150 with a SHARP PC5000 ; and with an Osborne 1 using YAM. It has successfully transfered about ; a megabyte of data using Christensen Protocol ; ; Note: ******************************************************* ; ; it may be neccessary to change the name of the device in AUXNAM ; system equates ; ; ssize equ 200 ;stack size cr equ 13 lf equ 10 can equ 24 nak equ 21 ack equ 6 soh equ 1 eot equ 04h commode equ 5 ;character to return to command mode from terminal ; mode (CTRL E) ; ; msdos calls ; stop equ 4ch readky equ 01h writky equ 02h dirky equ 06h ;check keyboard status buffky equ 0ah ;read a buffer of input dispky equ 09h auxred equ 03h auxwrt equ 04h hndlrd equ 3fh hndlwr equ 40h parse equ 29h iocntl equ 44h openhn equ 3dh openfl equ 0fh closfl equ 10h seqrd equ 14h seqwrt equ 15h create equ 16h delete equ 13h setdma equ 1ah ; ; msdos macros ; msdos macro op,dxval mov dx,offset dxval mov ah,op int 21h endm msdos1 macro op mov ah,op int 21h endm ; ; Remark: Originally, only one macro was defined with an ; IFDIF ,<> conditional to detect the presence/absence of ; the second parameter, but this seemed to cause the assembler to loop data segment ; ; change of device name may be required ; ifdef hp150 auxnam db 'COM1',0 ;name of device to use for communication else auxnam db 'AUX',0 ;name of device to use for communication endif fcb db 60 dup (0) inbuff db 20,0 db 20 dup (0) ;input command buffer fbuff db 128 dup (?) ;file buffer block dw 0 ;block number for file copyrt db cr,lf,'MiniMod - a small MSDOS Modem program' db cr,lf,'=====================================',cr,lf db cr,lf,'Copyright Dave Moore, QCOM Pty Ltd May 84 229-3544' db cr,lf,'$' greet db cr,lf,'Minimodem :$' ovrwrt db cr,lf,'Overwrite ?$' filep db cr,lf,'File name :$' cancel db cr,lf,'Command cancelled $',cr,lf nocomm db cr,lf,'Command invalid',cr,lf,'$' seqerr db cr,lf,'Block sequence error',cr,lf,'$' dupblk db cr,lf,'Duplicate block ignored',cr,lf,'$' chkerr db cr,lf,'Checksum error $',cr,lf,'$' hnderr db cr,lf,'Handle status error : ',cr,lf,'$' hexfl db 0 ;send/receive in hex flag halffl db 0 ;half duplex flag hexwrk db ' $' ;kex work area auxhnd dw ? ;handler for aux errmes db 'error 0' errmes1 db ' opening AUX handle',cr,lf,'$' nofile db cr,lf,'no file found',cr,lf,'$' tcomp db cr,lf,'Transfer complete',cr,lf,'$' abandn db cr,lf,'Transfer abandoned',cr,lf,'$' aster db '*$' badtr db cr,lf,'Got for ACK',cr,lf,'$' goodtr db '^$' timout db 'Christensen timed out ',cr,lf,'$' expec db 'Expected :' expec1 db ' ',cr,lf,'$' recvd db 'Received :' recvd1 db ' ',cr,lf,'$' blokno db cr,lf,'Block no : $' fprint db cr,lf,62 dup (' ') db cr,lf,62 dup (' ') db cr,lf,62 dup (' ') db cr,lf,62 dup (' ') db cr,lf,62 dup (' ') db cr,lf,62 dup (' ') db cr,lf,62 dup (' ') db cr,lf,62 dup (' '),cr,lf,'$' ifdef hp150 ; ; device control strings to set 8 bit and disable handshaking ; dishnd db 3,8 ;disable handshaking set8bt db 2,8 ;set 8 bit mode endif prompt db 0 ;prompt character for current f command promps db 0 ;prompt command for fw command nocntl db cr,lf,'Device cannot accept control strings' db cr,lf,'Modem aborted',cr,lf,'$' rspnse db 0 ;response to send down line rcvblk db 1 ;received block number wait dw 2000 ;loops to check for response from other end choke dw 3000 ;choke on output for slow lines data ends ; stack segment stack dw ssize dup (?) stack ends ; code segment byte public 'code' assume cs:code,ds:data,ss:stack start: mov ax,data mov ds,ax mov es,ax mov ax,stack mov ss,ax mov sp,ssize ; ; open aux handle ; mov al,2 msdos openhn,auxnam jnc open1 ;on successful mov bx,offset errmes1 call hex msdos dispky,errmes jmp excom open1: mov auxhnd,ax ;save handle for aux ; ; set status for aux handler ; mov bx,auxhnd mov al,0 msdos1 iocntl or dx,20h ;set raw mode and dx,0ffh ;clear top byte mov al,1 mov bx,auxhnd push dx msdos1 iocntl ;save new device status pop dx ifdef hp150 or dx,8000h ;check ctrl bit jz hp1 ;on cannot do control strings mov bx,auxhnd mov al,3 mov cx,2 ;bytes to send msdos iocntl,set8bt mov bx,auxhnd mov al,3 mov cx,2 ;bytes to send msdos iocntl,dishnd jmp main hp1: msdos dispky,nocntl jmp excom endif ; ; start main procedure ; main: msdos dispky,greet msdos buffky,inbuff ;rea a command line mov al,inbuff+1 cmp al,0 jz main ;on nothing entered cmp al,1 jnz len2 jmp len1 ;length 1 command len2: mov ax,word ptr inbuff+2 or ax,2020h ;set lower case cmp ax,'ht' ;th = terminal half duplex jz thcom cmp ax,'hp' jz phcom cmp ax,'bp' jz pbcom cmp ax,'xe' ;ex = exit minimod jz excom cmp ax,'gp' ;pg = set prompt character jz pgcom cmp ax,'wf' ;fw = transfer file with wait jz fwcom cmp ax,'cp' ;pc - change choke parameter jz pccom cmp ax,'wp' ;pw - change wait parameter jz pwcom err: msdos dispky,nocomm jmp main thcom: mov halffl,1 jmp termmd excom: msdos1 stop jmp main fwcom: mov al,promps mov prompt,al call readfw jmp main pbcom: mov hexfl,0 ;send/receive in binary jmp main phcom: mov hexfl,1 ;send/receive in hex jmp main pgcom: mov al,inbuff+4 mov promps,al jmp main pccom: mov al,inbuff+4 sub al,'0' mov ah,0 add ax,ax add ax,ax mov ah,al add ax,1 mov choke,ax jmp main pwcom: mov al,inbuff+4 sub al,'0' mov ah,0 add ax,ax add ax,ax mov ah,al mov wait,ax add ax,1 jmp main ; ; one byte commands ; len1: mov al,inbuff+2 or al,20h ;set lower case cmp al,'t' jz tcom cmp al,'r' jz chread cmp al,'f' jz fcom cmp al,'s' jnz err ; ; christensen write ; call sendch jmp main ; ; christensen read ; chread: call readch jmp main ; ; f com -transmit a file ; fcom: mov prompt,0 call readfw jmp main ; ; terminal mode ; tcom: mov halffl,0 ;set full duplex termmd: msdos dirky,0ffh jz termm1 ;on no character input from console cmp al,commode jz termm2 ; return to command mode push ax call sendrm ;send to remote pop dx mov al,halffl cmp al,0 jz termm1 ;go and look at remote msdos1 writky jmp termmd ; termm1: call recvrm jz termmd ;on no character received from remote mov dl,al msdos1 writky jmp termmd termm2: jmp main ; ; look for a character from the remote line ; ; return in al with zero flag clear if found recvrm: mov bx,auxhnd mov al,6 msdos1 iocntl ;check ready for input cmp al,0ffh jz recvr1 ;on character ready cmp al,0 jz recv0 mov bx,offset hnderr+24 call hex msdos dispky,hnderr cmp al,al ;return zero recv0: ret ;return with zero flag set ifdef handle ; ; read using file handle ; recvr1: mov cx,1 mov bx,auxhnd msdos hndlrd,inbuff+2 mov al,inbuff+2 else recvr1: msdos1 auxred endif or ah,0ffh ;set not zero ret ; ; send a character down the line ; sendrm: mov dl,al ifdef handle mov bx,auxhnd mov cx,1 mov inbuff+2,al msdos hndlwr,inbuff+2 else msdos1 auxwrt endif ; ; wait for duration of choke ; mov ax,choke sendr1: dec ax jnz sendr1 ret ; ; christensen send routine ; sendch: call filenm mov block,1 msdos openfl,fcb mov byte ptr fcb+20h,0 cmp al,0 jz send1 ;on found msdos dispky,nofile ret send1: msdos setdma,fbuff ;set dma address ; ; wait for nak to start transmission ; send2: call recvrm jz send2 ;on nothin received cmp al,nak jnz send2 send3: msdos seqrd,fcb cmp al,0 jz send4 ;normal completion cmp al,3 jz send4 ;partial record mov al,eot ;completion message call sendrm ;send completion code msdos dispky,tcomp msdos closfl,fcb ret send4: call sendbl ;send block ; ; wait for response from remote ; call recvwt jnz send6 ;on not time out mov al,can ;cancel transmission call sendrm msdos dispky,timout ret send6: cmp al,ack jz send7 cmp al,can jz send8 ;on remote cancelled mov bx,offset badtr+6 call hex ; msdos dispky,badtr jmp send4 ;resend block send7: inc block msdos dispky,aster jmp send3 ;send next block send8: msdos dispky,cancel ret ; ; ; christensen receive routine ; readch: call filenm mov block,1 msdos openfl,fcb cmp al,0 jnz read0 ;on not found msdos dispky,ovrwrt msdos buffky,inbuff mov al,byte ptr inbuff+2 or al,20h ;make lower case cmp al,'y' jz read0 ;on overwrite jmp read11 read0: msdos setdma,fbuff ;set dma address msdos create,fcb ;create new file mov byte ptr fcb+20h,0 ;set relative record mov rspnse,nak ;set nak is to be sent ; ; send naks until something appears on the line ; read1: mov al,rspnse call sendrm call recvwt ;wait for reply jz read1 ;on nothin received cmp al,can jz read9 ;remote cancelled request cmp al,eot jz read8 ;complete transfer cmp al,soh jnz read1 ;on not a soh ; ; start of header detected - read a block ; read2: call rchead jz read7 ;on time out reading header call rcbody ;read body of block jz read7 ;on time out ; ; check that checksum and block number are correct ; cmp al,dl jnz read4 ;on checksum error mov al,rcvblk ;received block mov dl,byte ptr block ;expected block cmp al,dl js read5 ;on dup block jnz read6 ;on missing block mov rspnse,ack ;set successful msdos dispky,aster ;send asterisk to console inc block ;expect next block msdos seqwrt,fcb jmp read1 ;do another block ; ; checksum error ; read4: call dspchk ;diagnode checksum problem mov rspnse,nak ;redo this block jmp read1 ; ; sent a duplicate block - ignore but acknowledge as ok ; read5: msdos dispky,dupblk mov rspnse,ack jmp read1 ; ; we have missed a block ; ; can only happen if a nak gets corrupted to an ack ; read6: msdos dispky,seqerr jmp read9 ;not recoverable ; ; timed out ; read7: msdos dispky,timout jmp read10 ; ; transfer completed successfully ; read8: msdos dispky,tcomp mov al,ack call sendrm jmp read10 ; ; transfer cancelled ; read9: msdos dispky,cancel read10: msdos closfl,fcb ret read11: msdos dispky,abandn jmp read10 ; ; transfer header for block ; rchead: mov dx,1 ;checksum (includes starting soh) push dx call recvhx ;block number jz rch1 ;on time out mov rcvblk,al ;save received block number pop dx add dl,al ;add to checksum push dx mov bx,offset blokno+10 call hex msdos dispky,blokno call recvhx ;complement (one's) of block number pop dx add dl,al mov ah,0 sahf ;set not zero on return ret rch1: pop dx ret ; ; transfer bosy of block ; ; return zero if time out ; rcbody: mov bx,offset fbuff mov ax,80h rcb1: push ax ;save loop counter push bx ;and byte pointer push dx ;and checksum call recvhx ;get byte jz rcb2 ;on time out pop dx pop bx mov ds:[bx],al ;put byte in buffer inc bx add dl,al ;add to checksum pop ax dec ax jnz rcb1 ;on more to go push dx call recvhx ;read checksum jz rcb2 pop dx mov ah,0 sahf ;set non-zero rcb2: ret ; ; send a block from fbuff ; sendbl: mov al,soh call sendhx mov al,byte ptr block call sendhx mov al,byte ptr block not al call sendhx ;send one's complement of block number mov bx,offset fbuff mov ax,80h mov dx,0 sendb1: push ax ;save loop counter mov al,ds:[bx] ;get next byte add dx,ax ;add to checksum push dx inc bx push bx call sendhx ;send character pop bx pop dx pop ax dec ax jnz sendb1 mov al,dl ;send checksum call sendhx ret ; ; send a file with optional wait for prompt ; readfw: call filenm msdos openfl,fcb mov byte ptr fcb+20h,0 cmp al,0 jz readf1 msdos dispky,nofile ret readf1: msdos setdma,fbuff ; ; send characters until cr ; readf2: msdos seqrd,fcb mov cx,80h mov bx,offset fbuff cmp al,0 jz readf3 cmp al,3 jz readf3 ret ;on end of file readf3: mov al,ds:[bx] ;get next character push bx push cx call sendrm ;send it call recvrm ;look for echo of character jz readfa mov dl,al msdos1 writky readfa: msdos dirky,0ffh jz readfb ;on no local keyboard cmp al,commode jz readf7 ;on exit from fw command call sendrm readfb: pop cx pop bx mov al,ds:[bx] ;reload character ; and al,7fh ;ignore top bit cmp al,9 jnz readfe ;on not tab ; ; tab sent - wait for line to stop returning spaces ; push bx push cx readfc: call recvwt jz readfd ;on have stopped arriving mov dl,al msdos1 writky ;echo to local keyboard jmp readfc readfd: pop cx pop bx readfe: cmp al,cr jnz readf6 ; ; wait for prompt from other end ; mov al,prompt cmp al,0 jz readf6 ;on no wait required push bx push cx readf4: msdos dirky,0ffh jz readfg cmp al,commode jz readf7 call sendrm readfg: call recvwt ;wait for character jz readf4 ;on nothing read push ax mov dl,al msdos1 writky pop ax cmp al,prompt jnz readf4 readf5: pop cx pop bx ; readf6: inc bx dec cx jnz readf3 jmp readf2 readf7: pop cx pop bx ret ; ; receive from line, possibly in hex ; recvhx: mov al,hexfl cmp al,0 jz recv1 ;on not hex call recvwt jz recv2 ;ON TIME OUT call dechx add ax,ax add ax,ax add ax,ax add ax,ax push ax call recvwt jz recv2 call dechx pop dx add ax,dx cmp ax,256 ;set not zero ret recv1: call recvwt recv2: ret ; ; convert to binary from hex ; dechx: mov ah,0 cmp al,'A' js dechx1 sub ax,'A'-10 ret dechx1: sub ax,'0' ret ; ; send character in al in hex or binary ; sendhx: push ax mov al,hexfl cmp al,0 jz sendh2 ;on not send in hex pop ax mov bx,offset hexwrk call hex mov al,hexwrk call sendrm mov al,hexwrk+1 call sendrm ret sendh2: pop ax call sendrm ret ; ; receive from line with time out ; recvwt: mov bx,wait ;set wait time recvw1: dec bx jz recvw2 ;on time out push bx call recvrm pop bx jz recvw1 ;on no character recvw2: ret ; ; read in a file name parse ; filenm: msdos dispky,filep ;ask for file name msdos buffky,inbuff ;read file name mov al,0 ;set parse defaults mov si,offset inbuff+2 mov di,offset fcb msdos1 parse ret ; ; convert byte in al to hex ; bx points to first of two bytes to store value in ; hex: push ax shr al,1 shr al,1 shr al,1 shr al,1 call hex1 pop ax and al,0fh call hex1 ret hex1: cmp al,10 js hex2 add al,'A'-10 jmp hex3 hex2: add al,'0' hex3: mov ds:[bx],al inc bx ret ; ; checksum error ; dspchk: push dx mov bx,offset recvd1 call hex msdos dispky,recvd pop ax mov bx,offset expec1 call hex msdos dispky,expec msdos dispky,chkerr ; ; display buffer read ; mov di,offset fbuff mov bx,offset fprint+2 mov cl,80h dspc1: mov al,ds:[di] call hex inc bx inc bx inc di dec cl jnz dspc1 msdos dispky,fprint ret code ends end start