shithub: femtolisp

Download patch

ref: cd1803dc65daac66b7895d716238e53b6c507af4
parent: 27361e559d2f21cf0ce729b7f58bb60b1fcb96a1
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Thu Jan 16 21:00:51 EST 2025

add a DOS port, via djgpp cross-compiler

--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@
 builtin_fns.h
 *.core
 plan9/flisp.boot.s
+cross/*-toolchain
--- a/3rd/fn.h
+++ b/3rd/fn.h
@@ -125,6 +125,7 @@
 }
 
 // sanity checks!
+/*
 static_assert(Tix_base_bitmap + Tix_width_bitmap == 64,
 	      "index fields must fill a 64 bit word");
 
@@ -136,7 +137,7 @@
 
 static_assert(Tunmask(shift,0xFEDCBAULL) == 5,
 	      "extracting the shift works");
-
+*/
 //  ..key[o%5==0].. ..key[o%5==1].. ..key[o%5==2].. ..key[o%5==3].. ..key[o%5==4]..
 // |               |               |               |               |               |
 //  7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
 
 Tested on: 9front/arm64, OpenBSD/386, NetBSD/sparc64, Alpine/amd64, Android/arm64.
 
-Also runs on: MacOS 4.x-9.x.
+Also runs on: MacOS 4.x-9.x, DOS.
 
 Some of the changes from the original include:
 
@@ -47,7 +47,7 @@
 
 ### MacOS 4.x-9.x (m68k or PowerPC)
 
-Install and build [Retro68](https://git.sr.ht/~ft/retro68). This fork grabs the
+Clone and build [Retro68](https://git.sr.ht/~ft/retro68). This fork grabs the
 necessary patches for more things to work as expected:
 
 	git clone https://git.sr.ht/~ft/retro68
@@ -70,6 +70,16 @@
 
 NOTE: this isn't a full-fledged port and is going to stay low priority unless somebody
 wants to spend time polishing it.
+
+### DOS
+
+Build DJGPP cross-compiler, then:
+
+	ln -s djgpp-toolchain-prefix cross/djgpp-toolchain
+	meson setup build . -Dbuildtype=minsize --cross-file cross/djgpp.txt
+	ninja -C build
+
+Result is `build/flisp.exe`.
 
 ## Characteristics
 
--- /dev/null
+++ b/cross/djgpp.txt
@@ -1,0 +1,21 @@
+[constants]
+toolchain = '@DIRNAME@/djgpp-toolchain/'
+path = toolchain + 'bin/'
+prefix = path + 'i586-pc-msdosdjgpp-'
+
+[built-in options]
+c_args = ['-D__dos__']
+cpp_args = c_args
+
+[binaries]
+c = prefix + 'gcc'
+cpp = prefix + 'g++'
+as = prefix + 'as'
+ar = prefix + 'ar'
+strip = prefix + 'strip'
+
+[host_machine]
+system = 'dos'
+cpu_family = 'x86'
+cpu = 'i586'
+endian = 'little'
--- /dev/null
+++ b/dos/platform.h
@@ -1,0 +1,43 @@
+#pragma once
+
+#define _XOPEN_SOURCE 700
+#include <assert.h>
+#include <ctype.h>
+#include <machine/endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <float.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <wctype.h>
+#include <wchar.h>
+
+#define __os_name__ "dos"
+
+#define nil NULL
+#define USED(x) ((void)(x))
+#define nelem(x) (int)(sizeof(x)/sizeof((x)[0]))
+
+#define PATHSEP '\\'
+#define PATHSEPSTRING "\\"
+#define PATHLISTSEP ':'
+#define PATHLISTSEPSTRING ":"
+#define ISPATHSEP(c) ((c) == '\\')
+
+#include "cc.h"
+#include "mem.h"
--- a/flisp.boot
+++ b/flisp.boot
@@ -15,7 +15,8 @@
 	      #fn("8000z0700}2:" #(aset!)) 0 0 0 0 0 0 0 0 0 0 0 #fn("9000n3012082>1|:" #(#fn("6000n1A061:" #())))
 	      0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 #fn("8000z0700}2:" #(aref)) 0 #fn("5000n10\x8e:" #())
 	      0)
-	    *properties* #table(*funvars* #table(>= ((a . rest))  void? ((x))  length= ((lst n))  help ((term))  lz-unpack ((data
+	    *os-version* "6.12.9-0-edge" *properties*
+	    #table(*funvars* #table(>= ((a . rest))  void? ((x))  length= ((lst n))  help ((term))  lz-unpack ((data
   :to destination)
   (data :size decompressed-bytes))  = ((a . rest))  <= ((a . rest))  car ((lst))  /= ((a . rest))  void (rest)  *prompt* (#f)  nan? ((x))  lz-pack ((data
   (level 0)))  cons? ((value))  vm-stats (nil)  * ((number…))  cdr ((lst))  > ((a . rest))  + ((number…)))  *doc* #table(+ "Return sum of the numbers or 0 with no arguments."  >= "Return #t if the arguments are in non-increasing order (previous\none is greater than or equal to the next one)."  void? "Return #t if x is #<void> and #f otherwise."  length= "Bounded length test.\nUse this instead of (= (length lst) n), since it avoids unnecessary\nwork and always terminates."  help "Display documentation for the specified term, if available."  lz-unpack "Return decompressed data previously compressed using lz-pack.\nEither destination for the decompressed data or the expected size of\nthe decompressed data must be specified.  In the latter case a new\narray is allocated."  = "Return #t if the arguments are equal."  <= "Return #t if the arguments are in non-decreasing order (previous\none is less than or equal to the next one)."  *builtins* "VM instructions as closures."  car "Returns the first element of a list or nil if not available."  /= "Return #t if not all arguments are equal. Shorthand for (not (= …))."  void "Return the constant #<void> while ignoring any arguments.\n#<void> is mainly used when a function has side effects but does not\nproduce any meaningful value to return, so even though #t or nil could\nbe returned instead, in case of #<void> alone, REPL will not print\nit."  arg-counts "VM instructions mapped to their expected arguments count."  *prompt* "Function called by REPL to signal the user input is required.\nDefault function prints \"#;> \"."  nan? "Return #t if the argument is NaN, regardless of the sign."  Instructions "VM instructions mapped to their encoded byte representation."  lz-pack "Return data compressed using Lempel-Ziv.\nThe data must be an array, returned value will have the same type.\nThe optional level is between 0 and 10.  With level 0 a simple LZSS\nusing hashing will be performed.  Levels between 1 and 9 offer a\ntrade-off between time/space and ratio.  Level 10 is optimal but very\nslow."  vm-stats "Print various VM-related information, such as the number of GC calls\nso far, heap and stack size, etc."  cons? "Returns #t if the value is a cons cell."  * "Return product of the numbers or 1 with no arguments."  > "Return #t if the arguments are in strictly decreasing order (previous\none is greater than the next one)."  cdr "Returns the tail of a list or nil if not available."  *properties* "All properties of symbols recorded with putprop are recorded in this table."))
@@ -56,10 +57,10 @@
 	    > #fn(";000z1\x8d\x8a6862086>1_486<^10162:" #(#fn("7000n21V;IE041<0L2;3;04A<1<1=62:" #())) >)
 	    >= #fn(";000z1\x8d\x8a6862086>1_486<^10162:" #(#fn("7000n21V;IL0401<L2;I5040\x8e340O:A<1<1=62:" #())) >=)
 	    Instructions #table(call.l 81  trycatch 75  largc 79  loadg.l 68  aref2 23  box 90  cadr 36  argc 62  setg 71  load0 21  nan? 94  vector? 45  fixnum? 41  loadc0 17  loada0 0  div0 59  keyargs 89  call 5  loada.l 69  brt.l 50  sub2 78  add2 29  loadc.l 70  loadc 9  builtin? 43  set-car! 47  brt 25  ret 10  loadi8 66  tapply 77  loadvoid 93  loada1 1  shift 46  boolean? 39  atom? 24  cdr 13  brne.l 83  / 58  loadf 31  equal? 52  apply 54  dup 11  loadt 20  jmp.l 48  null? 38  not 35  = 60  set-cdr! 30  eq? 33  * 57  load1 27  bound? 42  brf 3  function? 44  box.l 91  < 28  brnn.l 84  jmp 16  loadv 2  for 76  lvargc 80  dummy_eof 95  + 55  brne 19  compare 61  neg 37  loadv.l 67  number? 40  vargc 74  brn 85  brbound 88  vector 63  loadc1 22  setg.l 72  cons? 18  brf.l 49  aref 92  symbol? 34  aset! 64  car 12  cons 32  tcall.l 82  - 56  brn.l 86  optargs 87  closure 14  pop 4  eqv? 51  list 53  seta 15  seta.l 73  brnn 26  loadnil 65  loadg 7  loada 8  tcall 6)
-	    __init_globals #fn("7000n07021l237022@402384w4^1425w6427w8479w:47;w<47=w>:" #(*os-name*
+	    __init_globals #fn("7000n07021d37022@402384w4^147025d;350426;I50427w8429w:47;w<47=w>47?w@:" #(*os-name*
   "macos" #fn("6000n0702161:" #(princ "\e[0m\e[1m#;> \e[0m"))
-  #fn("6000n0702161:" #(princ "#;> ")) *prompt* "/" *directory-separator* "\n" *linefeed* *stdout*
-  *output-stream* *stdin* *input-stream* *stderr* *error-stream*) __init_globals)
+  #fn("6000n0702161:" #(princ "#;> ")) *prompt* "dos" "\\" "/" *directory-separator* "\n"
+  *linefeed* *stdout* *output-stream* *stdin* *input-stream* *stderr* *error-stream*) __init_globals)
 	    __rcscript #fn("=000n0708421c360O@T08422c37023@G08424c3=07526514O@4027^184;390428845185;3=0429857:2;53863B02<86513907=8661:O:" #(*os-name*
   "unknown" "plan9" "home" "macos" princ "\e]0;femtolisp v0.999\a" "HOME" #fn(os-getenv)
   #fn(string) *directory-separator* ".flisprc" #fn(path-exists?) load) __rcscript)
binary files a/flisp.boot.builtin b/flisp.boot.builtin differ
--- a/flisp.h
+++ b/flisp.h
@@ -5,6 +5,9 @@
 #include "mp.h"
 #include "utf.h"
 #endif
+#ifdef __dos__
+int wcwidth(Rune c);
+#endif
 #include "utf8.h"
 #include "ios.h"
 #include "tbl.h"
--- a/ios.c
+++ b/ios.c
@@ -22,7 +22,7 @@
 	return nil;
 }
 
-#if !defined(__plan9__) && !defined(__macos__)
+#if !defined(__plan9__) && !defined(__macos__) && !defined(__dos__)
 static int
 _enonfatal(int err)
 {
@@ -39,7 +39,7 @@
 	ssize_t r;
 
 	while(1){
-#if !defined(__plan9__) && !defined(__macos__)
+#if !defined(__plan9__) && !defined(__macos__) && !defined(__dos__)
 		errno = 0;
 #endif
 		r = read(fd, buf, n);
@@ -47,7 +47,7 @@
 			*nread = (size_t)r;
 			break;
 		}
-#if defined(__plan9__) || defined(__macos__)
+#if defined(__plan9__) || defined(__macos__) || defined(__dos__)
 		return r;
 #else
 		if(!_enonfatal(errno)){
@@ -84,7 +84,7 @@
 	ssize_t r;
 
 	while(1){
-#if !defined(__plan9__) && !defined(__macos__)
+#if !defined(__plan9__) && !defined(__macos__) && !defined(__dos__)
 		errno = 0;
 #endif
 		r = write(fd, buf, n);
@@ -92,7 +92,7 @@
 			*nwritten = (size_t)r;
 			break;
 		}
-#if defined(__plan9__) || defined(__macos__)
+#if defined(__plan9__) || defined(__macos__) || defined(__dos__)
 		return r;
 #else
 		if(!_enonfatal(errno)){
--- a/meson.build
+++ b/meson.build
@@ -4,7 +4,6 @@
 	version: '0.999',
 	meson_version: '>=1.1.0',
 	default_options: [
-		'c_std=c2x',
 		'warning_level=3',
 		'buildtype=debugoptimized',
 		'b_ndebug=if-release'
@@ -64,8 +63,6 @@
 	)
 endif
 
-platform = 'posix'
-
 src_common = [
 	'nan.c',
 ]
@@ -109,7 +106,17 @@
 		cpp.find_library('RetroConsole', required: true),
 		cpp.find_library('retrocrt', required: true),
 	]
+elif host_machine.system() == 'dos'
+	flisp_exe_name = 'flisp.exe'
+	platform = 'dos'
+	add_project_arguments(
+		'-DINITIAL_HEAP_SIZE=2*1024*1024',
+		'-DALLOC_LIMIT_TRIGGER=256*1024*1024',
+		language: 'c',
+	)
+	extras = []
 else
+	platform = 'posix'
 	flisp_exe_name = 'flisp'
 	add_project_arguments(
 		'-DINITIAL_HEAP_SIZE=8*1024*1024',
@@ -157,6 +164,10 @@
 	'types.c',
 	'utf8.c',
 ]
+
+if host_machine.system() == 'dos'
+	src_flisp += ['3rd/wcwidth.c']
+endif
 
 math = cc.find_library('m', required: false)
 
--- /dev/null
+++ b/sys_dos.c
@@ -1,0 +1,52 @@
+#include "flisp.h"
+#include "timefuncs.h"
+
+double
+sec_realtime(void)
+{
+	return 0.0;
+}
+
+uint64_t
+nanosec_monotonic(void)
+{
+	return 0;
+}
+
+void
+timestring(double s, char *buf, int sz)
+{
+	time_t tme = (time_t)s;
+	struct tm tm;
+
+	localtime_r(&tme, &tm);
+	strftime(buf, sz, "%c", &tm);
+}
+
+double
+parsetime(const char *s)
+{
+	return -1;
+}
+
+void
+sleep_ms(int ms)
+{
+	if(ms != 0){
+		struct timeval timeout;
+		timeout.tv_sec = ms/1000;
+		timeout.tv_usec = (ms % 1000) * 1000;
+		select(0, nil, nil, nil, &timeout);
+	}
+}
+
+static const uint8_t boot[] = {
+#include "flisp.boot.h"
+};
+
+int
+main(int argc, char **argv)
+{
+	setlocale(LC_NUMERIC, "C");
+	flmain(boot, sizeof(boot), argc, argv);
+}
--- a/system.lsp
+++ b/system.lsp
@@ -1114,13 +1114,13 @@
 
 ; initialize globals that need to be set at load time
 (define (__init_globals)
-  (let ((defprompt (if (= *os-name* "macos")
+  (let ((defprompt (if (equal? *os-name* "macos")
                        (λ () (princ "\x1b[0m\x1b[1m#;> \x1b[0m"))
                        (λ () (princ "#;> ")))))
     (set! *prompt*
       "Function called by REPL to signal the user input is required.
 Default function prints \"#;> \"." defprompt))
-  (set! *directory-separator* "/")
+  (set! *directory-separator* (or (and (equal? *os-name* "dos") "\\") "/"))
   (set! *linefeed* "\n")
   (set! *output-stream* *stdout*)
   (set! *input-stream*  *stdin*)