;**************************************************************************** ; FILE DOWNLOAD UTILITY FOR CIS A PROTOCOL. ; WRITTEN 3/10/82 BY BOB RICHARDSON ; COPYRIGHT (C) 1982 PERFORMANCE BUSINESS MACHINES ; Made available by permission - further distribution must include ; the original copyright notice and author's name ; INVOKED BY "DOW FNAME.FTP" AND USES DEFAULT FCB AND COMMAND LINE ; ; THIS PROGRAM IS DEPENDENT ON BIOS TO PROVIDE PROPER SUPPORT FOR THE ; MODEM AS A CONSOLE, READER AND PUNCH. THE IOBYTE IS NOT USED. BYE ; WILL PROBABLY WORK, OR THE SOURCE MAY BE MODIFIED TO ACCESS THE SERIAL ; STUFF DIRECTLY AND ADD THE CODE TO HANDLE THE UART OR SIO OR WHATEVER ; ; 3/24/82 -- FIRST SOURCE RELEASE .z80 ; equates soh equ 01h ; start of header etx equ 03h ; end of text eot equ 04h ; end of transmission enq equ 05h ; enq char - not used si equ 0fh ; shift in - starts protocol on terminal so equ 0eh ; shift out - ends protocol ; knak equ 15h ; nak dle equ 10h ; data link escape - used to mask chars for transparency esc equ 1bh ; escape eof equ 1ah ; ctl-z ctlz equ 1ah ; also cr equ 0dh ; carriage return lf equ 0ah ; line feed tof equ 0ch ; top of form ; cldboot equ 00h ; bios coldboot vector iobyte equ 0003h ; addr of iobyte deffcb equ 05ch ; addr of default fcb command equ 080h ; addr of command line bdos equ 05h ; addr of bdos jmp ; BDOS FUNCTIONS pstrg equ 09h ; print string function rdcbuf equ 0ah ; read console buffer fn$opn equ 0fh ; open file function fn$rds equ 014h ; read sequential disk fn$std equ 01ah ; set dma addr fn$cls equ 010h ; close file ; ; ; BIOS OFFSETS FOR VARIOUS CALLS const equ 03h ; constat call conin equ 06h ; conin conout equ 09h ; character out to console list equ 0ch ; character to line printer punch equ 0fh ; char to punch device rdr equ 12h ; get char from reader device ; ; ; Version Info vers equ '1' ; ascii version rev equ '2' ; ascii rev level ; ; Historical information ; 3-21-1982 First complete version assembled and released ; by the author, Bob Richardson of Micropro Corp. ; any and all source copys must retain this notice and ; the copyright notice - this file made available by ; permission. ; ;**************************************************************************** ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; CODE BEGINS: ; MAIN DRIVER LOOP- downld: ld sp,downld ; set up local stack for Charlie Strom call announce ; give copyright notice and rev,vers info call procol ; turn on protocol, open file, and start dowrty: call sndhdr ; then send or resend protocol header call waitack ; and wait for ack response jp c,dowrty ; retry if nak response jp nz,comfail ; error so dump job downlp: call getrec ; get disk record jp nz,fin ; eof - send eot record downl1: call putrec ; send rec call waitack ; and wait for ack jp c,downl1 ; try resend if nak jp nz,comfail ; send com failure and abort jp downlp ; loop till eof ; fin: call puteot ; send eot message call waitack ; and wait for ack jp c,fin ; resend if nak jp nz,comfail ; abort if knak call complete ; turn off protocol and send all done message jp cldboot ; terminate ;**************************************************************************** ; end of driver - start of worker routines ; ; biosvct: ld hl,(cldboot+1) ;get start of bios table add hl,de ; get addr for branch jp (hl) ; return handled to inline location ; ;************************************************************************ ; Get rev and version and copyright notice to operator announce: ld de,cpyrite ; copyright notice call prnmes ; to console ret ; to caller ; cpyrite: defb cr,lf,'Download Vers. ',vers,'.',rev,cr,lf defb ' Copyright (C) 1982 PBM Division MicroPro International Corporation ','$' ; ; ; ;****************************************************************************; ; test for errors and then kick it off ; procol: ld de,deffcb ; get default fcb ld c,fn$opn ; open file function call bdos ; see if we can open file cp 04h ; test for successful open jp nc,nofil ; send no file message if not found ld a,0 ; get zero ld (deffcb+32),a ; to current record ld (masking),a ; and start masking ctl chars in msg text call rmtnm ; get name for remote terminal ld a,(conbuff+1) ; start of data in buffer ld c,a ; is count for move ld b,0 ; with high order=0 ld hl,conbuff+2 ; start of actual name call noblnk ; bypass all blanks jp z,comfail ; if this passes, we are in TROUBLE - processor ; or operator are down! ld de,filespec ; addr in esc a message push bc ; save number of bytes(non space) ldir ; move filespec to message pop hl ; restore operator count ld a,cr ; terminate the string with cr ld (de),a ; in esc a message inc hl ; correct count to show cr included ld (tmpsav),hl ; and save for header xmit ret ; and return to caller ; ; ; tmpsav: defw 00h ; temporary save for count of chars in name ; ;************************************************************************* ; this routine actually turns the protocol on and sends header to terminal sndhdr: ld a,si ; get shift in char call punout ; send it ld a,esc ; send esc call punout ; charge ld a,'A' ; esc a for message call punout ; mush ye huskies mush ld hl,(tmpsav) ; get the restored count from save area push hl ; compatibility - yes, I know - could be less ld hl,escames ; get message balance addr pop bc ; restore count from command line ld a,c ; get count in accumulator add a,escalen ; and add in normal length ld b,a ; get in byte counter call prmesout ; send message as normal xor a ; set z flag ret ; and return ; noblnk: ld a,(hl) ; get char cp 20h ; test blank ret nz ; non blank dec c ; reduce count ret z ; return with error inc hl ; increment buffer pointer jp noblnk ; nofil: ld de,noflmes ; file not found message call prnmes ; to console jp cldboot ; and terminate abnormally ; noflmes: defb cr,lf,'FILE NOT AVAILABLE ON HOST- CHECK DIRECTORY$' ; ;*********************************************************************** ; control record for a-protocol escames: defb 'D' ; Download defb 'B' ; Binary transfer is always used! why save time escalen equ $-escames ; length for send routine filespec: defs 16h ; name of file to download ; ;*************************************************************************** ;get name for remote computer ; rmtnm: ld de,remquery ; ask the terminal what it wants to call it call prnmes ; to the operating system such as it is ld de,conbuff ; get a response call mesinp ; and then ld hl,conbuff+2 ; convert to insure upper case ld a,(conbuff+1) ; get char count xferred cp 0 ; insure some characters jp z,naminv ; else name is invalid ld c,a ; blank test counter call noblnk ; insure some non blank stuff jp z,naminv ; else name is invalid ld b,a ; in byte counter ; roll lower to upper case if necessary rmtnm1: ld a,(hl) ; pick up char cp 061h ; test for lower case jr c,rmtntl ; not lower if carry cp 07bh ; still looking if less than z jr nc,rmtntl ; so go on about business and 05fh ; else roll ld (hl),a ; and save rmtntl: inc hl ; bump character pointer djnz rmtnm1 ; and get next character ret ; and return to caller ; and then open and setup for further code ; naminv: ld hl,command+1 ; use the command line input ld de,conbuff+2 ; for the remote name ld a,(command) ; length ld c,a ; to counter with ld (conbuff+1),a ; count in command line ld b,0 ; zero high order ldir ; move characters ret ; to caller ; conbuff: defb 010h ; sixteen bytes max I'll allow defb 00h ; initial count defs 16 ; and blank buffer ; remquery: defb cr,lf,' I need a file name for your computer',cr,lf,'->','$' ; ;************************************************************************** ; send a record in Cis protocol format ; text ; prmesout: push bc ; save byte count push hl ; save buffer pointer xor a ; get zero ld (chksum),a ; and init checksum ld a,soh ; get start of header char call punout ; and send it ld a,(currec) ; get current record call sumupd ; and update checksum call punout ; and send it pop hl ; restore buffer addr pop bc ; restore count to b ; pmeslp: push hl ; save pointer push bc ; and char count ld a,(hl) ; get char call sumupd ; update checksum call tstmsk ; test if masking necessary call punout ; send char pop bc ; restore count pop hl ; get buffer pointer inc hl ; increment it djnz pmeslp ; and loop until all done ; ld a,etx ; get etx char call punout ; send it ld a,(chksum) ; get check sum cp 020h ; test for < ascii space jp nc,pmesl1 ; if = or greater, do not mask or 040h ; else add to supply transparency push af ; save checksum ld a,dle ; send dle call punout ; to remote pop af ; restore char pmesl1: call punout ; send it ret ; and return ;************************************************************************* ; Test here for masking of control chars, handle if necessary ; masking is selective, and in any case EOT is not masked tstmsk: push af ; save char ld a,(masking) ; get switch value cp 00h ; test for on status jp nz,tstmsr ; if off return immediate pop af ; restore original char push af cp 05h ; test if one of the offending chars ; NUL SOH STX ETX or EOT jp c,tstms1 ; mask if so cp dle ; or if equal the dle jp z,tstms1 ; go masked cp knak ; or if = to jp z,tstms1 ; the fatal nak mask it ; common return tstmsr: pop af ret ; common return if no masking necessary ; tstms1: ld a,dle ; send dle char first call punout ; and send it pop af ; followed by char+40 or 040h ; to insure transparecy ret ; masking: defb 00h ; flag for control char masking ; ;****************************************************************** ; Update the checksum ; sumupd: push af ; save char ld e,a ; and leave it in reg ld a,(chksum) ; get old checksum rlca ; and rotate it add a,e ; add new byte adc a,0 ; and possible carry ld (chksum),a ; and save it pop af ; restore character ret ; and return ; ; ;************************************************************************** ; Read a record from the disk and prepare to send it ; getrec: ld de,buffer ; buffer address ld c,fn$std ; set dma function call bdos ; set bufferaddr ld de,deffcb ; get fcb addr ld c,fn$rds ; read a record call bdos ; helps to take this step or a ; set z flag if not eof ret ;**************************************************************************** ; Actually send the record to the terminal putrec: ld hl,buffer ; get buffer address ld b,128 ; get buffer length call prmesout ; and send record to terminal ret ;************************************************************************** ; communications failure!!! comfail: ld a,knak ; turn off protocol mode call punout ; at terminal end ld de,failmes ; get comm failure message call prnmes ; send message jp cldboot ; and abort ; failmes: defb CR,LF,' Communications Failure - Download aborted','$' ; ******************************************************************* ; send an eot message puteot: ld a,0ffh ; turn of the switch to insure ld (masking),a ; that eot is sent unmasked ; ld hl,eotmes ; get addr of eot char ld b,1 ; setup call prmesout ; and send it ret ;************************************************************************ ; FINISHED - SEND SHIFT OUT TO TURN OFF PROTOCOL MODE AT REMOTE complete: ld a,so ; turn off protocol mode at term call punout ; now ld de,dcommes ; get download complete call prnmes ; send it ; ret dcommes: defb cr,lf,' DOWNLOAD COMPLETE ','$' eotmes: defb eot ;************************************************************************* ; WAIT FOR AN ACK OR NAK FROM HOST - RETURN WHEN WE SEE ONE ; THIS ROUTINE ALLOWS EASILY INSERTING TIME OUT CODE ; waitack: call pcharin ; get protocol char cp '.' ; is it ack jp z,gotack ; then handle cp '/' ; is it nak? jp z,rexmit ; then retransmit cp knak ; check for abort jp nz,waitack ; else loop ; ld a,01 ; set nz, clear carry or a ; and return ret ; received a nak rexmit: scf ; return carry set ret ; received an ack - record ok - from terminal gotack: call updrnum ; update current record number scf ; return carry clear ccf xor a ; set zero flag ret ;********************************************************************* ; SUBROUTINE TO UPDATE THE CURRENT RECORD NUMBER - NUMBER IS ASCII CHAR ; updrnum: ld a,(currec) ; get current record number inc a ; and increment cp '9'+1 ; test for overflow jr c,updrok ; still valid if carry ld a,'0' ; else change it updrok: ld (currec),a ; and save result ret ; then return ;************************************************************************* ;USER CONFIGURATION AREA - THESE ARE THE IO ROUTINES WHICH ARE USER MODIFIABLE ; AT LEAST TO SOME EXTENT ; ;*********************************************************************** ; This routine uses the bios punch call to access the console port ; it could be changed easily to access the port directly ; it must send the char in the accumulator to the modem port as 8 bit byte punout: push af ; save char ld c,a ; get char in proper register ld de,punch ; get offset call biosvct ; go doit pop af ; restore char ret ; ; ; ;******************************************************************** ; SUBROUTINE TO READ 1 CHAR FROM THE INPUT STREAM IN PROTOCOL MODE ; CHAR IS NOT CHECKSUMMED, AND PARITY MAY BE STRIPPED - RETURN CHAR IN A pcharin: ld de,conin ; get 1 char via bios call biosvct ; and return ret ; to caller ; ; ; ;************************************************************************** ; ROUTINE TO PRINT A MESSAGE ON THE CONSOLE DEVICE- uses standard cp/m convention ; prnmes: ld c,pstrg ; print string function call bdos ; to cpm ret ; to caller ; ; ;************************************************************************** ; ROUTINE TO READ A BUFFER FROM OPERATOR - RETURNS STANDARD CONSOLE BUFFER mesinp: ld c,rdcbuf ; read console buffer function call bdos ; call op/sys ret ; to caller ; data areas ; currec: defb '1' ; initial record number chksum: defb 00h ; initial check sum buffer equ $ ; record buffer for diskrecord ;*************************************************************************** ; BEST OF LUCK AND BEST REGARDS - BOB R. end