.define .mon
.define uxfinish
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text

! monitor instruction
! a small collection of UNIX system calls implemented under CP/M

!	ux_indir=e.mon
!	ux_fork=e.mon
!	ux_wait=e.mon
!	ux_link=e.mon
!	ux_exec=e.mon
!	ux_chdir=e.mon
!	ux_mknod=e.mon
!	ux_chmod=e.mon
!	ux_chown=e.mon
!	ux_break=e.mon
!	ux_stat=e.mon
!	ux_seek=e.mon
!	ux_mount=e.mon
!	ux_umount=e.mon
!	ux_setuid=e.mon
!	ux_getuid=e.mon
!	ux_stime=e.mon
!	ux_ptrace=e.mon
!	ux_alarm=e.mon
!	ux_fstat=e.mon
!	ux_pause=e.mon
!	ux_utime=e.mon
!	ux_stty=e.mon
!	ux_gtty=e.mon
!	ux_access=e.mon
!	ux_nice=e.mon
!	ux_sync=e.mon
!	ux_kill=e.mon
!	ux_dup=e.mon
!	ux_pipe=e.mon
!	ux_times=e.mon
!	ux_prof=e.mon
!	ux_unused=e.mon
!	ux_setgid=e.mon
!	ux_getgid=e.mon
!	ux_sig=e.mon
!	ux_umask=e.mon
!	ux_chroot=e.mon

	EPERM	= 1
	ENOENT	= 2
	ESRCH	= 3
	EINTR	= 4
	EIO	= 5
	ENXIO	= 6
	E2BIG	= 7
	ENOEXEC	= 8
	EBADF	= 9
	ECHILD	= 10
	EAGAIN	= 11
	ENOMEM	= 12
	EACCES	= 13
	EFAULT	= 14
	ENOTBLK	= 15
	EBUSY	= 16
	EEXIST	= 17
	EXDEV	= 18
	ENODEV	= 19
	ENOTDIR	= 20
	EISDIR	= 21
	EINVAL	= 22
	ENFILE	= 23
	EMFILE	= 24
	ENOTTY	= 25
	ETXTBSY	= 26
	EFBIG	= 27
	ENOSPC	= 28
	ESPIPE	= 29
	EROFS	= 30
	EMLINK	= 31
	EPIPE	= 32
	EDOM	= 33
! Structure of filearea maintained by this implementation
! First iobuffer of 128 bytes
! Then the fcb area of 36 bytes
! The number of bytes left in the buffer, 1 byte
! The iopointer into the buffer, 2 bytes
! The openflag 0 unused, 1 reading, 2 writing, 1 byte
! The filedescriptor starting at 3, 1 byte
! The number of CTRL-Zs that have been absorbed, 1 byte
! The byte read after a sequence of CTRL-Zs, 1 byte

	maxfiles=8
	filesize=128+36+1+2+1+1+1+1

	filefcb=0	! pointers point to fcb
	position=33
	nleft=36
	iopointer=37
	openflag=39
	fildes=40
	zcount=41
	zsave=42

	.assert [ filefcb] <> 0

0:	.space maxfiles*filesize
	filearea = 0b+128
sibuf:
	.data2 0
	.space 82
siptr:	.space 2
saveargs:
	.space 128
argc:	.space 2
ttymode:.data1 9,9,8,21;.data2 06310+RAW*040	! raw = 040

return:
	.data2 0,0
uxinit:
	xor a
	ld c,maxfiles
	ld hl,0b
1:	ld b,filesize
2:	ld (hl),a
	inc hl
	djnz 2b
	dec c
	jr nz,1b
	ret

uxfinish:
	ld a,maxfiles-1
1:	push af
	call closefil
	pop af
	dec a
	cp 0377
	jr nz,1b
	ret

.mon:
	pop ix
	ld (return),ix	! return adres
	pop de		! system call number
	xor a
	or d
	jr nz,unimpld	! too big
	ld a,e
	and 0300	! only 64 system calls
	jr nz,unimpld
	sla e
	ld hl,systab
	add hl,de
	ld e,(hl)
	inc hl
	ld d,(hl)
	ex de,hl
	jp (hl)

systab:	
	.data2 e.mon	! ux_indir
	.data2 ux_exit
	.data2 e.mon	! ux_fork
	.data2 ux_read
	.data2 ux_write
	.data2 ux_open
	.data2 ux_close
	.data2 e.mon	! ux_wait
	.data2 ux_creat
	.data2 e.mon	! ux_link
	.data2 ux_unlink
	.data2 e.mon	! ux_exec
	.data2 e.mon	! ux_chdir
	.data2 ux_time
	.data2 e.mon	! ux_mknod
	.data2 e.mon	! ux_chmod
	.data2 e.mon	! ux_chown
	.data2 e.mon	! ux_break
	.data2 e.mon	! ux_stat
	.data2 e.mon	! ux_seek
	.data2 ux_getpid
	.data2 e.mon	! ux_mount
	.data2 e.mon	! ux_umount
	.data2 e.mon	! ux_setuid
	.data2 e.mon	! ux_getuid
	.data2 e.mon	! ux_stime
	.data2 e.mon	! ux_ptrace
	.data2 e.mon	! ux_alarm
	.data2 e.mon	! ux_fstat
	.data2 e.mon	! ux_pause
	.data2 e.mon	! ux_utime
	.data2 e.mon	! ux_stty
	.data2 e.mon	! ux_gtty
	.data2 e.mon	! ux_access
	.data2 e.mon	! ux_nice
	.data2 ux_ftime
	.data2 e.mon	! ux_sync
	.data2 e.mon	! ux_kill
	.data2 unimpld
	.data2 unimpld
	.data2 unimpld
	.data2 e.mon	! ux_dup
	.data2 e.mon	! ux_pipe
	.data2 e.mon	! ux_times
	.data2 e.mon	! ux_prof
	.data2 e.mon	! ux_unused
	.data2 e.mon	! ux_setgid
	.data2 e.mon	! ux_getgid
	.data2 e.mon	! ux_sig
	.data2 unimpld
	.data2 unimpld
	.data2 unimpld
	.data2 unimpld
	.data2 unimpld
	.data2 ux_ioctl
	.data2 unimpld
	.data2 unimpld
	.data2 unimpld
	.data2 unimpld
	.data2 unimpld	! ux_exece
	.data2 e.mon	! ux_umask
	.data2 e.mon	! ux_chroot
	.data2 unimpld
	.data2 unimpld

emptyfile:
	! searches for a free filestructure
	! returns pointer in iy, 0 if not found
	ld ix,filearea
	ld l,maxfiles
1:
	xor a
	or (ix+openflag)
	jr nz,3f
	ld a,maxfiles+3
	sub l
	ld (ix+fildes),a
! #ifdef	CPM1
	push iy
	push ix
	ld de,-128
	add ix,de
	push ix
	pop de
	ld c,setdma
	call bdos
	pop ix
	pop iy
	or a		! to clear C
! #endif
	ret
3:
	ld de,filesize
	add ix,de
	dec l
	jr nz,1b
	scf
	ret

findfile:
	ld ix,filearea
	ld de,filesize
0:
	dec a
	ret m
	add ix,de
	jr 0b

getchar:
	push iy
	push de
	push hl
	dec (ix+nleft)
	jp p,0f
	push ix
	pop hl
	ld de,-128
	add hl,de
	ld (ix+iopointer),l
	ld (ix+iopointer+1),h
	ex de,hl
	push ix
	ld c,setdma
	call bdos
! #ifdef	CPM1
	ld c,seqread
! #else
! 	ld c,randomread
! #endif
	pop de
	call bdos
	or a
	jr z,1f
	ld (ix+zcount),0
	pop hl
	pop de
	pop iy
	scf
	ret
1:
	inc (ix+position)
	jr nz,2f
	inc (ix+position+1)
2:
	ld a,127
	ld (ix+nleft),a
0:
	ld h,(ix+iopointer+1)
	ld l,(ix+iopointer)
	ld a,(hl)
	inc hl
	ld (ix+iopointer),l
	ld (ix+iopointer+1),h
	pop hl
	pop de
	pop iy
	ret
	or a

putchar:
	push hl
	ld h,(ix+iopointer+1)
	ld l,(ix+iopointer)
	ld (hl),a
	dec (ix+nleft)
	jr z,0f
	inc hl
	ld (ix+iopointer+1),h
	ld (ix+iopointer),l
	pop hl
	ret
0:
	pop hl
flsbuf:
	push hl
	push de
	push iy
	push ix
	pop hl
	ld de,-128
	add hl,de
	ld (ix+iopointer+1),h
	ld (ix+iopointer),l
	ex de,hl
	push ix
	ld c,setdma
	call bdos
	pop de
! #ifdef	CPM1
	ld c,seqwrite
! #else
! 	ld c,randomwrite
! #endif
	call bdos
	or a
	jr z,1f
	pop iy
	pop de
	pop hl
	scf
	ret
1:
	inc (ix+position)
	jr nz,2f
	inc (ix+position+1)
2:
	ld a,128
	ld (ix+nleft),a
	ld b,a
	push ix
	pop hl
	ld de,-128
	add hl,de
	ld a,26			! ctrl z
1:	ld (hl),a
	inc hl
	djnz 1b
	pop iy
	pop de
	pop hl
	or a
	ret

parsename:
	! parses file name pointed to by hl and fills in fcb
	! of the file pointed to by ix.
	! recognizes filenames as complicated as 'b:file.zot'
	! and as simple as 'x'

	push iy
	push ix
	pop de
	xor a
	push de
	ld b,36		! sizeof fcb
0:	ld (de),a
	inc de
	djnz 0b
	pop de
	inc hl
	ld a,(hl)
	dec hl
	cp ':'		! drive specified ?
	jr nz,1f
	ld a,(hl)
	inc hl
	inc hl
	dec a
	and 15
	inc a		! now 1<= a <= 16
	ld (de),a
1:	inc de
	ld b,8		! filename maximum of 8 characters
1:	ld a,(hl)
	or a
	jr nz,8f
	dec hl
	ld a,'.'
8:
	inc hl
	cp '.'
	jr z,2f
	and 0177	! no parity
	bit 6,a
	jr z,9f
	and 0337	! UPPER case
9:
	ld (de),a
	inc de
	djnz 1b
	ld a,(hl)
	inc hl
	cp '.'
	jr z,3f
	ld a,' '
	ld (de),a
	inc de
	ld (de),a
	inc de
	ld (de),a
	pop iy
	ret		! filenames longer than 8 are truncated
2:	ld a,' '	! fill with spaces
0:	ld (de),a
	inc de
	djnz 0b
3:	ld b,3		! length of extension
1:	ld a,(hl)
	inc hl
	or a
	jr z,4f
	cp 0100
	jp m,2f
	and 0137
2:	ld (de),a
	inc de
	djnz 1b
	pop iy
	ret
4:	ld a,' '
0:	ld (de),a
	inc de
	djnz 0b
	pop iy
	ret

! various routines
ux_close:
	pop hl
	ld a,l
	sub 3
	jp m,1f
	cp maxfiles
	call m,closefil
1:	ld hl,0
	push hl ; jr rtn

closefil:
	call findfile
	ld a,(ix+openflag)
	or a
	jr z,3f
	ld (ix+openflag),0
	cp 1
	jr z,2f
	ld a,(ix+nleft)
	cp 128
	jr z,2f
	call flsbuf
2:
	push iy
	push ix
	pop de
	ld c,close
	call bdos
	pop iy
3:	ret

ux_ioctl:
	pop hl
	ld a,l
	sub 3
	jp p,1f
	pop hl
	ld a,h
	cp 't'
	jr nz,e.mon
	ld a,l
	cp 8
	jr z,tiocgetp
	cp 9
	jr z,tiocsetp
	jr e.mon
1:	pop hl
	pop hl
	ld hl,-1
	push hl ; jr rtn
tiocgetp:
	pop de
	ld hl,ttymode
2:	push bc
	ld bc,6
	ldir
	ld h,b
	ld l,c
	pop bc
	push hl ; jr rtn
tiocsetp:
	pop hl
	ld de,ttymode
	jr 2b

ux_time:
	call time4
rtn:	ld ix,(return) ; jp (ix)

ux_ftime:
	pop hl
	ld (retarea+6),hl
	call time4
	ld hl,(retarea+6)
	pop de
	ld (hl),e
	inc hl
	ld (hl),d
	inc hl
	pop de
	ld (hl),e
	inc hl
	ld (hl),d
	inc hl
	xor a
	ld (hl),a
	inc hl
	ld (hl),a
	inc hl
	ld (hl),a
	inc hl
	ld (hl),a
	inc hl
	ld (hl),a
	inc hl
	ld (hl),a
	ld ix,(return) ; jp (ix)

time4:
	pop hl
	ld (retarea),iy
	ld (retarea+2),bc
	ld (retarea+4),hl
	ld hl,(timebuf+2)
	push hl
	ld hl,(timebuf)
	push hl
	ld hl,0
	push hl
	ld hl,50
	push hl
	call .dvu4
	ld iy,(retarea)
	ld bc,(retarea+2)
	ld hl,(retarea+4)
	jp (hl)
ux_exit:
	call uxfinish
	ld c,reset
	call bdos
	! no return

ux_creat:
	call emptyfile
	jr c,openfailed
	pop hl
	call parsename
	pop hl			! file mode, not used under CP/M
	push iy
	push ix
	push ix
	pop de
	ld c,delete
	call bdos
	pop de
	ld c,makefile
	call bdos
	pop iy
	ld l,1
	jr afteropen
ux_open:
	call emptyfile
	jr nc,1f
openfailed:
	pop hl
	pop hl		! remove params
	ld hl,EMFILE
	push hl
	push hl ; jr rtn
1:
	pop hl		! filename
	call parsename
	push iy
	ld c,open
	push ix
	pop de
	call bdos
	pop iy
	pop hl
afteropen:
	inc a
	jr nz,1f
	ld hl,ENOENT
	push hl
	push hl ; jr rtn
1:
	inc l
	ld (ix+openflag),l
	xor a
	ld (ix+nleft),a
	ld (ix+zcount),a
	ld (ix+zsave),26
	bit 1,l
	jr z,2f
	ld (ix+nleft),128
2:
	ld (ix+position),a
	ld (ix+position+1),a
	push ix
	pop hl
	push bc
	ld b,128
3:	dec hl
	ld (hl),26
	djnz 3b
	pop bc
	ld (ix+iopointer+1),h
	ld (ix+iopointer),l
	ld h,a
	ld l,(ix+fildes)
	push hl
	ld l,a
	push hl ; jr rtn

ux_read:
	pop hl
	ld a,l
	sub 3
	jp p,readfile
	ld a,(ttymode+4)
	bit 5,a
	jr z,1f			! not raw
	push iy
! #ifdef	CPM1
!raw echo interface
	ld c,consolein
	call bdos
! #else
! !no echo interface
! 4:
! 	ld c,diconio
! 	ld e,0xff
! 	call bdos
! 	or a
! 	jr z,4b
!end of no echo interface
! #endif
	pop iy
	pop hl
	ld (hl),a
	pop hl
	ld hl,1
	push hl
	ld hl,0
	push hl ; jr rtn
1:
	ld hl,sibuf+1		! read from console assumed
	dec (hl)
	jp p,2f
	dec hl			! go read console line
	ld (hl),80		! max line length
	push iy
	push hl
	ld c,readconsole
	ex de,hl
	call bdos
	ld c,writeconsole
	ld e,'\n'
	call bdos
	pop hl
	pop iy
	inc hl
	inc (hl)
	ld (siptr),hl		! ready for transfer
	push hl
	ld e,(hl)
	ld d,0
	add hl,de
	ld (hl),'\r'
	inc hl
	ld (hl),'\n'
	pop hl
2:
	push bc
	pop ix
	ld b,(hl)
	inc b			! bytes remaining
	pop hl			! copy to
	pop de			! bytes wanted (probably 512)
	push ix
	ld ix,(siptr)		! copy from
	xor a			! find out minimum of ramaining and wanted
	or d
	jr nz,3f		! more than 255 wanted (forget that)
	ld a,b
	cp e
	jp m,3f			! not enough remaining
	ld b,e
3:
	ld c,b			! keep copy
0:
	inc ix
	ld a,(ix)
	ld (hl),a
	inc hl
	djnz 0b
	ld a,(sibuf+1)
	sub c
	inc a
	ld (sibuf+1),a
	ld (siptr),ix
	pop hl
	push bc
	ld c,b
	push bc			! load 0
	ld b,h
	ld c,l
	ld ix,(return) ; jp (ix)
readfile:
	call findfile
	pop de
	pop hl			! count
	push bc
	ld bc,0
0:
	xor a
	or l
	jr z,1f
	dec l
3:
! warning: this may not work if zcount overflows
	ld a,(ix+zcount)
	or a
	jr nz,5f
	ld a,(ix+zsave)
	cp 26
	jr z,4f
	ld (ix+zsave),26
	jr 8f
4:
	call getchar
	jr c,2f
	ld (de),a
	sub 26		! CTRL-Z
	jr z,7f
	ld a,(ix+zcount)
	or a
	jr z,6f
	ld a,(de)
	ld (ix+zsave),a
5:
	ld a,26
	dec (ix+zcount)
8:
	ld (de),a
6:
	inc de
	inc bc
	jr 0b
1:
	dec l
	dec h
	jp p,3b
2:
	pop hl
	push bc
	ld b,h
	ld c,l
	ld hl,0
	push hl ; jr rtn
7:
	inc (ix+zcount)
	jr 4b

ux_write:
	pop hl
	ld a,l
	sub 3
	jp p,writefile
	pop hl			! buffer address
	pop de			! count
	push de
	ld ix,0
	push ix
	push bc
	ld b,e			! count now in 'db'
0:
	ld a,b
	or a
	jr nz,1f
	ld a,d
	or a
	jr nz,2f
	pop bc
	ld ix,(return) ; jp (ix)
2:
	dec d
1:
	dec b
	ld e,(hl)
	inc hl
	push bc
	push de
	push hl
	ld c,writeconsole
	call bdos
	pop hl
	pop de
	pop bc
	jr 0b
writefile:
	call findfile
	pop de
	pop hl			! count
	push bc
	ld bc,0
0:
	xor a
	or l
	jr z,1f
	dec l
3:
	ld a,(de)
	inc de
	call putchar
	jr c,4f
	inc bc
	jr 0b
1:
	dec l
	dec h
	jp p,3b
	ld ix,0
2:
	pop hl
	push bc
	ld b,h
	ld c,l
	push ix
	ld ix,(return) ; jp (ix)
4:
	ld ix,ENOSPC
	jr 2b

ux_unlink:
	pop hl
	ld ix,fcb
	call parsename
	push bc
	ld c,delete
	ld de,fcb
	call bdos
	pop bc
	inc a
	jr nz,1f
	ld hl,ENOENT
	push hl ; jr rtn
1:
	ld hl,0
	push hl ; jr rtn

ux_getpid:
	ld hl,12345		! nice number
	push hl ; jr rtn






retarea: .data2 0	! base of buffer for result values (max 8 bytes)
	 .data2 0
	 .data2 0
	 .data2 0

trapproc:
	.data2 0

nextp:	.data1 0

header:
ntext:	.data2 0
ndata:	.data2 0
nproc:	.data2 0
entry:	.data2 0
nline:	.data2 0

hp:	.data2 0
pb:	.data2 0
pd:	.data2 0