;----------------------------------------------------------------------- ; Small-C Run-time Library (ASM Version) May 10, 1988 ; ; V5d As of July 29, 1983 7:47am (br) ; Fixed GETC()/CGET() so it skips check for CR when ; calling GETCHAR(). ; - Bill Randle ; ; V5c As of July 27, 1983 9:55 pm ; Fixed bug in initialization of RSTDIN and RSTDOUT. ; Fixed bug in PUTS(). ; - Bill Randle ; ; V5b As of July 19, 1983 9:50 pm ; Fixed bug in FCLOSE(). ; - Bill Randle ; ; V5a As of April 14, 1983 12:55 pm ; Masked parity bit in compares for EOL, LF & ^Z. ; - Bill Randle ; ; V5 As of April 12, 1983 12:30 am ; Modified to reuse package for Small-C, ver 2. ; Removed "QZ" in front of names. ; Added runtime initialization and command line ; processing from RUNTIM.MAC [by Bill Danielson, 3/83]. ; Added I/O redirection for stdin and stdout. ; Added FPUTS(), FGETS(), DOLDDR(), DOLDIR(), ; UNLINK() and ABORT() functions. ; - Bill Randle ; ; V4d As of July 16, 1980 9:00 pm ; Added EXIT() function ; - GTF ; ; V4c As of July 9, 1980 9:15 pm ; Fixed bug in CPMIO which returned wrong error status. ; Added PUTS() function ; Un-hardwired I/O buffer count. ; Made GETCHAR() print LF after reading CR. ; Made GETCHAR() return -1 on EOF (=CTRL-Z) ; Added EOL and LF equates, instead of magic numbers ; - GTF ; ; V4b As of July 7, 1980 3:00 pm ; Changed putc() to test code returned by cput() ; - GTF ; ; V4 As of June 26, 1980 12:15pm ; Changed all @'s and ?'s to "QZ" for ASM compatibility ; - GTF ; ; V3b As of June 9, 1980 12pm ; corrected cp to chp in @gets ; changed lower case to hex constants in @fopen and fcb ; - RJ ; ;------------------------------------------------------------------ ; ; May need following statement when using ASM ; ;;; ORG 100H ; ; ; JMP START ; ; Delete following extern when using ASM ; LINK EQU 1 ; IF LINK PUBLIC CPM, PUTC, FPUTC, PUTCHAR, GETC, FGETC, GETCHAR PUBLIC FOPEN, UNLINK, FCLOSE, ABORT, EXIT, PUTS, FPUTS PUBLIC GETS, FGETS ; EXTRN MAIN, CCDSGI, CCSXT ENDIF ; LINK ; ; ======================================== ; I/O subroutines for CP/M ; By Glen Fisher ; The Code Works(tm) ; [modified by Bill Randle] ; ======================================== ; NULL EQU 0 ; Pointer to nothing FCBSIZE EQU 36 ; Size, in bytes, of an FCB NEXTP EQU 0 ; Offset to next-character pointer in I/O structure UNUSED EQU 2 ; Offset to unused-positions-count in I/O structure BUFFER EQU 4 ; Offset to disk sector buffer in I/O structure FLAG EQU 33 ; File-type flag byte (in unused part of FCB) FREEFLG EQU 128 ; This I/O structure is available for the taking EOFFLG EQU 2 ; The end of this file has been hit WRTFLG EQU 1 ; This file open for writing BUFSIZ EQU 128 ; How long the sector buffer is NBUFS EQU 4 ; Number of I/O buffers (change buffer declarations, too) ; ; CP/M system call codes ; CLOSE EQU 16 ; Close a file CPMSTR EQU 9 ; Print '$' delimited string on console CREATE EQU 22 ; Make a file DMA EQU 26 ; Set DMA (I/O address) DELETE EQU 19 ; Delete a file GETCH EQU 1 ; Read character from console GETSTR EQU 10 ; Read string from console LSTOUT EQU 5 ; Write character to list device OPEN EQU 15 ; Open a file PUTCH EQU 2 ; Write character to console QUERY EQU 25 ; Get logged-in drive id READ EQU 20 ; Read a sector SELECT EQU 14 ; Log-in a drive WRITE EQU 21 ; Write a sector ; LF EQU 10 ; Line feed EOL EQU 13 ; End-of-line character (=carriage return) CTRLZ EQU 26 ; End-of-file mark for text files TBUFF EQU 80H ; Address of default I/O address BDOS EQU 5 ; Entry point to CP/M BDOS CPMARG EQU 80H ; CP/M command line MAXARG EQU 32 ; Maximum number of args in input line STDIN EQU 0 ; Default for stdin STDOUT EQU 1 ; Default for stdout STDERR EQU 2 ; Default for stderr STDLIST EQU 4 ; Default for stdlist ; DFLTDSK:DS 1 ; Drive to use if no drive is named UNIT: DS 2 ; I/O structure address to act on IP: DS 2 ; Int *ip; CHP: DS 2 ; Char *chp; DP: DS 2 ; Char *dp; FILE: DS 2 ; File name MODE: DS 2 ; Char *mode;(read or write) ZCH: DS 2 ; Char ch; ZT: DS 2 ; Int t; FN: DS 2 ; Int fn; i/o function (for cpmio) ; SVCHP: DS 2 ; Char *svchp; saved character pointer RSTDIN: DS 2 ; Int rstdin; unit of redirected stdin RSTDOUT:DS 2 ; Int rstdout; unit of redirected stdout ; ; First thing, we save CPM's stack pointer and current disk and initial- ; ize STDIN and STDOUT. Second thing, we run through the CPM input line ; and modify it so that we can pass the C program the command line in ; the ARGC, ARGV form that it expects ; ; HL = pointer to next argv entry ; DE = pointer to next character in command line ; B = number of characters left in line ; C = argument count (argc) ; START: LXI H,0 ; Get CPM's stack pointer DAD SP SHLD STACK ; Save it for later MVI C,QUERY ; Get logged-in disk CALL BDOS INR A ; Make it so it will work in FCB STA DFLTDSK LDA BDOS+2 ; Get base of BDOS MOV H,A ; Save page in HL MVI L,0 SPHL ; Set stack pointer LXI H,STDIN SHLD RSTDIN ; Init RSTDIN LXI H,STDOUT SHLD RSTDOUT ; Init RSTDOOUT MVI C,0 ; Init ATGC LXI H,ARGV ; Pointer to first entry of ARGV array ; ; Unfortunately, CPM does not tell us what the first word of ; the command line was (the name of pgm), so we fake ; it by pointing it to a ascii string with 'pgmname' in it ; LXI D,PGM ; Pointer to 'pgmname' string CALL SVARG ; Save next argument ; Ok, now for the real stuff. Set DE pair to point to CPM command line ; and start searching for arguments ; LXI D,CPMARG ; Pointer to CPM argument line LDAX D ; Load # character in line MOV B,A ; Save it in B ; NXTSP: INX D ; Point to next character DCR B ; Decrement character count JM ENDCMD ; End of cmd line LDAX D ; Load next character in line CPI ' ' ; Space? JZ NXTSP ; Yes...continue searching CPI '>' ; Redirect output? JZ RDOUT ; Yes...open the file for output CPI '<' ; Redirect input? JZ RDINP ; Yes...open the file for input CALL SVARG ; Nope, save starting point of this arg ; ; Loop looking for either end of line of a space ; NXTCH: INX D ; Point to next character DCR B ; Decrement character count JM ENDWRD ; End of cmd line, but need to end arg LDAX D ; Load next character in line CPI ' ' ; Space? JNZ NXTCH ; Nope...keep looking MVI A,0 ; Yes, replace it with a zero byte STAX D JMP NXTSP ; Look for start of next argument ; ENDWRD: MVI A,0 STAX D ; ENDCMD: MVI B,0 ; Zero B (BC now is 16 bit ATGC) PUSH B ; First arg to main procedure LXI H,ARGV ; Point to argv array PUSH H ; Second argument to main procedure MVI A,2 ; Load up the argument count CALL MAIN ; Transfer to the C world.... JMP EXIT ; Return to CP/M ; SVARG: MOV M,E ; Save pointer to start of string INX H MOV M,D INX H INR C ; Increment ARGC RET ; ARGV: DS MAXARG*2 ; PGM: DB 'PGMNAME',0 ; RDINP: CALL DEBLNK ; Skip leading spaces JM RDERR ; End of line reached PUSH H CALL CPYNAM ; Copy filename to temp buffer PUSH D ; Save registers PUSH B LXI H,NAMBUF ; Begining of filename PUSH H LXI H,RDOPN ; Mode PUSH H CALL FOPEN ; Open the file POP D POP D MOV A,H ORA L ; Check return status JZ RDERR SHLD RSTDIN ; Save unit for redirected input POP B ; Restore registers POP D POP H MVI A,0FFH CMP B ; End of command line? JZ ENDCMD JMP NXTSP ; Get next argument ; RDOUT: CALL DEBLNK ; Skip leading spaces JM RDERR ; End of line reached PUSH H CALL CPYNAM ; Copy filename to temp buffer PUSH D ; Save registers PUSH B LXI H,NAMBUF ; Begining of filename PUSH H LXI H,WROPN ; Mode PUSH H CALL FOPEN ; Open the file POP D POP D MOV A,H ORA L ; Check return status JZ RDERR SHLD RSTDOUT ; Save unit for redirected input POP B ; Restore registers POP D POP H MVI A,0FFH CMP B ; End of command line? JZ ENDCMD JMP NXTSP ; Get next argument ; DEBLNK: INX D ; Skip leading spaces DCR B RM ; End of line reached LDAX D CPI ' ' JZ DEBLNK RET ; CPYNAM: LXI H,NAMBUF ; Copy filename to temp buffer PUSH B ; Save reg C MVI C,16 ; Maximum filename length ; CPY1: MOV M,A INX D INX H DCR B JM ENDNAM DCR C JZ RDERR LDAX D CPI ' ' JNZ CPY1 ; ENDNAM: MVI M,0 POP H MOV C,L ; Restore reg C RET ; RDERR: LXI D,RDEMSG ; Error message MVI C,CPMSTR CALL BDOS ; Make sure it gets put on the terminal JMP EXIT ; RDOPN: DB 'r',0 WROPN: DB 'w',0 NAMBUF: DS 16 RDEMSG: DB 'IOLIB: Unable to open < or > file$' ; ; LLDDR (source, dest, n) ; DOLDDR: INX SP ; Skip over return address INX SP POP B ; Load n POP D ; Load destination POP H ; Load source DB 0EDH, 0B8H ; Do LDDR instruction PUSH H ; Restore stack PUSH D PUSH B DCX SP DCX SP RET ; ; DOLDIR (source, dest, n) ; DOLDIR: INX SP ; Skip over return address INX SP POP B ; Load n POP D ; Load destination POP H ; Load source DB 0EDH, 0B0H ; Do LDIR instruction PUSH H ; Restore stack PUSH D PUSH B DCX SP DCX SP RET ; ; End of memory function ; Returns top memory location in HL ; TOPOFMEM: LDA BDOS+2 ; Get base of BDOS MOV H,A ; Save page in HL MVI L,0 RET ; ; Return the first free location ; FIRSTFREE: LHLD MEMRY$ RET ; MEMRY$: DW 0 ; ; This assembly routine allows CP/M calls from Small C. ; ; CPM (cpmfunction#, inputparameter) ; ; Since this function returns whatever is returned in register ; it cannot be used to call ReturnVersionNumber, ReturnLoginVector, ; WriteProtectDisk, or GetAddr. ; CPM: POP H ; Pop return address POP D ; Pop input parameter in DE register pair POP B ; Pop function code into C register PUSH B ; Restore stack PUSH D PUSH H CALL BDOS ; Call CP/M MOV L,A ; Sign extend A into HL register pair RLC SBB A MOV H,A RET ; ; EXIT () Stop execution of the program, restore the logged-in drive ; and reboot CP/M ; EXIT: LHLD RSTDOUT MOV A,H ORA A ; See if STDOUT has been redirected JZ EXIT1 PUSH H CALL FCLOSE ; If so, close the file POP B ; EXIT1: LDA DFLTDSK ; Grab orig. logged-in disk MOV E,A DCR E ; (cvt. back to 0-n) MVI C,SELECT ; And log it in again CALL BDOS LHLD STACK ; Load stack pointer SPHL JMP 0 ; Return to CP/M ; STACK: DW 0 ; ; ABORT (reason) ; ABORT: POP B POP D PUSH D PUSH B PUSH D ; Error code LXI H,ABTMSG ; Load abort message PUSH H LXI H,STDERR PUSH H CALL FPUTS POP B POP B LXI H,2 CALL CCDSGI PUSH H ; ; Someday this should write out the correct error code ; ; CALL OUTDEC## ; Inside C compiler POP B JMP EXIT ; ABTMSG: DB 0DH, 0DH, 'Aborted, reason = ',0 ; ; GRABIO () Find an input buffer, and return its address. If there ; isn't one, return a NULL. ; GRABIO: MVI B,NBUFS LXI H,IOBUFS+FLAG LXI D,FCBSIZE+BUFFER+BUFSIZ MVI A,FREEFLG ; GRAB2: CMP M ; Flag byte == freeflg? JZ GRAB3 ; If so, found a free buffer DAD D ; On to next buffer DCR B JNZ GRAB2 ; If there is one... LXI H,NULL ; There ain't RET ; Give up ; GRAB3: MVI M,0 ; Mark buffer as taken LXI D,-FLAG ; Back up to buffer start DAD D RET ; And hand it back ; ; FREEIO (unit) Mmark a buffer as free ; FREEIO: POP B ; Save rtn addr POP H ; Get buffer address PUSH H ; Put the stack back together PUSH B LXI D,FLAG ; Find flag byte DAD D MVI M,FREEFLG ; Mark buffer as 'free' LXI H,NULL ; Return something RET ; IOBUFS: DS FCBSIZE-3 DB FREEFLG,0,0 DS BUFFER+BUFSIZ DS FCBSIZE-3 DB FREEFLG,0,0 DS BUFFER+BUFSIZ DS FCBSIZE-3 DB FREEFLG,0,0 DS BUFFER+BUFSIZ DS FCBSIZE-3 DB FREEFLG,0,0 DS BUFFER+BUFSIZ ; ; FOPEN (name,mode) ; FOPEN: POP B ; Get arguments POP H ; Mode SHLD MODE POP D XCHG SHLD FILE PUSH H PUSH D PUSH B CALL GRABIO ; Unit = grabio() SHLD UNIT MOV A,H ; If(unit==NULL) ORA ; Return(NULL) RZ LXI D,FCBSIZE ; Ip = unit+FCBSIZE DAD D SHLD IP LHLD IP ; Ip[NEXTP] = &ip[BUFFER] LXI D,BUFFER DAD D XCHG LHLD IP LXI B,NEXTP DAD B MOV M,E INX H MOV M,D LHLD UNIT ; Fcb(unit,name) PUSH H LHLD FILE PUSH H CALL FCB POP H POP H LHLD UNIT ; Cpmdisk(*unit) MOV L,M MVI H,0 PUSH H CALL CPMDISK POP H LHLD MODE ; If(*mode=='r' || *mode=='R') MOV A,M CPI 72H ; 'r' ? JZ FOPIF0 CPI 52H ; 'R' ? JNZ FOPIF1 ; FOPIF0: MVI C,OPEN ; If(CPM(OPEN,unit)<0) LHLD UNIT XCHG CALL BDOS ORA A JP FOPIF2 LHLD UNIT ; Freeio(unit) PUSH H CALL FREEIO POP H LXI H,NULL ; Return(NULL) RET ; FOPIF2: LHLD IP ; Ip[UNUSED] = 0 LXI D,UNUSED DAD D LXI D,0 MOV M,E INX H MOV M,D JMP FOPIF4 ; ; Else if(*mode=='w' || *mode=='W') ; FOPIF1: LHLD MODE MOV A,M CPI 77H ; 'w' JZ FOPIFA CPI 57H ; 'W' JNZ FOPIF5 ; FOPIFA: MVI C,DELETE ; Cpm(DELETE,unit) LHLD UNIT XCHG CALL BDOS MVI C,CREATE ; If(cpm(CREATE,unit)<0) LHLD UNIT XCHG CALL BDOS ORA A JP FOPIF3 LHLD UNIT ; Freeio(unit) PUSH H CALL FREEIO POP H LXI H,NULL ; Return(NULL) RET ; FOPIF3: LHLD IP ; Ip[UNUSED] = BUFSIZ LXI D,UNUSED DAD D LXI D,BUFSIZ MOV M,E INX H MOV M,D LHLD UNIT ; Unit[FLAG] = WRITE_FL LXI D,FLAG DAD D MVI A,WRTFLG ORA M MOV M,A JMP FOPIF4 ; FOPIF5: LHLD UNIT ; Else freeio(unit) PUSH H CALL FREEIO POP H LXI H,NULL ; Return(NULL) RET ; FOPIF4: LHLD UNIT ; Return(unit) RET ; ; FCLOSE (unit) ; FCLOSE: POP B POP H SHLD UNIT PUSH H PUSH B MOV A,H ; If (unit<256) ORA A ; /* assume stdin, stdout, etc. */ MVI L,0 RZ ; Return NULL LXI H,1 ; T = 1; SHLD ZT LHLD UNIT ; If(unit[FLAG] & WRITE_FL) LXI D,FLAG DAD D MOV A,M ANI WRTFLG JZ FCLIF1 LXI H,CTRLZ ; Putc(CTRL_Z,unit) PUSH H LHLD UNIT PUSH H CALL PUTC POP H POP H LHLD UNIT ; Ip = unit + FCBSIZE LXI D,FCBSIZE DAD D SHLD IP LHLD IP ; Cp = ip[NEXTP] LXI D,NEXTP DAD D MOV E,M INX H MOV D,M XCHG SHLD CHP LHLD IP ; Dp = &ip[BUFFER]+BUFSIZ LXI D,BUFFER+BUFSIZ DAD D SHLD DP ; ; While(cp'a'-'A') /* lower case? */ JC FCBIF2 SUI 61H-41H ; A -= 'a'-'A' JMP FCBIF2 ; FCBIF1: LDA DFLTDSK ; Else A = default_drive ; FCBIF2: STAX B ; *fp++ = A INX B MVI H,' ' ; Fp = fcbfill(fp,name,' ',8 MVI L,8 CALL FCBFILL MVI L,3 ; Fp = fcbfill(fp,name,' ',3 CALL FCBFILL MVI H,0 ; Fp = fcbpad(fp,0,4) MVI L,4 CALL FCBPAD LXI H,16 ; Fp[16] = 0 DAD B MVI M,0 RET ; Return; ; ; FCBFILL (dest,name,pad,size) ; B D H L ; FCBFILL:MOV A,L ; While(L>0 && (A= *D)~='.' && A~=0) ORA A JZ FILL2 LDAX D CPI '.' JZ FILL2 CPI 0 JZ FILL2 CPI 61H ; If(A>='a' && A<='z') JC FILL1 CPI 7AH+1 ; 'z' 9 JNC FILL1 SUI 61H-41H ; A = A - 'a' + 'A' ; FILL1: STAX B ; *B++ = A INX B INX D ; D++ DCR L ; L-- JMP FCBFILL ; FILL2: LDAX D ; While(*D~='.' && *D~=0) CPI '.' JZ FILL3 CPI 0 JZ FILL3 INX D ; D++ JMP FILL2 ; FILL3: CPI '.' ; If(*D=='.') JNZ FILL4 INX D ; D++ ; ; FCBPAD (dest,pad,size) ; B H L ; FILL4: FCBPAD: MOV A,L ; While(L>0) ORA A JZ PAD2 MOV A,H ; *B++ = H STAX B INX B DCR L ; L-- JMP FCBPAD ; PAD2: RET ; Return ; ; GETC (unit) ; FGETC: GETC: POP B POP H ; Get arguments PUSH H PUSH B ; ; C=cget(unit) ; PUSH H CALL CGET POP D MOV A,L ; If(c=='\r') ANI 7FH ; /* mask parity in compare */ CPI EOL JNZ GETCRET PUSH H ; Cget(unit) PUSH D ; /* to skip LF */ CALL CGET POP H POP H ; GETCRET:RET ; ; CGET (unit) ; CGET: POP D POP H PUSH H PUSH D MOV A,H ORA A ; If(unit < 256) JNZ CGET1 ; /* assume stdin */ CALL GETCHAR ; Getchar() POP D ; /* return to caller of getc() POP D ; To bypass CR check */ RET ; Return ; CGET1: SHLD UNIT LXI D,FLAG ; If(unit[FLAG] & EOF_FL) DAD D MOV A,M ANI EOFFLG JZ GTCIF1 LXI H,-1 ; Return(-1) RET ; GTCIF1: LHLD UNIT ; Ip = unit + FCBSIZE LXI D,FCBSIZE DAD D SHLD IP LXI D,NEXTP ; Cp = ip[NEXTP] DAD D MOV E,M INX H MOV D,M XCHG SHLD CHP LHLD IP ; If(ip[UNUSED]==0) LXI D,UNUSED DAD D MOV A,M INX H ORA M JNZ GTCIF2 LXI H,READ ; If(cpmio(READ,unit)~=0) PUSH H LHLD UNIT PUSH H CALL CPMIO POP D POP D MOV A,H ORA L JZ GTCIF3 LXI H,-1 ; Return(-1) RET ; GTCIF3: LHLD IP ; Else { ip[UNUSED] = BUFSIZ LXI D,UNUSED DAD D LXI D,BUFSIZ MOV M,E INX H MOV M,D LHLD IP ; Cp = &ip[BUFFER] LXI D,BUFFER DAD D SHLD CHP ; GTCIF2: LHLD IP ; Ip[UNUSED]-- LXI D,UNUSED DAD D MOV E,M INX H MOV D,M DCX D MOV M,D DCX H MOV M,E LHLD CHP ; Ip[NEXTP] = cp+1 INX H XCHG LHLD IP LXI B,NEXTP DAD B MOV M,E INX H MOV M,D LHLD CHP ; If(*cp==CTRL_Z) MOV A,M ANI 7FH ; /* mask parity in compare */ CPI CTRLZ JNZ GTCIF4 LHLD UNIT ; Unit[FLAG] |= EOF_FL LXI D,FLAG DAD D MOV A,M ORI EOFFLG MOV M,A LXI H,-1 ; Return(-1) RET ; GTCIF4: MOV A,M MOV L,A ; Return(*cp & 0377) MVI H,0 RET ; ; GETCHAR () ; GETCHAR:LHLD RSTDIN ; If(rdstdin >= 256) MOV A,H ; /* stdin has been redirected */ ORA A JZ GETCHR1 PUSH H CALL GETC ; Getc(rdstdin) POP B ; Return RET ; ; } else { /* read from console */ ; GETCHR1:MVI C,GETCH ; T = cpm(GETCH,0) & 0377 CALL BDOS MOV L,A MVI H,0 ANI 7FH ; /* mask parity in compare */ CPI CTRLZ ; If(t==CTRLZ) JNZ GET1CHAR LXI H,-1 ; T = -1 ; GET1CHAR: CPI EOL ; If(t==EOL) JNZ GET2CHAR PUSH H ; Putchar('\n') MVI C,PUTCH MVI E,LF CALL BDOS POP H ; GET2CHAR: ; Return(t) RET ; ; GETS (buff) ; GETS: POP B POP H PUSH H PUSH B PUSH H ; Buff LHLD RSTDIN ; If(rstdin >= 256) MOV A,H ORA A JZ GETS1 XCHG LXI H,80 PUSH H ; Len PUSH D ; Unit CALL FGETS ; Return(fgets(buff, 80, rstdin)) POP B POP B POP B RET ; GETS1: POP H ; } else { SHLD CHP DCX H ; Save = buff[-1]; save2 = buff[-2] MOV D,M ; Buff[-1] = 0; buff[-2] = 79 MVI M,0 DCX H MOV E,M MVI M,79 PUSH H PUSH D XCHG ; Cpm(GETSTR,buff-2) MVI C,GETSTR CALL BDOS LHLD CHP ; Buff[buff[-1]] = 0; (9 Jun 80. Was cp) DCX H MOV E,M INX H MVI D,0 DAD D MVI M,0 POP D ; Buff[-1] = save; buff[-2] = save2 POP H MOV M,E INX H MOV M,D INX H MVI C,PUTCH ; Putchar('\n') MVI E,LF CALL BDOS LHLD CHP ; Return(buff) RET ; } ; ; FGETS (cp,len,unit) ; FGETS: INX SP ; Skip return address INX SP POP B ; Unit POP D ; Length POP H ; Cp PUSH H PUSH D PUSH B DCX SP DCX SP MOV A,B ; If(unit < 256) ORA A ; /* assume stdin */ JNZ FGETS1 PUSH H CALL GETS ; Gets(cp) POP B ; Return (cp) RET ; } else { ; FGETS1: SHLD SVCHP ; Save_cp = cp PUSH D ; Keep stack right ; FGETS2: POP D DCX D ; While (--len) PUSH D MOV A,D ORA E JZ FGETS4 PUSH H ; Save cp PUSH B ; Unit CALL GETC ; C = getc(unit) POP B MOV A,H ; If(c==EOF) /* c>255 */ ORA A JZ FGETS3 POP D ; Cp LHLD SVCHP ; If (cp<>save_cp) XCHG ; /* read something */ MOV A,H CMP D JNZ FGETS4 ; Goto fgets4 MOV A,L CMP E JNZ FGETS4 ; Else LXI H,0 ; /* no characters */ POP D ; Fix stack RET ; Return (NULL) ; FGETS3: MOV A,L ; Else { POP H MOV M,A ; *cp++ = c INX H ANI 7FH ; /* mask parity in compare */ CPI LF ; If(c=='\n') JNZ FGETS2 ; FGETS4: MVI M,0 ; *cp='\0' POP D ; Fix stack LHLD SVCHP ; Return save cp RET ; ; PUTC (c,unit) ; FPUTC: PUTC: POP B ; Return address POP D ; Unit POP H ; C PUSH H PUSH D PUSH B MOV A,D ORA A ; If(unit < 256) JNZ PUTC4 ; /* assume stdout, stderr */ MOV A,E ; /* or stdlist. */ CPI STDOUT ; If(unit == stdout) JNZ PUTC1 PUSH H CALL PUTCHAR ; Putchar(c) POP H RET ; Return ; PUTC1: CPI STDERR ; Elseif(unit == stderr) JNZ PUTC2 CALL PUTCON ; Putconsole(c) RET ; Return ; PUTC2: CPI STDLIST ; Elseif(unit == stdlist) JNZ PUTC3 PUSH H CALL PUTLST ; Putlist(c) POP H RET ; Return ; PUTC3: JMP PTCER1 ; Else goto putcerr ; PUTC4: PUSH H ; If(cput(c,unit)<0) PUSH D ; Goto putcerr CALL CPUT POP D MOV A,H ORA A JM PUTCERR MOV A,L ; If(c=='\r') CPI EOL JNZ PUTCRET LXI H,LF ; Cput('\n',unit) PUSH H PUSH D CALL CPUT POP D POP D MOV A,H ORA A JM PUTCERR ; PUTCRET:POP H ; Return(c) RET ; PUTCERR:POP B ; Return(-1) ; PTCER1: LXI H,-1 RET ; ; PUTLIST (c) ; PUTLST: POP B POP D PUSH D PUSH B SHLD ZCH MVI C,LSTOUT ; Cpm(LSTOUT,c) CALL BDOS LDA ZCH CPI EOL ; If(c==EOL) JNZ PUTLS1 MVI E,LF ; Cpm(LSTOUT,LF) MVI C,LSTOUT CALL BDOS ; PUTLS1: LHLD ZCH ; Return(c & 0377) MVI H,0 RET ; ; CPUT (c,unit) ; CPUT: POP B POP D POP H PUSH H PUSH D PUSH B SHLD ZCH XCHG SHLD UNIT LXI D,FCBSIZE ; Ip = unit + FCBSIZE DAD D SHLD IP LXI D,NEXTP ; Cp = ip[NEXTP] DAD D MOV E,M INX H MOV D,M XCHG SHLD CHP LHLD IP ; If(ip[UNUSED]==0) LXI D,UNUSED DAD D MOV A,M INX H ORA M JNZ PTCIF1 LXI H,WRITE ; If(cpmio(WRITE,unit)~=0) PUSH H LHLD UNIT PUSH H CALL CPMIO POP D POP D MOV A,H ORA L JZ PTCIF2 LXI H,-1 ; Return(-1) RET ; PTCIF2: LHLD IP ; Else { ip[UNUSED] = BUFSIZ LXI D,UNUSED DAD D LXI D,BUFSIZ MOV M,E INX H MOV M,D LHLD IP ; Cp = &ip[BUFFER] LXI D,BUFFER DAD D SHLD CHP ; PTCIF1: LHLD IP LXI D,UNUSED ; Ip[UNUSED]-- DAD D MOV E,M INX H MOV D,M DCX D MOV M,D DCX H MOV M,E LHLD CHP ; Ip[NEXTP] = cp+1 INX H XCHG LHLD IP LXI B,NEXTP DAD B MOV M,E INX H MOV M,D LDA ZCH ; Return((*cp = c) & 0377) LHLD CHP MOV M,A MVI H,0 MOV L,A RET ; ; PUTCHAR (c) ; PUTCHAR:POP B POP H PUSH H PUSH B PUSH H LHLD RSTDOUT ; If(rdstdout >= 256) MOV A,H ; /* stdout has been redirected */ ORA A JZ PUTCHR1 PUSH H CALL PUTC ; Putc(c, rdstdout) POP B POP B ; Return RET ; PUTCHR1:POP H ; PUTCON: SHLD ZCH ; /* send to console */ XCHG ; Cpm(PUTCH,c) MVI C,PUTCH CALL BDOS LDA ZCH ; If(c==EOL) ANI 7FH ; /* mask parity in compare */ CPI EOL JNZ PUTCHIF1 MVI E,LF ; Cpm(PUTCH,LF) MVI C,PUTCH CALL BDOS ; PUTCHIF1: LHLD ZCH ; Return(c & 0377) MVI H,0 RET ; ; PUTS (cp) ; PUTS: POP B ; Get arguments POP H PUSH H PUSH B PUSH H ; Cp LHLD RSTDOUT MOV A,H ; If(rstdout >= 256) ORA A JZ PUTS1 PUSH H CALL FPUTS ; Return (fputs(cp, rstdout)) POP B POP B RET ; PUTS1: POP H ; } else { MOV A,M ; While(*cp) ORA A JZ PUTSRET MOV E,M ; Putchar(*cp++) INX H PUSH H MVI C,PUTCH CALL BDOS JMP PUTS1 ; PUTSRET:RET ; ; FPUTS (cp,unit) ; FPUTS: POP B POP D ; Unit POP H ; Cp PUSH H PUSH D PUSH B ; FPUTS1: MOV A,M ; While((c=*cp++) <> NULL) INX H ORA A JZ FPUTS3 PUSH H MOV C,A MVI B,0 PUSH B PUSH D CALL PUTC ; If(putc(c,unit)==EOF) POP D POP B MOV A,H ORA A JZ FPUTS2 POP B RET ; Return(EOF) ; FPUTS2: POP H JMP FPUTS1 ; FPUTS3: LXI H,0 RET ; Return(NULL) ; ; CPMIO (fn,unit) ; CPMIO: POP B POP D POP H SHLD FN XCHG SHLD UNIT PUSH D PUSH H PUSH B LHLD UNIT ; Cpmdisk(*unit) MOV L,M MVI H,0 PUSH H CALL CPMDISK POP H LHLD UNIT ; Ip = unit+FCBSIZE LXI D,FCBSIZE ; Cpm(DMA,&ip[BUFFER]) DAD D LXI D,BUFFER DAD D XCHG MVI C,DMA CALL BDOS LHLD FN ; T = cpm(fn,unit) MOV C,L LHLD UNIT XCHG CALL BDOS CALL CCSXT SHLD ZT MVI C,DMA ; Cpm(DMA,TBUFF) LXI D,TBUFF CALL BDOS LHLD ZT ; If(t~=0) return(-1) MOV A,H ; Else return(0) ORA L JNZ CPMIF1 LXI H,0 JMP CPMIF2 ; CPMIF1: LXI H,-1 ; CPMIF2: RET ; ; CPMDISK (disk) ; CPMDISK:POP D POP H PUSH H PUSH D MOV A,L ; If(d~=0) ORA H JZ DISKIF1 XCHG ; Cpm(SELECT,d-1) DCX D MVI C,SELECT CALL BDOS ; DISKIF1:RET ; ;------------------- end of small-C library ---------------------------- ;