; The majority of assembly code is collected here.

	INCLUDE	"hardware.i"

	; Imported
	XREF	_udata
	XREF	_unix
	XREF	_clk_int
	XREF	_di
	XREF	_retaddr

	; Exported
	XDEF	_doexec
	XDEF	SysCall
	XDEF	_leave_kernel
	XDEF	_call_clock
	XDEF	_qcopy
	XDEF	_qswap
	XDEF	_save_isp
	XDEF	_save_pisp
	XDEF	_unsleep
	XDEF	_lunsleep
	XDEF	_ChrAt
	XDEF	_renderClear
	XDEF	_Screen
	XDEF	_CharSet
	XDEF	_renderUp
	XDEF	_renderDown
	XDEF	_keymap
	XDEF	_key_delay
	XDEF	_leave_trap
	XDEF	_get_ccr
	XDEF	_move_isp
	XDEF	_save_int
	XDEF	_qzero
	XDEF	_get_isp
	XDEF	_get_msp

	INCLUDE "sys/offsets.i"

	MACHINE	68020

	SECTION CODE

; Starts a program after an exec()
_doexec:
	move.l	4(sp),a0
	move.l	a0,usp			; New stack pointer
	move.l	8(sp),a0		; Eventual destination
	move.w	#$0000,-(sp)		; Format code (I hope)
	move.l	#user_exit,-(sp)	; Don't clear OSYS until in user mode
	move.w	#$0000,-(sp)		; Fake SR
	rte				; And now: to user mode


	; The level 3 isr calls this so it can call clk_int properly
	; Note that the low-level isr does a full movem before this
_call_clock:
	unlk	a5			; Dump isr's locals and ret addr
	add.l	#8,sp			; Dump low-level isr's ret addr & arg
	move.l	usp,a3			; User context
	move.l	a3,-(sp)
	move.l	sp,a3			; Kernel context
	move.l	a3,-(sp)		; Variation of link/unlk
	jsr	_clk_int		; Enter the kernel proper

	; Saves the context of a process that was pre-empted (in user space)
	; onto it's own interrupt stack so that it can be restarted later.
	; XXX THIS IS BROKEN.  IF THE CURRENT ISR IS THE CURRENT PROCESS'
	; XXX THEN IT WILL DIE, HORRIBLY.
_save_int:
	move.l	_udata+OPTAB,a0		; Get address of private stack
	move.l	OISP(a0),a0
	sub.l	#72,a0			; Leave room for whole context
	move.l	4(sp),a1		; Copy from pointer
	move.l	#17,d0			; Number of copies to do
sicp:	move.l	(a1)+,(a0)+
	dbf	d0,sicp
	sub.l	#72,a0			; Pretend stack pointer
	move.l	a0,d0			; Return the context pointer
	rts


; Trap server for 0 (system calls)
; Note that we construct the stack for unix() in the order that UZI did it
; not the order that the 68000 would normally.
; This version of SysCall corrupts the user's a0 and a1, but these are allowed
; scratch registers anyway.
SysCall:
	move.l	(sp)+,d0
	move.l	(sp)+,d1
	move.l	_udata+OPTAB,a0		; Process table entry
	move.l	OISP(a0),sp		; Private stack now
	move.l	d1,-(sp)		; PC low and SR
	move.l	d0,-(sp)		; PC high and format
	movem.l	d0-d7/a0-a6,-(sp)	; Call me suspicious...
	jsr	_di			; Turn interrupts off
	move.l	usp,a3			; This is where the parameters are
	move.l	a3,-(sp)		; Save user context
	move.l	sp,a2			; Sort of a link instruction thingy
	move.l	0(a3),-(sp)		; Copy callno
	move.l	4(a3),-(sp)		; Copy uret
	move.l	8(a3),-(sp)		; Copy argn
	move.l	12(a3),-(sp)		; Copy argn1
	move.l	16(a3),-(sp)		; Copy argn2
	move.l	20(a3),-(sp)		; Copy argn3
	move.l	a2,-(sp)		; Save kerenl exit context
	jsr	_unix			; Enter the kernel proper


	; Global exit point from kernel.  We have to take account
	; of the possibility of a newly switched in process having
	; been previously switched out by something other than a
	; system call, namely the clock interrupt.
	; Also we must ensure that the isp is constantly valid i.e.
	; lower than any data we need in case an interrupt occurs during
	; the exit proceedure.
_leave_kernel:
	move.l	4(sp),a0		; Kernel exit context pointer
	move.l	8(sp),d1		; Return type
	move.l	a0,sp			; Stack frame before calling unix()
	move.l	(sp)+,a1
	move.l	a1,usp			; Retrieve user context
	tst.l	d1			; C param: return type
	beq	justgo
	move.l	_udata+ORVAL,(sp)	; C stack again: return value => d0
	clr.w	60(sp)			; Eventual user sr
	tst.l	_udata+OERR		; Is there an error?
	beq	clrgo			; no - carry on
	ori.w	#1,60(sp)		; Arrange for carry to be set
	move.l	_udata+OERR,(sp)	; Return code is error code
clrgo:	movem.l	(sp)+,d0-d7/a0-a6	; Retrieve registers
	move.l	2(sp),a0		; Will jump here eventually
	move.l	#user_exit,2(sp)	; Reroute the exit destination
	rte
justgo:	movem.l	(sp)+,d0-d7/a0-a6	; Retrieve registers
	rte

user_exit:				; This is executed in user mode
	move.w	ccr,d1			; Keep condition codes
	clr.b	_udata+OSYS		; Out of kernel space
	move.l	a0,_retaddr
	move.w	d1,ccr			; Replace condition codes
	jmp	(a0)			; Return to original program

	; Leave kernel to a trapped signal
_leave_trap:
	move.l	#_udata+OSTORE,a2	; Space in the user area
	move.l	4(sp),a0		; Context
	move.l	8(sp),d1		; Return type (1=syscall)
	move.l	d1,(a2)
	move.l	12(sp),a1		; Pointer to function
	move.l	a1,4(a2)
	move.l	16(sp),d0		; Argument to function
	move.l	a0,sp			; Kernel exit context
	move.l	(sp)+,a3
	move.l	a3,usp			; User context
	swap	d0			; Only 16 bits will be kept
	move.w	60(sp),d0		; Real exit conditions
	move.l	d0,8(a2)
	move.l	62(sp),12(a2)		; Real return address
	movem.l	(sp)+,d0-d7/a0-a6	; Retrieve registers
	move.l	#inuser,2(sp)		; Return to just down there \/
	clr.w	0(sp)			; Status reg is clear
	rte				; And carry on
inuser:
	move.l	_udata+OSTORE+12,-(sp)	; Real return address
	move.l	_udata+OSTORE,-(sp)	; Return type
	move.l	_udata+OSTORE+8,-(sp)	; Real exit conditions
	move.l	_udata+OERR,-(sp)	; Possable error code
	move.l	_udata+ORVAL,-(sp)	; Return value
	movem.l	d0-d7/a0-a6,-(sp)	; Save all registers
	move.l	#_udata+OSTORE,a0	; Space in user area
	move.l	8(a0),d0		; Get the function argument back
	swap	d0			; Function argument back in lower half
	and.l	#$ffff,d0		; Mask off upper half
	move.l	d0,-(sp)		; This is the function argument
	move.l	4(a0),a1		; Pointer to function
	jsr	(a1)			; Calls the signal handler
	add.l	#4,sp			; Discard argument
	movem.l	(sp)+,d0-d7/a0-a6	; Recover all registers
	tst.l	12(sp)			; Test return type
	beq	simgo			; We pre-empted in user space
	move.l	(sp),d0			; C stack again: return value => d0
	tst.l	4(sp)			; Is there an error?
	beq	noerr			; no - carry on
	move.l	4(sp),d0		; Return code is error code
	add.l	#16,sp
	ori.w	#1,ccr			; Arrange for carry to be set
	rts
noerr:	add.l	#16,sp			; Dump some of the stack
	move.w	#0,ccr			; Clear status bits
	rts				; Rest of stack is return address
simgo:	move.w	#$4000,INTENA		; Disable interrupts
	move.l	8(sp),_udata+OSTORE	; Real exit conditions
	add.l	#16,sp			; Remove stuff from stack
	move.l	_udata+OSTORE,-(sp)	; Save them locally
	move.w	#$c000,INTENA		; Enable interrupts
	move.l	(sp)+,ccr		; Restore condition codes
	rts				; Return address was still there



	; Faster version of bcopy for longword multiples
_qcopy:
	move.l	4(sp),a0		; Source
	move.l	8(sp),a1		; Desination
	move.l	12(sp),d0		; Count
	subq.l	#1,d0			; 1 less for dbf
qcop:	move.l	(a0)+,(a1)+		; Read 32 bytes
	dbf	d0,qcop			; loop
	rts


	; Preserves a process' current isp
_save_isp:
	move.l	(sp),a1			; Keep return value in scratch reg
	movem.l	d0-d7/a0-a6,-(sp)	; This stack will be preserved
	move.l	sp,_udata+OCISP		; Save the isp
	move.l	a1,-(sp)
	rts

	; Saves a parent during fork()
_save_pisp:
	move.l	(sp),a1			; Return address saved
	link	a5,#0			; Pretend function call
	movem.l	d0-d7/a0-a6,-(sp)	; Save registers
	move.l	sp,_udata+OCISP		; This is the saved sp
	move.l	a1,-(sp)		; Return address again
	rts

	; Continues a process that stopped because of a psleep()
_unsleep:
	move.l	_udata+OCISP,sp		; Gets back the last stack pointer
	movem.l	(sp)+,d0-d7/a0-a6	; Registers
	unlk	a5			; Leaves return addr to after psleep()
	rts				; Should continue the system call

	; Continues a process that stopped because of a psleep() after swapin
	; was called during an interrupt.  Exits from the interrupt and re-enters
	; the kernel via trap#2
	; We don't want to be doing a (slow) fork() while some intr events are 
	; suppressed.
_lunsleep:
	move.l	4(sp),a0		; Context pointer
	clr.w	-(sp)			; Fake format
	move.l	#out,-(sp)		; Fake pc
	clr.w	-(sp)			; Fake sr
	rte				; Out of interrupt state
out:	move.l	#reent,$88		; Reroute trap #2
	trap	#2			; Back into master state
reent:	move.l	_udata+OCISP,sp		; Last stack frame
	movem.l	(sp)+,d0-d7/a0-a6	; Retrieve registers
	unlk	a5			; Leaves return addr to what called psleep()
	rts				; Hope, Prayer and Magic


	; Places a character on screen at a particular position
_ChrAt:	move.l	#_Screen,a0		; Base of screen memory
	add.l	4(sp),a0		; Memory offset for X
	move.l	8(sp),d0		; Cursor Y position
	mulu.w	#640,d0			; Multiplied by 640
	add.l	d0,a0			; Memory offset for Y
	move.l	12(sp),d0		; Character number
	and.l	#$ff,d0			; Mask off upper bits
	mulu.w	#8,d0			; Address of character matrix
	move.l	#_CharSet,a1		; Address of charset base
	add.l	d0,a1			; current character
	move.l	#7,d0			; count
nline:	move.b	(a1)+,(a0)		; Write a byte, move on in char matrix
	add.l	#80,a0			; Next line on screen
	dbf	d0,nline		; Countdown
	rts

	; Clears the screen
_renderClear:
	move.l	#_Screen,a0
	move.l	#5119,d0
fill:	clr.l	(a0)+
	dbf	d0,fill
	rts

	; Scrolls the screen up
_renderUp:
	move.l	#_Screen,a0	; Destination
	move.l	#_Screen+640,a1	; Source
	move.l	#4959,d0
scrcp1:	move.l	(a1)+,(a0)+
	dbf	d0,scrcp1
	bra	clrline

	; Scrolls the screen down
_renderDown:
	move.l	#_Screen+20480,a0
	move.l	#_Screen+19840,a1	; Source
	move.l	#4959,d0
scrcp2:	move.l	-(a1),-(a0)
	dbf	d0,scrcp2
	move.l	a1,a0

clrline:
	move.l	#159,d0
clrlp:	clr.l	(a0)+
	dbf	d0,clrlp
	rts

_key_delay:
	move.l	#$100,d0
kdly:	dbf	d0,kdly
	rts

	; Must be called in supervisor mode only
_get_ccr:
	move	sr,d0
	rts

	; Transports a child process onto its own stack
_move_isp:
	move.l	4(sp),a0		; Source
	addq	#4,a0			; Because we copy with predecrement
	move.l	8(sp),a1		; Destination
	addq	#4,a1
	move.l	12(sp),d0		; Max copy size
	subq	#1,d0			; For dbcc's benifit
scpl:	move.l	-(a0),-(a1)
;	cmp	a0,sp			; Don't copy past sp
	dbf	d0,scpl			; Testing - pesimal length copy
	move.l	sp,a0			; Current stack
	move.l	4(sp),a1
	sub.l	a0,a1			; Positive offset into stack
	move.l	8(sp),a0		; Pointer into new area
	sub.l	a1,a0			; Offset into stack area
	move.l	a0,sp			; Now on child's stack
	rts				; Fingers crossed...

	; Swaps the contents of two memory areas
_qswap:
	move.l	12(sp),d0		; Length in words
	move.l	4(sp),a0		; One area
	move.l	8(sp),a1		; ...and the other
	subq	#1,d0			; For dbf
swpl:	move.l	(a0),d1			; From first
	move.l	(a1),(a0)+		; From second to first
	move.l	d1,(a1)+		; to second
	dbf	d0,swpl
	rts

	; Quickly clear a memory area
_qzero:
	move.l	4(sp),a0		; Base address
	move.l	8(sp),d0		; Size
	subq	#1,d0
qzlp:	clr.l	(a0)+
	dbf	d0,qzlp
	rts

	; Return the current isp
_get_isp:
	movec	isp,d0
	rts

	; Return the current msp
_get_msp:
	movec	msp,d0
	rts

	SECTION END
	

	SECTION DATA

	; Video memory
	EVEN
_CharSet:
	INCBIN	"charset.256"
_Screen:
	ds.b	20480
_keymap:
	INCBIN	"kmap"

	
	SECTION END

