home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
simtel
/
sigm
/
vols200
/
vol224
/
iolib.c
< prev
next >
Wrap
Text File
|
1994-07-13
|
15KB
|
797 lines
/* name...
iolib
purpose...
to provide a "standard" interface between c
programs and the CPM I/O system.
notes...
Compile using -M option.
history...
31 Jul 84 fbuf is now a pointer rather than
an array, and the disk buffer is
allocated from the heap.
28 Jul 84 Prepended '_' to all global
variables that shouldn't be visible
elsewhere.
27 Jul 84 Permitting I/O redirection. err()
temporarily resets STDOUT to 1 so
error messages go to console.
26 Jul 84 ccgo is initializing fmode[] at run
time, so compiled code need not be
changed before assembly.
14 Jul 84 gets() emitting LF after reading input.
1 Jul 84 Declaring _dfltdsk in c rather than
assembly, so references from c don't
have to be corrected.
27 Jun 84 Changed ENDDATA to _END in alloc().
17 Jun 84 including iolib.h, so output can be
separately assembled.
14 Jun 84 Several bugs fixed, 'A' file mode
supported, putb() and getb() installed,
getc(1) comes from keyboard & putc(c,2)
goes to console.
29 Oct 83 Saving return addr in cpm().
13 Oct 83 QERR added from clibv.asm,
reformatted for ZMAC.
18 Sep 83 written (jrvz).
*/
#include iolib.h
#asm
BDOS = 5
CR = 13
; cpm(bc,de) int bc,de; BDOS call */
QCPM: POP HL
POP DE
POP BC
PUSH BC
PUSH DE
PUSH HL
CALL BDOS
JP CCSXT ;move A to HL & sign extend
;
; /* return address of a block of memory */
; alloc(b)
; int b; /* # bytes desired */
;
QALLOC: POP HL ;return addr
POP DE ;block size
PUSH DE
PUSH HL
LD HL,(HEAPTOP) ;current top of heap
EX DE,HL
ADD HL,DE ;hl=new top of heap
LD (HEAPTOP),HL
EX DE,HL ;hl=old top of heap
RET
HEAPTOP: DEFW _END
;
; /* reset the top of heap pointer to addr* */
;
; free(addr)
; int addr;
;
QFREE: POP DE
POP HL ;addr
PUSH HL
PUSH DE
LD (HEAPTOP),HL
RET
;
; /* return number of bytes between top of heap
; and end of TPA. Remember that this includes
; the stack! */
;
; avail()
;
QAVAIL: LD HL,(6) ;end of TPA
PUSH HL
LD HL,(HEAPTOP) ;top of heap
JP CCSUB ;find (6)-HEAPTOP
;
; error...print message & walkback trace (if available)
;
; err(s) char *s;
; { int str;
; puts("\nERROR ");
QERR: LD HL,(QSTDOUT)
LD (ERR6),HL
LD HL,1
LD (QSTDOUT),HL
LD HL,MSGE
PUSH HL
CALL QPUTS
POP HL
; puts(s);
POP DE
POP HL
PUSH HL
PUSH DE
PUSH HL
CALL QPUTS
POP HL
; str=current;
LD HL,(CURRENT)
; while(str)
ERR4: LD (ERR2),HL
LD A,H
OR L
JR Z,ERR5
; {puts("\ncalled by ");
LD HL,MSGE2
PUSH HL
CALL QPUTS
POP HL
; puts(*(str+1));
LD HL,(ERR2)
INC HL
INC HL
CALL CCGINT
PUSH HL
CALL QPUTS
POP HL
; str=*str;
LD HL,(ERR2)
CALL CCGINT
; }
JR ERR4
; }
ERR5: LD HL,(ERR6)
LD (QSTDOUT),HL
RET
;
MSGE: DB CR,'ERROR: ',0
MSGE2: DB CR,'CALLED BY ',0
ERR2: DW 0
ERR6: DW 0 ;temporary storage for STDOUT
CURRENT: DW 0
#endasm
#define LF 10
int _dfltdsk, /* "current disk" at beginning of execution */
stdin, /* 0 initially, or unit number for input file
if input has been redirected by args() */
stdout; /* 1 initially, or unit number for output file
if output has been redirected by args() */
getchar()
{ return getc(stdin);
}
putchar(c) char c;
{ putc(c,stdout);
return c;
}
gets(buf) char *buf; /* input a string (editing permitted) */
{ char s1,s2;
int i;
if(stdin) /* input has been redirected */
{i=80;
while(i--)
{s1=getc(stdin);
if((s1==-1)|(s1=='\n')) break;
*buf++=s1;
}
*buf=0;
}
else
{s2=buf[-2]; s1=buf[-1]; /* save 2 bytes */
buf[-2]=80; /* assumed string length */
cpm(10,buf-2);
buf[buf[-1]]=0; /* mark end using count left by cpm */
buf[-1]=s1; buf[-2]=s2; /* restore the bytes */
putchar('\l'); /* LF */
}
}
puts(buf) char *buf; /* print a null-terminated string */
{ char c;
while(c=*buf++) putchar(c);
}
#define NBUFS 3
/* = number of files which can be open at once */
/* NOTE: ccgo() must initialize this many */
/* elements of fmode[]. */
#define LGH 1024
/* length of each file buffer */
/* = some multiple of 128: 128, 256, 384, 512... */
#define BUFLGH 3171
/* =NBUFS*(LGH+33) */
/* used in ccgo() to allocate the disk buffer */
#define MFREE 11387
#define MREAD 22489
#define MWRITE 17325
#define MEOF -8734
char *_fbuf; /* for fcb's and disk buffers */
int _ffcb[NBUFS], /* pointers to the fcb's */
_fnext[NBUFS], /* pointers to the next char to be fed
to the program (for an input file) or
the next free byte in the buffer (for
an output file) */
_ffirst[NBUFS], /* ptrs to the starts of the buffers */
_flast[NBUFS], /* ptrs to the ends of the buffers */
_fmode[NBUFS], /* MFREE => buffer is free
MREAD => open for reading
MWRITE => open for writing
MEOF => was open for reading, but
EOF encountered
= MFREE initially */
_ex,_cr; /* extent & current record at beginning
of this buffer full (used for "A" access) */
fopen(name,mode) /* open file in fmode
"r", "w", or "a" (upper or lower case) */
char *name,*mode;
{ char c,*fcb;
int index,i,unit;
index=NBUFS;
while(index--) /* search for free buffer */
{if(_fmode[index]==MFREE)break;
}
if(index==-1)
{err("OUT OF DISK BUFFERS"); exit();
}
unit=index+5;
_ffcb[index]=_fbuf+index*(33+LGH);
_ffirst[index]=_ffcb[index]+33;
_flast[index]=_ffirst[index]+LGH;
fcb=_ffcb[index];
i=11; while(i) fcb[i--]=' '; /* clear file name */
fcb[12]=fcb[32]=0; /* clear ex & cr */
if (name[1]==':') /* transfer disk */
{fcb[0]= (*name&15) +1; /* either case */
name=name+2;
}
else fcb[0]=_dfltdsk;
while(c=upper(*name++)) /* transfer name */
{if(c=='.')break;
fcb[++i]=c;
}
if(c=='.') /* transfer extension */
{i=8;
while(c=upper(*name++)) {fcb[++i]=c;}
}
c=upper(*mode);
/* puts("OPENING FILE "); puts(fcb+1); putchar('\n');
puts("fcb at "); hex(fcb);
puts("\nbuffer "); hex(_ffirst[index]);
puts("through "); hex(_flast[index]); */
if((c=='R')|(c=='A'))
{if(cpm(15,fcb)<0)
{/* err("INPUT FILE DOESN\'T EXIST");*/
return 0; /* file not found */
}
_fmode[index]=MREAD; /* open for reading */
_fnext[index]=_flast[index];
/* forces immed. read */
if(c=='A') /* append mode requested? */
{while(getc(unit)!=-1)
{} /* read to EOF */
fcb[12]=_ex; /* reset to values at...*/
cpm(15,fcb); /* ...beginning of buffer */
fcb[32]=_cr;
_fmode[index]=MWRITE;
}
/* puts("\nnext char at "); hex(_fnext[index]); */
return unit;
}
else if(c=='W')
{cpm(19,fcb); /* delete file */
i=cpm(22,fcb); /* create file */
if(i<0) return 0; /* creation failure */
_fmode[index]=MWRITE; /* open for writing */
_fnext[index]=_ffirst[index];
/* buffer is empty */
/* puts("\nnext char at "); hex(_fnext[index]);*/
return unit;
}
else return 0;
}
fclose(unit) int unit; /* close a file */
{ int index,i,werror;
/* puts("\nfclose: closing unit "); hexb(unit); */
index=unit-5;
i=fchk(index);
if((i==MREAD)|(i==MEOF)) /* don't close read files */
{_fmode[index]=MFREE;
return 1; /* success */
}
putb(26,unit); /* append ^Z (CP/M EOF) */
werror=fflush(unit);
_fmode[index]=MFREE;
if((cpm(16,_ffcb[index])<0)|werror)
return 0; /* failure */
return 1; /* success */
}
fchk(index) int index; /* check for legal index */
{ int i;
if((index>=0)&(index<=NBUFS))
{i=_fmode[index];
if((i==MREAD)|(i==MWRITE)|(i==MEOF))
return i;
}
err("INVALID UNIT NUMBER");
exit();
}
getc(unit) int unit; /* get character from file
(return -1 at EOF) */
{ int c;
while((c=getb(unit))==LF){} /* discard LF */
if(c==26) /* CP/M EOF? */
{if(unit>=5) /* leave _fnext[index] pointing
at the ^Z */
{_fmode[unit-5]=MEOF;
--_fnext[unit-5];
}
return -1;
}
return c;
}
getb(unit) int unit; /* get byte from file
(return -1 at EOF) */
{ int index,mode,i;
char *next,*last,c,*fcb;
if(unit==0) /* STDIN */
{c=cpm(1,0);
if(c=='\n')cpm(2,LF); /* add LF after CR */
return c;
}
index=unit-5;
mode=fchk(index);
if(mode==MEOF) return -1; /* already found eof */
if(mode!=MREAD) {err("CAN\'T READ OUTFILE"); exit();}
next=_fnext[index];
if(next==_flast[index]) /* empty buffer? */
{fcb=_ffcb[index];
_ex=fcb[12]; _cr=fcb[32]; /* save for fopen() */
next=_ffirst[index];
last=next+LGH;
while(next<last)
{cpm(26,next); /* set DMA */
if(cpm(20,fcb))break;
next=next+128;
}
cpm(26,128); /* reset DMA */
if(next==_ffirst[index]) /* no records read? */
{/* _fmode[index]=MEOF; not needed */
return -1;
}
_flast[index]=next;
next=_ffirst[index];
}
c=*next++;
_fnext[index]=next;
return c;
}
putc(c,unit) char c; int unit; /* write a character to a file */
{ putb(c,unit);
if(c=='\n')putb(LF,unit); /* add LF after CR */
return c;
}
putb(c,unit) char c; int unit; /* write a byte to a file */
{ int index,werror;
char *next;
if(unit==1) /* STDOUT */
{cpm(2,c);
return c;
}
index=unit-5;
if(fchk(index)!=MWRITE)
{err("CAN\'T WRITE TO INFILE");
exit();
}
if(_fnext[index]==_flast[index]) werror=fflush(unit);
else werror=0;
next=_fnext[index];
*next++=c;
_fnext[index]=next;
if(werror) return werror;
return c;
}
fflush(unit) int unit; /* flush buffer to disk
(on error returns nonzero)*/
{ int index,i;
char *next,*going;
index=unit-5;
if(fchk(index)!=MWRITE)
{err("CAN\'T FLUSH INFILE");
exit();
}
/* puts("\nfflush: must write out "); hex(_ffirst[index]);
puts("through "); hex(_fnext[index]); */
next=_fnext[index];
going=_fnext[index]=_ffirst[index];
while(going<next)
{cpm(26,going); /* set DMA */
/* puts("\n writing from "); hex(going); */
if(cpm(21,_ffcb[index])) return -1; /* error? */
going=going+128;
}
cpm(26,128); /* reset DMA */
return 0; /* no error */
}
upper(c) int c; /* converts to upper case */
{ if(c>='a')return c-32;
return c;
}
exit()
{ if(stdout>=5)fclose(stdout);
#asm
JP 0
#endasm
}
#asm
;
; Runtime library initialization.
; Set up default drive for CP/M.
CCGO: LD C,25 ;get current disk
CALL BDOS
INC A ;now in range 1...16
LD (Q_DFLTDSK),A
LD HL,11387 ;initialize the elements of
LD (Q_FMODE),HL ;_fmode[] (# entries = NBUFS)
LD (Q_FMODE+2),HL ;to reflect free buffers
LD (Q_FMODE+4),HL
LD HL,(HEAPTOP) ;current top of heap
LD (Q_FBUF),HL ;initialize buffer pointer
LD DE,3171 ;= BUFLGH
ADD HL,DE ;hl=new top of heap
LD (HEAPTOP),HL
LD HL,0
LD (QSTDIN),HL ;initialize input unit
INC HL
LD (QSTDOUT),HL ;initialize output unit
RET
;Fetch a single byte from the address in HL and
; sign extend into HL
CCGCHAR: LD A,(HL)
QARGC:
CCSXT: LD L,A
RLCA
SBC A,A
LD H,A
RET
;Fetch integer from (HL+2)
CCCDR: INC HL
INC HL
;Fetch a full 16-bit integer from the address in HL
CCCAR:
CCGINT: LD A,(HL)
INC HL
LD H,(HL)
LD L,A
RET
;Store a 16-bit integer in HL at the address in TOS
CCPINT: POP BC
POP DE
PUSH BC
LD A,L
LD (DE),A
INC DE
LD A,H
LD (DE),A
RET
;Inclusive "or" HL and TOS into HL
CCOR: POP BC
POP DE
PUSH BC
LD A,L
OR E
LD L,A
LD A,H
OR D
LD H,A
RET
;Exclusive "or" HL and TOS into HL
CCXOR: POP BC
POP DE
PUSH BC
LD A,L
XOR E
LD L,A
LD A,H
XOR D
LD H,A
RET
;"And" HL and TOS into HL
CCAND: POP BC
POP DE
PUSH BC
LD A,L
AND E
LD L,A
LD A,H
AND D
LD H,A
RET
;Test if HL = TOS and set HL = 1 if true else 0
CCEQ: POP BC
POP DE
PUSH BC
CALL CCCMP
RET Z
DEC HL
RET
;Test if TOS ~= HL
CCNE: POP BC
POP DE
PUSH BC
CALL CCCMP
RET NZ
DEC HL
RET
;Test if TOS > HL (signed)
CCGT: POP BC
POP DE
PUSH BC
EX DE,HL
CALL CCCMP
RET C
DEC HL
RET
;Test if TOS <= HL (signed)
CCLE: POP BC
POP DE
PUSH BC
CALL CCCMP
RET Z
RET C
DEC HL
RET
;Test if TOS >= HL (signed)
CCGE: POP BC
POP DE
PUSH BC
CALL CCCMP
RET NC
DEC HL
RET
;Test if TOS < HL (signed)
CCLT: POP BC
POP DE
PUSH BC
CALL CCCMP
RET C
DEC HL
RET
;Common routine to perform a signed compare
; of DE and HL
;This routine performs DE - HL and sets the conditions:
; Carry reflects sign of difference (set means DE < HL)
; Zero/non-zero set according to equality.
CCCMP: LD A,E
SUB L
LD E,A
LD A,D
SBC A,H
LD HL,1 ;preset true condition
JP M,CCCMP1
OR E ;"OR" resets carry
RET
CCCMP1: OR E
SCF ;set carry to signal minus
RET
;
;Test if TOS >= HL (unsigned)
CCUGE: POP BC
POP DE
PUSH BC
CALL CCUCMP
RET NC
DEC HL
RET
;
;Test if TOS < HL (unsigned)
CCULT: POP BC
POP DE
PUSH BC
CALL CCUCMP
RET C
DEC HL
RET
;
;Test if TOS > HL (unsigned)
CCUGT: POP BC
POP DE
PUSH BC
EX DE,HL
CALL CCUCMP
RET C
DEC HL
RET
;
;Test if TOS <= HL (unsigned)
CCULE: POP BC
POP DE
PUSH BC
CALL CCUCMP
RET Z
RET C
DEC HL
RET
;
;Common routine to perform unsigned compare
;carry set if DE < HL
;zero/nonzero set accordingly
CCUCMP: LD A,D
CP H
JP NZ,CUCMP1
LD A,E
CP L
CUCMP1: LD HL,1
RET
;
;Shift DE arithmetically right by HL and return in HL
CCASR: EX DE,HL
DEC E
RET M ; 7/2/82 jrvz
LD A,H
RLA
LD A,H
RRA
LD H,A
LD A,L
RRA
LD L,A
JP CCASR+1
;Shift TOS arithmetically left by HL and return in HL
CCASL: POP BC
POP DE
PUSH BC
EX DE,HL
CCASL4: DEC E
RET M ; jrvz 7/2/82
ADD HL,HL
JP CCASL4
;Subtract HL from TOS and return in HL
CCSUB: POP BC
POP DE
PUSH BC
LD A,E
SUB L
LD L,A
LD A,D
SBC A,H
LD H,A
RET
;Form the two's complement of HL
CCNEG: CALL CCCOM
INC HL
RET
;Form the one's complement of HL
CCCOM: LD A,H
CPL
LD H,A
LD A,L
CPL
LD L,A
RET
;Multiply TOS by HL and return in HL
CCMULT: POP BC
POP DE
PUSH BC
LD B,H
LD C,L
LD HL,0
CCMLT1: LD A,C
RRCA
JP NC,CMLT2
ADD HL,DE
CMLT2: XOR A
LD A,B
RRA
LD B,A
LD A,C
RRA
LD C,A
OR B
RET Z
XOR A
LD A,E
RLA
LD E,A
LD A,D
RLA
LD D,A
OR E
RET Z
JP CCMLT1
;Divide DE by HL and return quotient in HL, remainder in DE
CCDIV: LD B,H
LD C,L
LD A,D
XOR B
PUSH AF
LD A,D
OR A
CALL M,CCDENEG
LD A,B
OR A
CALL M,CCBCNEG
LD A,16
PUSH AF
EX DE,HL
LD DE,0
CCDIV1: ADD HL,HL
CALL CCRDEL
JP Z,CCDIV2
CALL CCPBCDE
JP M,CCDIV2
LD A,L
OR 1
LD L,A
LD A,E
SUB C
LD E,A
LD A,D
SBC A,B
LD D,A
CCDIV2: POP AF
DEC A
JP Z,CCDIV3
PUSH AF
JP CCDIV1
CCDIV3: POP AF
RET P
CALL CCDENEG
EX DE,HL
CALL CCDENEG
EX DE,HL
RET
CCDENEG: LD A,D
CPL
LD D,A
LD A,E
CPL
LD E,A
INC DE
RET
CCBCNEG: LD A,B
CPL
LD B,A
LD A,C
CPL
LD C,A
INC BC
RET
CCRDEL: LD A,E
RLA
LD E,A
LD A,D
RLA
LD D,A
OR E
RET
CCPBCDE: LD A,E
SUB C
LD A,D
SBC A,B
RET
#endasm
/*
hex(x) int x;
{ hexb(x>>8); hexb(x); putchar(' ');
}
hexb(x) int x;
{ hexn(x>>4); hexn(x);
}
hexn(x) int x;
{ x=x&15;
if(x<10) putchar(x+'0');
else putchar(x-10+'a');
}
*/