{File name FixedBlock.mc Description: Mesa Block Transfer opcodes Author: Jim Frandeen Created: January 30, 1981 Last edited Fiala 15-May-86 9:58:29 Copyright (C) 1981, 1982, 1983, 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Fiala 15-May-86 9:58:36 Change for 4MB storage at blSetDestDirty. Fiala 18-Apr-86 9:48:08 Deleted Checksum opcode (moved to CedarB0.mc and CedarMisc.mc with performance improvements); renamed file to "FixedBlock.mc". Fiala, BJackson 4-Jun-85 18:30:57: Fix Checksum for count=0 Frandeen March 23, 1981 2:46 PM: Update comments and try out new coding conventions. Frandeen March 16, 1981 11:42 AM: Add code to handle Checksum Misc opcode. Frandeen February 25, 1981 3:02 PM: Major rewrite. The goddamn BlockEqual instructions are not at all like the BlockTransfer instructions. The code offset is on the top of the stack. Frandeen February 24, 1981 10:50 PM: Fix the Zero Byte Page Carry Gotcha. Frandeen February 21, 1981 3:23 AM: Major rewrite. BlockEqual is a two-byte opcode. Frandeen February 17, 1981 11:10 PM: Change to check for zero count BEFORE we do any maps. Unfortunately, we get address fault otherwise. } {This module handles BlockTransfer and BlockEqual instructions. For the Block instructions, L2 is used to call SaveBlockRegs. It is also used to distinguish the opcodes as follows: Inst. L2 code long Block Transfer BLT 3 0 0 1 1 BLTC B 1 0 1 1 BLEQ 2 0 0 1 0 BLEQC A 1 0 1 0 BLTL 7 0 1 1 1 BLEQL 6 0 1 1 0 BLEQCL E 1 1 1 0 After L2Disp: BRANCH[NotBlockTransferInstruction, BlockTransferInstruction, 0E], BRANCH[ShortInstruction, LongInstruction, 0B], BRANCH[ZeroBase, CodeBase, 7], } Set[Transfer,0E]; Set[Block,0D]; Set[Long,0B]; Set[CodeBase,7]; { SUBROUTINES } {****************************************************************************************** PopCount subroutine L2Disp is pending on call. ******************************************************************************************} MacroDef[PopCountRet,at[#1,10,PopCountRet]]; {Return depending on the two middle bits of L2:} Set[PopShortBlock,0B]; {x01x = 0B = short Block} Set[PopLongBlock,0F]; {x11x = 0F = long Block} Set[PopBLEQC,9]; {x00x = 9 = BLEQC (called with no disp)} PopCount: TOS ← STK, pop, DISP4[PopCountRet,9], c*; { BLOCK EQUAL INSTRUCTIONS } {****************************************************************************************** BLEQL Block Equal Long (sBLTE = KFCB 56B) TOS = srcHi STK = srcLow STK-1 = count STK-2 = destHi STK-3 = destLow We will treat this just like BlockTransferLong. This means that if we update the stack because we are interrupted, we will come back in with the operands reversed in the stack. ******************************************************************************************} @BLEQL: at[6,8,BlockEqual] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQL, c1; rhT ← TOS {destHi} LRot0, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLEQL,10,SavebbRegsRet] T ← STK {destLow}, pop {count}, L2Disp, CALL[PopCount], c1; {TOS ← STK, pop {srcHi}, GOTO[BLongSourceCommon], c2;} {****************************************************************************************** BLEQCL Block Equal Code Long TOS = offset of source from code STK = count STK-1 = destHi STK-2 = destLow ******************************************************************************************} @BLEQCL: at[0,8,BlockEqual] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQCL, c1; Rx ← TOS {code offset}, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLEQCL,10,SavebbRegsRet] TOS ← STK {count}, pop {destHigh}, c1; rhT ← STK {destHigh}, pop{destLow}, {Continue next line} GOTO[BlockEqualCodeCommon], c2; {****************************************************************************************** BLEQ Block Equal (sBLTE = KFCB 52B) TOS = srcLow STK = count STK-1 = destLow We will treat this just like BlockTransfer. This means that if we update the stack because we are interrupted, we will come back in with the operands reversed in the stack. ******************************************************************************************} @BLEQ: at[2,8,BlockEqual], L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQ, c1; TT ← rhTT {srcHi} ← UvMDS,CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} rhT ← UvMDS {dstHi}, {Continue next line} at[BLEQ,10,SavebbRegsRet] push {destLow, now in stack}, GOTO[blShortSourceCommon], c1; {****************************************************************************************** BLEQC Block Equal Code (sBLTE = KFCB 54B) TOS = offset of source from codebase STK = count STK-1 = destLow ******************************************************************************************} @BLEQC: at[4,8,BlockEqual] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLEQC, c1; Rx ← TOS {code offset}, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLEQC,10,SavebbRegsRet] rhT ← UvMDS {dstHi}, {0 disp} CALL[PopCount], c1; {TOS ← STK {count}, pop {destLow}, c2;} BlockEqualCodeCommon: PopCountRet[PopBLEQC] TT ← rhTT ← UvChigh {srcHi}, c3; T ← STK {destLow}, pop, GOTO[blTestCount], c1; { BLOCK TRANSFER INSTRUCTIONS } {****************************************************************************************** BLTL Block Transfer Long TOS = destHi STK = destLow STK-1 = count STK-2 = srcHi STK-3 = srcLow ******************************************************************************************} @BLTL: opcode[353'b] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLTL, c1; rhT ← TOS {destHi} LRot0, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLTL,10,SavebbRegsRet] T ← STK {destLow}, pop {count}, L2Disp, CALL[PopCount], c1; {TOS ← STK {count}, pop{srcHi}, c2;} blLongSourceCommon: PopCountRet[PopLongBlock] TT ← rhTT ← STK {srcHi}, pop {srcLow}, GOTO[blPopSourceLow] c3; {****************************************************************************************** BLTC Block Transfer Code TOS = destLow STK = count STK-1 = offset of source from codebase ******************************************************************************************} @BLTC: opcode[354'b] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLTC, c1; TT ← rhTT {srcHi} ← UvChigh {dstHi}, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} rhT ← UvMDS {dstHi}, {continue next line} at[BLTC,10,SavebbRegsRet] push {destLow, now in stack}, GOTO[blShortSourceCommon], c1; {****************************************************************************************** BLT Block Transfer TOS = destLow STK = count STK-1 = srcLow ******************************************************************************************} @BLT: opcode[352'b] L ← ~ErrnIBnStkp, ULsave ← L, L2 ← BLT, c1; TT ← rhTT {srcHi} ← UvMDS, CALL[SaveBlockRegs], c2; {SaveBlockRegs subroutine here c3-c3} at[BLT,10,SavebbRegsRet] rhT ← UvMDS {dstHi}, push {destLow, now in stack}, c1; blShortSourceCommon: T ← STK {destLow}, pop {count}, L2Disp, CALL[PopCount], c2; {TOS ← STK {count}, pop {srcLow}, c3;} blPopSourceLow: PopCountRet[PopShortBlock] Rx ← STK {srcLow}, pop {next}, c1; blTestCount: [] ← TOS {count}, ZeroBr, CANCELBR[$,1], c2; blTestOffset: L2Disp, BRANCH[$,blNullCount], c3; blSaveStackP: uStackPSave ← L, BRANCH[blZeroOffset,blCodeOffset, CodeBase], c1; blZeroOffset: Q ← 0, GOTO[blSaveOffset], c2; blCodeOffset: Q ← UvC, c2; blSaveOffset: uSourceOffset ← Q, L1 ← L1w.Restore, GOTO[blMapSource], c3; blNullCount: {L2Disp is pending. Load L0 to return to BLTransferDone or BLCompareDone. Put one in TOS for TRUE in case this is BlockEqual. It doesn't matter which Restore entry we use so long as it is 3 cycles and we don't try to restore rhL. } TOS ← 1 {TRUE}, L0 ← BLEQfini, {continue next line} BRANCH[RestoreBlockRegsC3Initial, RestoreBlockRegsC3, Transfer], c1; {Restore here c2-c1}; blTransferDone: at[BLTfini,10,RestoreCallers] TOS ← STK, pop, GOTO[blDone], c2; blCompareDone: {1 (TRUE) or 0 (FALSE) is in TOS.} at[BLEQfini,10,RestoreCallers] {BlockCompare is a two-byte instruction.} PC ← PC + PC16, c2; blDone: Xbus ← uPCCross, XRefBr, c3; PC ← PC + PC16, BRANCH[IBDispOnly,blSetInt], c1; blSetInt: MesaIntRq, GOTO[NoWakeups {in Refill}], c2; {NoWakeups: GOTO[IgnoreInt] ,c3;} {IgnoreInt: Noop ,c1;} {IBDispOnly: IBDisp, GOTO[DISPNIonly] ,c2;} {****************************************************************************************** Map source and dest virtual addresses Entry State: Registers have been saved by SaveBlockRegs and must be restored. All arguments have been removed from the stack except for the case of Checksum. In the case of Checksum, we must set Stack pointer to 1 before we exit. L1 = L1w.Restore: return address from RLMapFix or WLMapFix in case of trap. rhTT = TT = source virtual address high Rx = source virtual address low rhT = dest virtual address high T = dest virtual address low Q = source offset (0 if not Code operation, else UvC) TOS = count ******************************************************************************************} {Map source real address into rhG, G. Test map for referenced. If not referenced, update the map since we are about to read it. We want to end up with the source VA in rhTT,,Q in case of a trap on source. Save rhTT (source high) and Rx (source low) so that we can restore it later. Set up L0 to return to BLMapDest if we call RLMapFix.} blMapSource: Map ← Q ← [rhTT, Rx + Q], c1; blMapSourcex: uSourceHighSave ← TT, PC ← Q, L0 ← L0.BLSource, c2; G ← rhG ← MD, XRefBr, uSourceLowSave ← Rx, c3; {Map dest real address into rhRx, Rx. We want to end up with the source VA in rhTT,,Q in case of a trap on dest. Return here from RLMapFix if we needed to set the referenced bit. Set up L0 to return to BLTestDestDirty if BlockTransfer or BLTestDestReferenced if BlockEqual.} blMapDest: Map ← [rhT, T], L2Disp, BRANCH[blSetSourceReferenced, blSourceMapOK], c1, at[L0.BLSource, 10, RMapFixCaller]; blSourceMapOK: Q ← rhT, L0 ← L0.BLDestR, BRANCH[blMapDestRead, blMapDestWrite, Transfer], c2; {Come here for BlockTransfer to map the dest address for write. If not dirty, we will have to fix the map since we are about to write it.} blMapDestWrite: rhRx ← Rx ← MD, XwdDisp, c3; blTestDestDirty: {Return here from WLMapFix after we set dirty bit.} rhTT ← Q LRot0, DISP2[blSetDestDirty], c1, at[L0.BLDest,10,WMapFixCaller]; blSetSourceReferenced: {Come here if source map must be fixed for referenced. Q = virtual sourceLow. rhTT = virtual sourceHi. Set Rx to map data. L0 = L0.BLSource to return to BLMapDest above after we set referenced bit. L1 = L1w.restore = L1r.Restore to return to BLReadTrap in case of trap.} Rx ← G, CANCELBR[RLMapFix,0F], c2; blSetDestDirty: {Come here if dest map must be fixed for dirty. Rx = map data. rhTT = virtual destHi. Set Q to virtual destLow. L0 = L0.BLDest for return to BLTestDestDirty after we set dirty bit. L1 = L1w.restore = L1r.restore to return to BLWriteTrap in case of trap.} Q ← T, CALL[WLMapFix], c2, at[0, 4, blSetDestDirty]; Q ← T, CALL[WLMapFix], c2, at[2, 4, blSetDestDirty]; Q ← T, CALL[WLMapFix], c2, at[3, 4, blSetDestDirty]; blDestDirty: {Restore while we have a spare cycle in case we get to Update.} TT ← rhTT ← uSourceHighSave, GOTO[blStart], c2, at[1, 4, blSetDestDirty]; blMapDestRead: {Come here if Block Equal. Test dest for referenced. If not referenced, we will have to fix the map since we are about to read it. } rhRx ← Rx ← MD, XRefBr, c3; blTestDestReferenced: at[L0.BLDestR,10,RMapFixCaller] {Return here from RLMapFix after we set dest referenced.} rhTT ← Q LRot0, BRANCH[blSetDestReferenced, blDestReferenced], c1; blSetDestReferenced: {Come here if dest map must be fixed for referenced. Rx = map data. rhTT = virtual destHi. Set Q to virtual destLow. L0 = L0.BLDest for return to blTestDestReferenced after we set dirty bit. L1 = L1w.restore = L1r.Restore to return to blWriteTrap in case of trap.} Q ← T, CALL[RLMapFix], c2; blDestReferenced: {Restore while we have a spare cycle in case we get to Update.} TT ← rhTT ← uSourceHighSave, GOTO[blStart], c2; blReadTrap: at[L1r.Restore,10,RFixForTrap] {Return here from RLMapFix in case of trap on either source or dest (if Block Equal).} stackP ← uStackPSave, {Continue next line} L0 ← restore.trap, GOTO[blRestoreBlockRegs], c1; blWriteTrap: at[L1w.Restore,10,WFixForTrap] {Return here from WLMapFix in case of trap on dest.} stackP ← uStackPSave, L0 ← restore.trap, c1; blRestoreBlockRegs: {StackP has been restored from uStackPSave. Restore all other registers. Q = virtual address low. TT = virtual address high.} uFaultParm0 ← Q, push, CALL[RestoreBlockRegsC1], c2; {Restore here. c3-c3} at[restore.trap,10,RestoreCallers] {Restore TOS so VArhTT can save it again} TOS ← STK, GOTO[VArhTT], c1; {****************************************************************************************** Block inner loop Entry State: L1 = L1w.Restore: return address from RLMapFix or WLMapFix in case of trap. rhTT = source virtual address high uSourceHighSave = source virtual address High PC = source virtual address low uSourceLowSave = source virtual address low rhG,,G = source real address high rhT = dest virtual address high T = dest virtual address low rhRx,,Rx = dest real address high TOS = count Registers used: Q = words transferred L = source word read ******************************************************************************************} blStart: L {words transferred} ← 0, c3; {Note that it is necessary to do the first memory reference separately. Unfortunately, we cannot use Q to add to the virtual address each time through the loop. When the low byte of the virtual address is zero, we will never get a page cross branch. This is the Zero Byte Page Cross Gotcha. } MAR ← G ← [rhG, PC+0], c1; Noop, c2; PC ← MD {source}, L2Disp, c3; blFirstDest: MAR ← Rx ← [rhRx, T + 0], {Continue next line} {Set L0 to return to BLTransferDone or BLCompareDone.} L0 ← BLEQfini, BRANCH[blReadNextDest, blWriteNextDest, Transfer], c1; {Read next source word: high 10 bits from rhG,,G, low 8 bits from G + 1.} blReadSource: MAR ← G ← [rhG, G + 1], MesaIntBr, c1; DISP2[blSourceCheck], c2; PC ← MD {source}, L2Disp, GOTO[blNextDest], c3, at[0,4,blSourceCheck]; GOTO[blUpdate], {source page cross} c3, at[2,4,blSourceCheck]; GOTO[blUpdate], {interrupt} c3, at[1,4,blSourceCheck]; GOTO[blUpdate], {source page cross and interrupt} c3, at[3,4,blSourceCheck]; blNextDest: {Send real address of dest: high 10 bits from rhRx,,Rx, low 8 bits from Rx + 1.} MAR ← Rx ← [rhRx, Rx + 1], BRANCH[blReadNextDest, blWriteNextDest, Transfer], c1; blWriteNextDest: {Come here if BlockTransfer to store the source word in the dest address.} MDR {dest} ← PC {source}, TOS {count} ← TOS - 1, ZeroBr, DISP2[blDestWriteCheck], c2; blIncrementWordsTransferred: L {words transferred} ← L + 1, BRANCH[blReadSource, blCountZero], c3, at[0,4,blDestWriteCheck]; TOS {count} ← TOS + 1, CANCELBR[blUpdate,1], c3, at[2,4,blDestWriteCheck]; blReadNextDest: {Come here if BlockEqual to compare the source word to the dest word.} Noop, BRANCH[blNoDestReadPageCross, blDestReadPageCross,1], c2; blDestReadPageCross: Noop, GOTO[blUpdate], {Dest page cross} c3; blNoDestReadPageCross: PC ← MD {source} xor PC {dest}, c3; [] ← PC, NZeroBr, c1; blDecrementCount: TOS {count} ← TOS - 1, ZeroBr, BRANCH[blIncrementWordsTransferred, blFalse], c2; blFalse: TOS ← 0 {false}, CANCELBR[RestoreBlockRegsC1,1], c3; {RestoreBlockRegs here. Return to blCompareDone. c1-c1} blCountZero: TOS ← 1 {TRUE}, {next line} GOTO[RestoreBlockRegsC3], c1; {Return to blTransferDone or blCompareDone. c2-c1} {****************************************************************************************** blUpdate Come here if we got a source or dest page cross or an interrupt request. L1 = L1w.Restore: return address from RLMapFix or WLMapFix in case of trap. TT = rhTT =uSourceHighSave source virtual address high uSourceLowSave = source virtual address low uSourceOffset = 0 if not code, UvC if code operation rhT = dest virtual address high T = dest virtual address low TOS = count L = number of words transferred uStackPSave = stack pointer at beginning of operation. ******************************************************************************************} blUpdate: T {new destLow} ← T {destLow} + L {words transferred}, {Continue next line} CarryBr, CANCELBR[$,0F], c1; blTestDestCarry: Rx ← uSourceLowSave {srcLow}, {Continue next line} BRANCH[blNoDestCarry, blDestCarry], c2; blDestCarry: Q ← rhT {destHi} + 1, LOOPHOLE[byteTiming], c3; rhT ← Q LRot0, GOTO[blTestDestCarry], {delay 1 cycle} c1; blNoDestCarry: Rx {sourceLow} ← Rx {sourceLow} + L {words transferred}, CarryBr, c3; blTestSourceCarry: L ← ~ErrnIBnStkp, {Continue next line} L2Disp, BRANCH[blUpdateStack, blSourceCarry], c1; blSourceCarry: TT ← TT {sourceHigh} + 1, CANCELBR[$,0F], c2; rhTT ← TT LRot0, GOTO[blTestSourceCarry], {delay 1 cycle} c3; {****************************************************************************************** If we must interrupt, we must save the state in the stack. This is messy because BlockEqual and BlockTransfer are currently different. Current State: TT = rhTT = source virtual address high Rx = source virtual address low rhT = dest virtual address high T = dest virtual address low TOS = count stack pointer points to next. This is the way the stack should look when we are done: BLEQL BLEQCL BLEQ and BLEQC TOS = srcHi TOS = srcLow TOS = srcLow STK = srcLow STK = count STK = count STK-1 = count STK-1 = destHi STK-1 = destLow STK-2 = destHi STK-2 = destLow STK-3 = destLow BLTL BLTC and BLT TOS = destHi TOS = destLow STK = destLow STK = count STK-1 = count STK-1 = srcLow STK-2 = srcHi STK-3 = srcLow We treat BLEQ just like BLT, and we treat BLEQL just like BLTL. This means that the operands will be reversed in the stack. ******************************************************************************************} blUpdateStack: L2Disp, Q ← rhT {destHigh}, push {destLow/sourceLow}, DISP4[blStackFormat,0C], c2; blSourceFirst: {BlockTransfer} STK ← Rx {sourceLow}, push {srcHi/count}, BRANCH[blShortSourceFirst,blLongSourceFirst, Long], c3, at[0F,10,blStackFormat]; blShortSourceFirst: L2Disp, GOTO[blStoreCount], c1; blLongSourceFirst: STK ← TT {sourceHi}, push {count}, L2Disp, GOTO[blStoreCount], c1; blDestFirst: {BlockEqual} STK ← T {destLow}, push {destHi/count}, at[0E,10,blStackFormat], BRANCH[blShortDestFirst,blLongDestFirst, Long], c3; blShortDestFirst: L2Disp, GOTO[blStoreCount], c1; blLongDestFirst: STK ← Q {destHi}, push {count}, L2Disp, GOTO[blStoreCount], c1; blStoreCount: STK ← TOS {count}, push {sourceLow/destLow}, L2Disp, BRANCH[blSourceLast, blDestLast, Transfer], c2; blSourceLast: {BlockEqual} STK ← Rx {sourceLow}, push {srcHi/next}, L2Disp, BRANCH[blShortSourceLast,blLongSourceLast, Long], c3; blLongSourceLast: MesaIntBr, BRANCH[blStoreLongSourceHiLast, blTestInterrupt, CodeBase], c1; blStoreLongSourceHiLast: {Two extra words here to handle Block Equal Code Long. Not too bad.} STK ← TT {sourceHi}, CANCELBR[$,1], c2; Noop, c3; blShortSourceLast: MesaIntBr , CANCELBR[blTestInterrupt,0F], c1; blDestLast: {BlockTransfer} STK ← T {destLow}, push {destHi/next}, BRANCH[blShortDestLast,blLongDestLast, Long], c3; blShortDestLast: MesaIntBr, GOTO[blTestInterrupt], c1; blLongDestLast: STK ← Q {destHi}, MesaIntBr, c1; blTestInterrupt: {We came through here because the interrupt request was set or because we crossed a page boundary. If the interrupt request is not on, go back to start on the next page.} Q {sourceOffset} ← uSourceOffset, ClrIntErr, BRANCH[blNoInterrupt, blInterrupt], c2; blNoInterrupt: {Restore the stack pointer the way it was when all arguments were removed.} stackP ← L, GOTO[blMapSource], c3; blInterrupt: [] ← uWP {wakeups pending}, ZeroBr, c3; {Restore the stack pointer the way it was when all arguments were removed. We must have the original stack pointer in L when we get to TestCount.} stackP ← L, L ← uStackPSave, BRANCH[blTestWakeupsDisabled, blTestCount {none pending}], c1; blTestWakeupsDisabled: [] ← uWDC {wakeup disable count}, L0 ← restore.int, NZeroBr, c2; Noop, BRANCH[blDoInt {not disabled}, blMapSource {disabled}], c3; blDoInt: stackP ← uStackPSave, CALL[RestoreBlockRegsC1], c1; {RestoreBlockRegs here. Return to bbInit: c2-c2}; {RestoreRRegsAndRhG here. Return to bbInit: c1-c2}; {Rx ← pInt, push, c3, at[restore.int,10,RestoreCallers]}; {TOS ← STK, pop, GOTO[SaveRegs], c1;}