{ file: <CoPilot>DLion>BlockB0.mc Created by E. Fiala 9 July 1986 Edited: Trow 18-Dec-87 16:03:45 Fix interrupts. Trow 22-Oct-87 16:15:09 Change XwdDisp to XdwDisp. Trow 12-Oct-87 19:17:06 Reverse targets 1 and 2 of XwdDisp. Trow 8-Oct-87 18:28:57 Added bank cross targets for Daybreak. Fiala 19-Nov-86 17:57:33 Removed MBus and PCBus opcodes to MBusB0.mc Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Formerly, all of this code was in CedarB0.mc. Opcodes defined here are as follows: LocalBlkZ (not debugged and not used at present) LongBlkZ Checksum WriteMBus ReadMBus WritePCBus ReadPCBus LongBlkZ and Checksum are performance critical. Checksum replaces a slower implementation formerly in Block.mc. Notes: 1) Check for occurrences of rhB ← rC LRot0, rB ← rC which does not cause an assembler error but doesn't work (??). } SetTask[0]; {*************************************************************************** aLocalBlkZ At entry to opcode: TOS Cardinal count of words to zero. Preceding Bank 1 code has been: Bank ← MSBank0, c1; {db} rhTT ← UvMDS, GOTOABS[BxLocalBlkZ], c2; {db} TT ← UvL, GOTOABS[B0LocalBlkZ], c3, at[BxLocalBlkZ]; At return: The count word is removed from the stack. Point <rhTT, TT> at the last word remaining to be zeroed; this word is at LOCAL + 4 + TOS - 1 since the data part of the local frame begins at LOCAL + 4. Zero the block in reverse order so that the only state variable is TOS, decremented after each word is zeroed. There is no 64k boundary crossing to worry about since the local frame is confined to the MDS. The block-zeroing subroutine is called and returns to the same exit as LongBlkZ. Timing = 1 click/word. ****************************************************************************} {db TT ← UvL, GOTOABS[B0LocalBlkZ], c3, at[BxLocalBlkZ];} {db @LocalBlkZa: TT ← TT + 3, c1, at[B0LocalBlkZ]; TT ← TT + TOS, L1 ← L1.LBZ2, c2; fXpop, push, CALL[BZero1], c3; db} {*************************************************************************** aLongBlkZ At entry to opcode: TOS Cardinal count of words to zero. STK High half of long pointer to block. STK-1 Low half of long pointer to block. Preceding Bank 1 code has been: Bank ← MSBank0, L1 ← L1.LBZ2, c1; {db} Q ← rhTT ← STK, GOTOABS[BxLongBlkZ], c2; {db} Rx ← TOS - 1, pop, GOTOABS[B0LongBlkZ], c3, at[BxLongBlkZ]; At return: The count word is removed from the stack, but the long pointer remains. The block is zeroed in reverse order to avoid state save and restore. After each iteration the count in TOS is decremented. Timing = 1 click/word. ****************************************************************************} {db} Rx ← TOS - 1, {CarryBr,} pop, GOTOABS[B0LongBlkZ], c3, at[BxLongBlkZ]; @LongBlkZa: TT ← STK, fXpop, fZpop, push, c1, at[B0LongBlkZ]; TT ← TT + Rx, CarryBr, push, c2; LBZ0: Q ← Q + 1, BRANCH[LBZ1, $], c3; rhTT ← Q LRot0, c1; GOTO[LBZ0], c2; {<rhTT, TT> points at the last word remaining to be zeroed. TOS holds the cardinal word count. Call the block-zeroing subroutine.} LBZ1: Map ← Q ← [rhTT, TT], push, CALL[BZero], c1; LBZ2: Bank ← MSBank1, push, GOTO[LBZ7] {In AssignRef}, c1, at[L1.LBZ2, 10, BZRets]; {*************************************************************************** BZero block-zeroing subroutine At entry to BZero1 and on any interrupt or page fault: TOS Cardinal count of words to zero. <rhTT, TT> Long pointer to last word of the block. L1 Return link. At exit: TOS, STK + 1 0. TT, rhTT, Q, Rx, rhRx, L0, L2 smashed The block is zeroed in reverse order to avoid state save and restore. After each iteration the count in TOS is decremented. On interrupts and page faults, the shadow cell for TOS on the stack is also written, and it is important that TOS and its shadow value in STK+1 = 0 on return for FreeObject. Timing = 1 click/word + 2 clicks/page. ****************************************************************************} {Entry from LocalBlkZ and FreeObject} BZero1: Map ← Q ← [rhTT, TT], push, c1; {Entry from LongBlkZ} BZero: [] ← STK ← TOS, ZeroBr, L2 ← L2.BZ1, pop, c2; {db} Rx ← rhRx ← MD, XdwDisp, BRANCH[$, BZ9], c3; BZ1: MAR ← [rhRx, Q + 0], DISP2[BZ4], c1, at[L2.BZ1, 10, NcWMapFixRets]; BZ4: CALL[NcWMapFix] {Will return via L2 to BZ1}, c2, at[0, 4, BZ4]; {WP=0, D=0 => set D=1 and proceed} {db} CALL[NcWMapFix] {WP fault}, c2, at[1, 4, BZ4]; {WP=1, D=0 => WP fault} CALL[NcWMapFix] {Page fault}, c2, at[3, 4, BZ4]; {WP=1, D=1 => Page fault} {db} MDR ← 0, MesaIntBr, c2, at[2, 4, BZ4]; {WP=0, D=1 => ok to write} BZ1a: TOS ← TOS - 1, ZeroBr, BRANCH[$, BZInt], c3; MAR ← Q ← [rhRx, Q - 1], BRANCH[$, BZ8], c1; BZ7: MDR ← 0, MesaIntBr, BRANCH[BZ1a, BZ5, 1], c2; {page cross; no word has been written} BZ5: Q ← 377'b, CANCELBR[$], c3; TT ← TT - Q - 1, CarryBr, L2 ← L2.BZ6, c1; Q ← rhTT, BRANCH[$, BZ6], c2; Q ← Q - 1, CALL[BankFix], c3; BZ6: TT ← TT or 377'b, GOTO[BZero1], c3, at[L2.BZ6, 10, BankFixRets]; BZInt: [] ← uWP, ZeroBr {Wakeups pending?}, BRANCH[$, BZ8a], c1; [] ← uWDC, NZeroBr {Wakeups disabled?}, BRANCH[$, BZ2a], c2; {db} rInt ← rInt or IntStat, ClrIntErr, BRANCH[$, BZ2b], c3; Bank ← MSBank1, c1; {db} push, GOTOABS[BxIntContinue1], c2; {db} STK ← TOS, pop, GOTOABS[B1IntContinue], c3, at[BxIntContinue1]; {no wakeups} BZ2a: {db} rInt ← rInt or IntStat, ClrIntErr, CANCELBR[$], c3; {wakeups disabled} BZ2b: MAR ← Q ← [rhRx, Q - 1], GOTO[BZ7], c1; BZ8a: {Finished} CANCELBR[BZ8b, 3], c2; BZ8: {Finished} CANCELBR[$, 3], c2; BZ8b: {noop} c3; {This code is copied from Block.mc. It sets MesaIntRq true if the next opcode crosses a page boundary. Discussion of this is in Refill.mc.} BZ9: Xbus ← uPCCross, XRefBr, push, CANCELBR[$, 3], c1; STK ← TOS, pop, L1Disp, BRANCH[$, BZSetInt], c2; {This instruction sets up TT for the FreeObject opcode.} TT ← LShift1(uBSISave), SE ← 0, RET[BZRets], c3; BZSetInt: MesaIntRq, RET[BZRets], c3; {****************************************************************************** Checksum TOS = uStack5 = srcHi STK = uStack4 = srcLo STK-1 = uStack3 = count STK-2 = uStack2 = csum The 16-bit Pup checksum is initialized to zero; each 16-bit word in the block is ones-complement added to the checksum; following each addition, the checksum is left-cycled 1. A final result of "minus one" (177777B) is converted to zero. 177777B is specifically defined to mean that the Pup carries no checksum. During the inner loop, TOS holds the word count, <rhTT, Q> the current VA, T the checksum, Rx and rhRx the high part of the real address. Timing: 2 clicks/word + 5 clicks/page. Previous instructions in bank 1 have been: Bank ← MSBank0, L0 ← L0.CSRem2, c1, at[7, 10, Misc0n]; {db} rhTT ← TOS LRot0 {srcHi}, GOTOABS[BxCSum], c2; {db} TT ← STK {srcLo}, GOTOABS[B0CSum], c3, at[BxCSum]; **********************************************************************************} {db} TT ← STK {srcLo}, GOTOABS[B0CSum], c3, at[BxCSum]; Checksum: TOS ← uStack3 {count}, ZeroBr, c1, at[B0CSum]; BRANCH[$, CSDon0], c2; CALL[NcRdOne], c3; CSLp: MAR ← Q ← [rhRx, Q + 1], c1; CSLp1: {db} {MesaIntBr,} BRANCH[$, CSPgO, 1], c2; TT ← MD, BRANCH[$, CSInt], c3; CSRmLp: T ← T + TT, CarryBr, c1; CSInt5: TOS ← TOS - 1, ZeroBr, BRANCH[$, CSWrap], c2; T ← T LRot1, BRANCH[CSLp, CSDone], c3; CSWrap: T ← (T + 1) LRot1, BRANCH[CSLp, CSDone], c3; {Finished with final word. If csum is all 1's, change it to 0.} CSDon0: T ← uStack2 {csum}, c3; CSDone: [] ← T xor ~0, NZeroBr, pop, c1; Xbus ← uPCCross, XRefBr, pop, BRANCH[$, CSDon3], c2; STK ← TOS ← 0, pop, BRANCH[CSDon5, CSDon4], c3; CSDon3: STK ← TOS ← T, pop, BRANCH[CSDon5, CSDon4], c3; CSDon4: MesaIntRq, c1; Noop, c2; Noop, c3; CSDon5: Bank ← MSBank1, GOTO[CSDon6] {In CRefType}, c1; {Interrupt request: Ignore current word and check for done first. ClrIntErr must occur before or in the same click as uWP testing.} CSInt: [] ← uWP, ZeroBr {Wakeups pending?}, c1; [] ← uWDC, NZeroBr {Disabled?}, BRANCH[$, CSInt2], c2; uStack2 ← T, ClrIntErr, BRANCH[CSInt4, CSInt3], c3; CSInt2: ClrIntErr, CANCELBR[$], {no wakeups} c3; CSInt3: T ← T + TT, CarryBr, GOTO[CSInt5], {Wakeups disabled} c1; {The interrupt will take. Save the state; tricky part is reconstructing the virtual address from <low 8 bits in Q, high 8 bits in uStack4>; srcHi is already correct since it is updated on every page overflow.} CSInt4: TT ← uStack4 {old srcLo}, c1; TT ← TT and ~0FF, c2; Q ← Q and 0FF, c3; TT ← TT or Q, push, c1; uStack4 ← TT {current srcLo}, c2; uStack3 ← TOS {count}, c3; Bank ← MSBank1, c1; {db} Q ← rhTT, GOTOABS[BxIntContinue2], c2; {db} TOS ← STK ← Q, pop, GOTOABS[B1IntContinue], c3, at[BxIntContinue2]; {Page crossing: First, update the stack values from the working registers in preparation for a possible page fault. Then copy the updated source address into <rhTT, TT> for mapping. } CSPgO: TT ← uStack4 {old srcLo}, CANCELBR[$], c3; {Set VA to the last word of the old page and then add 1.} TT ← TT or 0FF, push {point stkp at srcHi}, c1; TT ← TT + 1, CarryBr, c2; CSPgO1: uStack3 ← TOS {count}, BRANCH[CSRem, $], c3; Q ← rhTT + 1, LOOPHOLE[byteTiming], c1; rhTT ← Q LRot0, STK ← Q, GOTO[CSPgO1], c2; CSRem: Noop, c1; CSRem1: uStack4 ← TT {srcLo}, L0 ← L0.CSRem2, c2; uStack2 ← T {csum}, pop, CALL[NcRdOne], c3; CSRem2: TT ← uStack2 {csum}, c1, at[L0.CSRem2, 10, NcRdOneRets]; Noop, c2; GOTO[CSRmLp], c3;