:IF[WithMIOC]; ****************************************
INSERT[MIOCDefs];
TITLE[MIOC];

%
Edit by Ed Fiala 17 March 1982: WithMIOC conditional; move IMReserve
to Mesa1/2Occupied.Mc; absorb MIOCInit.Mc.
Edit by Jim Frandeen September 2, 1980 2:27 PM:
Change DoInt to NotifyInterrupt.
Edit by July 24, 1980 1:26 PM:
New poller command to handle reg 5 bit sharing.
Edit by April 11, 1980 4:53 PM:
Reorder commands and timeout code in poller.
Edit by March 19, 1980 4:59 PM:
New IMRESERVE for intraframe fill code.
Edit by Danielson March 5, 1980 6:26 PM:
Fix tty timeout code
Edit by Danielson February 21, 1980 11:18 AM:
Reduce display jitter with extra tasks
Edit by Danielson January 25, 1980 1:44 PM:
Added new status code
Edit by Danielson January 16, 1980 1:34 PM: MIOC microcode
Contains printer code, default RS232C and poller.
%

SetTask[0];

*MIOC initialization code
*In Pilot mode, R0 contains CSB address.
*In Alto mode, you figure it out yourself based on task.

OnPage[IOInitPage];

:IF[AltoMode]; ****************************************
NOP, At[IOInitLoc];
T ← CTask;*Get current task
IOcsb ← T;
T ← 177400C;*Get base of CSBs
IOcsb ← (LSh[IOcsb,4]) + T;*Get actual CSB address
:ELSE; ************************************************
T ← IOR0, At[IOInitLoc];*Get CSB address
IOcsb ← T;*Save CSB address
:ENDIF; ***********************************************
IOiocbHi ← T ← Zero;*IOCB base (hi half)
LoadPage[IOPage], IOcsbHi ← T;*IOCB base (hi half)
IOMisc ← T, GoToP[IOSetTPC];*Set Misc bits

*MIOC board needs servicing.
*Read wake reason and dispatch to routine to handle wakeup.

OnPage[IOPage];

IOSetTPC:
Call[.+1], At[IOStartLoc];*Set the TPC
IOWaitWake:
INPUT [IOTemp0,IOReadWakeup];*Read interrupt vector
IOWakeDisp[IOTemp0];*Set up for dispatch
Disp[IODoneInterrupt];*Dispatch on wakeup

GoTo[IODoneInterrupt], At[IOWakeupLoc,4]; * Plotter Data

*Printer requested service. Load wakeup mask and issue naked
*notify.

NOP, At[IOWakeUpLoc,5];*Issue naked notify
NOP, At[IOWakeUpLoc,6];
PFetch1[IOcsb,IOInt1,IOCSBPrinterMask!], Call[IODoNotify];
GoTo[IODoneInterrupt];

*Now clear the wakeup, wait for the wakeup latency and TASK.

IODoneInterrupt: IOStrobe, At[IOWakeupLoc,0];
NOP;* How many NOPs needed????
Call[IODoTask];
GOTO [IOWaitWake];

* Issue a naked notify by branching to code in main microcode.
* When called from parts of the MIOC code, it expects thef
* appropriate mask word in T. This code is at an absolute location so that the RS232 microcode can call it with a CALLEXTERNAL.

IODoNotify:
LoadPageExternal[NotifyInterruptPage], At[IODoNotifyLoc];* Load correct page
GoToExternal[NotifyInterruptLoc], T ← IOInt1;*Goto interrupt code. Mask in T.

* Have gotten interrupt from SIO chip. Read interrupt vector and
* dispatch. Since vector contains 0 in lowest bit, dispatch
* addresses are at offset 0,2,4,6,10,12,14,16.

IOGotInt:
NOP, At[IOWakeupLoc,2];
Call[IOIntrAck];
IOIntDisp[IOTemp0];
Disp[.+1];

* Interrupt vector = 0 (Transmitter has become empty -- channel B).
* Issue command to clear transmitter interrupt.

CALL [IOWrRegB], T ← (IOResetXmtr), AT [IOIntrLoc,0];
GOTO [IODoReturn], AT [IOIntrLoc,1];

* Interrupt vector = 1 (External status change -- channel B).
* Clear the interrupt.

CALL [IOWrRegB], T ← (IOResetExt), AT [IOIntrLoc,2];
GOTO [IODoReturn], AT [IOIntrLoc,3];

* Interrupt vector = 3. (Special received condition -- channel B).
* Just read character to clear interrupt.

NOP, AT [IOIntrLoc,6];

* Interrupt vector = 2 (Receiver FIFO not empty).
* Just read character to clear interrupt.

CALL [IORdDataB], AT [IOIntrLoc,4];
GOTO [IODoReturn], AT [IOIntrLoc,5];

* Interrupt vector = 4 (Transmitter has become empty -- channel A).
* Issue command to clear transmitter interrupt.

IOWrStart: CALL [IOWrRegA], T ← (IOResetXmtr), AT [IOIntrLoc,10];
GOTO [IODoReturn], AT [IOIntrLoc,11];

* Interrupt vector = 5 (External status change -- channel A).
* Clear the interrupt.

IOStatStart: CALL [IOWrRegA], T ← (IOResetExt), AT [IOIntrLoc,12];
GOTO [IODoReturn], AT [IOIntrLoc,13];

* Interrupt vector = 7. (Special received condition -- channel A).
* Just read character to clear interrupt.

IOSpecStart: NOP, AT [IOIntrLoc,16];

* Interrupt vector = 6 (Receiver FIFO not empty).
* Just read character to clear interrupt.

IORdStart: CALL [IORdDataA], AT [IOIntrLoc,14];
GOTO [IODoReturn], AT [IOIntrLoc,15];

* Fill instructions. Unused at present.

NOP, AT [IOIntrLoc,7];
NOP, AT [IOIntrLoc,17];

* Return from SIO interrupt. If not a fake wakeup, this is, one
* generated by a poller command, issue the chip command to fake
* a return from interrupt Z80 instruction.

IODoReturn: LU ← (IOMisc) AND (IOMiscFake), AT [IOFixedLoc,10];
SKIP [ALU#0], IOMisc ← (IOMisc) AND NOT (IOMiscFake);
CALL [IOWrRegA], T ← IOReturnIntr;* Return interrupt!
GOTO [IODoneInterrupt];

* Issue commands to SIO chip.
* For writes: T contains data to write.
* For reads: Data returned in T and IOTemp0.

IORdRegA: IOTemp0 ← OR[IOCmd,IORd,IOChanA]C, GOTO [IODoRd], AT [IORdRegALoc];
IORdRegB: IOTemp0 ← OR[IOCmd,IORd,IOChanB]C, GOTO [IODoRd], AT [IORdRegBLoc];
IOWrRegA: IOTemp0 ← (OR[IOCmd,IOWr,IOChanA]C), GOTO [IODoWr], AT [IOWrRegALoc];
IOWrRegB: IOTemp0 ← (OR[IOCmd,IOWr,IOChanB]C), GOTO [IODoWr], AT [IOWrRegBLoc];
IORdDataA: IOTemp0 ← OR[IOData,IORd,IOChanA]C, GOTO [IODoRd], AT [IORdDataALoc];
IORdDataB: IOTemp0 ← OR[IOData,IORd,IOChanB]C, GOTO [IODoRd], AT [IORdDataBLoc];
IOWrDataA: IOTemp0 ← OR[IOData,IOWr,IOChanA]C, GOTO [IODoWr], AT [IOWrDataALoc];
IOWrDataB: IOTemp0 ← OR[IOData,IOWr,IOChanB]C, GOTO [IODoWr], AT [IOWrDataBLoc];
IOIntrAck: IOTemp0 ← OR[IOCmd,IOWr,IOM1]C, GOTO [IODoRd];

* Issue chip commands.
* First issue the appropriate OUTPUT instruction. Make sure the
* OUTPUT is done by loading a zero into the register used for
* the OUTPUT. Loop doing INPUTs until the CS line is no longer true (R >=0).

IODoWr: IOTemp0 ← (IOTemp0) OR (T);
* Or in data to write
IODoRd:OUTPUT[IOTemp0, IOLoadRS232];
* Output command/data to chip
UseCTask;
T ← APC&APCTask;
IOSave ← T;
IOTemp0 ← 0C, CALL [IODoTask];* Wait for OUTPUT to complete
INPUT [IOTemp0, IOReadRS232];
SKIP [R>=0], T ← IOTemp0;* Wait for CS low (R>=0)
IODoTask: RETURN;
NOP;
CALL [IODoTask];
GOTO [IODoTask], APC&APCTask ← IOSave;
IOOutTask: GOTO [IODoTask];

* Poller.
* Uses timer in mode 3 (square wave generator). By using this
* mode, we don’t have to reload the timer from microcode.

CALL [IODOTask], PFetch4[IOcsb, IOPollCmd, IOcsbPoller!], AT [IOWakeupLoc,7];

* Process any poller command

IOPollerDisp[IOPollCmd];
DISP [.+1], T ← (IOPollCmd) AND (100000C);

* Command = 0. Do nothing
* Command = 1. Start transmitter
* Command = 2. Issue chip command (write chip)
* Command = 3. Cannot be used
* Command = 4. Nop
* Command = 5. Initlaize overlay
* Command = 6. Issue chip command (read chip)
* Command = 7. Reset status bits
* Command = 10. Set Reg 5.

GOTO [IOCheckTimeout], AT [IOPollLoc,0];
GOTO [IOStartXmtr], IOMisc ← (IOMisc) OR (IOMiscFake), AT [IOPollLoc,1];
IOPollChipCmd: CALL [IOWrRegA], T ← (LDF[IOPollCmd,5,3]) OR (T), AT [IOPollLoc,2];
CALL [IOWrRegA], T ← (IOPollCmd) AND NOT (057400C), AT [IOPollLoc,3];
IOPollClr: GOTO [IOPollSet], IOPollCmd ← 0C, AT [IOPollLoc,4];
GOTO [IOInitOverlay], IOMisc ← (IOMisc) OR (IOMiscFake), AT [IOPollLoc,5];
GOTO [IOPollChipCmd], AT [IOPollLoc,6];
PFetch1[IOcsb, IOPollStat, IOcsbStat!], AT [IOPollLoc,7];
T ← RHMASK[IOPollCmd], CALL [IODoTask];
IOPollStat ← (IOPollStat) AND NOT (T);
GOTO [IOPollClr], PStore1[IOcsb, IOPollStat, IOcsbStat!];
PFetch1[IOcsb, IOPollRegs, IOcsbRegs!], AT [IOPollLoc,10];
CALL [IOWrRegA], T ← 5C;
T ← (IOMisc) AND (IOMiscEnabled);
IOPollRegs ← (IOPollRegs) OR (T);
CALL [IOWrRegA], T ← RSH[IOPollRegs,10];
GOTO [IOPollClr];
IOPollSet: IOResults ← T, CALL [IODoTask];
GOTO [IOCheckTimeout], PStore2[IOcsb, IOPollCmd, IOcsbPoller!];

* Check for timeout and if a timeout occurred branch to code in
* overlay to handle it

IOCheckTimeout: T ← (IOTimerInit), GOTO [IONoTimeout, R<0];
SKIP [R<0], IOTimer ← (IOTimer) - 1;
GOTO [IONoTimeout], PStore1[IOcsb, IOTimer, IOcsbTimer!];
IOTimeout: IOTimer ← T;
IOMisc ← (IOMisc) OR (IOMiscFake);
GOTOEXTERNAL[IOTimeoutLoc], PStore1[IOcsb, IOTimer, IOcsbTimer!];
IONoTimeout: GOTO [IODoneInterrupt];

* Start transmitter requested. Save registers and branch to overlay code

IOStartXmtr: IOPollCmd ← 0C;
GOTOEXTERNAL[IOStartXmtrLoc], PStore2[IOcsb, IOPollCmd, IOcsbPoller!];

* Initialize overlay requested. Save registers and branch to overlay code

IOInitOverlay: IOPOllCmd ← 0C;
GOTOEXTERNAL[IOInitOverlayLoc], PStore2[IOcsb, IOPollCmd, IOcsbPoller!];

* Nop instructions for start transmitter, timeout, and initialize overlay

GOTO [IODoneInterrupt], AT [IOStartXmtrLoc];
GOTO [IODoneInterrupt], AT [IOTimeoutLoc];
GOTO [IODoneInterrupt], IOMisc ← 0C, AT [IOInitOverlayLoc];

END[MIOC];

:ELSE; ************************************************

TITLE[No.MIOC.Microcode];

:ENDIF; ***********************************************