/*
* This file is part of the CC8 OS/8 C compiler.
*
* The CC8 OS/8 C compiler is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* The CC8 OS/8 C compiler is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the CC8 OS/8 C compiler as ../GPL3.txt. If not, see
* <http://www.gnu.org/licenses/>.
*/
/*
* PDP8/E LIBC routines for Small-C compiler.
*
* This is a complex collection of mixed C and SABR assembly routines.
* Some functions have been substantially shortened to save space
* relative to the original versions. Over time, we expect to rewrite
* the remaining pure C routines in hand-optimized SABR.
*/
#asm
// DECLARE LIBC GLOBALS. ALTHOUGH THESE OVERLAP THOSE DECLARED IN
// INIT.H AND HEADER.SB, THOSE ARE IN A DIFFERENT FIELD AND BELONG
// TO CODE IN THAT FIELD. THERE IS NO PARTICULAR NEED FOR THESE
// VARIABLES TO MATCH UP TO THOSE IN ANY WAY. CODE IN NEITHER FIELD
// MODIFIES THE OTHER'S GLOBALS.
/ 000 AND 001 BELONG TO THE PDP-8 INTERRUPT SYSTEM
/ 002 THRU 007 ARE RESERVED FOR USER CODE
/ 010-017 ARE THE PDP-8 AUTO-INDEX REGISTERS; YES, IN ALL FIELDS!
/ 020-177 BELONG TO [F]PRINTF; SEE BELOW
ABSYM POP 147
ABSYM PSH 150
ABSYM JLC 151
ABSYM STKP 152
ABSYM PTSK 153
ABSYM POPR 154
ABSYM PCAL 155
ABSYM TMP 156
ABSYM GBL 157
ABSYM ZTMP 146
ABSYM ZPTR 145
ABSYM ZCTR 144
ABSYM FPTR 160
/
/
/
/
/
DUMMY ARGST
DUMMY ARGNM
ARGST, BLOCK 2
ARGNM, BLOCK 2
/
ENTRY LIBC
LIBC, BLOCK 2
CLA CLL
TAD I LIBC
DCA ARGST
INC LIBC#
TAD I LIBC
DCA ARGST#
INC LIBC#
TAD I ARGST
DCA STKP
IAC
TAD LCALL / INIT ? LCALL==-1
SZA CLA
JMP LB1
TAD STKP
DCA GBL / SET LOCAL GBL(LITPTR)
ISZ GBL
TAD PVL
DCA PSH
TAD OVL
DCA POP
TAD MVL
DCA PTSK
TAD PVR
DCA POPR
TAD PVC
DCA PCAL
RIF
TAD (6201 / BUILD CDF + IF INSTR...
DCA PCL1 / ...AND SAVE AS FIRST OF PCL1 SUBROUTINE
TAD PCL1
DCA DCC0
JMS MCC0
TAD STKP
DCA I ARGST / UPDATE MASTER STKP
DCA ZPTR / INIT PRINTF FLAG
DCA FPTR / INIT FPRINTF FLAG
LB1, ACL / CALL INDEX IN MQ
SPA
JMP LRET
TAD CPNT
DCA LCALL
TAD I LCALL
DCA LCALL
JMSI PCAL
LCALL, -1
RETRN LIBC
LRET, CLA MQL
DCA LCALL / INIT OK
RETRN LIBC
/
PUSH, 0
CDF1
ISZ STKP
DCAI STKP
TADI STKP
JMPI PUSH
PPOP, 0
CDF1
DCA TMP
TADI STKP
MQL
CMA
TAD STKP
DCA STKP
TAD TMP
JMPI PPOP
PUTSTK, 0
JMSI POP
SWP
DCA JLC
SWP
DCAI JLC
TADI JLC
JMPI PUTSTK
POPRET, JMSI POP
SWP
DCA ZTMP
TAD STKP
DCA I ARGST / UPDATE MASTER STKP
SWP
CDF1
JMPI ZTMP
PCALL, 0
CLA CLL
PCL1, 0
TADI PCALL
DCA ZTMP
TAD PCALL
IAC
JMSI PSH / PUSH RETURN
CLA
TAD STKP
DCA I ARGST / UPDATE MASTER STKP
CDF1
JMPI ZTMP
PVL, PUSH
OVL, PPOP
MVL, PUTSTK
PVR, POPRET
PVC, PCALL
/
CPNT, CLIST
CPAGE 41 / # OF ENTRIES IN CLIST BELOW, IN OCTAL
/
/ THIS IS THE DISPATCH LIST FOR THIS LIBRARY
/ MAKE SURE LIBC.H MATCHES
/
CLIST, ITOA
PUTS
DISPXY
GETC
GETS
ATOI
STRPD
XINIT
MEMCPY
KBHIT
PUTC
STRCPY
STRCAT
STRSTR
EXIT
ISNUM
ISALPHA
TOUPPER
MEMSET
FGETC
FOPEN
FPUTC
FCLOSE
REVCPY
ISALNUM
ISSPACE
FGETS
FPUTS
STRCMP
CUPPER
FPRINTF
PRINTF
SPRINTF
SSCANF
SCANF
FSCANF
#endasm
#define stdout 0
#define NULL 0
#define isdigit isnum
fgetc()
{
#asm
CLA CLL
CALL 2,CHRIO
ARG (-4
ARG FRSL
TAD FRSL
TAD (D-26 /^Z
SNA CLA
DCA FRSL
TAD FRSL
CDF1
JMPI POPR
FRSL,BLOCK 2
// CHRIO - CHARACTER I/O.
/
/ CALL CHRIO(IDEVN,ICHAR)
/
/ IDEV = FORT II DEVICE NUMBER.
/
/ ICHAR = 7 OR 8 BIT CHARACTER.
/
/ IF IDEV IS POSITIVE, THE CHAR IS OUTPUTTED.
/
/ IF IDEV IS NEGATIVE, THE NEXT CHAR IS
/ READ FROM THE DEVICE, AND PUT IN ICHAR.
/
//
ENTRY CHRIO
CHRIO, BLOCK 2
JMS GETP
SPA /WHAT IS DEVICE SIGN?
JMP RCHAR /NEG DEV. MEANS READ.
JMS SETDEV /POS DEV. MEANS WRITE.
0000
JMS GETP
DCA ICHAR
JMS CHSUB
JMP XIT
IDEV, 0
ICHAR, 0
ADDR, 0
RCHAR, CIA /READ A CHAR.
JMS SETDEV
2000 /SET BIT FOR READ. (8 UNITS NOW!)
JMS GETP
CLA
TAD CDFB
DCA CDFCH
JMS CHSUB
CDFCH, HLT
AND (177 / 7 BIT FOR NOW
DCAI ADDR
XIT, CLA
RETRN CHRIO
SETDEV, 0
TAD (-1
AND (7
CLL RAR;RTR;RTR
TAD I SETDEV
INC SETDEV
DCA IDEV
JMP I SETDEV
CHSUB, 0
TAD ICHAR
AND (377 / DEAL IN 8 BIT CHARS, MAX
TAD IDEV
CALL 0,GENIO
JMP I CHSUB
GETP, 0
TAD CHRIO
DCA CDFA
CDFA, HLT
TADI CHRIO#
DCA CDFB
INC CHRIO#
TADI CHRIO#
DCA ADDR
INC CHRIO#
CDFB, HLT
TADI ADDR
JMP I GETP
#endasm
}
fputc(ch)
int ch;
{
ch;
#asm
DCA FRSL
CALL 2,CHRIO
ARG (4
ARG FRSL
CDF1
TAD FRSL
#endasm
}
sixbit(p)
char *p;
{
*p++;
#asm
AND (77 / MASK OFF LOWER 6 BITS
BSW
MQL
#endasm
*p;
#asm
AND (77
MQA
#endasm
}
fputs(p)
int *p;
{
while (*p++)
#asm
DCA FRSL
CALL 2,CHRIO
ARG (4
ARG FRSL
CDF1
#endasm
}
xinit()
{
puts("PDP-8 C Compiler V2.0:\r\n");
}
memcpy(dst,src,cnt)
int dst,src,cnt;
{
#asm
CLA
TAD STKP
TAD (-4
DCA 14
CMA
TADI 14
DCA 13
CMA
TADI 14
DCA 12
TADI 14
CIA
DCA ZTMP
CP1, TADI 12
DCAI 13
ISZ ZTMP
JMP CP1
#endasm
}
kbhit()
{
#asm
CLA CMA
KSF
CLA
#endasm
}
fopen (fnm,flg)
char *fnm;
int flg;
{
char *p;
p=fnm;
p=strstr(fnm,".");
if (p==0)
return(-1);
if (*flg=='w') {
#asm
CLA
TAD FC1#
DCA FBSE#
JMP FC3
FC1, CALL 0,OOPEN
FC2, CALL 0,IOPEN
#endasm
}
if (*flg=='r') {
#asm
CLA
TAD FC2#
DCA FBSE#
FC3, CDF1
#endasm
*p++=0;
sixbit(p);
#asm
PAGE
DCA ZTMP
TAD FC2# / CODE
AND (77
TAD (200
DCA FDCT
CDF0
TADI FDCT
DCA FEX1
TAD FDCT
TAD (100
DCA FDCT
TADI FDCT
TAD (121 / OFFSET OF EXTENSION (FILEEX) IN IOPEN CODE
DCA FDCT
FEX1, HLT
TAD ZTMP
DCAI FDCT
CDF1
#endasm
fnm;
#asm
DCA ZTMP / PACK 6 8BIT CHARS INTO FILENAME
TAD (-3
DCA FDCT
TAD FDCA
DCA FP4
FP1, CAM
TADI ZTMP
SNA
JMP FP2
AND (77 / MASK OFF LOWER 7 BITS
BSW
MQL
ISZ ZTMP
FP2, TADI ZTMP / WILL USE STACK FIELD
AND (77
SZA
ISZ ZTMP
MQA
FP4, DCA FFNM
ISZ FP4
ISZ FDCT
JMP FP1
TAD (56 / ASCII '.'
DCAI ZTMP / PUT . BACK INTO FNM
CLA CLL CMA
TAD STKP
DCA STKP
FBSE, CALL 2,IOPEN
ARG FDEV
ARG FFNM
JMPI POPR
FDCA, DCA FFNM
FDCT, 0
FFNM, TEXT /TM@@@@/
FDEV, TEXT /DSK@@@/
#endasm
}
}
fclose()
{
#asm
CALL 0,OCLOS
#endasm
}
puts(p)
char *p;
{
while (*p++)
#asm
TLS
XC1, TSF
JMP XC1
#endasm
}
dispxy(x,y)
int x,y;
{
x; /* put x param in AC */
#asm
DILX / load x into display X reg
#endasm
y;
#asm
DILY / load y into display Y reg
DIXY / pulse display at loaded X,Y coordinate
#endasm
}
getc()
{
#asm
CLA CLL
GT1, KSF
JMP GT1
KRB
TAD (D-254
CLA
KRB
SNL / DO NOT ECHO BS
TLS
TAD (D-131 / ? ^C
SNA CLA
JMP OSRET
KRB
AND (177 / 7 BITS!
#endasm
}
gets(p)
char *p;
{
int q,tm;
tm=1;
q=p;
while (tm) {
getc();
#asm
AND (177
TAD (D-13 / CR IS END OF STRING -> 0
SZA
TAD (D13
DCAI STKP
#endasm
if (tm-127) /* Handle BS */
*p++=tm;
else
if (p-q) {
puts("\b \b");
p--;
}
}
putc(10); /* newline */
return q;
}
atoi(p,rsl)
char *p;
int *rsl;
{
*p;
#asm
OPDEF MUY 7405
CLA CLL
DCA ZTMP / FINAL VALUE
DCA ZCTR / CAHR COUNTER
AT777, TADI JLC
TAD(-40 / SPACE+1
SZA CLA
JMP AT000
ISZ JLC
JMP AT777
AT000, TAD (7000 / NOP
DCA TMP / STORE SIGN
TADI JLC
TAD (-55 / -
SZA CLA
JMP AT001
TAD (7041 / CIA
DCA TMP
ISZ JLC
ISZ ZCTR
AT001, TAD (12 / DEFAULT BASE 10
DCA FPTR
TADI JLC
TAD (-60 / 0
SZA CLA
JMP AT004
TAD (10
DCA FPTR
ISZ JLC
ISZ ZCTR
AT002, TADI JLC
TAD (-170 / LC X
SZA CLA
JMP AT003
TAD (20
DCA FPTR
ISZ JLC
ISZ ZCTR
AT003, TADI JLC
TAD (-142 / LC B
SZA CLA
JMP AT004
CLA CLL IAC RAL
DCA FPTR
ISZ JLC
ISZ ZCTR
AT004, TADI JLC
SNA
JMP AT006
TAD (-60 / 0
SPA
JMP AT006
TAD (-12 / 10
SMA
JMP AT005
TAD (12
JMP AT51 / RANGE 0-9
AT005, TAD (-47 / LC A-F 0-5
SPA
JMP AT006
TAD (12
AT51, DCA ZPTR
TAD FPTR
CIA
TAD ZPTR
SMA CLA
JMP AT006
TAD ZTMP
CALL 1,MPY
ARG FPTR
CDF1
TAD ZPTR
DCA ZTMP
ISZ JLC
ISZ ZCTR
JMP AT004
AT006, CLA
TAD TMP
DCA XINV
CLA CLL CMA
TAD STKP
DCA TMP
TADI TMP
DCA TMP
TAD ZTMP
XINV, NOP
DCAI TMP
DCA FPTR
DCA ZPTR
TAD ZCTR
#endasm
;
}
putc(p)
char p;
{
p;
#asm
TLS
MP1, TSF
JMP MP1
#endasm
}
strcmp( dm , sm )
char *dm,*sm;
{
int rsl;
rsl=0;
while (*dm)
rsl|=(*sm++-*dm++);
return rsl;
}
strcpy( dm , sm )
char *dm,*sm;
{
while (*dm++=*sm++);
}
strcat( dm , sm )
char *dm,*sm;
{
int qm;
qm=dm;
while(*dm) dm++;
strcpy(dm,sm);
return qm;
}
strstr ( s , o )
char *s , *o ;
{
char *x , *y , *z ;
for ( x = s ; * x ; x ++ ) {
for ( y = x , z = o ; * z && * y == * z ; y ++ ) z ++ ;
if ( z >= o && ! * z ) return x ;
} return 0 ;
}
exit(retval)
int retval;
{
#asm
OSRET, CALL 0,EXIT
HLT
#endasm
}
isalnum(vl)
int vl;
{
return (isnum(vl) + isalpha(vl));
}
isnum(vl)
int vl;
{
vl;
#asm
TAD (D-48 / ASCII '0'
SPA
JMP XNO
TAD (D-10 / # OF DECIMAL DIGITS
SMA CLA
XNO, CLA SKP
CMA
#endasm
}
isspace(vl)
int vl;
{
vl;
#asm
SNA
JMP YNO
TAD (D-33 / ONE PAST ASCII ' '
SMA CLA
YNO, CLA SKP
CMA
#endasm
}
isalpha(vl)
int vl;
{
vl; /* Include '?' and '@' as alpha vars */
#asm
TAD (D-65 / ASCII 'A'
SPA
JMP ANO
TAD (D-26 / # OF UPPERCASE ENGLISH LETTERS
SPA
JMP BNO
TAD (D-6 / 'a' - 'Z' IN ASCII
SPA
JMP ANO
TAD (D-26 / # OF LOWERCASE ENGLISH LETTERS
BNO, SMA CLA
ANO, CLA SKP
CMA
#endasm
}
cupper(p) /* In place convert to uppercase */
int p;
{
p;
#asm
DCA ZTMP
CPP1, CLA
TADI ZTMP
SNA
JMP CPP2
TAD (D-97 / ASCII 'a'
SPA
JMP CPP3
TAD (D-26 / # OF LOWERCASE ENGLISH LETTERS
SMA
JMP CPP3
TAD (D91 / 97 + 26 - 91 = 32 = ('a' - 'A')
DCAI ZTMP
CPP3, ISZ ZTMP
JMP CPP1
CPP2,
#endasm
}
toupper(p)
int p;
{
p;
#asm
TPP1, DCA ZTMP / AALT ENTRY USED BY ATOI
TAD ZTMP
TAD (D-97 / SEE cupper() COMMENTARY
SPA
JMP TPP3
TAD (D-26
SMA
JMP TPP3
TAD (D91
JMP TPP2
TPP3, CLA CLL
TAD ZTMP
TPP2,
#endasm
}
strpd(buff,sym)
char *buff,*sym;
{
strcpy(buff," "); /* 9 spaces */
while (*sym)
*buff++=*sym++;
}
/* Arbitrary fgets(). Read until LF, CR/LF are retained*/
/* EOF returns null, else strlen(*p) */
fgets(p)
char *p;
{
char *q;
q=p;
while(*p=fgetc()) {
if (*p++==10)
break;
}
*p=0;
return (p-q);
}
memset(dst, dt, sz)
char *dst;
int dt,sz;
{
#asm
CLA
TAD STKP
TAD (-4
DCA 14
CMA
TADI 14
DCA 13
TADI 14
DCA 12
TADI 14
CIA
DCA ZTMP
CP2, TAD 12
DCAI 13
ISZ ZTMP
JMP CP2
#endasm
}
/*
** reverse string in place
*/
reverse(s) char *s; {
char *j;
int c;
j = s + strlen(s) - 1;
while(s < j) {
c = *s;
*s++ = *j;
*j-- = c;
}
}
/*
This is somewhat involved in that the vararg system in SmallC is
rather limited.
For printf and fprintf, we pass a static buffer to sprintf(), which
isn't formally allocated at the SABR level. We use just locations
10020-10170, (104 chars + NUL terminator) which is otherwise unused.
The first 20 (octal) locations and the last several on this page are
taken: see the ABSYM declarations the top of of this file.
LOADER also reserves this space for itself, placing a small library
of routines here, but none of the code in this module calls them,
so we can safely stomp over them. (They're used by FORTRAN II.)
*/
fprintf(nxtarg) int nxtarg;
{
#asm
ISZ FPTR
JMP PRINTF
#endasm
}
printf(nxtarg) int nxtarg;
{
#asm
TAD (K20 / SEE BLOCK COMMENT ABOVE
DCA ZPTR / SPRINTF EXPECTS ITS BUFFER LOCATION IN ZPTR
JMP SPRINTF
#endasm
}
/*
** sprintf(obfr, ctlstring, arg, arg, ...)
** Called by printf().
*/
sprintf(nxtarg) int nxtarg; {
int arg, left, pad, cc, len, maxchr, width;
char *ctl, *sptr, str[17],*obfr,zptr;
#asm
TAD ZPTR
DCAI STKP / POINTS TO ZPTR
#endasm
cc = 0;
nxtarg = &nxtarg-nxtarg;
if (zptr)
obfr=zptr;
else
obfr = *nxtarg++;
ctl = *nxtarg++;
while(*ctl) {
if(*ctl!='%') {*obfr++=*ctl++; ++cc; continue;}
else ++ctl;
if(*ctl=='%') {*obfr++=*ctl++; ++cc; continue;}
if(*ctl=='-') {left = 1; ++ctl;} else left = 0;
if(*ctl=='0') pad = '0'; else pad = ' ';
width=0;
if(isdigit(*ctl)) {
ctl+=atoi(ctl, &width);
}
maxchr=0;
if(*ctl=='.') {
ctl+=atoi(++ctl,&maxchr)+1;
}
arg = *nxtarg++;
sptr = str;
switch(*ctl++) {
case 'c': str[0] = arg; str[1] = NULL; break;
case 's': sptr = arg; break;
case 'd': itoa(arg,str,10); break;
case 'b': itoab(arg,str,2); break;
case 'o': itoab(arg,str,8); break;
case 'u': itoab(arg,str,10); break;
case 'x': itoab(arg,str,16); break;
case 'X': itoab(arg,str,16); cupper(str); break;
default: return -1;
}
len = strlen(sptr);
if(maxchr && maxchr<len) len = maxchr;
if(width>len) width = width - len; else width = 0;
if(!left) while(width--) {*obfr++=pad; ++cc;}
while(len--) {*obfr++=*sptr++; ++cc; }
if(left) while(width--) {*obfr++=pad; ++cc;}
}
*obfr=0;
zptr;
#asm
SNA / IF ZPTR, EITHER USE PUTS OR FPUTS
JMP PF1
JMSI PSH
CLA
TAD FPTR
SNA CLA
JMP PF2
JMSI PCAL
FPUTS
JMP PF3
PF2, JMSI PCAL
PUTS
PF3, JMSI POP
PF1, CLA
DCA ZPTR
DCA FPTR
#endasm
return(cc);
}
/*
** itoa(n,s,r) - Convert n to numeric string form in s, radix r
*/
itoa(n, s, r) char *s; int n; int r; {
char *ptr;
ptr = s;
if (r == 10 && n < 0) {
n = -n;
*ptr++='-';
}
itoab(n,ptr,r);
}
/*
** itoab(n,s,b) - Convert "unsigned" n to characters in s using base b.
** NOTE: This is a non-standard function.
*/
itoab(n, s, b) int n; char *s; int b; {
char *ptr;
int lowbit;
ptr = s;
b >>= 1;
do {
lowbit = n & 1;
n = (n >> 1) & 4095;
*ptr = ((n % b) << 1) + lowbit;
if(*ptr < 10) *ptr += '0'; else *ptr += 87; /* 87 == 'a' - 10 */
++ptr;
} while(n /= b);
*ptr = 0;
reverse (s);
}
strlen(p)
char *p;
{
#asm
DCA TMP
#endasm
while (*p++)
#asm
ISZ TMP
#endasm
#asm
TAD TMP
#endasm
}
fscanf(nxtarg) int nxtarg;
{
fgets(16); /* USE PRINTF BUFFER FOR INPUT STRING */
#asm
JMP SC1
#endasm
}
scanf(nxtarg) int nxtarg;
{
gets(16); /* USE PRINTF BUFFER FOR INPUT STRING */
#asm
SC1, CLA CLL
TAD (20
DCA ZPTR / FOR FSCANF,SCANF, BUFFER LOCATION IN ZPTR = 20 (8)
JMP SSCANF
#endasm
}
#define EOF 0
sscanf(nxtarg) int nxtarg; {
char *ctl;
int u;
int *narg, ac, width, ch, cnv, base, ovfl, sign, *ibfr,zptr;
#asm
TAD ZPTR
DCAI STKP / POINTS TO ZPTR
#endasm
ac = 0;
nxtarg = &nxtarg-nxtarg;
if (zptr)
ibfr=zptr;
else
ibfr = *nxtarg++;
ctl = *nxtarg++;
while(*ctl) {
if(*ctl++ != '%') continue;
narg = *nxtarg++;
ctl += atoi(ctl, &width);
if (!width)
width=-1;
if(!(cnv = *ctl++)) break;
switch(cnv) {
case 'c':
*narg = *ibfr++;
break;
case 's':
while(width--)
if((*narg++ = *ibfr++) == 0) break;
*narg = 0;
break;
default:
switch(cnv) {
case 'b': base = 2; break;
case 'd': base = 10; break;
case 'o': base = 8; break;
case 'x': base = 16; break;
default: return (ac);
}
*narg = u = 0;
sign = 1;
while (isspace(*ibfr))
ibfr++;
while(width-- && (ch=*ibfr++)>32) {
if(ch == '-') {sign = -1; continue;}
if(ch < '0') break;
if(ch >= 'a') ch -= 87;
else if(ch >= 'A') ch -= 55;
else ch -= '0';
u = u * base + ch;
}
*narg = sign * u;
}
++ac;
}
#asm
CLA
DCA ZPTR / CLEAR FLAGS
DCA FPTR
#endasm
return (ac);
}
revcpy(dst,src,cnt)
int *dst,*src,cnt;
{
dst+=cnt;
src+=cnt;
while (cnt--)
*dst--=*src--;
}