{File name: Kernel.mc Description: Kernel for the Dandelion, Author: R. Garner, 26 Sept 1979, R. Garner, March 13, 1980 7:15 PM -- updated for Rev D CP R. Garner, March 13, 1980 7:15 PM -- updated for Rev E IOP R. Garner, April 7, 1980 4:45 PM -- ClrIntErr & MStatus[0]¬0 R. Garner, July 2, 1980 11:47 AM -- KStart at Loc 0 R. Garner, October 29, 1980 8:46 AM -- BreakPoints changed Jarvis, February 4, 1980 6:07 PM Dennis Grundler: 2-Sep-84 15:51:05, add copyright notice.} { Copyright (C) 1979, 1980 by Xerox Corporation. All rights reserved.} {Init the TPC's for all the tasks (except 6 & 7) to point to Idle. Idle must be at[0F,10] to cancel possible pending condition bits.} Reserve[1, 0F6E], Reserve[0F70, 0F76], Reserve[0F78, 0F7E]; SetTask[0]; StartAddress[Idle]; SetTask[1]; StartAddress[Idle]; SetTask[2]; StartAddress[Idle]; SetTask[3]; StartAddress[Idle]; SetTask[4]; StartAddress[Idle]; SetTask[5]; StartAddress[Idle]; SetTask[7]; StartAddress[KStart]; Idle: DCtl ¬ 0, CANCELBR[Idle, 0F], c*, at[0FEF]; { Overview: Locations Burdock knows about in the kernel rK, RHrK; U-registers: uKernMD, uBreakID, uIOPData, uKTemp; microinstructions: KBuffer, KBufferExtension, KWait, K1Entry, K2Entry, K3Entry0, K3Entry1 and the breakpoint ID's. The Kernel can be entered by one of two ways: Either via a breakpoint or the IOP asynchronously interrupting the CP via the IOPWait line. If entry is via a breakpoint, the kernel can be entered in any cycle (and inter-cycle state information must be preserved). IOPWait caused entry always occurs between clicks (so all state information is already saved by the CP and there is no memory state across clicks which can be lost, saved or restored). Upon entering the kernel, it interrupts the IOP and waits for a command byte. There are 3 possible commands that the IOP can specify: Refresh, ExitKernel, and ExecuteBufffer. Refresh is used by the IOP when it is writing the CS, ExitKernel causes the CP to leave the kernel task, and ExecuteBuffer causes the instructions which the IOP wrote in the buffer area to be executed. When the kernel is entered via a breakpoint, an R register (rK) must be used to hold memory data or a breakpoint ID (or an RH reg for ID), and a Link register to hold condition bits (or a breakpoint ID). When being entered via an IOPWait, no R register state need be lost (currently rK is lost) (i.e. rK and RHrK can first be saved away, then later restored. There should also be a second kind of ExitKernel command which doesn't write Mem[0]). Currently, the kernel is written assuming it can always use rK, so this register is lost in the IOPWait caused entry also. (rK is used in the wait loop and in the overlay code which Burdock uses to read and write some registers.) Register Useage: rK Holds memory data of broken click or BreakID, and used as a temporary. RHrK Used at KLeaving, burdock memory overlay code, and temporarily holds breakID for c3 breakpoints. Holds IOPStatus at KEntry. Burdock should read value before overlay used. UKernMD Holds click's captured memory data and used in memory write overlay (should be read by Burdock before the overlay is used). UBreakID Temporarly holds breakpoint identification number. UKTemp Temporary used in register write overlays. (Same as UBreakID, i.e., UBreakID should be read by Burdock before it uses an overlay which uses UKTemp.) uIOPIDataSave Saved value of IOPData. uKTRefCnt Refresh counter in IOPAttn transition wait loop (same as uIOPIDataSave). uIOPCtlSave Saved value of IOPCtl. UKSaveDisp Holds condition bits. } { Breakpoint code } {There are three seperate entry points to the kernel corresponding to breakpoints set in c1, c2 or c3. The following code must be written into the control store in order to cause the respective breakpoint. There are currently 16 breapoints available of any sort. c2 & c3 breakpoints loose the pending dispatch/branch bits and memory data is lost. BreakID=-1 is the mouse halt breakpoint. If the BreakID is 16 or greater, then the kernel entry points were entered in the wrong cycle (disaster!) and BreakID-16 is the "actual" Breakpoint identification if the microcode was off by one cycle. BreakID-32 is the "actual" Breakpoint identification if the microcode was off by two cycles. If EnterKernel is executed in c1, the next click which runs will be the kernel. The first EnterKernel executed should be in c1 else an IO click can interviene which will cause the IO microcode to be entered at the Kernel task level instead of the kernel microcode. (When entering the kernel task level the SwitchProm does not cause Swc2 to be true, i.e. the kernel's TPC is not used to define the entry point to the Kernel code.)} {Breakpoint in c1: RHrK ¬ BreakID, EnterKernel, DISP4[K1Entry], c1;} {Breakpoint in c2: RHrK ¬ BreakID, CANCELBR[K2Entry, 0F], c2;} {Breakpoint in c3: RHrK ¬ BreakID, CANCELBR[K3Entry, 0F], c3;} { Kernel Entry } {The 16 way branches at K1Entry are necessary in order to preserve the branch conditions in UKSaveDisp, i.e. the entry points can not be "at[0F,10]" since this would cancel all pending branch conditions. The "Kc" table (IA.7=0) is used by the Write LInk overlay, location K1E is overwritten and restored. Burdock knows the address of K1Entry, K2Entry, K3Entry, K1E, & K2E} {Cycle1 Breakpoint entry} K1Entry: uKSaveR ¬ rK, GOTO[Kc0], c2, at[K1EntryLoc]; uKSaveR ¬ rK, GOTO[Kc1], c2, at[1,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc2], c2, at[2,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc3], c2, at[3,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc4], c2, at[4,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc5], c2, at[5,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc6], c2, at[6,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc7], c2, at[7,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc8], c2, at[8,10,K1Entry]; uKSaveR ¬ rK, GOTO[Kc9], c2, at[9,10,K1Entry]; uKSaveR ¬ rK, GOTO[KcA], c2, at[0A,10,K1Entry]; uKSaveR ¬ rK, GOTO[KcB], c2, at[0B,10,K1Entry]; uKSaveR ¬ rK, GOTO[KcC], c2, at[0C,10,K1Entry]; uKSaveR ¬ rK, GOTO[KcD], c2, at[0D,10,K1Entry]; uKSaveR ¬ rK, GOTO[KcE], c2, at[0E,10,K1Entry]; uKSaveR ¬ rK, GOTO[KcF], c2, at[0F,10,K1Entry]; {Folowing constraints for WriteLink overlay} Kc0: rK ¬ 0, GOTO[K1E], c3, at[KcLoc]; Kc1: rK ¬ 1, GOTO[K1E], c3, at[1,10,Kc0]; Kc2: rK ¬ 2, GOTO[K1E], c3, at[2,10,Kc0]; Kc3: rK ¬ 3, GOTO[K1E], c3, at[3,10,Kc0]; Kc4: rK ¬ 4, GOTO[K1E], c3, at[4,10,Kc0]; Kc5: rK ¬ 5, GOTO[K1E], c3, at[5,10,Kc0]; Kc6: rK ¬ 6, GOTO[K1E], c3, at[6,10,Kc0]; Kc7: rK ¬ 7, GOTO[K1E], c3, at[7,10,Kc0]; Kc8: rK ¬ 8, GOTO[K1E], c3, at[8,10,Kc0]; Kc9: rK ¬ 9, GOTO[K1E], c3, at[9,10,Kc0]; KcA: rK ¬ 0A, GOTO[K1E], c3, at[0A,10,Kc0]; KcB: rK ¬ 0B, GOTO[K1E], c3, at[0B,10,Kc0]; KcC: rK ¬ 0C, GOTO[K1E], c3, at[0C,10,Kc0]; KcD: rK ¬ 0D, GOTO[K1E], c3, at[0D,10,Kc0]; KcE: rK ¬ 0E, GOTO[K1E], c3, at[0E,10,Kc0]; KcF: rK ¬ 0F, GOTO[K1E], c3, at[0F,10,Kc0]; K1E: Noop, c1, at[K1ELoc]; KBEntry: UKSaveDisp ¬ rK, c2, at[KBELoc]; rK ¬ RHrK, c3; UBreakID ¬ rK, GOTO[KEntry], c1; {Cycle2 Breakpoint entry} K2Entry: GOTO[K3Entry], c3, at[K2EntryLoc]; {Cycle3 Breakpoint entry} K3Entry: uKSaveR ¬ rK, rK ¬ 0, EnterKernel, GOTO[KBEntry], c1, at[K3EntryLoc]; {IOP "Mouse Halt" Entry: KMouseStop is reached when IOPWait is activated by the IOP while a user program is running, i.e., after an Exit Kernel command. (KMouseStop is held in the kernel's TPC while the kernel is not running). Note that KMouseStop is actually executed twice. If IOPWait is activated while the kernel is running the kernel should continue to run correctly. (The SwitchProm causes a task switch, which causes the kernel to restart where it was the previous click, which doesn't hurt since the kernel loop or refresh code can be repeated with no harm)} KMouseStop: RHrK ¬ (~rK xor rK) LRot0, GOTO[K2Entry], c* {c2}; {Kernel Breakpoint Entry: Control comes here on MouseStop & Normal breakponts: First Verify that we're in cycle 2: Keep cycling until the cycle number is good. (The loop between KEntry and KBadCy+2 is not an integral number of clicks, so eventually KEntry is executed in c2. Each time through, we add 16 to the breakID) We also save the value of the IOPCtl register by reading it in from IOPStatus.} KEntry: XC2npcDisp, rK ¬ RShift1 ~IOPStatus, c2; uIOPCtlSave ¬ rK, BRANCH[KBadCy, KGoodCy, 0D], c3; KBadCy: rK ¬ UBreakID, c*, at[0D,10,KGoodCy]; rK ¬ rK + 10, c*; UBreakID ¬ rK, GOTO[KEntry] c*; KGoodCy: Noop, c1, at[0F,10,KBadCy]; IOPCtl ¬ IOPAttnInMode, c2; RHrK ¬ 0FF, GOTO[KTRef], c3; {IOPAttn Transition Wait Loop: Wait for 0 to 1 transition of IOPAttn (bit 10 of IOPStatus). This signifies that the IOP has finished using IOPIData & we can save it in uIOPDataSave. uIOPStatusSave holds IOPStatus which burdock can use to asertain the value of IOPReq. We must do memory refresh while waiting for positive transition. uKTRefCnt = UIOPDataSave holds the refresh count.} KTrans: [] ¬ ~RHrK and rK, YDisp, SuppressTimingWarning, c3; RHrK ¬ rK LRot0, BRANCH[KTransB, KTDone, 7], c1; KTransB: rK ¬ uKTRefCnt-1, NibCarryBr, SuppressTimingWarning, c2, at[7,10,KTDone]; uKTRefCnt ¬ rK, BRANCH[$, KTRef], c3; rK ¬ IOPStatus RShift1, c1; KTD: rK ¬ rK RShift1, GOTO[KTrans], c2; {Do a refresh for the KTrans loop. Note that we are cycle synchronous.} KTRef: rK ¬ IOPStatus RShift1, Refresh, GOTO[KTD], c1; {Save IOPIData and IOPStatus. Also set IOPCtl to InMode for upcoming first command from Burdock.} KTDone: rK ¬ IOPIData, c2, at[0F, 10, KTransB]; uIOPIDataSave ¬ rK, rK ¬ IOPStatus, c3; uIOPStatusSave ¬ rK, c1; IOPCtl ¬ IOPInMode, c2; KWait: rK ¬ 0, GOTO[GetIOPByte], c*, at[KWaitLoc]; {Kernel Wait Loop: wait for command from IOP. So that the commands execute in the correct cycles, cycle synchronization must be checked on exit from the Wait loop. The 3 microinstructions of the wait loop can be executed in any cycle. The kernel inner loop gets out of sync because IOPWait is raised and dropped by Burdock arbitrarily as Burdock reads registers, sets breakpoints, etc. The overlay code also returns to KWait out of sync. Once a command is issued, IOPWait is NOT asserted untill after the command completes and the kernel has returned to the Wait loop, where is waits for another command while periodically refreshing. Thus, IOPWait's can occur any time in the kernel idle loop, but not outside of it. Memory refresh is acomplished by exiting the loop every 33 clicks. There are 3 Refresh statements at the KRefresh exit: one per each each possible cycle. (If Refresh is executed in c2 or c3, there is no affect on the memory controller). Memory refresh should be done at least once per 33 clicks (128 refreshes/2mS, 411 nS clicks). Note that the XLDisp reduces to a two way branch because X.8 of IOPStatus is 0.} GetIOPByte: rK ¬ rK and ~10'x, BRANCH[$, KRefresh], {see KRefCmd before change} c*, at[GetIOPByteLoc]; [] ¬ IOPStatus, XLDisp {Branch on IOPReq}, c*; rK ¬ rK+1, NibCarryBr, BRANCH[GetIOPByte, KCommand], c*; {Only one of the following Refreshes will be executed} KRefresh: Refresh, c*; Refresh, c*; Refresh, GOTO[GetIOPByte], c*; {Command Dispatch: First synchronize before dispatching. The idea here is that if the XC2npcDisp's at KCommand & KCNotC2 cause the BRANCH at KCom to go to KCC2, then KCom must have ben executed in c3 and KCC2 is executed in c1. Entries of the dispatch table are on the following pages at each command. Even though the commands fit in 2 bits, bit 4 of IOPIData must be zero; bits 0-3 and 5 are ignored.} KCommand: Xbus ¬ 0, XC2npcDisp, CANCELBR[$], c2; KC: rK¬ rK and ~u1F, ZeroBr, BRANCH[KCNotC2, KCC2, 1], c3; KCNotC2: Xbus ¬ 0, XC2npcDisp, CANCELBR[KC], c*, at[1, 4, KCC2]; KCC2: []¬ rK LRot12, XDisp, BRANCH[GetIOPRet, $], c1, at[3, 4, KCNotC2]; []¬ IOPIData, XDisp, CANCELBR[$, 7] {max 7-bit index}, c2; DISP2[KTable], c3; GetIOPRet: rK¬ IOPIData, DISP4[GetIOPByte], c2; { Command = 0, ExitKernel } {Leave the Kernel's TPC pointing to the KMouseStop entry point. This is the last click executed by the kernel (until the next EnterKernel or IOPWait). Also restore the IOPCtl register to the correct state.} KTable: ExitKernel, c1, at[0, 4, KTable]; rK ¬ uKSaveR, c2; IOPCtl ¬ uIOPCtlSave, GOTO[KMouseStop], c3; {The ExitKernel function causes the CP to begin normal task scheduling. The task to run next click will be either the emulator or the appropriate IO task. The task which was breakpointed will start up at the TPC value as written by Burdock. This will point to the command buffer which was previously written by Burdock to contain the condition bits and memory data restoration instructions, as given below, which correspond to the cycle which was breakpointed. Note that UKSaveDisp holds the branch/dispatch bits which will be pending for the breakpointed instruction. For c2 & c3 breakpoints, UKSaveDisp should be set by the user if something besides 0 is desired.} {If c1 was breakpointed: KBuffer: Noop, c1; Noop, c2; Xbus ¬ UKSaveDisp, XDisp, c3; , c1;} {If c2 was breakpointed: KBuffer: Xbus ¬ UKSaveDisp, XDisp, c1; , c2;} {If c3 was breakpointed: KBuffer: Noop, c1; Xbus ¬ UKSaveDisp, XDisp, c2; , c3;} { Command = 1, Refresh } {This command causes two refresh cycles. Given the 128 refreshes/2 msec scheme, with 411 nSec clicks and 333 nS 8085 clock, this translates to 1 refresh per 33 clicks and 1 refresh per 46 8085 clocks. Thus, the IOP can keep IOPWait active for about 92 clocks before it needs to execute a Refresh command. If more time is needed, add more Refresh's to this command. Note that refreshes are also done in the command wait loop (which, of course, doesn't execute when IOPWait is true).} KRefCmd: Refresh, rK¬ 0, ZeroBr {set pending branch}, GOTO[GetIOPByte], c1, at[1, 4, KTable]; { Command = 2, WriteU } {Data sent (1) low data byte, (2) high data byte, (3) reg number. Currently, 0 = uKAddr, 1 = uKCount, 2 = UKernMD, 3 = UKTemp.} KWriteU: rK ¬ 20, CALL[GetIOPByte], c1, at[2, 4, KTable]; uKernAC ¬ rK, CANCELBR[$,0], {low data byte} c3, at[2, 10, GetIOPByte]; RHrK ¬ rK LRot0, {write low half into RH} c1; rK ¬ 40, CALL[GetIOPByte], c2; rK ¬ rK LRot8, CANCELBR[$,0], c3, at[4, 10, GetIOPByte]; uKSaveQ ¬ Q, c1; Q ¬ rK or uKernAC, {complete data word} c2; rK ¬ 60, CALL[GetIOPByte], c3; rK ¬ rK or 0C, CANCELBR[$,0], {U reg number=[0..3]} c3, at[6, 10, GetIOPByte]; [] ¬ rK, AltUaddr, c1; KernelURegs ¬ Q, c2; Q ¬ uKSaveQ, GOTO[KWait], c3; { Command = 3, Execute Buffer } {This command executes the instructions placed into KBuffer by Burdock. Burdock uses this mechanism to read and write all of Dandelions registers: It creates the instructions necessary to address the particular register (as outlined below), writes them into the control store beginning at location KBuffer, and then executes a command 3.} {KBuffer is a 18 word area which must be written by the IOP before executing an ExitKernel or ExecuteBuffer command. Microinstructions in KBuffer must have ia.7=1 for the pRet's of the read link overlay and the breakpoint restart overlays.} Noop, c1, at[3, 4, KTable]; Noop, c2; GOTO[KBuffer], c3; KBuffer: Noop, c*, at[KBufferLoc]; Noop, c*, at[5, 10, KBuffer]; Noop, c*, at[6, 10, KBuffer]; Noop, c1, at[7, 10, KBuffer]; Noop, c*, at[8, 10, KBuffer]; Noop, c*, at[9, 10, KBuffer]; Noop, c1, at[0A, 10, KBuffer]; Noop, c*, at[0B, 10, KBuffer]; Noop, c*, at[0C, 10, KBuffer]; Noop, c1, at[0D, 10, KBuffer]; Noop, c*, at[0E, 10, KBuffer]; GOTO[KWait], c*, at[0F, 10, KBuffer]; KBufferExt: u1F¬ rK, c1, at[KBufferExtenLoc]; rK ¬ 8, c2, at[9,10, KBufferExt]; rK ¬ rK LRot8, c3, at[0A, 10, KBufferExt]; MCtl ¬ rK {MCtl ¬ 800}, GOTO[KTDone], c1, at[0B, 10, KBufferExt]; Noop, c2, at[0C, 10, KBufferExt]; Noop, c3, at[0D, 10, KBufferExt]; {Kernel Start location: Burdock begins the kernel executing at KStart after it has been loaded into the control store. Set uIOPCtlSave so IOPCtl is set correctly when we leave the kernel. Set IOPCtl & init the refresh counter at KTDone and go into the kernel wait loop. Note that KStart will actualy be exected twice. Also note that KStart is at location 0, so if a trap occurs while the kernel is running, it will be restarted. Second instruction executed by Kernel must be at[0F, 10], to cancel random branch conditions at start up.} KStart: uIOPCtlSave ¬ IOPDisableMode, ClrIntErr, CANCELBR[$, 0F], c*, at[0]; rK¬ 1F, GOTO[KBufferExt], c*, at[0F, 10, KBufferExt]; {KBuffer OverLays: Only byte sized data chunks are transfered from the CP to the IOP. For example, in order to read an R register, first the code to transfer the high byte is overlayed & executed, then the code to transfer the low byte is writen into KBuffer and executed. Some of the operations imply that UKTemp must be written first. Each overlay must return to KWait to init the refresh counter since the overlay probably changed rK. Following is the template code which Burdock should use to read and write CP registers. These instructions are contained in Burdock.} { Read R [0-7] KBuffer: IOPOData ¬ R LRot8, GOTO[KWait], c1; Read R [8-15] KBuffer: IOPOData ¬ R LRot0, GOTO[KWait], c1;} { Write R Burdock should first do: UKTemp ¬ , KBuffer: R ¬ UKTemp, GOTO[KWait], c1;} { Read RH KBuffer: IOPOData ¬ RH, GOTO[KWait], c1;} { Write RH KBuffer: RH ¬ data, GOTO[KWait], c1;} { Read Q [0-7] KBuffer: IOPOData ¬ Q LRot8, GOTO[KWait], c1; Read Q [8-15] KBuffer: IOPOData ¬ Q LRot0, GOTO[KWait], c1;} { Write Q Burdock should first do: UKTemp ¬ , KBuffer: Q ¬ UKTemp, GOTO[KWait], c1;} { Read U [0-7] KBuffer: rK ¬ U, c1; IOPOData ¬ rK LRot8, GOTO[KWait], c2; Read U [8-15] KBuffer: IOPOData ¬ U, GOTO[KWait], c1;} { Write U KBuffer: rK ¬ , c1; rK ¬ rK LRot8, c2; rK ¬ rK or , c3; U ¬ rK, GOTO[KWait], c1;} { Read Memory [0-7] Burdock should first do: UKTemp ¬ . KBuffer: RHrK ¬ , c1; rK ¬ UKTemp, c2; Noop, c3; MAR ¬ [RHrK, rK+0], c1; Noop, c2; rK ¬ MD, c3; KBuffer+6: IOPOData ¬ rK LRot8, GOTO[KWait], c1; Read Memory [8-15] KBuffer+6: IOPOData ¬ rK LRot0, GOTO[KWait], c1;} { Read IOXIn [0-7] KBuffer: rK ¬ IOXIn, c1; IOPOData ¬ rK LRot8, GOTO[KWait], c2; Read IOXIn [8-15] KBuffer: IOPOData ¬ IOXIn, GOTO[KWait], c1;} { Write IOOut Burdock should first do: UKTemp ¬ , KBuffer: IOOut ¬ Ybus ¬ UKTemp, GOTO[KWait], c1;} { Read Link [2-3] KBuffer: Noop, c1; LDisp, c2; KBuffer+2: DISP2[KLink, 0C], c3; KLink7: IOPOData ¬ 0, GOTO[KWait], c1, at[0FF7]; KLinkB: IOPOData ¬ 0, GOTO[KWait], c1, at[0FFB]; KLinkC: IOPOData ¬ 0, GOTO[KWait], c1, at[0FFC]; KLinkD: IOPOData ¬ 1, GOTO[KWait], c1, at[0FFD]; KLinkE: IOPOData ¬ 2, GOTO[KWait], c1, at[0FFE]; KLinkF: IOPOData ¬ 3, GOTO[KWait], c1, at[0FFF]; Read Link [0] KBuffer+2: BRANCH[KLink7, KLinkF, 7], c3; Read Link [1] KBuffer+2: BRANCH[KLinkB, KLinkF, 0B], c3;} { Write Link Location K1E is overwriten & restored. KBuffer: [] ¬ , XDisp, c1; L ¬ 0, DISP4[Kc0], c2;} { Read pc16 KBuffer: rK ¬ 0+pc16, c1; IOPOData ¬ rK LRot0, Cin¬pc16, GOTO[KWait], c2;} { Write pc16 KBuffer: rK ¬ +pc16, YDisp, c1; BRANCH[KFlip, KNoFlip, YOdd], c2; KFlip: Cin¬pc16, c3, at[0FFE]; KNoFlip: GOTO[KWait], c3, at[0FFF];} { Read ~stackP : Use Read IOXin} { Write stackP KBuffer: stackP ¬ , GOTO[KWait], c1;} { Read IB : Use Read IOXin} { Write IB Burdock should first do: UKTemp ¬ , KBuffer: IB ¬ UKTemp, GOTO[KWait], c1;} { Read IBPtr : Use Read IOXin} { Write IBPtr KBuffer: IBPtr ¬ , GOTO[KWait], c1;} { Read MesaInt KBuffer: MesaIntBr, c1; BRANCH[MIZero, MIOne], c2; MIZero: IOPOData ¬ 0, GOTO[KWait], c3; MIOne: IOPOData ¬ 1, GOTO[KWait], c3;} { Write MesaInt KBuffer: ClrIntErr, GOTO[KWait], c1; KBuffer: MesaIntRq, GOTO[KWait], c1;} { Read EKErr : Use Read IOXin} { WriteCPMemoryBlock } {IOP sets up uKAddr, uKCount, and RHrK with KWriteU. IOP must be careful to load the high byte of the address last because KWriteUReg writes the low order byte of the U register into RHrK as a side effect. The IOP then sends 2*count bytes of data.} {KBuffer: rK¬ 0C0'x, CALL[GetIOPByte], c1; UKernMD¬ rK, c3, at[0C, 10, GetIOPByte]; rK¬ 0A0'x, CALL[GetIOPByte], c1; rK¬ rK LRot8, c3, at[0A, 10, GetIOPByte]; rK¬ rK or UKernMD, c1; UKernMD¬ rK, c2; rK¬ uKAddr, c3; MAR¬ [RHrK, rK+0], c1; MDR¬ UKernMD, c2; rK¬ rK+1, CarryBr, c3; uKAddr¬ rK, BRANCH[MoreB2, MoreB3], c1; MoreB2: rK¬ RHrK, GOTO[MoreB4], c2; MoreB3: rK¬ RHrK{+1}, GOTO[MassBugPatch], c2; MoreB4: RHrK¬ rK LRot0, c3; rK¬ uKCount, c1; rK¬ rK-1, ZeroBr, c2; MoreBlock: uKCount¬ rK, BRANCH[KBuffer, DoneBlock], c3; DoneBlock: rK¬ 0, GOTO[KWait], c1; MassBugPatch: rK¬ rK+1, GOTO[MoreB4], c*; } {END [KStart];}