/* * 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--; }