INSERT[D0LANG];
:TITLE[Kernel];

%
Modified 1 May 1980 by ERF. Bum 11b mi on page 17, 2b mi on page 16,
place timer task code on 16 to limit required IMReserve, add At for exit
from rsr overlay, beautify, reduce overlay IMReserve.
Modified October 3, 1979 by CT.  Made bit 3 the 'Midas present' bit in FFault
Modified August 1, 1979 by CT.  put one-time initialization on page 15
Modified July 26, 1979 by CT.  Added resetmemerrs at DoOverlay
Modified June 28, 1979 by CT.

This kernel consists of three parts:
1) A section that occupies part of pages 0 and 17, runs at task 17, and handles
all communication with Midas with the exception of Mouse halt testing
(which is done by a timer).  This section refreshes the memory frequently
without using a timer.

2) A section that occupies part of page 16, runs at task 16, and handles
kernel initialization, normal memory refresh and Mouse halt testing while a program is running.

3) An initialization section on page 15 that can be wiped out at will.

The idea is that if you have a simple program, you can use the page 16 and 17 parts of
this kernel, and you will get minimal memory refresh and mouse halt testing.
If you need something more complex, you overwrite the stuff on page 16,
but in this case you must supply the code for mouse halt testing.
%
SetTask[17];

RV[REFR,77];	*memory refresh address

*The following registers hold the volatile state of the processor on a fault: 
RV[RXALU,76];	*ALU result and SALUF
RV[RXAPC,75];	*APCTask&APC
RV[RXCTASK,74];	*CTASK.NCIA
RV[RXPPB,73];	*Page,Parity,BootReason
RV[RXSTK,72];	*Stackpointer

RV[RTMP,71];	*temporary

*The following registers are used for D0-Midas communication (RTMP is also used):
RV[RWSTAT,70];	*status register
RV[RDATA,67];	*holds data

*FFault determines how faults will be treated when programs are running.  If it is
*zero, all faults will be reported to Midas.  If FFault is nonzero, the kernel will
*send control through location 120 when a fault occurs and PARITY # 0 (faults with
*PARITY = 0 are breakpoints).
RV[FFault,66];

*Registers between 360 and 365 are used by the Midas overlays.  The following 
*registers, used by WriteMI,  are also in this range.
RV[RADDR,65];
RV[RCNT,64];
RV[RW0,63];
RV[RW1,62];


*Constants for Recv and Send
MC[RecvByte,12];
MC[RecvWord,16];
MC[SendByte,21];
MC[SendWord,25];

IMReserve[17,101,11];	*space for Midas overlays (7500-7517)
IMReserve[17,113,5];

Set[CMDisp,7420];	*8-way dispatch on Midas command
Set[RWDisp,7440];	*4-way dispatch on state bits of RWSTAT

*After loading kernel.mb, Midas starts it at 7000
Start:
	RTMP ← 6000C, At[7000];
	RTMP ← (RTMP) + (400C);	*Notify Task 0, address 6400
KNotify:
	APCTask&APC ← RTMP, GoTo[MidasRestart];

	SetTask[0];
*R definitions
RV[R0,0];
RV[R1,1];
RV[R2,2];
RV[R3,3];
RV[R4,4];
RV[R5,5];
RV[R6,6];
RV[R7,7];
RV[R10,10];
RV[R11,11];
RV[R12,12];
RV[R13,13];
RV[R14,14];
RV[R15,15];
RV[R16,16];
RV[R17,17];


*Clear R0-R17 to avoid R parity errors later
	R1 ← 0C, At[6400];
	R2 ← 0C;
	R3 ← 0C;
	R4 ← 0C;
	R5 ← 0C;
	R6 ← 0C;
	R7 ← 0C;
	R10 ← 0C;
	R11 ← 0C;
	R12 ← 0C;
	R13 ← 0C;
	R14 ← 0C;
	R15 ← 0C;
	R16 ← 0C;
	R17 ← 0C;
*Clear R20-R377 using Stkp
	R0 ← 20C;
RClear:
	StkP ← R0;
	Stack ← 0C;
	LU ← (R0) xor (377C);
	R0 ← (R0) + 1, GoTo[RClear,ALU#0];
	R0 ← 5C;	*Notify task 17, location 6405
	R0 ← (R0) + (176400C);
	APCTask&APC ← R0;
p15ret:	Return;

SetTask[17];

	RTMP ← 400C, At[6405];	*set Printer idle, don't drive bus 
	Printer ← RTMP;

	RTMP ← 100000C;
ClrTimers:
	LoadTimer[RTMP];	*Clear out all Timers
	ResetMemErrs;		*Clear any pending memory errors
*Note: FFault[2] disables H4 parity errors in the normal (non-diagnostic) fault handler
	FFault ← 30000C;	*Initialize so that Midas takes faults, indicate midas is present
	RTMP ← (RTMP) + 1;
	LU ← (RTMP) and (17C);	*there are 16d timers
	REFR ← 0C, DblGoTo[InitDone,ClrTimers,ALU=0];
InitDone:
	LU ← Timer;	*Set up the Refresh timer
	RTMP ← 50000C;
	RTMP ← (RTMP) or (277C);	*simple timer,value 11d,slot 17b
	LoadTimer[RTMP];

*Notify task 16, address 7010 to set up timer task
	RTMP ← 167000C;
	RTMP ← (RTMP) or (10C);
	APCTask&APC ← RTMP, GoTo[p15ret];

*Set TPC[16] to TimerTask and return to task 17, address 7400.
	RTMP ← 177400C, Call[KNotify], At[7010];

*The simple timer task assumes slot 17 expired, since all others were cleared.
*At's are used on these so that KernelOccupied does not have to reserve the
*initialization mi that are on page 16b.
TimerTask:
	Refresh[REFR], At[7011];
	LU ← Timer, At[7005];		*read timer to clear the wakeup
*Build simple timer constant, value 11d, slot 17b
	RTMP ← 50000C, At[7006];
	RTMP ← (RTMP) or (277C), At[7007];
	AddToTimer[RTMP], At[7012];
CheckStop:
	T ← 10000C, At[7013];
	LU ← (Printer) and T, At[7014];
	REFR ← (REFR) + (20C), Skip[ALU#0], At[7001];
	  Return, At[7002];
*Midas recognizes a mouse halt as a task 16 breakpoint that was not set by
*the user.  It continues from (absolute) MidasStop+1
MidasStop:
	LU ← T, GoTo[MidasStop], SetFault, At[7003];
MidasRestart:
	Return, At[7004];

*Page Zero stuff 

*Emulator buffer refill code is on page 0
	LoadPageExternal[0], GoToExternal[377], At[0];

	T ← APCTask&APC, At[1];	*Fault entry.  Save APC first, then the other volatile regs.
	RXAPC ← T, At[100];
*T ← (CTask&NCIA) xnor (170000C), At[101]; is better here
	T ← CTask&NCIA, At[101];
	RXCTASK ← T, At[102];
	T ← (SStkP&NStkP) xor (377C), At[103];	*(stkp is read complemented)
	RXSTK ← 20C, At[104];	*Set StkP to 20 in case stack ovf was pending
	StkP ← RXSTK, RXSTK ← T, NoRegILockOK, At[106];
	T ← (ALUResult&SALUF) xnor (0C), At[107];	*(both read complemented)
	RXALU ← T, At[110];
	T ← Page&Par&Boot, LoadPage[0], At[111]; *page, parity, bootreason
	RXPPB ← T, ResetErrors, At[112];
*Notify Task 17, address 7505 (breakpoint communication)
	RTMP ← 177400C, At[113];
	RTMP ← (RTMP) or (105C), At[114];
	APCTask&APC ← RTMP, At[115];
	Return, At[116];

*Go overlay sends control to UserFault (120) if PARITY # 0 and FFault<0
UserFault:
	LoadPage[17], At[120];	*User may overwrite these instructions if desired
	GoToP[MidasFault], At[117];				

*The following is the page 17 portion of the kernel.  We get here
*after setting up the timer task.
	RDATA ← 40000C, At[7400];	*send #100 to Midas
	RWSTAT ← SendByte, Call[Send];

NextCom:
	RWSTAT ← RecvByte, Call[Recv], At[7404];
	Dispatch[RDATA,15,3];
	Disp[DoOverlay];

DoOverlay:
	ResetMemErrs, GoTo[OverlayArea], At[CMDisp,0]; *Midas overlay
OverlayArea:
	Return, At[7500];	*placeholder for overlay
MidasFault:
	Return, At[7512];	*placeholder for fault in Midas go overlay

WriteMI:	
	RWSTAT ← RecvWord, Call[Recv], At[CMDisp,2];	*Write Control Store
	RADDR ← T;			*Can't have Call here
	RWSTAT ← RecvByte, Call[Recv];	*Get Count (byte)
	RCNT ← T;
WriteMILoop:
	RWSTAT ← RecvWord, Call[Recv];	*Get Data 0 (word)
	RW0 ← T, Call[RecvW];	*Get Data 1 (word)
	RW1 ← T, Call[RecvB];	*Get Data 2 (byte)
	LU ← RW0;		*T has data 2
	APCTask&APC ← RADDR;
	WriteCS0&2;
	LU ← RW1, At[CMDisp,12];	*force writecs to have JA.7 = 0
	APCTask&APC ← RADDR;
	WriteCS1;
	RCNT ← (RCNT) - 1, At[CMDisp,14];	*force writecs to have JA.7 = 0
	RADDR ← (RADDR) + 1, GoTo[WriteMILoop,ALU#0];
	GoTo[NextCom];

*Read a single R register.  Midas will use an overlay to read RM 0 and RM 17,
*to avoid generating stack overflow.
ReadR:	RWSTAT ← RecvByte, Call[Recv], At[CMDisp,4];	*Get Address
	StkP ← RDATA;
	T ← Stack;
*d0rsr overlay exits here
ReadRX:	RDATA ← T, At[7406];
	RWSTAT ← SendWord, Call[Send];
	GoTo[NextCom];

*Write a single R register.  Midas will use an overlay to write RM 0 and
*RM 10 - RM 17 to avoid generating stack overflow.
WriteR:	RWSTAT ← RecvByte, Call[Recv], At[CMDisp,6];	*Get Address
	StkP ← RDATA, Call[RecvW];	*Get (word) data
	Stack ← T, GoTo[NextCom];

*SUBROUTINES Send and Receive communicate with Midas.

Send:	T ← RSh[RDATA,10], At[7460];	*get msbyte (location 7460 is known to overlays)
	RTMP ← T, GoTo[RPRT];	*will get WrStrb on

RecvB:	RWSTAT ← RecvByte, Skip, At[7410];
RecvW:	RWSTAT ← RecvWord, At[7411];
Recv:	RDATA ← Zero, At[7464];	*location 7464 is known to overlays
RW:	Refresh[REFR];	*Refresh the memory
	RTMP ← 30C;
	RTMP ← (RTMP) - 1, GoTo[.,R>=0];
	REFR ← (REFR) + (20C);
*Get Printer data, insisting that it yield the same data three times.
	T ← Printer;
	RTMP ← T;
	LU ← (Printer) - T;
	LU ← (Printer) - T, Skip[ALU=0];
	  GoTo[RW];
	T ← LdF[RTMP,0,2], Skip[ALU=0]; *Get strobe/ack bits
	  GoTo[RW];
	LU ← (LdF[RWSTAT,16,2]) xor T;	*Compare to desired bits
	T ← RTMP, GoTo[RW,ALU#0];	*if reached, clear all bits
	RTMP ← 400C;
	Printer ← RTMP, RTMP ← T, NoRegILockOK;	*restore RTMP
	Dispatch[RWSTAT,13,2];	*dispatch on state bits of rwstat
	LU ← (RWSTAT) and (4C), Disp[ReadStrobeOff];	*setup byte/word

ReadStrobeOff:	*Get Another byte if word set
	UseCTask, GoTo[ReadMore,ALU#0], At[RWDisp,0];
	T ← RDATA, Return;
ReadMore:
	RWSTAT ← RecvByte, GoTo[RW];	*Go get another byte

ReadStrobeOn:
	T ← RHMask[RTMP], At[RWDisp,1];	*Get Data Byte
	RDATA ← (LSh[RDATA,10]) or T;	*Merge Byte
	RWSTAT ← (RWSTAT) and (4C);	*State←0, Look for RDStrb off, retain byte/word
	RTMP ← 100400C;	*Set RdAck
RPRT:	Printer ← RTMP, GoTo[RW];	
	

*Here on Write Ack On - state ← 3
	RWSTAT ← (RWSTAT) xor (11C), GoTo[RW], At[RWDisp,2];
	
*Here on Write Ack Off
	RDATA ← LSh[RDATA,10], GoTo[SendMore,ALU#0], At[RWDisp,3];
	LU ← Zero, GoTo[ReadStrobeOff]; *must do non-tasking return
SendMore:
	RWSTAT ← SendByte, GoTo[Send];


:END[Kernel];