diff --git a/build.lua b/build.lua index 0e167a079..1e2405907 100644 --- a/build.lua +++ b/build.lua @@ -4,6 +4,7 @@ vars.cflags = { vars.ackcflags = { "-O6" } +vars.ackldflags = {} vars.plats = { "cpm", "linux386", diff --git a/examples/build.lua b/examples/build.lua index fced8e299..d30f3745c 100644 --- a/examples/build.lua +++ b/examples/build.lua @@ -4,6 +4,10 @@ local conly = { rpi = true } +local softfp = { + cpm = true, +} + local sourcefiles = filenamesof( "./hilo.b", "./hilo.bas", @@ -15,6 +19,11 @@ local sourcefiles = filenamesof( "./startrek.c" ) +local usesfp = { + ["mandelbrot.c"] = true, + ["startrek.c"] = true, +} + local installmap = {} for _, file in ipairs(sourcefiles) do local b = basename(file) @@ -22,6 +31,12 @@ for _, file in ipairs(sourcefiles) do local _, _, e = b:find("%.(%w*)$") for _, plat in ipairs(vars.plats) do + local flags = {} + + if softfp[plat] and usesfp[b] then + flags[#flags+1] = "-fp" + end + if (e == "c") or not conly[plat] then local exe = ackprogram { name = be.."_"..plat, @@ -29,6 +44,7 @@ for _, file in ipairs(sourcefiles) do vars = { plat = plat, lang = e, + ["+ackldflags"] = flags, } } diff --git a/examples/hilo.p b/examples/hilo.p index be953a09e..161ff8049 100644 --- a/examples/hilo.p +++ b/examples/hilo.p @@ -9,7 +9,6 @@ program hilo(input, output); type string = packed array [0..255] of char; - charstar = packed array [0..0] of char; var playing : Boolean; @@ -30,47 +29,25 @@ function random(range : integer) : integer; random := seed mod range; end; -{ Pascal doesn't provide string input, so we interface to the read() syscall - and do it manually. But... we can't interface to read() directly because - that conflicts with a Pascal keyword. Luckily there's a private function - uread() in the ACK Pascal library that we can use instead. } - -function uread(fd : integer; var buffer : charstar; count : integer) : integer; - extern; +{ Pascal doesn't provide string input, so we read characters until the + end of line and put them in a string. } -function readchar : char; - var - c : charstar; - dummy : integer; - - begin - c[0] := chr(0); - dummy := uread(0, c, 1); - readchar := c[0]; - end; - procedure readstring(var buffer : string; var length : integer); var - finished : Boolean; c : char; begin write('> '); length := 0; - finished := FALSE; - seed := 0; - while not finished do + repeat begin - c := readchar; - if (ord(c) = 10) then - finished := true - else - begin - buffer[length] := c; - length := length + 1; - end - end; + read(c); + buffer[length] := c; + length := length + 1; + end + until eoln; + readln; { discard end of line } end; procedure getname; diff --git a/examples/startrek.c b/examples/startrek.c index 572c53597..d7472e5c5 100644 --- a/examples/startrek.c +++ b/examples/startrek.c @@ -58,7 +58,18 @@ * */ +/* For `ack -mcpm -fp`, the i80 code was too big. Make it smaller by + * removing the game's intro and replacing part of libc. */ +#ifdef __i80 +#define SMALL +#endif + +#ifdef SMALL +#include +#include +#else #include +#endif #include #include #include @@ -134,13 +145,26 @@ int cint(double d); void compute_vector(void); void sub1(void); void sub2(void); +#ifndef SMALL void showfile(char *filename); int openfile(char * sFilename, char * sMode); void closefile(void); int get_line(char *s); +#endif void randomize(void); int get_rand(int iSpread); double rnd(void); +#ifdef SMALL +#undef atof +#define atof trek_atof +#define getchar trek_getchar +#define putchar trek_putchar +#define printf trek_printf +double atof(const char *); +static int getchar(void); +static void putchar(int c); +void printf(const char *fmt, ...); +#endif /* Global Variables */ @@ -196,14 +220,20 @@ char sQ[194]; /* Visual Display of Quadrant */ string sG2; /* Used to pass string results */ +#ifndef SMALL FILE *stream; bool bFlag = FALSE; /* Prevent multiple file opens */ +#endif void reads(char* buffer) { +#ifdef SMALL + read(0, buffer, sizeof(string)); +#else fflush(stdout); fgets(buffer, sizeof(string), stdin); +#endif } /* Main Program */ @@ -224,6 +254,7 @@ intro(void) { string sTemp; +#ifndef SMALL printf ("\n\n"); printf (" *************************************\n"); printf (" * *\n"); @@ -239,6 +270,7 @@ intro(void) if (sTemp[0] == 'y' || sTemp[0] == 'Y') showfile("startrek.doc"); +#endif /* !SMALL */ printf ("\n\n\n\n\n\n\n"); printf(" ------*------\n"); @@ -1879,6 +1911,7 @@ cint (double d) return(i); } +#ifndef SMALL void showfile(char *filename) { @@ -1934,6 +1967,7 @@ get_line(char *s) else return(strlen(s)); } +#endif /* !SMALL */ /* Seed the randomizer with the timer */ void @@ -1960,3 +1994,164 @@ rnd(void) return(d); } + +#ifdef SMALL +/* These are small but incomplete replacements for functions in libc. + * Local variables are static for smaller i80 code. */ + +double +atof(const char *str) +{ + static double d; + static char n, prec; + + d = 0.0; + n = prec = 0; /* Forget to skip whitespace before number */ + if (*str == '-') { n = 1; str++; } + for (;;) + { + if (*str >= '0' && *str <= '9') + { + d = 10.0 * d + (double)(*str - '0'); + if (prec != 0) prec++; + } + else if (*str == '.') + prec++; + else + break; /* Forget to parse exponent like "e10" */ + str++; + } + while (prec > 1) { d /= 10.0; prec--; } + return n ? -d : d; +} + +static int +getchar(void) +{ + static unsigned char c[2]; /* CP/M read() needs 2 bytes */ + + if (read(0, c, 2) > 0) + return c[0]; + else + return -1; +} + +static void +putchar(int c) +{ + write(1, &c, 1); +} + +struct printf_buf { + char p_buf[13]; + char p_len; + char *p_str; +}; + +static struct printf_buf pfb; + +static void +pfb_put(int c) +{ + if (pfb.p_str != pfb.p_buf) + { + pfb.p_str--; + *pfb.p_str = c; /* Prepend character to buffer */ + pfb.p_len++; + } + else + pfb.p_len = 0; /* No room in buffer; force empty string */ +} + +void +printf(const char *fmt, ...) +{ + static va_list ap; + static const char *s; + + va_start(ap, fmt); + while (*fmt != '\0') + { + if (*fmt != '%') + { + s = fmt; + do { s++; } while ( *s != '\0' && *s != '%'); + write(1, fmt, s - fmt); + fmt = s; + } + if (*fmt == '%') + { + static char prec, width; + + fmt++; /* Pass '%' */ + /* Read optional width.prec, as 4.2 in "%4.2f" */ + prec = width = 0; + if (*fmt >= '0' && *fmt <= '9') + { + width = *fmt - '0'; + fmt++; + } + if (*fmt == '.') + { + fmt++; + if (*fmt >= '0' && *fmt <= '9') + { + prec = *fmt - '0'; + fmt++; + } + } + if (*fmt == 's') /* Format "%s" */ + { + static const char *s; + + s = va_arg(ap, const char *); + write(1, s, strlen(s)); + } + else /* Format "%d" or "%f" */ + { + static double d; + static char n, pad; + + if (*fmt == 'd') + { + d = (double)va_arg(ap, int); + prec = 0; /* No digits after point */ + pad = '0'; + } + else + { + d = va_arg(ap, double); + /* Move digits before point */ + for (n = prec; n != 0; n--) d *= 10.0; + pad = ' '; + } + /* Set up buffer */ + pfb.p_len = 0; + pfb.p_str = pfb.p_buf + sizeof(pfb.p_buf); + /* Change negative number to positive */ + n = 0; + if (d < 0) { n = 1; d = -d; } + modf(d + 0.5, &d); /* Round last digit */ + for (;;) + { + d /= 10.0; /* Extract next digit */ + /* Use 10.5 instead of 10.0 to decrease error */ + pfb_put('0' + (int)(10.5 * modf(d, &d))); + if (prec != 0) + { + prec--; + if (prec == 0) pfb_put('.'); + } + else if (d < 1.0) + break; /* No more digits */ + } + if (n) pfb_put('-'); + while(pfb.p_len < width) pfb_put(pad); + write(1, pfb.p_str, pfb.p_len); + } + fmt++; /* Pass 's' in "%s" */ + } + } + va_end(ap); +} +#endif /* SMALL */ diff --git a/first/build.lua b/first/build.lua index b9fa0ab1f..2f56bf0c6 100644 --- a/first/build.lua +++ b/first/build.lua @@ -40,6 +40,7 @@ definerule("cfile", { srcs = { type="targets" }, deps = { type="targets", default={} }, + suffix = { type="string", default=".o" }, commands = { type="strings", default={ @@ -56,7 +57,7 @@ definerule("cfile", end hdrpaths = uniquify(hdrpaths) - local outleaf = basename(e.name)..".o" + local outleaf = basename(e.name)..e.suffix return normalrule { name = e.name, diff --git a/mach/i80/libem/flp.s b/mach/i80/libem/flp.s index 404c8d128..9b9c8df31 100644 --- a/mach/i80/libem/flp.s +++ b/mach/i80/libem/flp.s @@ -1,7 +1,6 @@ .define .adf4,.adf8,.sbf4,.sbf8,.mlf4,.mlf8,.dvf4,.dvf8 .define .ngf4,.ngf8,.fif4,.fif8,.fef4,.fef8 -.define .zrf4,.zrf8 -.define .cfi,.cif,.cuf,.cff,.cfu +.define .cfi,.cif4,.cif8,.cuf4,.cuf8,.cff4,.cff8,.cfu .define .cmf4,.cmf8 .sect .text .sect .rom @@ -25,12 +24,13 @@ .fif8: .fef4: .fef8: -.zrf4: -.zrf8: .cfi: -.cif: -.cuf: -.cff: +.cif4: +.cif8: +.cuf4: +.cuf8: +.cff4: +.cff8: .cfu: .cmf4: .cmf8: diff --git a/mach/i80/ncg/mach.c b/mach/i80/ncg/mach.c index 89d7b0149..e687ec25d 100644 --- a/mach/i80/ncg/mach.c +++ b/mach/i80/ncg/mach.c @@ -41,21 +41,12 @@ con_mult(sz) word sz; { fprintf(codefile,".data4\t%ld\n",atol(str)); } -void -con_float() { - static int warning_given; - int i = argval; - - if (!warning_given) { - fprintf(stderr, "warning: dummy floating point constant\n"); - warning_given = 1; - } - while (i > 0) { - fputs(".data4 0 !dummy float\n", codefile); - i -= 4; - } -} - +#define CODE_GENERATOR +#define IEEEFLOAT +#define FL_MSL_AT_LOW_ADDRESS 0 +#define FL_MSW_AT_LOW_ADDRESS 0 +#define FL_MSB_AT_LOW_ADDRESS 0 +#include void prolog(nlocals) full nlocals; { diff --git a/mach/i80/ncg/table b/mach/i80/ncg/table index e6d7e02f6..465f6ab1b 100644 --- a/mach/i80/ncg/table +++ b/mach/i80/ncg/table @@ -817,61 +817,28 @@ gen mvi a,{const1,0} /* Group 5: Floating point arithmetic */ /********************************************/ -pat adf $1==4 -kills ALL -gen Call {label,".adf4"} - -pat adf $1==8 -kills ALL -gen Call {label,".adf8"} - -pat sbf $1==4 -kills ALL -gen Call {label,".sbf4"} - -pat sbf $1==8 -kills ALL -gen Call {label,".sbf8"} - -pat mlf $1==4 -kills ALL -gen Call {label,".mlf4"} - -pat mlf $1==8 -kills ALL -gen Call {label,".mlf8"} - -pat dvf $1==4 -kills ALL -gen Call {label,".dvf4"} - -pat dvf $1==8 -kills ALL -gen Call {label,".dvf8"} - -pat ngf $1==4 -kills ALL -gen Call {label,".ngf4"} - -pat ngf $1==8 -kills ALL -gen Call {label,".ngf8"} +pat adf $1==4 leaving cal ".adf4" asp 4 +pat adf $1==8 leaving cal ".adf8" asp 8 +pat sbf $1==4 leaving cal ".sbf4" asp 4 +pat sbf $1==8 leaving cal ".sbf8" asp 8 +pat mlf $1==4 leaving cal ".mlf4" asp 4 +pat mlf $1==8 leaving cal ".mlf8" asp 8 +pat dvf $1==4 leaving cal ".dvf4" asp 4 +pat dvf $1==8 leaving cal ".dvf8" asp 8 +pat ngf $1==4 leaving cal ".ngf4" +pat ngf $1==8 leaving cal ".ngf8" pat fif $1==4 -kills ALL -gen Call {label,".fif4"} +leaving lor 1 cal ".fif4" asp 2 pat fif $1==8 -kills ALL -gen Call {label,".fif8"} +leaving lor 1 cal ".fif8" asp 2 pat fef $1==4 -kills ALL -gen Call {label,".fef4"} +leaving lor 1 adp 0-2 cal ".fef4" pat fef $1==8 -kills ALL -gen Call {label,".fef8"} +leaving lor 1 adp 0-2 cal ".fef8" /********************************************/ /* Group 6: Pointer arithmetic */ @@ -964,13 +931,9 @@ pat zre uses hlreg={const2,0} gen shld {label,$1} -pat zrf $1==4 -kills ALL -gen Call {label,".zrf4"} +pat zrf $1==4 leaving zer 4 -pat zrf $1==8 -kills ALL -gen Call {label,".zrf8"} +pat zrf $1==8 leaving zer 8 pat zer $1==2 yields {const2,0} @@ -1086,25 +1049,51 @@ kills ALL gen mvi a,{const1,0} Call {label,".cii"} -pat cfi -kills ALL -gen Call {label,".cfi"} +pat loc loc cfi $2==2 +leaving loc $1 loc $2 cal ".cfi" asp 4+$1 loe ".fra" -pat cif -kills ALL -gen Call {label,".cif"} +pat loc loc cfi $2==4 +leaving loc $1 loc $2 cal ".cfi" asp 4+$1 lfr 4 -pat cuf -kills ALL -gen Call {label,".cuf"} +pat loc loc cif $2==4 +leaving loc $1 cal ".cif4" asp $1-2 -pat cff -kills ALL -gen Call {label,".cff"} +pat loc loc cif $1==2 && $2==8 +with hl_or_de +gen push %1 + push %1 + push %1 leaving loc $1 cal ".cif8" -pat cfu -kills ALL -gen Call {label,".cfu"} +pat loc loc cif $1==4 && $2==8 +with hl_or_de hl_or_de +gen push %2 + push %2 + push %1 leaving loc $1 cal ".cif8" + +pat loc loc cuf $2==4 +leaving loc $1 cal ".cuf4" asp $1-2 + +pat loc loc cuf $1==2 && $2==8 +with hl_or_de +gen push %1 + push %1 + push %1 leaving loc $1 cal ".cuf8" + +pat loc loc cuf $1==4 && $2==8 +with hl_or_de hl_or_de +gen push %1 + push %2 + push %1 leaving loc $1 cal ".cuf8" + +pat loc loc cff $1==8 && $2==4 leaving cal ".cff4" asp 4 + +pat loc loc cff $1==4 && $2==8 leaving dup 4 cal ".cff8" + +pat loc loc cfu $2==2 +leaving loc $1 loc $2 cal ".cfu" asp 4+$1 loe ".fra" + +pat loc loc cfu $2==4 +leaving loc $1 loc $2 cal ".cfu" asp 4+$1 lfr 4 /*****************************************/ /* Group 9: Logical instructions */ @@ -1408,12 +1397,10 @@ gen mvi a,{const1,1} Call {label,".cmi4"} yields de pat cmf $1==4 -kills ALL -gen Call {label,".cmf4"} +leaving cal ".cmf4" asp 8 lfr 2 pat cmf $1==8 -kills ALL -gen Call {label,".cmf8"} +leaving cal ".cmf8" asp 16 lfr 2 pat cmu $1==2 with hl_or_de hl_or_de diff --git a/mach/proto/fp/FP.script b/mach/proto/fp/FP.script index de2c407ed..22bda6b90 100644 --- a/mach/proto/fp/FP.script +++ b/mach/proto/fp/FP.script @@ -37,5 +37,5 @@ g/_b64_add/s//.b64_add/g g/_b64_sft/s//.b64_sft/g g/_b64_rsft/s//.b64_rsft/g g/_b64_lsft/s//.b64_lsft/g -w +1,$p q diff --git a/mach/proto/fp/build.lua b/mach/proto/fp/build.lua new file mode 100644 index 000000000..62d0d8114 --- /dev/null +++ b/mach/proto/fp/build.lua @@ -0,0 +1,53 @@ +include("plat/build.lua") + +-- For now, all floats are little-endian. +local byte_order = "mach/i86/libfp/byte_order.h" + +-- For now, only cpm uses software floating-point. +for _, plat in ipairs({"cpm"}) do + local edits = {} + for _, src in fpairs("./*.c", "./*.e") do + + -- Compile each src file into assembly code. + local n = basename(src):gsub("%.%w*$", "") + local assembly = ackfile { + name = "s_"..plat.."/"..n, + srcs = { src }, + deps = { + "./*.h", + byte_order, + }, + suffix = ".s", + vars = { + ["+ackcflags"] = { "-I"..dirname(byte_order) }, + plat = plat + } + } + + -- Run FP.script to edit the assembly code. + edits[#edits+1] = normalrule { + name = "ed_"..plat.."/"..n, + ins = { + "./FP.script", + assembly, + }, + outleaves = { n..".s" }, + commands = { + "ed -s %{ins[2]} <%{ins[1]} >%{outs}" + } + } + end + + acklibrary { + name = "lib_"..plat, + srcs = { edits }, + vars = { plat = plat } + } + + installable { + name = "pkg_"..plat, + map = { + ["$(PLATIND)/"..plat.."/libfp.a"] = "+lib_"..plat, + } + } +end diff --git a/plat/build.lua b/plat/build.lua index 6fc96f85d..676778dd8 100644 --- a/plat/build.lua +++ b/plat/build.lua @@ -8,8 +8,10 @@ definerule("ackfile", { srcs = { type="targets" }, deps = { type="targets", default={} }, + suffix = { type="string", default=".o" }, }, function (e) + local c = (e.suffix == ".o" and "-c" or "-c"..e.suffix) local plat = e.vars.plat return cfile { @@ -28,8 +30,9 @@ definerule("ackfile", "util/misc+pkg", e.deps }, + suffix = e.suffix, commands = { - "ACKDIR=$(INSDIR) $(INSDIR)/bin/ack -m%{plat} -c -o %{outs} %{ins} %{hdrpaths} %{ackcflags}" + "ACKDIR=$(INSDIR) $(INSDIR)/bin/ack -m%{plat} "..c.." -o %{outs} %{ins} %{hdrpaths} %{ackcflags}" } } end @@ -88,7 +91,7 @@ definerule("ackprogram", }, _clibrary = acklibrary, commands = { - "ACKDIR=$(INSDIR) $(INSDIR)/bin/ack -m%{plat} -.%{lang} -o %{outs} %{ins}" + "ACKDIR=$(INSDIR) $(INSDIR)/bin/ack -m%{plat} -.%{lang} -o %{outs} %{ins} %{ackldflags}" } } end @@ -100,18 +103,25 @@ definerule("build_plat_libs", plat = { type="string" }, }, function(e) + local installmap = { + "lang/b/lib+pkg_"..e.plat, + "lang/basic/lib+pkg_"..e.plat, + "lang/cem/libcc.ansi+pkg_"..e.plat, + "lang/m2/libm2+pkg_"..e.plat, + "lang/pc/libpc+pkg_"..e.plat, + "lang/b/lib+pkg_"..e.plat, + ["$(PLATIND)/"..e.plat.."/libem.a"] = "mach/"..e.arch.."/libem+lib_"..e.plat, + ["$(PLATIND)/"..e.plat.."/libend.a"] = "mach/"..e.arch.."/libend+lib_"..e.plat, + } + + -- For now, only cpm uses software floating-point. + if e.plat == "cpm" then + installmap[#installmap+1] = "mach/proto/fp+pkg_"..e.plat + end + return installable { name = e.name, - map = { - "lang/b/lib+pkg_"..e.plat, - "lang/basic/lib+pkg_"..e.plat, - "lang/cem/libcc.ansi+pkg_"..e.plat, - "lang/m2/libm2+pkg_"..e.plat, - "lang/pc/libpc+pkg_"..e.plat, - "lang/b/lib+pkg_"..e.plat, - ["$(PLATIND)/"..e.plat.."/libem.a"] = "mach/"..e.arch.."/libem+lib_"..e.plat, - ["$(PLATIND)/"..e.plat.."/libend.a"] = "mach/"..e.arch.."/libend+lib_"..e.plat, - } + map = installmap, } end ) diff --git a/plat/cpm/README b/plat/cpm/README index 1712c2e0f..c7d7b1ac4 100644 --- a/plat/cpm/README +++ b/plat/cpm/README @@ -11,14 +11,15 @@ CP/M-compliant machine. This port only implements a very limited set of syscalls --- and most of those are stubs required to make the demo apps link. File descriptors 0, 1 and 2 -represent the console. All reads block. There's enough TTY emulation to allow -\n conversion and local echo (but it can't be turned off). +represent the console. Each read() blocks and reads an entire line (it can't +read part of a line) from the CP/M line editor, then appends \n. Each write() +converts \n to \r\n. The line editor and \n conversion can't be turned off. There's a special, if rather minimilist, interface to give applications access to CP/M. See include/cpm.h for details. -IEEE floating point is not available. Attempts to use floating-point numbers -will cause the program to terminate. +Link with `ack -fp` to enable software floating point. Otherwise, attempts to +use floating-point numbers will cause the program to terminate. Example command line diff --git a/plat/cpm/descr b/plat/cpm/descr index d084f89ea..eb38c5593 100644 --- a/plat/cpm/descr +++ b/plat/cpm/descr @@ -58,7 +58,7 @@ name led program {EM}/lib/ack/em_led mapflag -l* LNAME={PLATFORMDIR}/lib* mapflag -i SEPID=-b1:0 - mapflag -fp FLOATS={EM}/{ILIB}fp + mapflag -fp FLOATS={PLATFORMDIR}/libfp.a args {ALIGN} {SEPID?} \ ({RTS}:.b=-u _i_main) \ (.e:{HEAD}={PLATFORMDIR}/boot.o) \ diff --git a/plat/cpm/libsys/read.c b/plat/cpm/libsys/read.c index 8a0ba0cb1..f81bda777 100644 --- a/plat/cpm/libsys/read.c +++ b/plat/cpm/libsys/read.c @@ -10,29 +10,44 @@ int read(int fd, void* buffer, size_t count) { - char i; - + short save; + unsigned char before_n; + /* We're only allowed to read from fd 0, 1 or 2. */ - if ((fd < 0) || (fd > 2)) { errno = EBADF; return -1; } - - /* Empty buffer? */ - - if (count == 0) - return 0; - - /* Read one byte. */ - - cpm_bc_register = CPM_BDOS_CONSOLE_INPUT; + + /* We need room for at least 1 char plus '\n'. */ + if (count < 2) + { + errno = EINVAL; + return -1; + } + + /* Make room to append '\n' later. */ + before_n = count > 255 ? 255 : count - 1; + + /* Borrow 2 bytes of RAM before the buffer. */ + /* This might overwrite count!!! */ + save = ((short*)buffer)[-1]; + + /* Read one line from the console. */ + ((unsigned char*)buffer)[-2] = before_n; + cpm_bc_register = CPM_BDOS_READ_CONSOLE_BUFFER; + cpm_de_register = (char*)buffer - 2; + cpm_bdos(); + before_n = ((unsigned char*)buffer)[-1]; + + ((char*)buffer)[before_n] = '\n'; /* Append '\n'. */ + ((short*)buffer)[-1] = save; /* Give back borrowed bytes. */ + + /* Echo '\n' to console. */ + cpm_bc_register = CPM_BDOS_PRINT_STRING; + cpm_de_register = "\r\n$"; cpm_bdos(); - if (cpm_a_register == '\r') - cpm_a_register = '\n'; - *(char*)buffer = cpm_a_register; - - return 1; + return (int)before_n + 1; }