;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; D O R A D O C o n t r o l P r o g r a m ; ; M i d a s I n t e r f a c e ; ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; filed on DoradoMidasInt.masm ; E. McCreight ; last modified September 28, 1982 10:16 AM .EXPORT MidasCall,MidasSetup,TrapEncountered .EXPORT InhibitMidas,EnableMidas,MidasReturnPoint .EXPORT MidasInhibitDepth .IMPORT PacifyWatchdog,PacifyWatchdogIfJumper .IMPORT ZPtr .IMPORT ZPC,ZS,ZP,ZA,ZX,ZY .IMPORT ZSubPC,ZSubS,ZSubP,ZSubA,ZSubX,ZSubY .IMPORT ZSimBlk .SHORT ZPtr .SHORT ZPC,ZS,ZP,ZA,ZX,ZY .SHORT ZSubPC,ZSubS,ZSubP,ZSubA,ZSubX,ZSubY .SHORT ZSimBlk .PREDEFINE "MCS6502PREDEFS.SR" .GETNOLIST "DoradoIO.mdefs" ; V a r i a b l e s .LOC MidasData .SHORT MidasData ; Midas need not know where this stuff is, because it's ; only used by the microcomputer. MidasInhibitDepth: .BLK 1 ; =0 if Midas is enabled MidasWantsYou: .BLK 1 ; =0 if Midas doesn't want service MidasSubrDepth: .BLK 1 ; >0 if within Midas subr call ; CPIBus[Trap'] = 0 if trap loop wants Midas' help NumberOfTraps = 8 TrapPoints: .BLK 3*NumberOfTraps ; 8 software breakpoints RestartTrap: .BLK 1 ; index of restart point, if any. -1 if none. ; M i d a s I n t e r f a c e .LOC 0fffa ; Non-maskable interrupt location .ADR MidasCall .LOC MidasCode ; Control gets here on a Midas-generated non-maskable interrupt MidasCall: CLD PHA LDA MidasInhibitDepth BEQ OKForMidas LDAI 0ff STA MidasWantsYou PLA RTI OKForMidas: INC MidasInhibitDepth CLI ; allow timer interrupts MidasAlreadyInhibited: PLA STA ZA ; Unload state to memory STX ZX STY ZY StoreInterruptState: PLA STA ZP PLA STA ZPC ; low-order ZPC byte PLA STA ZPC+1 ; high-order ZPC byte TSX STX ZS LDAI WaitingForMidas,#LowAddrByte CMP ZPC BNE NoTrap LDAI WaitingForMidas,#HighAddrByte CMP ZPC+1 BEQ StoreInterruptState ; interrupted at trap jump, recover ; original state NoTrap: LDXI AllOutput ; make sure Midas DDR's are right STX DAC+DDR ; set data direction registers for output INX STX CPRegH+DDR ; set data direction registers for input STX CPRegL+DDR LDXI CPIBusDDRValue STX CPIBus+DDR ; mixed I/O register JSR RemoveTraps MidasWaitForSync: LDAI 0 STA MidasWantsYou MidasNoSyncYet: LDA CPRegH ; wait until AMSync ne MASync EOR CPIBus BPL MidasNoSyncYet LDA CPRegH ANDI MidasCommand TAX LDA CPRegL DEX BMI MidasCommandDone ; CPReg[5-7] = 0 (no-op) BEQ MidasLdHiAd ; = 1 DEX BEQ MidasLdLoAd ; = 2 DEX BEQ MidasLdLoAdFetch ; = 3 DEX BEQ MidasFetchInc ; = 4 DEX BEQ MidasStoreInc ; = 5 DEX BEQ MidasMisc ; = 6 MidasCommandDone: LDA CPRegH TAX LDAI MASync ; invert MASync so MASync = AMSync EOR CPIBus STA CPIBus TXA ANDI HoldMC ; See if Midas wants to hold us in the break BEQ MidasNoHold WaitForNextInterrupt: JSR PacifyWatchdog LDA MidasWantsYou BEQ WaitForNextInterrupt BNE MidasWaitForSync MidasNoHold: JSR InsertTraps LDX ZS TXS LDA ZPC+1 ; high order ZPC byte PHA LDA ZPC ; low-order ZPC byte PHA LDA ZP PHA LDY ZY LDX ZX LDA ZA PHA LDA MidasWantsYou ; an interrupt between here & ; RTI might be screwed up. BEQ NoApparentCalls MidasWantedUs: JMP MidasAlreadyInhibited NoApparentCalls: PLA DEC MidasInhibitDepth RTI ; Midas-Invoked Routines MidasLdHiAd: STA ZPtr+1 JMP MidasCommandDone MidasLdLoAd: STA ZPtr JMP MidasCommandDone MidasLdLoAdFetch: STA ZPtr LDYI 0 LDA@Y ZPtr STA MAByte JMP MidasCommandDone MidasFetchInc: LDYI 0 LDA@Y ZPtr STA MAByte JMP MidasIncAd MidasStoreInc: LDYI 0 STA@Y ZPtr MidasIncAd: INC ZPtr BNE MidasCommandDone INC ZPtr+1 MidasNop: JMP MidasCommandDone MidasMisc: TAX BEQ MidasCommandDone ; CPReg[8-15] = 0 DEX BEQ MidasCallSubroutine ; = 1 DEX BEQ MidasResumeSubroutine ; = 2 BNE MidasNop ; > 2 ; Code to call a subroutine from the Midas code. The ; subroutine is assumed to start at location ZSubPC, and the ; S, P, A, X, and Y registers are loaded from ZSubS, ZSubP, ZSubA, ; ZSubX, and ZSubY before starting. The same storage locations are ; re-loaded from their respective registers upon completion or ; trap. MidasCallSubroutine: TSX ; calculate a safe subroutine stack TXA SEC SBCI 40 TAX TXS ; move to new stack JSR MidasDoCall BreakInsideMidas: BRK ; this traps a "proceed" after a RTS MidasDoCall: JSR MidasReallyDoCall MidasReturnPoint: PHP MidasTrapReturn: INC MidasInhibitDepth DEC MidasSubrDepth STA ZSubA PLA STA ZSubP CLD ; add 1 to recovered PC to fix JSR addresses CLC PLA ADCI 1 STA ZSubPC PLA ADCI 0 STA ZSubPC+1 STX ZSubX STY ZSubY TSX STX ZSubS ; save subroutine stack LDX ZS TXS ; recover old stack JMP MidasCommandDone MidasResumeSubroutine: LDX ZSubS ; resume old subroutine stack TXS MidasReallyDoCall: LDA ZSubPC+1 PHA LDA ZSubPC PHA LDA ZSubP PHA LDY ZSubY LDX ZSubX LDA ZSubA DEC MidasInhibitDepth INC MidasSubrDepth RTI ; Code called from maskable interrupt when a trap due ; to a BRK instruction (00x) ; is discovered. Stack has 0: X, 1: A, 2: P, 3: PCLow, 4: PCHigh. TrapEncountered: JSR InhibitMidas LDAI -2 LDX MidasSubrDepth BEQ ExternalTrap LDAI -3 ExternalTrap: CLD CLC TSX ; back the PC up to before the trap ADCX StackTop+3 STAX StackTop+3 LDAX StackTop+4 SBCI 0 STAX StackTop+4 PLA TAX LDA MidasSubrDepth BEQ ExternalTrap2 PLA JMP MidasTrapReturn ExternalTrap2: LDAI 0ff-Trap' ; notify Midas of trap AND CPIBus STA CPIBus PLA JSR EnableMidas WaitingForMidas: JSR PacifyWatchdogIfJumper ; only pacify watchdog timer if debugging jumper is installed JMP WaitingForMidas ; wait until Midas notices ; Midas Instruction Simulation ; Conditional branches are of the form xxx1 0000 ; One-byte instructions are of the form xxxx 10x0 ; Three-byte instructions are of the form xxxx 11xx ; Two-byte instructions are of the form xxxx 0xxx, except: ; BRK (trap) is 00 ; JSR is 20 ; RTI is 40 ; RTS is 60 MidasBuildSimulation: LDX RestartTrap BPL SimulationNeeded LDXI 03a ; opcode for NOP LDYI 2 STX ZSimBlk ; NOP in first byte BNE OneByter SimulationNeeded: LDAX TrapPoints+2 STA ZSimBlk LDYI 2 CopyMoreParameters: LDA@Y ZSubPC STAY ZSimBlk DEY BNE CopyMoreParameters LDXI 0ea ; opcode for NOP LDYI 3 ; instruction length LDA ZSimBlk CMPI 20 BEQ ThreeByter ; JSR is 3 bytes long ANDI 8 BEQ TwoByter LDA ZSimBlk ANDI 5 BNE ThreeByter OneByter: DEY STX ZSimBlk+1 ; NOP in second byte TwoByter: DEY STX ZSimBlk+2 ; NOP in third byte ThreeByter: ; compute address of successor instruction TYA ; length of instruction CLC ADC ZSubPC STA ZSimBlk+4 LDA ZSubPC+1 ADCI 0 STA ZSimBlk+5 LDA ZSimBlk+1 ; compute PC-relative branch address CLC ADC ZSimBlk+4 STA ZSimBlk+7 LDA ZSimBlk+1 ANDI 80 BPL HOPCOffsetInA LDAI -1 HOPCOffsetInA: ADC ZSimBlk+5 STA ZSimBlk+8 LDAI 04c ; opcode for absolute jump STA ZSimBlk+3 ; JMP to successor STA ZSimBlk+6 ; JMP if PC-relative branch taken LDA ZSimBlk ANDI 1f CMPI 10 BNE NotConditionalBranch LDAI 3 STA ZSimBlk+1 ; change conditional branch target to .+6 NotConditionalBranch: RTS MidasStartSimulation: LDA ZSimBlk CMPI 20 ; JSR BNE NoJSRSimulation LDA ZSimBlk+5 ; a JSR leaves NextPC-1 on the stack PHA LDA ZSubPC+4 PHA TSX LDAX StackTop DECX StackTop TAY BNE SubPCDecremented DECX StackTop+1 SubPCDecremented: LDAI 04c ; JMP STA ZSimBlk NoJSRSimulation: LDA ZSubP PHA LDY ZSubY LDX ZSubX LDA ZSubA DEC MidasInhibitDepth INC MidasSubrDepth PLP JMP ZSimBlk ; Instruction trap insertion/removal RemoveTraps: LDAI Trap' ; no longer trapped, trap state can be inferred ORA CPIBus ; from ZP STA CPIBus LDXI 3*(NumberOfTraps-1) ; un-trap all the breakpoints RemoveNextTrap: LDAI 0ff CMPX TrapPoints BNE RemoveFormerTrap CMPX TrapPoints+1 BEQ TryRemovingNextTrap RemoveFormerTrap: LDAX@ TrapPoints BEQ RemoveRealTrap ; trap still there LDAI 0ff ; remove it from the trap list (should also complain) STAX TrapPoints STAX TrapPoints+1 BNE TryRemovingNextTrap RemoveRealTrap: LDAX TrapPoints+2 ; opcode that should be there STAX@ TrapPoints TryRemovingNextTrap: DEX DEX DEX BPL RemoveNextTrap RTS InsertTraps: LDAI -1 STA RestartTrap LDXI 3*(NumberOfTraps-1) ; trap all the breakpoints InsertNextTrap: LDAI 0ff CMPX TrapPoints BNE InsertRealTrap CMPX TrapPoints+1 BEQ TryInsertingNextTrap InsertRealTrap: LDA ZSubPC CMPX TrapPoints BNE NotRestartAddr LDA ZSubPC+1 CMPX TrapPoints+1 BNE NotRestartAddr STX RestartTrap NotRestartAddr: LDAX@ TrapPoints STAX TrapPoints+2 ; opcode that should be there LDAI 0 ; BRK opcode STAX@ TrapPoints TryInsertingNextTrap: DEX DEX DEX BPL InsertNextTrap RTS ; Midas User-Callable Utilities MidasSetup: LDAI Trap' ; we are running live code, not the trap loop ORA CPIBus STA CPIBus LDXI 17 LDAI 0ff InvalidateMoreTrapPoints: STAX TrapPoints DEX BPL InvalidateMoreTrapPoints LDAI 0 STA MidasWantsYou ; no Midas interrupts yet STA MidasInhibitDepth ; but we'll accept them STA MidasSubrDepth ; we're in the main code routine RTS InhibitMidas: ; must save all processor state PHP INC MidasInhibitDepth PLP RTS EnableMidas: ; must save all processor state PHP PHA LDAI 1 CMP MidasInhibitDepth BNE DropALevel ; not really enabling, just dropping a level ; *** July 23, 1980 6:53 PM When Midas takes/gives control ; of the CPBus from/to the microcomputer by changing the value ; of AHasCP, there is a race that clips the strobe and ; simultaneously changes the CPBus data source. The following ; code guarantees that if Midas obeys the standard "politeness" ; convention, the clipped strobe will do no damage to the ; control section because the MCP address and data are benign. LDAI 0 STA MCPBusH LDAI Clock STA MCPBusL DEC MidasInhibitDepth LDA MidasWantsYou BNE SimulateMidasInterrupt BEQ EnableExitP ; (unconditional) DropALevel: DEC MidasInhibitDepth EnableExitP: PLA PLP RTS SimulateMidasInterrupt: SEI TXA PHA TSX CLC CLD LDAX StackTop+3 ; add 1 to the stacked ZPC, so it looks ADCI 1 ; like an interrupt STAX StackTop+3 LDAX StackTop+4 ADCI 0 STAX StackTop+4 PLA TAX PLA JMP MidasCall .END