Browse Source

Added cpm platform.

dtrg 15 years ago
parent
commit
d6fc2d19f9

+ 49 - 0
mach/i80/libem/pmfile

@@ -0,0 +1,49 @@
+-- $Source$
+-- $State$
+-- $Revision$
+
+local d = ROOTDIR.."mach/i80/libem/"
+
+libem_i80 = acklibrary {
+	outputs = {"%U%/libem-%PLATFORM%.a"},
+
+	ackfile (d.."aar2.s"),
+	ackfile (d.."adi4.s"),
+	ackfile (d.."and.s"),
+	ackfile (d.."blm.s"),
+	ackfile (d.."cii.s"),
+	ackfile (d.."cmi4.s"),
+	ackfile (d.."cms.s"),
+	ackfile (d.."com.s"),
+	ackfile (d.."csa.s"),
+	ackfile (d.."csb.s"),
+	ackfile (d.."dup.s"),
+	ackfile (d.."dvi2.s"),
+	ackfile (d.."exg.s"),
+	ackfile (d.."flp.s"),
+	ackfile (d.."inn.s"),
+	ackfile (d.."ior.s"),
+	ackfile (d.."lar2.s"),
+	ackfile (d.."mli2.s"),
+	ackfile (d.."mli4.s"),
+	ackfile (d.."mlu2.s"),
+	ackfile (d.."ngi4.s"),
+	ackfile (d.."nop.s"),
+	ackfile (d.."rol4.s"),
+	ackfile (d.."ror4.s"),
+	ackfile (d.."sar2.s"),
+	ackfile (d.."sbi4.s"),
+	ackfile (d.."set.s"),
+	ackfile (d.."set2.s"),
+	ackfile (d.."sli2.s"),
+	ackfile (d.."sli4.s"),
+	ackfile (d.."sri2.s"),
+	ackfile (d.."sri4.s"),
+	ackfile (d.."xor.s"),
+	ackfile (d.."loi.s"),
+	ackfile (d.."sti.s"),
+	ackfile (d.."dvi4.s"),
+	ackfile (d.."rck.s"),
+
+	install = pm.install("%BINDIR%lib/%PLATFORM%/libem.a"),
+}

+ 7 - 1
mach/i80/libend/em_end.s

@@ -1,12 +1,18 @@
+! $Source$
+! $State$
+! $Revision$
+
 .sect .text
 .sect .rom
 .sect .data
 .sect .bss
 .sect .end ! only for declaration of _end, __end and endbss.
-.define	endtext,enddata,endbss,__end
+.define	endtext, endrom, enddata, endbss, __end
 
 	.sect .text
 endtext:
+	.sect .rom
+endrom:
 	.sect .data
 enddata:
 	.sect .end

+ 16 - 0
mach/i80/libend/pmfile

@@ -0,0 +1,16 @@
+-- $Source$
+-- $State$
+-- $Revision$
+
+local d = ROOTDIR.."mach/i80/libend/"
+
+libend_i80 = acklibrary {
+	outputs = {"%U%/libend-%PLATFORM%.a"},
+
+	ackfile (d.."edata.s"),
+	ackfile (d.."em_end.s"),
+	ackfile (d.."end.s"),
+	ackfile (d.."etext.s"),
+
+	install = pm.install("%BINDIR%lib/%PLATFORM%/libend.a"),
+}

+ 10 - 1
mach/i80/pmfile

@@ -3,6 +3,9 @@
 
 local d = ROOTDIR.."mach/i80/"
 
+include (d.."libem/pmfile")
+include (d.."libend/pmfile")
+
 mach_i80 = group {
 	ARCH = "i80",
 	
@@ -12,8 +15,10 @@ mach_i80 = group {
 	install = pm.install("%ROOTDIR%/lib/%ARCH%/descr", "%BINDIR%%PLATIND%/%ARCH%/descr")
 }
 
---
+support_i80 = group {
+	OPTIMISATION = "-O",
+
+	libem_i80,
+	libend_i80,
+}
+

+ 24 - 0
plat/cpm/.distr

@@ -0,0 +1,24 @@
+descr
+boot.s
+pmfile
+README
+include/ack/config.h
+include/unistd.h
+libsys/pmfile
+libsys/_hol0.s
+libsys/brk.c
+libsys/close.c
+libsys/creat.c
+libsys/errno.s
+libsys/getpid.c
+libsys/isatty.c
+libsys/kill.c
+libsys/libsys.h
+libsys/lseek.c
+libsys/open.c
+libsys/read.c
+libsys/signal.c
+libsys/time.c
+libsys/write.c
+libsys/_sys_rawread.s
+libsys/_sys_rawwrite.s

+ 33 - 0
plat/cpm/README

@@ -0,0 +1,33 @@
+# $Source$
+# $State$
+# $Revision$
+
+
+The cpm platform
+=================
+
+cpm is an i80-based BSP that generates CP/M executables that can be run on any
+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).
+
+There's a special, if rather minimilist, interface to give applications access
+to CP/M. See include/cpm.h for details.
+
+
+Example command line
+====================
+
+ack -mcpm -O -o cpm.com examples/paranoia.c
+
+The file cpm.com can then be run.
+
+(Although note that Paranoia is very big, and you'll need a CP/M machine with
+lots of memory for it to fit. Which it does, just.)
+
+
+David Given
+dg@cowlark.com

+ 66 - 0
plat/cpm/boot.s

@@ -0,0 +1,66 @@
+#
+! $Source$
+! $State$
+! $Revision$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.sect .bss
+STACKSIZE = 2*1024
+.comm stack, STACKSIZE
+.comm oldstack, 2
+
+.sect .text
+begtext:
+	lxi sp, oldstack + STACKSIZE
+	
+	lxi h, 0
+	push h
+	push h
+	push h
+	call __m_a_i_n
+	jmp EXIT
+	
+! Emergency exit routine.
+
+.define EXIT, __exit
+EXIT:
+__exit:
+	rst 0
+	
+! Define symbols at the beginning of our various segments, so that we can find
+! them. (Except .text, which has already been done.)
+
+.define begtext, begdata, begbss
+.sect .data;       begdata:
+.sect .rom;        begrom:
+.sect .bss;        begbss:
+
+! Some magic data. All EM systems need these.
+
+.define .trppc, .ignmask, _errno
+.comm .trppc, 2
+.comm .ignmask, 2
+.comm _errno, 2
+
+! These are used specifically by the CP/M port.
+
+.define .trapproc, .retadr, .retadr1
+.define .bcreg, .areg
+.define .tmp1, .fra, block1, block2, block3
+
+.comm .trapproc, 2
+.comm .retadr, 2        ! used to save return address
+.comm .retadr1, 2       ! reserve
+.comm .bcreg, 2
+.comm .areg, 1
+.comm .tmp1, 2
+.comm .fra, 8           ! 8 bytes function return area
+block1: .space 4        ! used by 32 bits divide and
+block2: .space 4        ! multiply routines
+block3: .space 4        ! must be contiguous (.comm doesn't guarantee this)

+ 69 - 0
plat/cpm/descr

@@ -0,0 +1,69 @@
+# $Source$
+# $State$
+# $Revision$
+
+var w=2
+var p=2
+var s=2
+var l=4
+var f=4
+var d=8
+var ARCH=i80
+var PLATFORM=cpm
+var PLATFORMDIR={EM}/lib/{PLATFORM}
+var CPP_F=-D__unix
+var ALIGN=-a0:1 -a1:1 -a2:1 -a3:1 -b0:0x0100
+var MACHOPT_F=-m8
+
+# Override the setting in fe so that files compiled for linux386 can see
+# the platform-specific headers.
+
+var C_INCLUDES=-I{PLATFORMDIR}/include -I{EM}/include/ansi
+
+name be
+	from .m.g
+	to .s
+	program {EM}/lib.bin/{PLATFORM}/ncg
+	args <
+	stdout
+	need .e
+end
+name as
+	from .s.so
+	to .o
+	program {EM}/lib.bin/{PLATFORM}/as
+	args - -o > <
+	prep cond
+end
+name led
+	from .o.a
+	to .out
+	program {EM}/lib.bin/em_led
+	mapflag -l* LNAME={PLATFORMDIR}/lib*
+	mapflag -i SEPID=-b1:0
+	mapflag -fp FLOATS={EM}/{ILIB}fp
+	args {ALIGN} {SEPID?} \
+	    (.e:{HEAD}={PLATFORMDIR}/boot.o) \
+		({RTS}:.ocm.b={PLATFORMDIR}/c-ansi.o) \
+		({RTS}:.c={PLATFORMDIR}/c-ansi.o) \
+		({RTS}:.mod={PLATFORMDIR}/modula2.o) \
+		({RTS}:.p={PLATFORMDIR}/pascal.o) \
+		-o > < \
+		(.p:{TAIL}={PLATFORMDIR}/libpascal.a) \
+		(.b:{TAIL}={PLATFORMDIR}/libbasic.a) \
+		(.mod:{TAIL}={PLATFORMDIR}/libmodula2.a) \
+		(.ocm:{TAIL}={PLATFORMDIR}/liboccam.a) \
+		(.ocm.b.mod.c.p:{TAIL}={PLATFORMDIR}/libc.a) \
+		{FLOATS?} \
+		(.e:{TAIL}={PLATFORMDIR}/libem.a \
+		           {PLATFORMDIR}/libsys.a \
+		           {PLATFORMDIR}/libend.a)
+	linker
+end
+name cv
+	from .out
+	to .img
+	program {EM}/bin/aslod
+	args < >
+	outfile cpm.com
+end

+ 19 - 0
plat/cpm/include/ack/config.h

@@ -0,0 +1,19 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#ifndef _ACK_CONFIG_H
+#define _ACK_CONFIG_H
+
+/* We're providing a time() system call rather than wanting a wrapper around
+ * gettimeofday() in the libc. */
+ 
+#define ACKCONF_TIME_IS_A_SYSCALL
+
+/* Since the i80 code generator doesn't support floating point, don't include
+ * it in the stdio libraries. */
+ 
+#define ACKCONF_NO_STDIO_FLOAT
+
+#endif

+ 66 - 0
plat/cpm/include/cpm.h

@@ -0,0 +1,66 @@
+/*
+ * unistd.h - standard system calls
+ */
+/* $Id$ */
+
+#ifndef _CPM_H
+#define _CPM_H
+
+#include <stdint.h>
+
+/* These interface provides a very bare-bones interface to the CP/M BDOS. Set
+ * the following four variables as you wish, call cpm_bdos(), and the contents
+ * of the variables will have been updated accordingly. */
+ 
+extern uint8_t cpm_a_register;
+extern uint16_t cpm_bc_register;
+extern uint16_t cpm_de_register;
+extern uint16_t cpm_hl_register;
+
+extern void cpm_bdos(void);
+
+/* Describes the available CP/M BDOS calls. They're a fairly conservative set
+ * taken from the CP/M 2.0 manual. */
+
+enum
+{
+	CPM_BDOS_SYSTEM_RESET,
+	CPM_BDOS_CONSOLE_INPUT,
+	CPM_BDOS_CONSOLE_OUTPUT,
+	CPM_BDOS_READER_INPUT,
+	CPM_BDOS_PUNCH_OUTPUT,
+	CPM_BDOS_LIST_OUTPUT,
+	CPM_BDOS_CONSOLE_IO,
+	CPM_BDOS_GET_IO_BYTE,
+	CPM_BDOS_SET_IO_BYTE,
+	CPM_BDOS_PRINT_STRING,
+	CPM_BDOS_READ_CONSOLE_BUFFER,
+	CPM_BDOS_GET_CONSOLE_STATUS,
+	CPM_BDOS_GET_VERSION_NUMBER,
+	CPM_BDOS_RESET_DISK_SYSTEM,
+	CPM_BDOS_SELECT_DISK,
+	CPM_BDOS_OPEN_FILE,
+	CPM_BDOS_CLOSE_FILE,
+	CPM_BDOS_SEARCHFIRST,
+	CPM_BDOS_SEARCHNEXT,
+	CPM_BDOS_DELETE_FILE,
+	CPM_BDOS_READ_SEQ,
+	CPM_BDOS_WRITE_SEQ,
+	CPM_BDOS_MAKE_FILE,
+	CPM_BDOS_RENAME_FILE,
+	CPM_BDOS_GET_LOGIN_VECTOR,
+	CPM_BDOS_GET_CURRENT_DISK,
+	CPM_BDOS_SET_DMA_ADDRESS,
+	CPM_BDOS_GET_ALLOC_VECTOR,
+	CPM_BDOS_WRITE_PROTECT,
+	CPM_BDOS_GET_RO_VECTOR,
+	CPM_BDOS_SET_FILE_ATTR,
+	CPM_BDOS_GET_DISK_PARMS,
+	CPM_BDOS_SETGET_USER,
+	CPM_BDOS_READ_RANDOM,
+	CPM_BDOS_WRITE_RANDOM,
+	CPM_BDOS_GET_FILE_SIZE,
+	CPM_BDOS_SET_RANDOM
+};
+
+#endif

+ 71 - 0
plat/cpm/include/unistd.h

@@ -0,0 +1,71 @@
+/*
+ * unistd.h - standard system calls
+ */
+/* $Id$ */
+
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+#include <stddef.h>
+
+/* Types */
+
+typedef int pid_t;
+typedef int mode_t;
+
+/* Constants for file access (open and friends) */
+
+enum
+{
+	O_ACCMODE = 0x3,
+	
+	O_RDONLY = 0,
+	O_WRONLY = 1,
+	O_RDWR = 2,
+	
+	O_CREAT = 0100,
+	O_TRUNC = 01000,
+	O_APPEND = 02000,
+	O_NONBLOCK = 04000
+};
+
+/* Special variables */
+
+extern char** environ;
+
+/* Implemented system calls */
+
+extern void _exit(int);
+extern pid_t getpid(void);
+extern void* sbrk(intptr_t increment);
+extern int isatty(int d);
+extern off_t lseek(int fildes, off_t offset, int whence);
+extern int close(int d);
+extern int open(const char* path, int access, ...);
+extern int creat(const char* path, mode_t mode);
+extern int read(int fd, void* buffer, size_t count);
+extern int write(int fd, void* buffer, size_t count);
+
+/* Unimplemented system calls (these are just prototypes to let the library
+ * compile). */
+
+extern int fcntl(int fd, int op, ...);
+
+/* Signal handling */
+
+typedef int sig_atomic_t;
+
+#define SIG_ERR ((sighandler_t) -1)           /* Error return.  */
+#define SIG_DFL ((sighandler_t) 0)            /* Default action.  */
+#define SIG_IGN ((sighandler_t) 1)            /* Ignore signal.  */
+
+#define SIGABRT         6       /* Abort (ANSI) */
+#define SIGILL          11      /* Illegal instruction */
+
+#define _NSIG           32      /* Biggest signal number + 1
+                                   (not including real-time signals).  */
+typedef void (*sighandler_t)(int);
+extern sighandler_t signal(int signum, sighandler_t handler);
+extern int raise(int signum);
+
+#endif

+ 55 - 0
plat/cpm/libsys/_bdos.s

@@ -0,0 +1,55 @@
+#
+! $Source$
+! $State$
+! $Revision$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.sect .text
+
+! Calls a BDOS routine.
+
+.define _cpm_bdos
+_cpm_bdos:
+	push b
+	
+	lda _cpm_a_register
+	
+	lhld _cpm_bc_register
+	mov b, h
+	mov c, l
+	
+	lhld _cpm_de_register
+	mov d, h
+	mov e, l
+	
+	lhld _cpm_hl_register
+	
+	call 5
+	
+	shld _cpm_hl_register
+	
+	mov h, d
+	mov l, e
+	shld _cpm_de_register
+	
+	mov h, b
+	mov l, c
+	shld _cpm_bc_register
+	
+	sta _cpm_a_register
+	
+	pop b
+	ret
+
+.sect .bss
+.define _cpm_a_register, _cpm_bc_register, _cpm_de_register, _cpm_hl_register
+.comm _cpm_a_register, 1
+.comm _cpm_bc_register, 2
+.comm _cpm_de_register, 2
+.comm _cpm_hl_register, 2

+ 19 - 0
plat/cpm/libsys/_hol0.s

@@ -0,0 +1,19 @@
+#
+! $Source$
+! $State$
+! $Revision$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.sect .bss
+
+! This data block is used to store information about the current line number
+! and file.
+
+.define hol0
+.comm hol0, 8

+ 50 - 0
plat/cpm/libsys/_inn2.s

@@ -0,0 +1,50 @@
+#
+! $Source$
+! $State$
+! $Revision$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+! Bit test on 16 bits set
+! Expects on stack:	bit number
+!			set to be tested
+! Yields in de-registers:	0 if bit is reset or bit number out of range
+!				1 if bit is set
+
+.sect .text
+.define .inn2
+.inn2:	pop h
+	shld .retadr
+
+	pop d		!bit number
+	pop h		!set to be tested
+	mov a,e
+	cpi 16
+	jnc 3f
+	cpi 8
+	jnc 1f
+	mov e,a
+	mov a,l		!l-reg contains the wanted bit
+	jmp 2f
+
+1:	sbi 8
+	mov e,a
+	mov a,h		!h-reg contains the wanted bit
+
+2:	dcr e
+	jm 4f
+	rar
+	jmp 2b
+
+3:	xra a		!return 0 if bit number out of range
+4:	ani 1
+	mov e,a
+	mvi d,0
+
+	lhld .retadr
+	pchl

+ 220 - 0
plat/cpm/libsys/_trap.s

@@ -0,0 +1,220 @@
+#
+! $Source$
+! $State$
+! $Revision$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.define .trp
+.define earray, erange, eset, eiovfl, efovfl, efunfl, eidivz, eidivz
+.define efdivz, eiund, efund, econv, estack, eheap, eillins, eoddz
+.define ecase, ememflt, ebadptr, ebadpc, ebadlae, ebadmon, ebadlin, ebadgto
+.define eunimpl
+
+.sect .text
+
+! Trap routine
+! Expects trap number on stack.
+! Just returns if trap has to be ignored.
+! Otherwise it calls a user-defined trap handler if provided.
+! When no user-defined trap handler is provided or when the user-defined
+! trap handler causes a new trap, a message is printed
+! and control is returned to the monitor.
+
+	EARRAY	=  0
+	ERANGE	=  1
+	ESET	=  2
+	EIOVFL	=  3
+	EFOVFL	=  4
+	EFUNFL	=  5
+	EIDIVZ	=  6
+	EFDIVZ	=  7
+	EIUND	=  8
+	EFUND	=  9
+	ECONV	= 10
+	ESTACK  = 16
+	EHEAP	= 17
+	EILLINS = 18
+	EODDZ	= 19
+	ECASE	= 20
+	EMEMFLT	= 21
+	EBADPTR = 22
+	EBADPC  = 23
+	EBADLAE = 24
+	EBADMON = 25
+	EBADLIN = 26
+	EBADGTO = 27
+	EUNIMPL = 63		! unimplemented em-instruction called
+
+earray:	lxi h,EARRAY
+	push h
+	call .trp
+	ret
+
+erange:	lxi h,ERANGE
+	push h
+	call .trp
+	ret
+
+eset:	lxi h,ESET
+	push h
+	call .trp
+	ret
+
+eiovfl:	lxi h,EIOVFL
+	push h
+	call .trp
+	ret
+
+efovfl:	lxi h,EFOVFL
+	push h
+	call .trp
+	ret
+
+efunfl:	lxi h,EFUNFL
+	push h
+	call .trp
+	ret
+
+eidivz:	lxi h,EIDIVZ
+	push h
+	call .trp
+	ret
+
+efdivz:	lxi h,EFDIVZ
+	push h
+	call .trp
+	ret
+
+eiund:	lxi h,EIUND
+	push h
+	call .trp
+	ret
+
+efund:	lxi h,EFUND
+	push h
+	call .trp
+	ret
+
+econv:	lxi h,ECONV
+	push h
+	call .trp
+	ret
+
+estack:	lxi h,ESTACK
+	push h
+	call .trp
+	ret
+
+eheap:	lxi h,EHEAP
+	push h
+	call .trp
+	ret
+
+eillins:lxi h,EILLINS
+	push h
+	call .trp
+	ret
+
+eoddz:	lxi h,EODDZ
+	push h
+	call .trp
+	ret
+
+ecase:	lxi h,ECASE
+	push h
+	call .trp
+	ret
+
+ememflt:lxi h,EMEMFLT
+	push h
+	call .trp
+	ret
+
+ebadptr:lxi h,EBADPTR
+	push h
+	call .trp
+	ret
+
+ebadpc:	lxi h,EBADPC
+	push h
+	call .trp
+	ret
+
+ebadlae:lxi h,EBADLAE
+	push h
+	call .trp
+	ret
+
+ebadmon:lxi h,EBADMON
+	push h
+	call .trp
+	ret
+
+ebadlin:lxi h,EBADLIN
+	push h
+	call .trp
+	ret
+
+ebadgto:lxi h,EBADGTO
+	push h
+	call .trp
+	ret
+
+eunimpl:lxi h,EUNIMPL
+	push h
+	call .trp
+	ret
+
+.trp:
+	pop h
+	xthl
+	push h			! trap number and return address exchanged
+	mov a,l
+	cpi 16
+	jnc 3f			! jump if trap cannot be ignored
+
+! check if trap has to be ignored
+	xchg			! de = trap number
+	lhld .ignmask
+	push h			! hl = set to be tested
+	push d
+	call .inn2		! de = 1 if bit is set, 0 otherwise
+	mov a,e
+	rar
+	jnc 3f			! jump if trap should not be ignored
+	pop h			! remove trap number
+	ret			! OGEN DICHT EN ... SPRING!!!
+
+3:
+	lhld .trapproc		! user defined trap handler?
+	mov a,l
+	ora h
+	jz 1f			! jump if there was not
+	xra a
+	sta .trapproc		! .trapproc := 0
+	sta .trapproc+1
+	lxi d,2f		
+	push d
+	pchl			! call user defined trap handler
+2:
+	pop d
+	ret
+1:
+	lxi h, 1
+	push h
+	lxi h, text
+	push h
+	lxi h, 6
+	push h
+	call _write
+	jmp EXIT
+
+.sect .rom
+text: .ascii "TRAP!\n"
+

+ 43 - 0
plat/cpm/libsys/brk.c

@@ -0,0 +1,43 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#define	OUT_OF_MEMORY (void*)(-1)	/* sbrk returns this on failure */
+#define STACK_BUFFER 128 /* number of bytes to leave for stack */
+
+extern char _end[1];
+static char* current = _end;
+
+int brk(void* newend)
+{
+	/* We determine the amount of free memory by looking at the address of the
+	 * BDOS vector at 0x0006. */
+	char* memtop = (char*) ((*(unsigned char*)0x0007)<<8);
+	char* p = newend;
+	
+	if ((p >= memtop) ||
+	    (p < _end))	
+		return -1;
+		
+	current = p;
+	return 0;
+}
+
+void* sbrk(intptr_t increment)
+{
+	char* old;
+	
+	if (increment == 0)
+		return current;
+		
+	old = current;
+	if (brk(old + increment) < 0)
+		return OUT_OF_MEMORY;
+		
+	return old;
+}

+ 14 - 0
plat/cpm/libsys/close.c

@@ -0,0 +1,14 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int close(int fd)
+{
+	errno = EBADF;
+	return -1;
+}

+ 13 - 0
plat/cpm/libsys/creat.c

@@ -0,0 +1,13 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int creat(const char* path, int mode)
+{
+	return open(path, O_CREAT|O_WRONLY|O_TRUNC, mode);
+}

+ 28 - 0
plat/cpm/libsys/errno.s

@@ -0,0 +1,28 @@
+#
+! $Source$
+! $State$
+! $Revision$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+#define D(e) .define e; e
+
+.sect .data
+
+! Define various ACK error numbers. Note that these are *not* ANSI C
+! errnos, and are used for different purposes.
+
+D(ERANGE)         = 1
+D(ESET)           = 2
+D(EIDIVZ)         = 6
+D(EHEAP)          = 17
+D(EILLINS)        = 18
+D(EODDZ)          = 19
+D(ECASE)          = 20
+D(EBADMON)        = 25
+

+ 13 - 0
plat/cpm/libsys/getpid.c

@@ -0,0 +1,13 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+pid_t getpid(void)
+{
+	return 0;
+}

+ 13 - 0
plat/cpm/libsys/isatty.c

@@ -0,0 +1,13 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int isatty(int fd)
+{
+	return 1;
+}

+ 14 - 0
plat/cpm/libsys/kill.c

@@ -0,0 +1,14 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int kill(pid_t pid, int sig)
+{
+	errno = EINVAL;
+	return -1;
+}

+ 14 - 0
plat/cpm/libsys/lseek.c

@@ -0,0 +1,14 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+	errno = EINVAL;
+	return -1;
+}

+ 14 - 0
plat/cpm/libsys/open.c

@@ -0,0 +1,14 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+int open(const char* path, int access, ...)
+{
+	errno = EACCES;
+	return -1;
+}

+ 29 - 0
plat/cpm/libsys/pmfile

@@ -0,0 +1,29 @@
+-- $Source$
+-- $State$
+-- $Revision$
+
+local d = ROOTDIR.."plat/cpm/libsys/"
+
+libsys_cpm = acklibrary {
+	ACKINCLUDES = {"%BINDIR%include"},
+	
+	ackfile (d.."errno.s"),
+	ackfile (d.."_hol0.s"),
+	ackfile (d.."_bdos.s"),
+	ackfile (d.."_trap.s"),
+	ackfile (d.."_inn2.s"),
+	ackfile (d.."open.c"),
+	ackfile (d.."creat.c"),
+	ackfile (d.."close.c"),
+	ackfile (d.."read.c"),
+	ackfile (d.."write.c"),
+	ackfile (d.."brk.c"),
+	ackfile (d.."getpid.c"),
+	ackfile (d.."kill.c"),
+	ackfile (d.."isatty.c"),
+	ackfile (d.."lseek.c"),
+	ackfile (d.."time.c"),
+	ackfile (d.."signal.c"),
+	
+	install = pm.install("%BINDIR%lib/%PLATFORM%/libsys.a"),
+}

+ 38 - 0
plat/cpm/libsys/read.c

@@ -0,0 +1,38 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <cpm.h>
+
+int read(int fd, void* buffer, size_t count)
+{
+	char i;
+	
+	/* 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;
+	cpm_bdos();
+
+	if (cpm_a_register == '\r') 
+		cpm_a_register = '\n';
+	*(char*)buffer = cpm_a_register;
+	
+	return 1;
+}

+ 14 - 0
plat/cpm/libsys/signal.c

@@ -0,0 +1,14 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+
+sighandler_t signal(int signum, sighandler_t handler)
+{
+	return SIG_DFL;
+}

+ 16 - 0
plat/cpm/libsys/time.c

@@ -0,0 +1,16 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+
+time_t time(time_t* t)
+{
+	if (t)
+		*t = 0;
+	return 0;
+}

+ 51 - 0
plat/cpm/libsys/write.c

@@ -0,0 +1,51 @@
+/* $Source$
+ * $State$
+ * $Revision$
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <cpm.h>
+
+void _sys_write_tty(char c)
+{
+	cpm_bc_register = CPM_BDOS_CONSOLE_OUTPUT;
+	cpm_de_register = c;
+	cpm_bdos();
+
+	if (c == '\n')
+	{
+		cpm_bc_register = CPM_BDOS_CONSOLE_OUTPUT;
+		cpm_de_register = '\r';
+		cpm_bdos();
+	}
+}
+
+int write(int fd, void* buffer, size_t count)
+{
+	int i;
+	char* p = buffer;
+	
+	/* We're only allowed to write to fd 0, 1 or 2. */
+	
+	if ((fd < 0) || (fd > 2))
+	{
+		errno = EBADF;
+		return -1;
+	}
+	
+	/* Write all data. */
+	
+	i = 0;
+	while (i < count)
+	{
+		_sys_write_tty(*p++);
+			
+		i++;
+	}
+	
+	/* No failures. */
+	
+	return count;
+}

+ 47 - 0
plat/cpm/pmfile

@@ -0,0 +1,47 @@
+-- $Source$
+-- $State$
+-- $Revision$
+
+local d = ROOTDIR.."plat/cpm/"
+
+include (d.."libsys/pmfile")
+
+local bootsector = ackfile {
+	file (d.."boot.s"),
+	install = pm.install("%BINDIR%lib/cpm/boot.o"),
+}
+
+local descr = group {
+	install = pm.install(d.."descr", "%BINDIR%%PLATIND%/%PLATFORM%/descr")
+}
+
+local headers = group {
+	install = {
+		pm.install(d.."include/ack/config.h", "%BINDIR%%PLATIND%/%PLATFORM%/include/ack/config.h"),
+		pm.install(d.."include/unistd.h",     "%BINDIR%%PLATIND%/%PLATFORM%/include/unistd.h"),
+		pm.install(d.."include/cpm.h",        "%BINDIR%%PLATIND%/%PLATFORM%/include/cpm.h"),
+	}
+}
+
+platform_cpm = group {
+	ARCH = "i80",
+	PLATFORM = "cpm",
+	OPTIMISATION = "-O",
+	
+	-- Ensure the descr and headers are installed first because we'll need
+	-- them to build the libraries.
+	
+	descr,
+	headers,
+	
+	-- Build the back-end support.
+	
+	mach_i80,
+	support_i80,
+	lang_runtimes,
+
+	-- Build the CP/M syscall library.
+	
+	libsys_cpm,
+	bootsector,
+}

+ 5 - 3
pmfile

@@ -95,6 +95,9 @@ include "plat/pc86/pmfile"             -- PC standalone
 include "mach/i386/pmfile"             -- generic i386
 include "plat/linux386/pmfile"         -- Linux executables
 
+include "mach/i80/pmfile"              -- generic 8080
+include "plat/cpm/pmfile"              -- CP/M
+
 default = group {
 	-- Lots of things use LLgen, so we need to build it first.
 	
@@ -161,8 +164,6 @@ default = group {
 	mach_6805,
 	mach_6809,
 	mach_arm,      lang_runtimes { ARCH="arm", OPTIMISATION="-O" },
-	mach_i386,     lang_runtimes { ARCH="i386", OPTIMISATION="-O" },
-	mach_i80,      lang_runtimes { ARCH="i80", OPTIMISATION="-O" },
 
 	mach_m68020,   lang_runtimes { ARCH="m68020", OPTIMISATION="-O" },
 --	mach_m68k2,    lang_runtimes { ARCH="m68k2", OPTIMISATION="-O" },
@@ -178,7 +179,8 @@ default = group {
 	-- Build the platforms.
 	
 	platform_pc86,
-	platform_linux386
+	platform_linux386,
+	platform_cpm
 }
 
 -- Ensure that the work directories exist.