p equ psw entry equ 05h rdcon equ 1 wrcon equ 2 prbuff equ 9 chcon equ 11 seldsk equ 14 openf equ 15 closef equ 16 deletf equ 19 rdrec equ 20 wrrec equ 21 creatf equ 22 inqdsk equ 25 setdma equ 26 ; reof equ 1 ;read end-of-file signal ; fn equ 1 ;EFCB layout ft equ 9 ex equ 12 rct equ 15 nr equ 32 status equ 33 driveno equ 34 nxbyte equ 35 buffer equ 36 ; ;used by start: tcorg equ 600h mstack equ tcorg+46h bpr equ tcorg+42h inst equ tcorg+0d08h move equ tcorg+0c4bh hlneg equ tcorg+0dc0h progend equ tcorg+060h tfcb equ 05ch tbuff equ 080h ; ;used by USERMC mceset equ tcorg+2bh toptoi equ tcorg+2eh pushk equ tcorg+31h pzero equ tcorg+1c5h ; ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ;::::::::::: tiny - c ::::::::::::::::::::::::::::::::::: ;::::::::::: cp/m installation :::::::::::::::::::::::: ;::::::::::: by t. a. gibson, 1978 :::::::::::::::::: ;::::::::::: copyright, 1978, tiny-c associates ::: ;::::::::::: all rights reserved :::::::::::::::: ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; org 100h jmp start jmp inch jmp aout jmp chrdy jmp fopen jmp rdblok jmp wrblok jmp fclose jmp usermc ; ; ;transforms unit in BC to efcb in DE unit mov d,b ;DE <- BC mov e,c mov a,e ;test for unit 1 dcr a ora d rnz lxi d,efcb ;use built in efcb ret ; ;restores BC, DE, HL and returns bdhret pop b dhret pop d pop h ret ; ;assures proper disk is logged on. (DE) is efcb. asudsk push h!push d!push b lxi h,driveno ;retrieve needed drive dad d mov a,m push p mvi c,inqdsk call fdos mov b,a ;current drive -> B pop p ;needed drive -> A cmp b ;if same do nothing jz bdhret mov e,a ;logon device (A) mvi d,0 mvi c,seldsk call fdos jmp bdhret ; ;assure correct DMA for buffer at (DE)+buffer. asudma push h!push d!push b xchg mvi c,setdma call fdos jmp bdhret ; ;restore default DMA buffer cpmbuff push h! push d! push b lxi d,80h mvi c,setdma call fdos jmp bdhret ; ;load buffer, (DE) is EFCB ldbuff push h! push d ! push b call asudsk call asudma mvi c,rdrec call fdos jmp bdhret ; ;write the buffer. (DE) is EFCB wrbuff push h! push d! push b call asudma ;must send it out call asudsk mvi c,wrrec call fdos jmp bdhret ; ;examines 1st 2 bytes of a drive:fn.ft string. ;(HL) points to the string. If a drive is specified, the ;drive is set up in the EFCB pointed to by (DE). ;Otherwise drive a is set up. drive push h! push d! push b inx h ;is there a colon in 2nd byte. mov a,m cpi ':' jnz dr2 dcx h ;get drive byte. Must be A,B,C or D, mov a,m sui 'A' jm dr99 cpi 4 jm dr3 sui 20h ; or a,b,c or d. jm dr3 cpi 4 jm dr3 dr99 mvi a,254 ;illegal drive signal ora a jmp bdhret dr2 mvi c,inqdsk ;default is current drive call fdos lxi h,driveno dad d mov m,a jmp bdhret dr3 lxi h,driveno ;driveno in A into EFCB. dad d mov m,a xra a ;success signal pop b! pop d! pop h ;restore environment. inx h! inx h ;bump string pointer past colon. ret ; ;safe entry into CP/M. fdos push h! push d! push b call entry jmp bdhret ; ;puts fn.ft into EFCB. (HL) points to string ended with ;null byte. (DE) points to EFCB. fnft push h! push d! push b xra a ;0 into ET field stax d inx d mvi b,8 ;length of FN field call fomov ;fn into FN jz ftdflt ;if 0 then end of string was reached. fdot mov a,m ;scan for . or null inx h cpi '.' jz ftype ora a jnz fdot ftdflt lxi h,dft ftype mvi b,3 ;mov ft into FT call fomov fex xra a ;0 EX thru RC stax d! inx d stax d! inx d stax d! inx d stax d! inx d jmp bdhret dft db 'TC ' ; ;moves a string to a field. (HL) points to string. (DE) ;points to field of length (B). Stops on . or null or ;field full. Pads field with blanks if needed. Returns ;last byte examined in A and Z set iff its null. fomov mov a,m ora a jz fpad cpi '.' jz fpad cpi '*' ;* => pad with ?'s jz qpad cpi 61h ;test for lower case alpha jc fm2 cpi 7bh ; 'z'+1 jnc fm2 ani 0dfh ;force upper case fm2 stax d inx d inx h dcr b jnz fomov ora a ;set or reset Z ret ;DE points past moved field ;HL points to next byte to examine. fpad push p ;save char that caused stop mvi a,' ' ;blanks into remainder of field fp2 stax d inx d dcr b jnz fp2 pop p ;restore character that caused stop ora a ret qpad push p mvi a,'?' jmp fp2 ; ;prints (A) in hex, and EFCB prefcb push psw push h! push d! push b call ahexout mvi a,' ' call aout lxi h,driveno ;drive letter into A dad d mov a,m adi 'A' call aout ;send drive letter mvi a,':' call aout ;send colon mvi c,8 ;length of filename lxi h,fn ;send filename dad d call prntnch mvi a,'.' ;send period call aout mvi c,3 ;send filetype lxi h,ft dad d call prntnch mvi a,' ' ;send blank call aout lxi h,ex ;send extent number dad d mov a,m call ahexout mvi a,' ' ;send blank call aout lxi h,rc ;send record count dad d mov a,m call ahexout mvi a,' ' call aout lxi h,nr ;send next record number dad d mov a,m call ahexout mvi a,' ' call aout lxi h,status ;send status dad d mov a,m call ahexout mvi a,' ' call aout lxi h,nxbyte ;send next byte of buffer dad d mov a,m call ahexout mvi a,' ' call aout ;send blank at end pop psw jmp bdhret ; ;opens a file for tiny-c, as specified in the ;Owner's Manual 6.1.2. DE (filesize) is ignored. ;All other parameters are used. fopen push p ;r/w flag call cpmbuff ;assure default buffer call unit ;set up pointer to EFCB in DE. call drive ;drive into EFCB jz f2 pop p ;improper drive. Clear stack. call ps ;print filename and bad drive message. lxi d,baddr ferr call pcs ;bad drive message xra a ;signal an error inr a ret baddr db 'improper drive$' f2 call fnft ;fn.ft into EFCB jz f3 pop p ;bad filename call ps ;clear stack, print filename and bad lxi d,badname ; filename message. jmp ferr badname db 'bad filename$' f3 lxi h,nr ;0 into NR and NXBYTE dad d xra a mov m,a lxi h,nxbyte dad d mov m,a pop p ;restore r/w flag, and put lxi h,status ; into status dad d mov m,a cpi 1 ;what kind of open jz ropen cpi 2 jz wopen call prefcb ;bad r/w flag, print EFCB and message lxi d,badrw jmp ferr badrw db 'rw must be 1 or 2$' ropen call asudsk ;assure correct disk mvi c,openf ;do a cpm open call fdos cpi 255 ;test for no file jnz openok call prefcb ;print EFCB and no-file message lxi d,nofile jmp ferr nofile db 'cant find file$' openok xra a ;signal open ok ret wopen call asudsk ;assure correct disk mvi c,deletf ;delete old file, if any call fdos mvi c,creatf call fdos cpi 255 ;test if disk is full jnz openok call prefcb ;print EFCB and full message lxi d,dirful jmp ferr dirful db 'directory full$' ; ;reads a block. HL points to memory area to read into. ; BC is a unit. If status of EFCB is not 1, returns an ; eof signal, and does not read. Otherwise reads one block ; into memory. If physical eof was reached, an eof signal is ; returned. Otherwise scans the read block for ^Z, and ; returns length of block in DE. Returns 0 in A for ok, ; -1 for eof, 1 for error. Leaves BC, HL unchanged. rdblok push b push h call unit ;efcb into DE lxi h,status ;test status dad d mov a,m cpi 1 jnz rdeof pop h push h call ldbuff ;read the record ora a jnz rdpeof ;possible eof lxi d,0 ;scan for ^Z mvi b,1ah ;^Z into A mvi c,128 ;scan limit rdscan mov a,m ;get byte cmp b ;is it ^Z jz rdout inr e ;add to length inx h ;to get next byte dcr c jnz rdscan rdout pop h ;restore B and H pop b xra a ;signal ok ret rdpeof cpi reof ;didn't read, see why. jnz rderr rdeof pop h ;eof exit, restore B and H pop b xra a ;signal eof dcr a ret rderr call prefcb ;print error code and efcb lxi d,rermsg jmp ferr rermsg db 'read error$' ; ;write a block. Unit in BC. HL..DE bracket a block of ; memory <= 128 bytes. If status in EFCB is not 2, signal ; an error. If block < 128 put an ^Z at (DE)+1. Then ; write the block. Return 0 in A for ok, 1 otherwise. wrblok mov a,e ;check length sub l cpi 127 ;E-L==127 implies 128 bytes in block jz wr2 xchg ;short block, put ^Z at (DE)+1 inx h mvi m,1ah xchg wr2 call unit ;efcb into DE push h ;save write address lxi h,status dad d ;test status mov a,m cpi 2 pop h ;restore write address jnz wrerr ;error if status not 2 call wrbuff ;write the block ora a ;zero signals no problem rz wrerr call prefcb ;print error code and efcb lxi d,wermsg jmp ferr wermsg db 'write error$' ;close a file, BC is the unit. fclose call unit ;EFCB into DE call cpmbuff ;assure default buffer lxi h,status ;status into A dad d mov a,m ;test status cpi 2 jnz zstat mvi c,closef ;writing, must close the file call fdos cpi 255 jz fcerr zstat xra a ;0 into status mov m,a ret fcerr call prefcb lxi d,fermsg jmp ferr fermsg db 'close error$' ; ;prints (A) restoring all registers aout push h! push d! push b! push p mvi c,wrcon mov e,a cpi 0DH jnz aout2 call fdos ;after also send mvi e,0AH mvi c,wrcon aout2 call fdos pop p jmp bdhret ; ;prints (A) in hex ahexout push p rrc! rrc! rrc! rrc call hexout pop p ;and fall into hexout ;prints one hex digit from (A) hexout push p ani 15 adi 90h daa aci 40h daa call aout pop p ret ; ;prints tiny-c string (null terminated) ps mov a,m ora a rz call aout inx h jmp ps ; ;prints (C) chars starting at (HL) prntnch mov a,m call aout dcr c inx h jnz prntnch ret ; ;prints cp/m style string, ($ terminated) pcs mvi c,prbuff jmp fdos ; ;Tests for char ready from the terminal, as specified ;in the Owner's Manual, Sec. 6.1.1. HOLD is a software ;buffer shared by chardy and inch, and HOLDF is a flag ;indicating whether or not a char is in HOLD. chrdy mvi c,chcon ;check the console status call fdos ani 1 ;mask of least signif bit jz tryhold mvi c,rdcon ;read from the console call fdos ani 07fh ;mask out hi bit sta hold xra a ;set holdf to -1 dcr a sta holdf tryhold lda holdf ;test hold flag ora a rz ;no char ready lda hold ;got one in hold ora a rnz mvi a,1 ;null byte, return a 1 ora a ret ; inch lda holdf ;try hold first ora a jz tryport xra a ;have one in hold sta holdf ;zero the flag lda hold ;return the held char cpi 0DH rnz mvi a,0AH ;echo a line feed when read call aout mvi a,0DH ret tryport call chrdy ;hold is empty, so wait for human jz tryport ; to type something. jmp inch ;He did it, so use inch to read the character. holdf db 0 hold db 0 ; ;It all starts here. Cold start if no filename given. ; Otherwise load the named file, and hot start it. A ; default filetype TC is provided. start lda tfcb+1 cpi ' ' jz tcorg ;cold start lhld mstack ;set SP sphl lxi b,-10 ;initialize tiny-c lhld bpr xchg lxi h,inst call move lhld bpr lxi d,9 dad d call hlneg shld progend lxi h,tbuff ;put null at end of string. mov a,m ;get length inx h ;beginning of string push h add l mov l,a ;hl -> end of string mvi m,0 pop h ;begin of string inx h ;skip over single blank lxi b,1 ;unit 1 mvi a,1 ;rw set to read call fopen jnz tcorg ;cold start on error lhld progend ;where to read st2 call hlneg lxi b,1 call rdblok jnz stout dad d mvi m,0 call hlneg shld progend jmp st2 stout push p ;save last read result lxi b,1 call fclose pop p cpi 0ffh ;test for end of file jnz tcorg ;if not end of file, then error, hence cold start jmp tcorg+6 ;hot start ; efcb ds 36 ;for unit 1 ; ;User Machine Calls -- defined in CP/M supplement. usermc mov a,l cpi 1 jz mc1001 cpi 2 jz mc1002 cpi 3 jz mc1003 jmp mceset ; mc1001 call toptoi ; FDOS ( fnum, arg ) push d ;arg -> stack call toptoi ;fnum -> C mov c,e pop d ;arg -> DE push b ;save fnum for later call entry mov e,a ;result -> DE mvi d,0 pop h ;fnum -> L mov a,l cpi 27 ;only function 27 returns 2 bytes jnz pushk mov d,b ;hi byte for 27 jmp pushk ; mc1002 call toptoi ;sefcb(string,efcb) push d call toptoi xchg ;string -> HL pop d ;efcb -> DE call drive jnz pushk ;illegal drive pushed addr of efcb call fnft jmp pzero ; mc1003 call toptoi ;efcb -> DE xra a call prefcb jmp pzero