% This file is part of the MMIXware package (c) Donald E Knuth 1999 @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES! \def\title{MMIX-IO} \def\MMIX{\.{MMIX}} \def\Hex#1{\hbox{$^{\scriptscriptstyle\#}$\tt#1}} % experimental hex constant @*Introduction. This program module contains brute-force implementations of the ten input/output primitives defined at the beginning of {\mc MMIX-SIM}. The subroutines are grouped here as a separate package, because they are intended to be loaded with the pipeline simulator as well as with the simple simulator. @^I/O@> @^input/output@> @c @@; @@; @@; @@; @@; @ Of course we include standard \CEE/ library routines, and we set things up to accommodate older versions of \CEE/. @= #include #include #ifdef __STDC__ #define ARGS(list) list #else #define ARGS(list) () #endif #ifndef FILENAME_MAX #define FILENAME_MAX 256 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef SEEK_END #define SEEK_END 2 #endif @ The unsigned 32-bit type \&{tetra} must agree with its definition in the simulators. @= typedef unsigned int tetra; typedef struct {tetra h,l;} octa; /* two tetrabytes make one octabyte */ @ Three basic subroutines are used to get strings from the simulated memory and to put strings into that memory. These subroutines are defined appropriately in each simulator. We also use a few subroutines and constants defined in {\mc MMIX-ARITH}. @= extern char stdin_chr @,@,@[ARGS((void))@]; extern int mmgetchars @,@,@[ARGS((char* buf,int size,octa addr,int stop))@]; extern void mmputchars @,@,@[ARGS((unsigned char* buf,int size,octa addr))@]; extern octa oplus @,@,@[ARGS((octa,octa))@]; extern octa ominus @,@,@[ARGS((octa,octa))@]; extern octa incr @,@,@[ARGS((octa,int))@]; extern octa zero_octa; /* |zero_octa.h=zero_octa.l=0| */ extern octa neg_one; /* |neg_one.h=neg_one.l=-1| */ @ Each possible handle has a file pointer and a current mode. @= typedef struct { FILE *fp; /* file pointer */ int mode; /* [read OK] + 2[write OK] + 4[binary] + 8[readwrite] */ } sim_file_info; @ @= sim_file_info sfile[256]; @ The first three handles are initially open. @= void mmix_io_init @,@,@[ARGS((void))@];@+@t}\6{@> void mmix_io_init() { sfile[0].fp=stdin, sfile[0].mode=1; sfile[1].fp=stdout, sfile[1].mode=2; sfile[2].fp=stderr, sfile[2].mode=2; } @ The only tricky thing about these routines is that we want to protect the standard input, output, and error streams from being preempted. @= octa mmix_fopen @,@,@[ARGS((unsigned char,octa,octa))@];@+@t}\6{@> octa mmix_fopen(handle,name,mode) unsigned char handle; octa name,mode; { char name_buf[FILENAME_MAX]; if (mode.h || mode.l>4) goto abort; if (mmgetchars(name_buf,FILENAME_MAX,name,0)==FILENAME_MAX) goto abort; if (sfile[handle].mode!=0 && handle>2) fclose(sfile[handle].fp); sfile[handle].fp=fopen(name_buf,mode_string[mode.l]); if (!sfile[handle].fp) goto abort; sfile[handle].mode=mode_code[mode.l]; return zero_octa; /* success */ abort: sfile[handle].mode=0; return neg_one; /* failure */ } @ @= char *mode_string[]={"r","w","rb","wb","w+b"}; int mode_code[]={0x1,0x2,0x5,0x6,0xf}; @ If the simulator is being used interactively, we can avoid competition for |stdin| by substituting another file. @= void mmix_fake_stdin @,@,@[ARGS((FILE*))@];@+@t}\6{@> void mmix_fake_stdin(f) FILE *f; { sfile[0].fp=f; /* |f| should be open in mode \.{"r"} */ } @ @= octa mmix_fclose @,@,@[ARGS((unsigned char))@];@+@t}\6{@> octa mmix_fclose(handle) unsigned char handle; { if (sfile[handle].mode==0) return neg_one; if (handle>2 && fclose(sfile[handle].fp)!=0) return neg_one; sfile[handle].mode=0; return zero_octa; /* success */ } @ @= octa mmix_fread @,@,@[ARGS((unsigned char,octa,octa))@];@+@t}\6{@> octa mmix_fread(handle,buffer,size) unsigned char handle; octa buffer,size; { register unsigned char *buf; register int n; octa o; o=neg_one; if (!(sfile[handle].mode&0x1)) goto done; if (sfile[handle].mode&0x8) sfile[handle].mode &=~ 0x2; if (size.h) goto done; buf=(unsigned char*)calloc(size.l,sizeof(char)); if (!buf) goto done; @; mmputchars(buf,n,buffer); free(buf); o.h=0, o.l=n; done: return ominus(o,size); } @ @= if (sfile[handle].fp==stdin) { register unsigned char *p; for (p=buf,n=size.l; p= octa mmix_fgets @,@,@[ARGS((unsigned char,octa,octa))@];@+@t}\6{@> octa mmix_fgets(handle,buffer,size) unsigned char handle; octa buffer,size; { char buf[256]; register int n,s; register char *p; octa o; int eof=0; if (!(sfile[handle].mode&0x1)) return neg_one; if (!size.l && !size.h) return neg_one; if (sfile[handle].mode&0x8) sfile[handle].mode &=~ 0x2; size=incr(size,-1); o=zero_octa; while (1) { @; mmputchars(buf,n+1,buffer); o=incr(o,n); size=incr(size,-n); if ((n&&buf[n-1]=='\n') || (!size.l&&!size.h) || eof) return o; buffer=incr(buffer,n); } } @ @= s=255; if (size.l @^big-endian versus little-endian@> @^system dependencies@> @= octa mmix_fgetws @,@,@[ARGS((unsigned char,octa,octa))@];@+@t}\6{@> octa mmix_fgetws(handle,buffer,size) unsigned char handle; octa buffer,size; { char buf[256]; register int n,s; register char *p; octa o; int eof; if (!(sfile[handle].mode&0x1)) return neg_one; if (!size.l && !size.h) return neg_one; if (sfile[handle].mode&0x8) sfile[handle].mode &=~ 0x2; buffer.l&=-2; size=incr(size,-1); o=zero_octa; while (1) { @; mmputchars(buf,2*n+2,buffer); o=incr(o,n); size=incr(size,-n); if ((n&&buf[2*n-1]=='\n'&&buf[2*n-2]==0) || (!size.l&&!size.h) || eof) return o; buffer=incr(buffer,2*n); } } @ @= s=127; if (size.l= octa mmix_fwrite @,@,@[ARGS((unsigned char,octa,octa))@];@+@t}\6{@> octa mmix_fwrite(handle,buffer,size) unsigned char handle; octa buffer,size; { char buf[256]; register int n; if (!(sfile[handle].mode&0x2)) return ominus(zero_octa,size); if (sfile[handle].mode&0x8) sfile[handle].mode &=~ 0x1; while (1) { if (size.h || size.l>=256) n=mmgetchars(buf,256,buffer,-1); else n=mmgetchars(buf,size.l,buffer,-1); size=incr(size,-n); if (fwrite(buf,1,n,sfile[handle].fp)!=n) return ominus(zero_octa,size); fflush(sfile[handle].fp); if (!size.l && !size.h) return zero_octa; buffer=incr(buffer,n); } } @ @= octa mmix_fputs @,@,@[ARGS((unsigned char,octa))@];@+@t}\6{@> octa mmix_fputs(handle,string) unsigned char handle; octa string; { char buf[256]; register int n; octa o; o=zero_octa; if (!(sfile[handle].mode&0x2)) return neg_one; if (sfile[handle].mode&0x8) sfile[handle].mode &=~ 0x1; while (1) { n=mmgetchars(buf,256,string,0); if (fwrite(buf,1,n,sfile[handle].fp)!=n) return neg_one; o=incr(o,n); if (n<256) { fflush(sfile[handle].fp); return o; } string=incr(string,n); } } @ @= octa mmix_fputws @,@,@[ARGS((unsigned char,octa))@];@+@t}\6{@> octa mmix_fputws(handle,string) unsigned char handle; octa string; { char buf[256]; register int n; octa o; o=zero_octa; if (!(sfile[handle].mode&0x2)) return neg_one; while (1) { n=mmgetchars(buf,256,string,1); if (fwrite(buf,1,n,sfile[handle].fp)!=n) return neg_one; o=incr(o,n>>1); if (n<256) { fflush(sfile[handle].fp); return o; } string=incr(string,n); } } @ @d sign_bit ((unsigned)0x80000000) @= octa mmix_fseek @,@,@[ARGS((unsigned char,octa))@];@+@t}\6{@> octa mmix_fseek(handle,offset) unsigned char handle; octa offset; { if (!(sfile[handle].mode&0x4)) return neg_one; if (sfile[handle].mode&0x8) sfile[handle].mode = 0xf; if (offset.h&sign_bit) { if (offset.h!=0xffffffff || !(offset.l&sign_bit)) return neg_one; if (fseek(sfile[handle].fp,(int)offset.l+1,SEEK_END)!=0) return neg_one; }@+else { if (offset.h || (offset.l&sign_bit)) return neg_one; if (fseek(sfile[handle].fp,(int)offset.l,SEEK_SET)!=0) return neg_one; } return zero_octa; } @ @= octa mmix_ftell @,@,@[ARGS((unsigned char))@];@+@t}\6{@> octa mmix_ftell(handle) unsigned char handle; { register long x; octa o; if (!(sfile[handle].mode&0x4)) return neg_one; x=ftell(sfile[handle].fp); if (x<0) return neg_one; o.h=0, o.l=x; return o; } @ One last subroutine belongs here, just in case the user has modified the standard error handle. @= void print_trip_warning @,@,@[ARGS((int,octa))@];@+@t}\6{@> void print_trip_warning(n,loc) int n; octa loc; { if (sfile[2].mode&0x2) fprintf(sfile[2].fp,"Warning: %s at location %08x%08x\n", trip_warning[n],loc.h,loc.l); } @ @= char *trip_warning[]={ "TRIP", "integer divide check", "integer overflow", "float-to-fix overflow", "invalid floating point operation", "floating point overflow", "floating point underflow", "floating point division by zero", "floating point inexact"}; @* Index.