mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 05:40:49 -04:00
added CYCLE quantum
updated QUANTUM and STEP commands to accoodate new quantum control window changed to support the three quantum options improved/corrected the conditions under which the ONSTEP command is run disassembly.ExecutedEntry() updates existing entry
This commit is contained in:
parent
0e6b1adc2a
commit
9f6cbdad58
|
@ -145,7 +145,7 @@ func (dsm *Disassembly) Start() {
|
|||
// have been called on the last CPU cycle of the instruction that triggers
|
||||
// the coprocessor reset. the TV will not have moved onto the beginning of
|
||||
// the next instruction yet so we must figure it out here
|
||||
dsm.disasm.LastStart = dsm.tv.AdjCoords(television.AdjCPUCycle, 1)
|
||||
dsm.disasm.LastStart = dsm.tv.AdjCoords(television.AdjCycle, 1)
|
||||
}
|
||||
|
||||
dsm.disasm.LastExecution = dsm.disasm.LastExecution[:0]
|
||||
|
|
|
@ -242,20 +242,25 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
mode = strings.ToUpper(mode)
|
||||
|
||||
if back {
|
||||
var instruction bool
|
||||
// step backwards
|
||||
|
||||
var adj television.Adj
|
||||
|
||||
switch mode {
|
||||
case "":
|
||||
// continue with current quantum state
|
||||
if dbg.Quantum() == govern.QuantumInstruction {
|
||||
instruction = true
|
||||
} else {
|
||||
// use current quantum state
|
||||
switch dbg.Quantum() {
|
||||
case govern.QuantumCycle:
|
||||
adj = television.AdjCycle
|
||||
case govern.QuantumClock:
|
||||
adj = television.AdjClock
|
||||
}
|
||||
|
||||
case "INSTRUCTION":
|
||||
dbg.setQuantum(govern.QuantumInstruction)
|
||||
instruction = true
|
||||
case "CYCLE":
|
||||
dbg.setQuantum(govern.QuantumCycle)
|
||||
adj = television.AdjCycle
|
||||
case "CLOCK":
|
||||
dbg.setQuantum(govern.QuantumClock)
|
||||
adj = television.AdjClock
|
||||
|
@ -269,7 +274,7 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
|
||||
var coords coords.TelevisionCoords
|
||||
|
||||
if instruction {
|
||||
if dbg.Quantum() == govern.QuantumInstruction {
|
||||
coords = dbg.cpuBoundaryLastInstruction
|
||||
} else {
|
||||
coords = dbg.vcs.TV.AdjCoords(adj, adjAmount)
|
||||
|
@ -277,59 +282,69 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
|
||||
dbg.setState(govern.Rewinding)
|
||||
dbg.unwindLoop(func() error {
|
||||
// update catchupQuantum before starting rewind process
|
||||
dbg.catchupQuantum = dbg.Quantum()
|
||||
|
||||
dbg.catchupContext = catchupStepBack
|
||||
return dbg.Rewind.GotoCoords(coords)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// step forwards
|
||||
|
||||
// step forward
|
||||
switch mode {
|
||||
case "":
|
||||
// continue with current quantum state
|
||||
switch mode {
|
||||
case "":
|
||||
// continue with current quantum state
|
||||
|
||||
// if quantum is instruction and CPU is not RDY then STEP is best
|
||||
// implemented as TRAP RDY
|
||||
if dbg.Quantum() == govern.QuantumInstruction && !dbg.vcs.CPU.RdyFlg {
|
||||
_ = dbg.halting.volatileTraps.parseCommand(commandline.TokeniseInput("RDY"))
|
||||
// if quantum is not the QuantumClock and CPU is not RDY then STEP
|
||||
// is best implemented as TRAP RDY. this means that the emulation
|
||||
// will stop on the next instruction boundary and will also skip
|
||||
// over instructions that trigger WSYNC
|
||||
//
|
||||
// this behaviour is more intuitive to the user because it means
|
||||
// they don't have to step over every cycle during the WSYNC state
|
||||
if dbg.Quantum() != govern.QuantumClock && !dbg.vcs.CPU.RdyFlg {
|
||||
// create volatile RDY trap
|
||||
_ = dbg.halting.volatileTraps.parseCommand(commandline.TokeniseInput("RDY"))
|
||||
dbg.runUntilHalt = true
|
||||
|
||||
// when the RDY flag changes the input loop will think it's
|
||||
// inside a video step. we need to force the loop to return
|
||||
// to the non-video step loop
|
||||
dbg.stepOutOfVideoStepInputLoop = true
|
||||
}
|
||||
case "INSTRUCTION":
|
||||
dbg.setQuantum(govern.QuantumInstruction)
|
||||
case "CYCLE":
|
||||
dbg.setQuantum(govern.QuantumCycle)
|
||||
case "CLOCK":
|
||||
dbg.setQuantum(govern.QuantumClock)
|
||||
default:
|
||||
// token not recognised so forward rest of tokens to the volatile
|
||||
// traps parser
|
||||
tokens.Unget()
|
||||
_ = dbg.halting.volatileTraps.parseCommand(tokens)
|
||||
|
||||
// trap may take many cycles to trigger
|
||||
dbg.runUntilHalt = true
|
||||
|
||||
// when the RDY flag changes the input loop will think it's
|
||||
// inside a video step. we need to force the loop to return
|
||||
// to the non-video step loop
|
||||
dbg.stepOutOfVideoStepInputLoop = true
|
||||
}
|
||||
case "INSTRUCTION":
|
||||
dbg.setQuantum(govern.QuantumInstruction)
|
||||
case "CLOCK":
|
||||
dbg.setQuantum(govern.QuantumClock)
|
||||
default:
|
||||
// do not change quantum
|
||||
tokens.Unget()
|
||||
|
||||
// ignoring error
|
||||
_ = dbg.halting.volatileTraps.parseCommand(tokens)
|
||||
|
||||
// trap may take many cycles to trigger
|
||||
dbg.runUntilHalt = true
|
||||
// continue emulation. note that we don't set runUntilHalt except in the
|
||||
// specific cases above in the above switch. this is because we do no
|
||||
// always set a volatile trap. without a trap the emulation will just
|
||||
// run until it receives a HALT instruction.
|
||||
dbg.continueEmulation = true
|
||||
}
|
||||
|
||||
// always continue
|
||||
dbg.continueEmulation = true
|
||||
|
||||
case cmdQuantum:
|
||||
mode, _ := tokens.Get()
|
||||
mode = strings.ToUpper(mode)
|
||||
switch mode {
|
||||
case "INSTRUCTION":
|
||||
dbg.setQuantum(govern.QuantumInstruction)
|
||||
case "CYCLE":
|
||||
dbg.setQuantum(govern.QuantumCycle)
|
||||
case "CLOCK":
|
||||
dbg.setQuantum(govern.QuantumClock)
|
||||
default:
|
||||
dbg.printLine(terminal.StyleFeedback, "set to %s", dbg.Quantum)
|
||||
dbg.printLine(terminal.StyleFeedback, "set to %s", strings.ToUpper(dbg.Quantum().String()))
|
||||
}
|
||||
|
||||
case cmdScript:
|
||||
|
@ -924,11 +939,11 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
return nil
|
||||
|
||||
case cmdLast:
|
||||
// if debugger is running in clock quantum then the live disasm
|
||||
// if debugger is running in a non-instruction quantum then the live disasm
|
||||
// information will not have been updated. for the purposes of the last
|
||||
// instruction however, we definitely do want that information to be
|
||||
// current
|
||||
if dbg.running && dbg.quantum.Load() == govern.QuantumClock {
|
||||
if dbg.running && dbg.quantum.Load() != govern.QuantumInstruction {
|
||||
dbg.liveBankInfo = dbg.vcs.Mem.Cart.GetBank(dbg.vcs.CPU.PC.Address())
|
||||
dbg.liveDisasmEntry = dbg.Disasm.ExecutedEntry(dbg.liveBankInfo, dbg.vcs.CPU.LastResult, true, dbg.vcs.CPU.PC.Value())
|
||||
}
|
||||
|
@ -978,9 +993,9 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
|
||||
// change terminal output style depending on condition of last CPU result
|
||||
if dbg.liveDisasmEntry.Result.Final {
|
||||
dbg.printLine(terminal.StyleCPUStep, s.String())
|
||||
dbg.printLine(terminal.StyleInstructionStep, s.String())
|
||||
} else {
|
||||
dbg.printLine(terminal.StyleVideoStep, s.String())
|
||||
dbg.printLine(terminal.StyleSubStep, s.String())
|
||||
}
|
||||
|
||||
case cmdMemMap:
|
||||
|
|
|
@ -37,16 +37,24 @@ the STEP to end on the programme after the corresponding RTS. Note that if there
|
|||
will run forever and you will need to stop the execution with the HALT command (or through the debugging GUI
|
||||
or with a CTRL-C on some terminals)`,
|
||||
|
||||
cmdQuantum: `Change or view stepping quantum. The stepping quantum defines the frequency
|
||||
at which the emulation is checked and reported upon by the debugger.
|
||||
cmdQuantum: `Change or view the stepping quantum. The stepping quantum defines the
|
||||
frequency at which the emulation is checked and reported upon by the emulation when
|
||||
in debugging mode.
|
||||
|
||||
There are two quantum modes. The INSTRUCTION quantum mode causes the debugger
|
||||
to step one CPU instruction at a time, regardless of how many cycles the
|
||||
instruction takes.
|
||||
|
||||
The VIDEO quantum mode meanwhile, causes the debugger to step one color clock
|
||||
(or one video cycle) at a time. Compared to the INSTRUCTION quantum mode, the
|
||||
VIDEO quantum will cause the emulation to run slower.`,
|
||||
The CYCLE quatum steps one CPU cycle. This can be useful to understand how the
|
||||
memory buses react at each step of an instruction.
|
||||
|
||||
The CLOCK quantum causes the debugger to step one color clock at a time. There
|
||||
are three color clocks per CYCLE. This quantum is useful to understand how
|
||||
and precisely when the registers in the TIA change.
|
||||
|
||||
The three quantums have been listed above in order of descending efficiency.
|
||||
In other words INSTRUCTION produces the fastest emulation and CLOCK produces
|
||||
the slowest emulation.`,
|
||||
|
||||
cmdScript: `Run commands from specified file or record commands to a file. The RECORD
|
||||
argument indicates that a new script is to be recorded. Recording will not
|
||||
|
|
|
@ -89,7 +89,7 @@ var commandTemplate = []string{
|
|||
cmdRun,
|
||||
cmdStep + " (BACK|OVER) (INSTRUCTION|CLOCK|SCANLINE|FRAME)",
|
||||
cmdHalt,
|
||||
cmdQuantum + " (INSTRUCTION|CLOCK)",
|
||||
cmdQuantum + " (INSTRUCTION|CYCLE|CLOCK)",
|
||||
cmdScript + " [RECORD %<new file>F|END|%<file>F]",
|
||||
cmdRewind + " [%<frame>N|LAST|SUMMARY]",
|
||||
cmdComparison + " [%<frame>N|LOCK|UNLOCK]",
|
||||
|
|
|
@ -193,17 +193,6 @@ type Debugger struct {
|
|||
// Quantum to use when stepping/running
|
||||
quantum atomic.Value // govern.Quantum
|
||||
|
||||
// catchupQuantum differs from the quantum field in that it only applies in
|
||||
// the catchupLoop (part of the rewind system). it is set just before the
|
||||
// rewind process is started.
|
||||
//
|
||||
// the value it is set to depends on the context. For the STEP BACK command
|
||||
// it is set to the current stepQuantum
|
||||
//
|
||||
// for PushGoto() the quantum is set to QuantumVideo, while for
|
||||
// PushRewind() it is set to the current stepQuantum
|
||||
catchupQuantum govern.Quantum
|
||||
|
||||
// record user input to a script file
|
||||
scriptScribe script.Scribe
|
||||
|
||||
|
@ -283,6 +272,9 @@ type Debugger struct {
|
|||
catchupContinue func() bool
|
||||
catchupEnd func()
|
||||
|
||||
// the context in which the catchup loop is running
|
||||
catchupContext catchupContext
|
||||
|
||||
// the debugger catchup loop will end on a video cycle if necessary. this
|
||||
// is what we want in most situations but occasionally it is useful to stop
|
||||
// on an instruction boundary. catchupEndAdj will ensure that the debugger
|
||||
|
|
|
@ -21,16 +21,19 @@ type Quantum int
|
|||
// List of valid QuantumModes.
|
||||
const (
|
||||
QuantumInstruction Quantum = iota
|
||||
QuantumCycle
|
||||
QuantumClock
|
||||
)
|
||||
|
||||
func (mode Quantum) String() string {
|
||||
switch mode {
|
||||
func (q Quantum) String() string {
|
||||
switch q {
|
||||
case QuantumInstruction:
|
||||
return "Instruction"
|
||||
case QuantumCycle:
|
||||
return "Cycle"
|
||||
case QuantumClock:
|
||||
return "Clock"
|
||||
default:
|
||||
return "unrecognised quantum mode"
|
||||
return "unrecognised quantum"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,31 @@ package govern
|
|||
// State indicates the emulation's state.
|
||||
type State int
|
||||
|
||||
// List of possible emulation states.
|
||||
//
|
||||
// EmulatorStart is the default state and should never be entered once the
|
||||
// emulator has begun.
|
||||
//
|
||||
// Initialising can be used when reinitialising the emulator. for example, when
|
||||
// a new cartridge is being inserted.
|
||||
//
|
||||
// Values are ordered so that order comparisons are meaningful. For example,
|
||||
// Running is "greater than" Stepping, Paused, etc.
|
||||
//
|
||||
// Note that there is a sub-state of the rewinding state that we can potentially
|
||||
// think of as the "catch-up" state. This occurs in the brief transition period
|
||||
// between Rewinding and the Running or Pausing state. For simplicity, the
|
||||
// catch-up loop is part of the Rewinding state
|
||||
const (
|
||||
EmulatorStart State = iota
|
||||
Initialising
|
||||
Paused
|
||||
Stepping
|
||||
Rewinding
|
||||
Running
|
||||
Ending
|
||||
)
|
||||
|
||||
func (s State) String() string {
|
||||
switch s {
|
||||
case EmulatorStart:
|
||||
|
@ -38,31 +63,3 @@ func (s State) String() string {
|
|||
|
||||
return ""
|
||||
}
|
||||
|
||||
// List of possible emulation states.
|
||||
//
|
||||
// EmulatorStart is the default state and should never be entered once the
|
||||
// emulator has begun.
|
||||
//
|
||||
// Initialising can be used when reinitialising the emulator. for example, when
|
||||
// a new cartridge is being inserted.
|
||||
//
|
||||
// Values are ordered so that order comparisons are meaningful. For example,
|
||||
// Running is "greater than" Stepping, Paused, etc.
|
||||
//
|
||||
// * There is a sub-state of the rewinding state that we can think of as the
|
||||
// "catch-up" state. This occurs in the brief transition period between
|
||||
// Rewinding and the Running or Pausing state.
|
||||
//
|
||||
// Currently, we handle this state in the CartUpLoop() function of the debugger
|
||||
// package. There is a good argument to be made for having the catch-up state
|
||||
// as a distinct State listed below.
|
||||
const (
|
||||
EmulatorStart State = iota
|
||||
Initialising
|
||||
Paused
|
||||
Stepping
|
||||
Rewinding
|
||||
Running
|
||||
Ending
|
||||
)
|
||||
|
|
|
@ -20,11 +20,28 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
)
|
||||
|
||||
// catchupContext is used to inform the loop of the context in which a
|
||||
// post-rewind catchup is running in
|
||||
type catchupContext int
|
||||
|
||||
// list of valid catchupContext values
|
||||
const (
|
||||
catchupGotoCoords catchupContext = iota
|
||||
catchupRewindToFrame
|
||||
catrupRerunLastNFrames
|
||||
catchupStepBack
|
||||
)
|
||||
|
||||
// CatchUpLoop implements the rewind.Runner interface.
|
||||
//
|
||||
// It is called from the rewind package and sets the functions that are
|
||||
// required for catchupLoop().
|
||||
func (dbg *Debugger) CatchUpLoop(tgt coords.TelevisionCoords) error {
|
||||
// emulation state assertion
|
||||
if dbg.State() != govern.Rewinding {
|
||||
panic("catchup loop must only be run in the rewinding state")
|
||||
}
|
||||
|
||||
switch dbg.Mode() {
|
||||
case govern.ModePlay:
|
||||
fpscap := dbg.vcs.TV.SetFPSCap(false)
|
||||
|
@ -44,8 +61,6 @@ func (dbg *Debugger) CatchUpLoop(tgt coords.TelevisionCoords) error {
|
|||
// turn off TV's fps frame limiter
|
||||
fpsCap := dbg.vcs.TV.SetFPSCap(false)
|
||||
|
||||
// we've already set emulation state to govern.Rewinding
|
||||
|
||||
dbg.catchupContinue = func() bool {
|
||||
newCoords := dbg.vcs.TV.GetCoords()
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ func (dbg *Debugger) unwindLoop(onRestart func() error) {
|
|||
func (dbg *Debugger) catchupLoop(inputter terminal.Input) error {
|
||||
var ended bool
|
||||
|
||||
callback := func() error {
|
||||
callback := func(_ bool) error {
|
||||
if dbg.unwindLoopRestart != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -77,7 +77,9 @@ func (dbg *Debugger) catchupLoop(inputter terminal.Input) error {
|
|||
ended = true
|
||||
dbg.catchupEnd()
|
||||
|
||||
if dbg.catchupQuantum == govern.QuantumInstruction {
|
||||
// small optimisation if the catchup context is rewind to frame. we
|
||||
// never want to call the input loop in this case
|
||||
if dbg.catchupContext == catchupRewindToFrame {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -411,7 +413,7 @@ func (dbg *Debugger) inputLoop(inputter terminal.Input, nonInstructionQuantum bo
|
|||
}
|
||||
|
||||
func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
||||
callback := func() error {
|
||||
callback := func(isCycle bool) error {
|
||||
var err error
|
||||
|
||||
// check for unwind loop
|
||||
|
@ -426,14 +428,18 @@ func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
|||
}
|
||||
dbg.counter.Step(1, dbg.liveBankInfo)
|
||||
|
||||
// process commandOnStep for clock quantum (equivalent for instruction
|
||||
// quantum is the main body of Debugger.step() below)
|
||||
if dbg.Quantum() == govern.QuantumClock && dbg.commandOnStep != nil {
|
||||
// we don't do this if we're in catchup mode
|
||||
if !catchup {
|
||||
err := dbg.processTokensList(dbg.commandOnStep)
|
||||
if err != nil {
|
||||
dbg.printLine(terminal.StyleError, "%s", err)
|
||||
q := dbg.Quantum()
|
||||
|
||||
// process commandOnStep for non-instruction quantums (equivalent for
|
||||
// instruction quantum is the main body of Debugger.step() below)
|
||||
if dbg.commandOnStep != nil {
|
||||
// we don't do this if we're in catchup mode or the "final" result from the CPU
|
||||
if !catchup && !dbg.vcs.CPU.LastResult.Final {
|
||||
if q == govern.QuantumClock || (q == govern.QuantumCycle && isCycle) {
|
||||
err := dbg.processTokensList(dbg.commandOnStep)
|
||||
if err != nil {
|
||||
dbg.printLine(terminal.StyleError, "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -442,7 +448,7 @@ func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
|||
// returns below
|
||||
dbg.continueEmulation = dbg.halting.check()
|
||||
|
||||
if dbg.Quantum() == govern.QuantumClock || !dbg.continueEmulation {
|
||||
if q == govern.QuantumClock || (q == govern.QuantumCycle && isCycle) || !dbg.continueEmulation {
|
||||
// start another inputLoop() with the clockCycle boolean set to true
|
||||
return dbg.inputLoop(inputter, true)
|
||||
}
|
||||
|
@ -502,11 +508,14 @@ func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
|||
|
||||
// process commandOnStep for instruction quantum (equivalent for clock
|
||||
// quantum is the vcs.Step() callback above)
|
||||
if dbg.Quantum() == govern.QuantumInstruction && dbg.vcs.CPU.RdyFlg {
|
||||
if dbg.commandOnStep != nil {
|
||||
err := dbg.processTokensList(dbg.commandOnStep)
|
||||
if err != nil {
|
||||
dbg.printLine(terminal.StyleError, "%s", err)
|
||||
if dbg.vcs.CPU.RdyFlg {
|
||||
q := dbg.Quantum()
|
||||
if q == govern.QuantumInstruction || (q != govern.QuantumInstruction && dbg.vcs.CPU.LastResult.Final) {
|
||||
if dbg.commandOnStep != nil {
|
||||
err := dbg.processTokensList(dbg.commandOnStep)
|
||||
if err != nil {
|
||||
dbg.printLine(terminal.StyleError, "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ func (dbg *Debugger) buildPrompt() terminal.Prompt {
|
|||
// decoded it already
|
||||
s.WriteString(fmt.Sprintf("%s %s", e.Address, e.Operator))
|
||||
|
||||
if e.Operand.String() != "" {
|
||||
s.WriteString(fmt.Sprintf(" %s", e.Operand))
|
||||
if e.Operand.Resolve() != "" {
|
||||
s.WriteString(fmt.Sprintf(" %s", e.Operand.Resolve()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,8 +84,8 @@ func (dbg *Debugger) RewindToFrame(fn int, last bool) bool {
|
|||
|
||||
// the function to push to the debugger/emulation routine
|
||||
doRewind := func() error {
|
||||
// upate catchupQuantum before starting rewind process
|
||||
dbg.catchupQuantum = dbg.Quantum()
|
||||
// upate catchup context before starting rewind process
|
||||
dbg.catchupContext = catchupRewindToFrame
|
||||
|
||||
if last {
|
||||
err := dbg.Rewind.GotoLast()
|
||||
|
@ -127,8 +127,8 @@ func (dbg *Debugger) GotoCoords(coords coords.TelevisionCoords) bool {
|
|||
|
||||
// the function to push to the debugger/emulation routine
|
||||
doRewind := func() error {
|
||||
// upate catchupQuantum before starting rewind process
|
||||
dbg.catchupQuantum = govern.QuantumClock
|
||||
// upate catchup context before starting rewind process
|
||||
dbg.catchupContext = catchupGotoCoords
|
||||
|
||||
err := dbg.Rewind.GotoCoords(coords)
|
||||
if err != nil {
|
||||
|
@ -190,8 +190,8 @@ func (dbg *Debugger) RerunLastNFrames(frames int) bool {
|
|||
// how we push the doRewind() function depends on what kind of inputloop we
|
||||
// are currently in
|
||||
dbg.PushFunctionImmediate(func() {
|
||||
// upate catchupQuantum before starting rewind process
|
||||
dbg.catchupQuantum = govern.QuantumClock
|
||||
// upate catchup context before starting rewind process
|
||||
dbg.catchupContext = catrupRerunLastNFrames
|
||||
|
||||
// set state to govern.Rewinding as soon as possible (but
|
||||
// remembering that we must do it in the debugger goroutine)
|
||||
|
|
|
@ -46,10 +46,10 @@ func (ct *ColorTerminal) TermPrintLine(style terminal.Style, s string) {
|
|||
case terminal.StyleFeedbackSecondary:
|
||||
ct.EasyTerm.TermPrint(ansi.DimPens["gray"])
|
||||
|
||||
case terminal.StyleCPUStep:
|
||||
case terminal.StyleInstructionStep:
|
||||
ct.EasyTerm.TermPrint(ansi.Pens["yellow"])
|
||||
|
||||
case terminal.StyleVideoStep:
|
||||
case terminal.StyleSubStep:
|
||||
ct.EasyTerm.TermPrint(ansi.DimPens["yellow"])
|
||||
|
||||
case terminal.StyleInstrument:
|
||||
|
|
|
@ -36,11 +36,11 @@ const (
|
|||
// secondary information from a command
|
||||
StyleFeedbackSecondary
|
||||
|
||||
// disassembly output at CPU cycle boundaries
|
||||
StyleCPUStep
|
||||
// disassembly output for CPU instruction boundaries
|
||||
StyleInstructionStep
|
||||
|
||||
// disassembly output at video cycle boundaries
|
||||
StyleVideoStep
|
||||
// disassembly output for non-CPU instruction boundaries
|
||||
StyleSubStep
|
||||
|
||||
// information about the machine
|
||||
StyleInstrument
|
||||
|
|
|
@ -64,7 +64,7 @@ type DisasmEntries struct {
|
|||
// Also returns a reference to the disassembly's symbol table. This reference
|
||||
// will never change over the course of the lifetime of the Disassembly type
|
||||
// itself. ie. the returned reference is safe to use after calls to
|
||||
// FromMemory() or FromCartrige().
|
||||
// FromMemory() or FromCartridge().
|
||||
func NewDisassembly(vcs *hardware.VCS) (*Disassembly, *symbols.Symbols, error) {
|
||||
dsm := &Disassembly{vcs: vcs}
|
||||
|
||||
|
@ -251,9 +251,8 @@ func (dsm *Disassembly) ExecutedEntry(bank mapper.BankInfo, result execution.Res
|
|||
o := dsm.disasmEntries.Entries[bank.Number][idx]
|
||||
if o != nil && o.Result.Final {
|
||||
e.updateExecutionEntry(result)
|
||||
} else {
|
||||
dsm.disasmEntries.Entries[bank.Number][idx] = e
|
||||
}
|
||||
dsm.disasmEntries.Entries[bank.Number][idx] = e
|
||||
|
||||
// bless next entry in case it was missed by the original decoding. there's
|
||||
// no guarantee that the bank for the next address will be the same as the
|
||||
|
@ -286,14 +285,14 @@ func (dsm *Disassembly) FormatResult(bank mapper.BankInfo, result execution.Resu
|
|||
Level: level,
|
||||
Bank: bank.Number,
|
||||
Label: Label{
|
||||
dsm: dsm,
|
||||
address: result.Address,
|
||||
bank: bank.Number,
|
||||
dsm: dsm,
|
||||
result: result,
|
||||
bank: bank,
|
||||
},
|
||||
Operand: Operand{
|
||||
dsm: dsm,
|
||||
result: result,
|
||||
bank: bank.Number,
|
||||
bank: bank,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -325,14 +324,14 @@ func (dsm *Disassembly) FormatResult(bank mapper.BankInfo, result execution.Resu
|
|||
switch result.ByteCount {
|
||||
case 3:
|
||||
operand := result.InstructionData
|
||||
e.Operand.nonSymbolic = fmt.Sprintf("$%04x", operand)
|
||||
e.Operand.partial = fmt.Sprintf("$%04x", operand)
|
||||
e.Bytecode = fmt.Sprintf("%02x %02x %02x", result.Defn.OpCode, operand&0x00ff, operand&0xff00>>8)
|
||||
case 2:
|
||||
operand := result.InstructionData
|
||||
e.Operand.nonSymbolic = fmt.Sprintf("$??%02x", result.InstructionData)
|
||||
e.Operand.partial = fmt.Sprintf("$??%02x", result.InstructionData)
|
||||
e.Bytecode = fmt.Sprintf("%02x %02x ?? ", result.Defn.OpCode, operand&0x00ff)
|
||||
case 1:
|
||||
e.Operand.nonSymbolic = "$????"
|
||||
e.Operand.partial = "$????"
|
||||
e.Bytecode = fmt.Sprintf("%02x ?? ??", result.Defn.OpCode)
|
||||
case 0:
|
||||
panic("this makes no sense. we must have read at least one byte to know how many bytes to expect")
|
||||
|
@ -343,10 +342,10 @@ func (dsm *Disassembly) FormatResult(bank mapper.BankInfo, result execution.Resu
|
|||
switch result.ByteCount {
|
||||
case 2:
|
||||
operand := result.InstructionData
|
||||
e.Operand.nonSymbolic = fmt.Sprintf("$%02x", operand)
|
||||
e.Operand.partial = fmt.Sprintf("$%02x", operand)
|
||||
e.Bytecode = fmt.Sprintf("%02x %02x", result.Defn.OpCode, operand&0x00ff)
|
||||
case 1:
|
||||
e.Operand.nonSymbolic = "$??"
|
||||
e.Operand.partial = "$??"
|
||||
e.Bytecode = fmt.Sprintf("%02x ??", result.Defn.OpCode)
|
||||
case 0:
|
||||
panic("this makes no sense. we must have read at least one byte to know how many bytes to expect")
|
||||
|
@ -372,11 +371,7 @@ func (dsm *Disassembly) FormatResult(bank mapper.BankInfo, result execution.Resu
|
|||
// decorate operand with addressing mode indicators. this decorates the
|
||||
// non-symbolic operand. we also call the decorate function from the
|
||||
// Operand() function when a symbol has been found
|
||||
if e.Result.Defn.IsBranch() {
|
||||
e.Operand.nonSymbolic = fmt.Sprintf("$%04x", absoluteBranchDestination(e.Result.Address, e.Result.InstructionData))
|
||||
} else {
|
||||
e.Operand.nonSymbolic = addrModeDecoration(e.Operand.nonSymbolic, e.Result.Defn.AddressingMode)
|
||||
}
|
||||
e.Operand.partial = addrModeDecoration(e.Operand.partial, e.Result.Defn.AddressingMode)
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware/cpu/execution"
|
||||
"github.com/jetsetilly/gopher2600/hardware/cpu/instructions"
|
||||
"github.com/jetsetilly/gopher2600/hardware/cpu/registers"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/memorymap"
|
||||
)
|
||||
|
||||
|
@ -77,7 +78,6 @@ type Entry struct {
|
|||
// the entries below are not defined if Level == EntryLevelUnused
|
||||
|
||||
// string representations of information in execution.Result
|
||||
//
|
||||
// entry.GetField() will apply white spacing padding suitable for columnation
|
||||
Label Label
|
||||
Bytecode string
|
||||
|
@ -88,14 +88,15 @@ type Entry struct {
|
|||
|
||||
// some fields in the disassembly entry are updated on every execution.
|
||||
func (e *Entry) updateExecutionEntry(result execution.Result) {
|
||||
// update result instance
|
||||
e.Result = result
|
||||
|
||||
// update address in label. we probably don't need to do this but it might
|
||||
// be useful to know what the *actual* address of the instruction. ie.
|
||||
// update result instance in Label. we probably don't need to do this but it
|
||||
// might be useful to know what the *actual* address of the instruction. ie.
|
||||
// which mirror is used by the program at that point in the execution.
|
||||
e.Label.address = e.Result.Address
|
||||
e.Label.result = e.Result
|
||||
|
||||
// update result instance in Operand fields
|
||||
// update result instance in Operand
|
||||
e.Operand.result = e.Result
|
||||
|
||||
// indicate that entry has been executed
|
||||
|
@ -215,17 +216,17 @@ func absoluteBranchDestination(addr uint16, operand uint16) uint16 {
|
|||
// returns any address label for the entry. Use GetField() function for
|
||||
// a white-space padded label.
|
||||
type Label struct {
|
||||
dsm *Disassembly
|
||||
address uint16
|
||||
bank int
|
||||
dsm *Disassembly
|
||||
result execution.Result
|
||||
bank mapper.BankInfo
|
||||
}
|
||||
|
||||
// String returns the address label as a symbol (if a symbol is available)
|
||||
// Resolve returns the address label as a symbol (if a symbol is available)
|
||||
// if a symbol is not available then the the bool return value will be false.
|
||||
func (lb Label) String() string {
|
||||
if lb.dsm.Prefs.Symbols.Get().(bool) {
|
||||
ma, _ := memorymap.MapAddress(lb.address, true)
|
||||
if e, ok := lb.dsm.Sym.GetLabel(lb.bank, ma); ok {
|
||||
func (l Label) Resolve() string {
|
||||
if l.dsm.Prefs.Symbols.Get().(bool) {
|
||||
ma, _ := memorymap.MapAddress(l.result.Address, true)
|
||||
if e, ok := l.dsm.Sym.GetLabel(l.bank.Number, ma); ok {
|
||||
return e.Symbol
|
||||
}
|
||||
}
|
||||
|
@ -237,67 +238,71 @@ func (lb Label) String() string {
|
|||
// returns the operand (with symbols if appropriate). Use GetField function for
|
||||
// white-space padded operand string.
|
||||
type Operand struct {
|
||||
nonSymbolic string
|
||||
dsm *Disassembly
|
||||
result execution.Result
|
||||
bank int
|
||||
dsm *Disassembly
|
||||
result execution.Result
|
||||
bank mapper.BankInfo
|
||||
|
||||
// partial is the operand that will be used as the result from Resolve()
|
||||
// when the execution result is not complete (ie. when not enough bytes have
|
||||
// been read)
|
||||
partial string
|
||||
}
|
||||
|
||||
// String returns the operand as a symbol (if a symbol is available) if
|
||||
// a symbol is not available then the the bool return value will be false.
|
||||
func (op Operand) String() string {
|
||||
// Resolve returns the operand as a symbol (if a symbol is available) if a symbol
|
||||
// is not available then the returned value will be be numeric possibly with
|
||||
// placeholders for unknown bytes
|
||||
func (op Operand) Resolve() string {
|
||||
if op.result.Defn == nil {
|
||||
return op.nonSymbolic
|
||||
return op.partial
|
||||
}
|
||||
|
||||
if op.dsm == nil || !op.dsm.Prefs.Symbols.Get().(bool) {
|
||||
return op.nonSymbolic
|
||||
return op.partial
|
||||
}
|
||||
|
||||
s := op.nonSymbolic
|
||||
if op.result.ByteCount != op.result.Defn.Bytes {
|
||||
return op.partial
|
||||
}
|
||||
|
||||
res := op.partial
|
||||
|
||||
// use symbol for the operand if available/appropriate. we should only do
|
||||
// this if operand has been decoded
|
||||
if op.result.Defn.AddressingMode == instructions.Immediate {
|
||||
// TODO: immediate symbols
|
||||
|
||||
} else if op.result.ByteCount > 1 {
|
||||
// instruction data is only valid if bytecount is 2 or more
|
||||
|
||||
operand := op.result.InstructionData
|
||||
// this if at least part of an operand has been decoded
|
||||
if op.result.Defn.Bytes > 1 {
|
||||
data := op.result.InstructionData
|
||||
|
||||
switch op.result.Defn.Effect {
|
||||
case instructions.Flow:
|
||||
if op.result.Defn.IsBranch() {
|
||||
operand = absoluteBranchDestination(op.result.Address, operand)
|
||||
data = absoluteBranchDestination(op.result.Address, data)
|
||||
|
||||
// look up mock program counter value in symbol table
|
||||
if e, ok := op.dsm.Sym.GetLabel(op.bank, operand); ok {
|
||||
s = e.Symbol
|
||||
if e, ok := op.dsm.Sym.GetLabel(op.bank.Number, data); ok {
|
||||
res = e.Symbol
|
||||
}
|
||||
} else if e, ok := op.dsm.Sym.GetLabel(op.bank, operand); ok {
|
||||
s = addrModeDecoration(e.Symbol, op.result.Defn.AddressingMode)
|
||||
} else if e, ok := op.dsm.Sym.GetLabel(op.bank.Number, data); ok {
|
||||
res = addrModeDecoration(e.Symbol, op.result.Defn.AddressingMode)
|
||||
}
|
||||
|
||||
case instructions.Subroutine:
|
||||
if e, ok := op.dsm.Sym.GetLabel(op.bank, operand); ok {
|
||||
s = e.Symbol
|
||||
if e, ok := op.dsm.Sym.GetLabel(op.bank.Number, data); ok {
|
||||
res = e.Symbol
|
||||
}
|
||||
|
||||
case instructions.Read:
|
||||
if e, ok := op.dsm.Sym.GetSymbol(operand, true); ok {
|
||||
s = addrModeDecoration(e.Symbol, op.result.Defn.AddressingMode)
|
||||
if e, ok := op.dsm.Sym.GetSymbol(data, true); ok {
|
||||
res = addrModeDecoration(e.Symbol, op.result.Defn.AddressingMode)
|
||||
}
|
||||
|
||||
case instructions.Write:
|
||||
fallthrough
|
||||
|
||||
case instructions.RMW:
|
||||
if e, ok := op.dsm.Sym.GetSymbol(operand, false); ok {
|
||||
s = addrModeDecoration(e.Symbol, op.result.Defn.AddressingMode)
|
||||
if e, ok := op.dsm.Sym.GetSymbol(data, false); ok {
|
||||
res = addrModeDecoration(e.Symbol, op.result.Defn.AddressingMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ func (e *Entry) GetField(field Field) string {
|
|||
|
||||
switch field {
|
||||
case FldLabel:
|
||||
s = e.Label.String()
|
||||
s = e.Label.Resolve()
|
||||
if s == "" {
|
||||
w = 0
|
||||
} else {
|
||||
|
@ -95,7 +95,7 @@ func (e *Entry) GetField(field Field) string {
|
|||
s = e.Operator
|
||||
|
||||
case FldOperand:
|
||||
s = e.Operand.String()
|
||||
s = e.Operand.Resolve()
|
||||
w = e.dsm.Sym.SymbolWidth() + widthOperandDecoration
|
||||
|
||||
case FldCycles:
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
//
|
||||
// See StringColumnated() for a fancier option.
|
||||
func (e *Entry) String() string {
|
||||
operand := e.Operand.String()
|
||||
operand := e.Operand.Resolve()
|
||||
return fmt.Sprintf("%s %s %s", e.Address, e.Operator, operand)
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func (e *Entry) StringColumnated(attr ColumnAttr) string {
|
|||
}
|
||||
|
||||
if attr.Label {
|
||||
if e.Label.String() != "" {
|
||||
if e.Label.Resolve() != "" {
|
||||
b.Write([]byte(e.GetField(FldLabel)))
|
||||
b.Write([]byte("\n"))
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func (dsm *Disassembly) Grep(output io.Writer, scope GrepScope, search string, c
|
|||
case GrepOperator:
|
||||
s = e.Operator
|
||||
case GrepOperand:
|
||||
s = e.Operand.String()
|
||||
s = e.Operand.Resolve()
|
||||
case GrepAll:
|
||||
s = e.String()
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ func (dsm *Disassembly) setCartMirror() {
|
|||
// mask off bits that indicate the cartridge/segment origin and reset
|
||||
// them with the chosen origin
|
||||
a := e.Result.Address&memorymap.CartridgeBits | dsm.Prefs.mirrorOrigin
|
||||
e.Operand.nonSymbolic = fmt.Sprintf("$%04x", absoluteBranchDestination(a, e.Result.InstructionData))
|
||||
e.Operand.partial = fmt.Sprintf("$%04x", absoluteBranchDestination(a, e.Result.InstructionData))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@ var FontAwesome []byte
|
|||
const (
|
||||
Run = '\uf04b'
|
||||
Halt = '\uf04c'
|
||||
BackClock = '\uf104'
|
||||
BackInstruction = '\uf100'
|
||||
BackScanline = '\uf106'
|
||||
BackFrame = '\uf102'
|
||||
BackArrow = '\uf104'
|
||||
BackArrowDouble = '\uf100'
|
||||
UpArrow = '\uf106'
|
||||
UpArrowDouble = '\uf102'
|
||||
StepOver = '\uf2f9'
|
||||
Disk = '\uf0c7'
|
||||
Mouse = '\uf8cc'
|
||||
|
@ -58,7 +58,7 @@ const (
|
|||
CoProcBug = '\uf188'
|
||||
ExecutionNotes = '\uf02b'
|
||||
CPUBug = '\uf188'
|
||||
CyclingInstruction = '\uf206'
|
||||
Paw = '\uf1b0'
|
||||
NonCartExecution = '\uf54c'
|
||||
CoProcExecution = '\uf135'
|
||||
DisasmGotoCurrent = '\uf530'
|
||||
|
|
|
@ -609,9 +609,9 @@ func (scr *screen) reflectionColor(ref *reflection.ReflectedVideoStep) color.RGB
|
|||
return reflectionColors[reflection.WSYNC]
|
||||
}
|
||||
case reflection.OverlayLabels[reflection.OverlayCollision]:
|
||||
if ref.Collision.LastVideoCycle.IsCXCLR() {
|
||||
if ref.Collision.LastColorClock.IsCXCLR() {
|
||||
return reflectionColors[reflection.CXCLR]
|
||||
} else if !ref.Collision.LastVideoCycle.IsNothing() {
|
||||
} else if !ref.Collision.LastColorClock.IsNothing() {
|
||||
return reflectionColors[reflection.Collision]
|
||||
}
|
||||
case reflection.OverlayLabels[reflection.OverlayHMOVE]:
|
||||
|
|
|
@ -142,13 +142,7 @@ func (win *winControl) drawStep() {
|
|||
|
||||
// step button
|
||||
imgui.TableNextColumn()
|
||||
|
||||
icon := fonts.BackInstruction
|
||||
if win.img.dbg.Quantum() == govern.QuantumClock {
|
||||
icon = fonts.BackClock
|
||||
}
|
||||
|
||||
win.repeatButton(fmt.Sprintf("%c ##Step", icon), func() {
|
||||
win.repeatButton(fmt.Sprintf("%c ##Step", fonts.BackArrowDouble), func() {
|
||||
win.img.term.pushCommand("STEP BACK")
|
||||
})
|
||||
|
||||
|
@ -159,21 +153,20 @@ func (win *winControl) drawStep() {
|
|||
|
||||
imgui.TableNextColumn()
|
||||
|
||||
if imguiToggleButton("##quantumToggle", win.img.dbg.Quantum() == govern.QuantumClock, win.img.cols.TitleBgActive) {
|
||||
if win.img.dbg.Quantum() == govern.QuantumClock {
|
||||
if imgui.BeginComboV("##quantum", "", imgui.ComboFlagsNoPreview) {
|
||||
if imgui.Selectable(govern.QuantumInstruction.String()) {
|
||||
win.img.term.pushCommand("QUANTUM INSTRUCTION")
|
||||
} else {
|
||||
}
|
||||
if imgui.Selectable(govern.QuantumCycle.String()) {
|
||||
win.img.term.pushCommand("QUANTUM CYCLE")
|
||||
}
|
||||
if imgui.Selectable(govern.QuantumClock.String()) {
|
||||
win.img.term.pushCommand("QUANTUM CLOCK")
|
||||
}
|
||||
imgui.EndCombo()
|
||||
}
|
||||
|
||||
imgui.SameLine()
|
||||
imgui.AlignTextToFramePadding()
|
||||
if win.img.dbg.Quantum() == govern.QuantumClock {
|
||||
imgui.Text("Colour Clock")
|
||||
} else {
|
||||
imgui.Text("CPU Instruction")
|
||||
}
|
||||
imgui.SameLineV(0, 5)
|
||||
imgui.Text(win.img.dbg.Quantum().String())
|
||||
|
||||
imgui.EndTable()
|
||||
}
|
||||
|
@ -188,7 +181,7 @@ func (win *winControl) drawStep() {
|
|||
imgui.TableNextRow()
|
||||
imgui.TableNextColumn()
|
||||
|
||||
win.repeatButton(fmt.Sprintf("%c ##Frame", fonts.BackFrame), func() {
|
||||
win.repeatButton(fmt.Sprintf("%c ##Frame", fonts.UpArrowDouble), func() {
|
||||
win.img.term.pushCommand("STEP BACK FRAME")
|
||||
})
|
||||
imgui.SameLineV(0.0, 0.0)
|
||||
|
@ -198,7 +191,7 @@ func (win *winControl) drawStep() {
|
|||
|
||||
imgui.TableNextColumn()
|
||||
|
||||
win.repeatButton(fmt.Sprintf("%c ##Scanline", fonts.BackScanline), func() {
|
||||
win.repeatButton(fmt.Sprintf("%c ##Scanline", fonts.UpArrow), func() {
|
||||
win.img.term.pushCommand("STEP BACK SCANLINE")
|
||||
})
|
||||
imgui.SameLineV(0.0, 0.0)
|
||||
|
@ -208,7 +201,6 @@ func (win *winControl) drawStep() {
|
|||
|
||||
imgui.EndTable()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (win *winControl) drawFPS() {
|
||||
|
|
|
@ -18,6 +18,7 @@ package sdlimgui
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/debugger/govern"
|
||||
"github.com/jetsetilly/gopher2600/gui/fonts"
|
||||
"github.com/jetsetilly/gopher2600/hardware/cpu/registers"
|
||||
|
||||
|
@ -194,15 +195,32 @@ func (win *winCPU) draw() {
|
|||
|
||||
imgui.SameLine()
|
||||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmOperand)
|
||||
imgui.Text(res.Operand.String())
|
||||
imgui.Text(res.Operand.Resolve())
|
||||
|
||||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmCycles)
|
||||
imgui.Text(fmt.Sprintf("%s cycles", res.Cycles()))
|
||||
if res.Result.PageFault {
|
||||
imgui.SameLine()
|
||||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmNotes)
|
||||
imgui.Text("(page-fault)")
|
||||
imgui.PopStyleColor()
|
||||
|
||||
if win.img.dbg.Quantum() == govern.QuantumClock {
|
||||
if !win.img.cache.Dbg.LiveDisasmEntry.Result.Final {
|
||||
imgui.SameLine()
|
||||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmCycles)
|
||||
|
||||
switch win.img.cache.VCS.TIA.ClocksSinceCycle {
|
||||
case 1:
|
||||
imgui.Text(fmt.Sprintf("%c", fonts.Paw))
|
||||
case 2:
|
||||
imgui.Text(fmt.Sprintf("%c", fonts.Paw))
|
||||
imgui.SameLineV(0, 4)
|
||||
imgui.Text(fmt.Sprintf("%c", fonts.Paw))
|
||||
case 3:
|
||||
imgui.Text(fmt.Sprintf("%c", fonts.Paw))
|
||||
imgui.SameLineV(0, 4)
|
||||
imgui.Text(fmt.Sprintf("%c", fonts.Paw))
|
||||
imgui.SameLineV(0, 4)
|
||||
imgui.Text(fmt.Sprintf("%c", fonts.Paw))
|
||||
}
|
||||
imgui.PopStyleColor()
|
||||
}
|
||||
}
|
||||
|
||||
imgui.PopStyleColorV(5)
|
||||
|
|
|
@ -618,10 +618,10 @@ func (win *winDbgScr) drawReflectionTooltip() {
|
|||
imgui.Text(e.Operator)
|
||||
imgui.PopStyleColor()
|
||||
|
||||
if e.Operand.String() != "" {
|
||||
if e.Operand.Resolve() != "" {
|
||||
imgui.SameLine()
|
||||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmOperand)
|
||||
imgui.Text(e.Operand.String())
|
||||
imgui.Text(e.Operand.Resolve())
|
||||
imgui.PopStyleColor()
|
||||
}
|
||||
|
||||
|
@ -681,7 +681,7 @@ func (win *winDbgScr) drawReflectionTooltip() {
|
|||
|
||||
imguiSeparator()
|
||||
|
||||
s := ref.Collision.LastVideoCycle.String()
|
||||
s := ref.Collision.LastColorClock.String()
|
||||
if s != "" {
|
||||
imgui.Text(s)
|
||||
} else {
|
||||
|
@ -711,7 +711,7 @@ func (win *winDbgScr) drawReflectionTooltip() {
|
|||
imguiColorLabelSimple("Audio phase 1", win.img.cols.reflectionColors[reflection.AudioPhase1])
|
||||
}
|
||||
if ref.AudioChanged {
|
||||
reg := strings.Split(e.Operand.String(), ",")[0]
|
||||
reg := strings.Split(e.Operand.Resolve(), ",")[0]
|
||||
imguiColorLabelSimple(fmt.Sprintf("%s updated", reg), win.img.cols.reflectionColors[reflection.AudioChanged])
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -454,7 +454,7 @@ func (win *winDisasm) drawBank(currBank mapper.BankInfo, focusAddr uint16) {
|
|||
for {
|
||||
if iterateFilter(e) {
|
||||
ct++
|
||||
if e.Label.String() != "" {
|
||||
if e.Label.Resolve() != "" {
|
||||
ct++
|
||||
}
|
||||
|
||||
|
@ -497,7 +497,7 @@ func (win *winDisasm) drawBank(currBank mapper.BankInfo, focusAddr uint16) {
|
|||
|
||||
// skip entries counting label as appropriate
|
||||
skip--
|
||||
if e.Label.String() != "" {
|
||||
if e.Label.Resolve() != "" {
|
||||
skip--
|
||||
}
|
||||
|
||||
|
@ -572,9 +572,7 @@ func (win *winDisasm) drawBank(currBank mapper.BankInfo, focusAddr uint16) {
|
|||
}
|
||||
|
||||
func (win *winDisasm) drawLabel(e *disassembly.Entry, bank int) {
|
||||
// no label to draw
|
||||
label := e.Label.String()
|
||||
if len(label) == 0 {
|
||||
if len(e.Label.Resolve()) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -589,7 +587,7 @@ func (win *winDisasm) drawLabel(e *disassembly.Entry, bank int) {
|
|||
imgui.SelectableV("", false, imgui.SelectableFlagsNone, imgui.Vec2{0, 0})
|
||||
imgui.SameLine()
|
||||
|
||||
imgui.Text(e.Label.String())
|
||||
imgui.Text(e.Label.Resolve())
|
||||
}
|
||||
|
||||
func (win *winDisasm) drawCoProcTooltip() {
|
||||
|
@ -618,7 +616,7 @@ func (win *winDisasm) drawEntry(currBank mapper.BankInfo, e *disassembly.Entry,
|
|||
if onBank && (e.Result.Address&memorymap.CartridgeBits == focusAddr) {
|
||||
imgui.TableSetBgColor(imgui.TableBgTargetRowBg0, win.img.cols.DisasmStep)
|
||||
|
||||
// focussed entry has been drawn so unset focus flag
|
||||
// focused entry has been drawn so unset focus flag
|
||||
win.focusOnAddr = false
|
||||
}
|
||||
|
||||
|
@ -674,13 +672,13 @@ func (win *winDisasm) drawEntry(currBank mapper.BankInfo, e *disassembly.Entry,
|
|||
imgui.PopStyleColor()
|
||||
imgui.SameLine()
|
||||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmOperand)
|
||||
imgui.Text(e.Operand.String())
|
||||
imgui.Text(e.Operand.Resolve())
|
||||
imgui.PopStyleColor()
|
||||
|
||||
// treat an instruction that is "cycling" differently
|
||||
if !e.Result.Final {
|
||||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmCycles)
|
||||
imgui.Text(fmt.Sprintf("%c cycling instruction (%s)", fonts.CyclingInstruction, e.Cycles()))
|
||||
imgui.Text(fmt.Sprintf("%c cycling instruction (%s)", fonts.Paw, e.Cycles()))
|
||||
imgui.PopStyleColor()
|
||||
} else {
|
||||
imgui.Text("Cycles:")
|
||||
|
@ -736,7 +734,7 @@ func (win *winDisasm) drawEntry(currBank mapper.BankInfo, e *disassembly.Entry,
|
|||
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.DisasmOperand)
|
||||
defer imgui.PopStyleColor()
|
||||
}
|
||||
imgui.Text(e.Operand.String())
|
||||
imgui.Text(e.Operand.Resolve())
|
||||
|
||||
// cycles column
|
||||
imgui.TableNextColumn()
|
||||
|
@ -760,7 +758,7 @@ func (win *winDisasm) drawEntry(currBank mapper.BankInfo, e *disassembly.Entry,
|
|||
exeAddress := win.img.cache.VCS.CPU.LastResult.Address & memorymap.CartridgeBits
|
||||
entryAddress := e.Result.Address & memorymap.CartridgeBits
|
||||
if exeAddress == entryAddress && currBank.Number == bank {
|
||||
imgui.Text(string(fonts.CyclingInstruction))
|
||||
imgui.Text(string(fonts.Paw))
|
||||
}
|
||||
} else if e.Level == disassembly.EntryLevelExecuted {
|
||||
if win.usingColor {
|
||||
|
|
|
@ -334,10 +334,10 @@ func (l terminalOutput) draw(wrap bool) {
|
|||
case terminal.StyleFeedbackSecondary:
|
||||
imgui.PushStyleColor(imgui.StyleColorText, l.cols.TermStyleFeedbackSecondary)
|
||||
|
||||
case terminal.StyleCPUStep:
|
||||
case terminal.StyleInstructionStep:
|
||||
imgui.PushStyleColor(imgui.StyleColorText, l.cols.TermStyleCPUStep)
|
||||
|
||||
case terminal.StyleVideoStep:
|
||||
case terminal.StyleSubStep:
|
||||
imgui.PushStyleColor(imgui.StyleColorText, l.cols.TermStyleVideoStep)
|
||||
|
||||
case terminal.StyleInstrument:
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
)
|
||||
|
||||
// While the continueCheck() function only runs at the end of a CPU instruction
|
||||
// (unlike the corresponding function in VCS.Step() which runs every video
|
||||
// cycle), it can still be expensive to do a full continue check every time.
|
||||
// (unlike the corresponding function in VCS.Step() which runs every color clock),
|
||||
// it can still be expensive to do a full continue check every time.
|
||||
//
|
||||
// It depends on context whether it is used or not but the PerformanceBrake is
|
||||
// a standard value that can be used to filter out expensive code paths within
|
||||
|
@ -45,20 +45,20 @@ func (vcs *VCS) Run(continueCheck func() (govern.State, error)) error {
|
|||
continueCheck = func() (govern.State, error) { return govern.Running, nil }
|
||||
}
|
||||
|
||||
// see the equivalient videoCycle() in the VCS.Step() function for an
|
||||
// see the equivalient colorClock() in the VCS.Step() function for an
|
||||
// explanation for what's going on here:
|
||||
videoCycle := func() error {
|
||||
colorClock := func() error {
|
||||
if err := vcs.Input.Handle(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vcs.TIA.QuickStep()
|
||||
vcs.TIA.QuickStep()
|
||||
vcs.TIA.QuickStep(1)
|
||||
vcs.TIA.QuickStep(2)
|
||||
|
||||
if reg, ok := vcs.Mem.TIA.ChipHasChanged(); ok {
|
||||
vcs.TIA.Step(reg)
|
||||
vcs.TIA.Step(reg, 3)
|
||||
} else {
|
||||
vcs.TIA.QuickStep()
|
||||
vcs.TIA.QuickStep(3)
|
||||
}
|
||||
|
||||
if reg, ok := vcs.Mem.RIOT.ChipHasChanged(); ok {
|
||||
|
@ -79,7 +79,7 @@ func (vcs *VCS) Run(continueCheck func() (govern.State, error)) error {
|
|||
for state != govern.Ending && state != govern.Initialising {
|
||||
switch state {
|
||||
case govern.Running:
|
||||
err := vcs.CPU.ExecuteInstruction(videoCycle)
|
||||
err := vcs.CPU.ExecuteInstruction(colorClock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -15,21 +15,20 @@
|
|||
|
||||
package hardware
|
||||
|
||||
func nullVideoCycleCallback() error {
|
||||
func nullCallback(_ bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Step the emulator state one CPU instruction. With a bit of work the optional
|
||||
// videoCycleCallback argument can be used for video-cycle stepping.
|
||||
func (vcs *VCS) Step(videoCycleCallback func() error) error {
|
||||
if videoCycleCallback == nil {
|
||||
videoCycleCallback = nullVideoCycleCallback
|
||||
// Step the emulator state one CPU instruction
|
||||
func (vcs *VCS) Step(colorClockCallback func(isCycle bool) error) error {
|
||||
if colorClockCallback == nil {
|
||||
colorClockCallback = nullCallback
|
||||
}
|
||||
|
||||
// the videoCycle function defines the order of operation for the rest of
|
||||
// the cycle function defines the order of operation for the rest of
|
||||
// the VCS for every CPU cycle. the function block represents the ϕ0 cycle
|
||||
//
|
||||
// the cpu calls the videoCycle function after every CPU cycle. this is a
|
||||
// the cpu calls the cycle function after every CPU cycle. this is a
|
||||
// bit backwards compared to the operation of a real VCS but I believe the
|
||||
// effect is the same:
|
||||
//
|
||||
|
@ -41,11 +40,11 @@ func (vcs *VCS) Step(videoCycleCallback func() error) error {
|
|||
//
|
||||
// in this emulation meanwhile, the CPU-TIA is reversed. each call to
|
||||
// Step() drives the CPU. After each CPU cycle the CPU emulation yields to
|
||||
// the videoCycle() function defined below.
|
||||
// the cycle() function defined below.
|
||||
//
|
||||
// the reason for this inside-out arrangement is simply a consequence of
|
||||
// the how the CPU emulation is put together. it is easier for the large
|
||||
// CPU ExecuteInstruction() function to call out to the videoCycle()
|
||||
// CPU ExecuteInstruction() function to call out to the cycle()
|
||||
// function. if we were to do it the other way around then keeping track of
|
||||
// the interim CPU state becomes trickier.
|
||||
//
|
||||
|
@ -56,41 +55,44 @@ func (vcs *VCS) Step(videoCycleCallback func() error) error {
|
|||
//
|
||||
// I don't believe any visual or audible artefacts of the VCS (undocumented
|
||||
// or not) rely on the details of the CPU-TIA relationship.
|
||||
videoCycle := func() error {
|
||||
//
|
||||
// at the end of the cycle() function the cycleCallback() function is called
|
||||
cycle := func() error {
|
||||
if err := vcs.Input.Handle(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vcs.TIA.QuickStep()
|
||||
if err := videoCycleCallback(); err != nil {
|
||||
vcs.TIA.QuickStep(1)
|
||||
if err := colorClockCallback(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vcs.TIA.QuickStep()
|
||||
if err := videoCycleCallback(); err != nil {
|
||||
vcs.TIA.QuickStep(2)
|
||||
if err := colorClockCallback(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reg, ok := vcs.Mem.TIA.ChipHasChanged(); ok {
|
||||
vcs.TIA.Step(reg)
|
||||
vcs.TIA.Step(reg, 3)
|
||||
} else {
|
||||
vcs.TIA.QuickStep()
|
||||
vcs.TIA.QuickStep(3)
|
||||
}
|
||||
if reg, ok := vcs.Mem.RIOT.ChipHasChanged(); ok {
|
||||
vcs.RIOT.Step(reg)
|
||||
} else {
|
||||
vcs.RIOT.QuickStep()
|
||||
}
|
||||
if err := videoCycleCallback(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vcs.Mem.Cart.Step(vcs.Clock)
|
||||
|
||||
if err := colorClockCallback(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := vcs.CPU.ExecuteInstruction(videoCycle)
|
||||
err := vcs.CPU.ExecuteInstruction(cycle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func (tv *Television) AdjCoords(adj Adj, amount int) coords.TelevisionCoords {
|
|||
coords := tv.GetCoords()
|
||||
|
||||
switch adj {
|
||||
case AdjCPUCycle:
|
||||
case AdjCycle:
|
||||
// adjusting by CPU cycle is the same as adjusting by video cycle
|
||||
// accept to say that a CPU cycle is the equivalent of 3 video cycles
|
||||
amount *= 3
|
||||
|
@ -80,6 +80,6 @@ type Adj int
|
|||
const (
|
||||
AdjFrame Adj = iota
|
||||
AdjScanline
|
||||
AdjCPUCycle
|
||||
AdjCycle
|
||||
AdjClock
|
||||
)
|
||||
|
|
|
@ -63,11 +63,8 @@ type TIA struct {
|
|||
// the VBLANK register also affects the RIOT sub-system
|
||||
riot RIOTports
|
||||
|
||||
// number of video cycles since the last WSYNC. also cycles back to 0 on
|
||||
// RSYNC and when polycounter reaches count 56
|
||||
//
|
||||
// cpu cycles can be attained by dividing videoCycles by 3
|
||||
videoCycles int
|
||||
// number of color clocks since the last CPU cycle boundary
|
||||
ClocksSinceCycle int
|
||||
|
||||
// the last signal sent to the television. many signal attributes are
|
||||
// sustained over many cycles; we use this to store that information
|
||||
|
@ -131,9 +128,8 @@ func (tia *TIA) Label() string {
|
|||
|
||||
func (tia *TIA) String() string {
|
||||
s := strings.Builder{}
|
||||
s.WriteString(fmt.Sprintf("%s %s %03d %04.01f",
|
||||
s.WriteString(fmt.Sprintf("%s %s",
|
||||
tia.hsync, tia.PClk,
|
||||
tia.videoCycles, float64(tia.videoCycles)/3.0,
|
||||
))
|
||||
return s.String()
|
||||
}
|
||||
|
@ -312,9 +308,6 @@ func (tia *TIA) newScanline() {
|
|||
// on
|
||||
tia.Hblank = true
|
||||
|
||||
// reset debugging information
|
||||
tia.videoCycles = 0
|
||||
|
||||
// rather than include the reset signal in the delay, we will
|
||||
// manually reset hsync counter when it reaches a count of 57
|
||||
}
|
||||
|
@ -382,20 +375,19 @@ func (tia *TIA) resolveDelayedEvents() {
|
|||
// TIA memory has changed then the changes will propogate at the correct time.
|
||||
// If the state of TIA memory has not changed then execution will be diverted
|
||||
// to QuickStep().
|
||||
func (tia *TIA) Step(reg chipbus.ChangedRegister) {
|
||||
func (tia *TIA) Step(reg chipbus.ChangedRegister, ct int) {
|
||||
tia.ClocksSinceCycle = ct
|
||||
|
||||
// make alterations to video state and playfield
|
||||
update := tia.Update(reg)
|
||||
|
||||
// if update has happened we can jump to QuickStep() for the remainder of
|
||||
// the Step() process
|
||||
if !update {
|
||||
tia.QuickStep()
|
||||
tia.QuickStep(ct)
|
||||
return
|
||||
}
|
||||
|
||||
// update debugging information
|
||||
tia.videoCycles++
|
||||
|
||||
// tick phase clock
|
||||
tia.PClk++
|
||||
if tia.PClk >= phaseclock.NumStates {
|
||||
|
@ -636,9 +628,8 @@ func (tia *TIA) Step(reg chipbus.ChangedRegister) {
|
|||
|
||||
// QuickStep ticks the TIA forward one colour clock without checking to see if
|
||||
// the state of TIA memory has changed.
|
||||
func (tia *TIA) QuickStep() {
|
||||
// update debugging information
|
||||
tia.videoCycles++
|
||||
func (tia *TIA) QuickStep(ct int) {
|
||||
tia.ClocksSinceCycle = ct
|
||||
|
||||
// tick phase clock
|
||||
tia.PClk++
|
||||
|
|
|
@ -25,9 +25,9 @@ import (
|
|||
type Collisions struct {
|
||||
mem chipbus.Memory
|
||||
|
||||
// LastVideoCycle records the combination of collision bits for the most recent
|
||||
// LastColorClock records the combination of collision bits for the most recent
|
||||
// video cycle. Facilitates production of string information.
|
||||
LastVideoCycle CollisionEvent
|
||||
LastColorClock CollisionEvent
|
||||
}
|
||||
|
||||
// CollisionEvent is an emulator specific value that records the collision
|
||||
|
@ -172,41 +172,41 @@ func (col *Collisions) Clear() {
|
|||
col.mem.ChipWrite(chipbus.CXM1FB, 0x00)
|
||||
col.mem.ChipWrite(chipbus.CXBLPF, 0x00)
|
||||
col.mem.ChipWrite(chipbus.CXPPMM, 0x00)
|
||||
col.LastVideoCycle = cxclr
|
||||
col.LastColorClock = cxclr
|
||||
}
|
||||
|
||||
// optimised tick of collision registers. memory is only written to when necessary.
|
||||
//
|
||||
// if this function is not called during a video cycle (which is possible for
|
||||
// reasons of optimisation) then the LastVideoCycle value must be reset
|
||||
// reasons of optimisation) then the LastCoorClock value must be reset
|
||||
// instead.
|
||||
func (col *Collisions) tick(p0, p1, m0, m1, bl, pf bool) {
|
||||
col.LastVideoCycle.reset()
|
||||
col.LastColorClock.reset()
|
||||
|
||||
if m0 {
|
||||
if p1 {
|
||||
v := col.mem.ChipRefer(chipbus.CXM0P)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= m0p1
|
||||
col.LastColorClock |= m0p1
|
||||
col.mem.ChipWrite(chipbus.CXM0P, v)
|
||||
}
|
||||
if p0 {
|
||||
v := col.mem.ChipRefer(chipbus.CXM0P)
|
||||
v |= 0x40
|
||||
col.LastVideoCycle |= m0p0
|
||||
col.LastColorClock |= m0p0
|
||||
col.mem.ChipWrite(chipbus.CXM0P, v)
|
||||
}
|
||||
|
||||
if pf {
|
||||
v := col.mem.ChipRefer(chipbus.CXM0FB)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= m0pf
|
||||
col.LastColorClock |= m0pf
|
||||
col.mem.ChipWrite(chipbus.CXM0FB, v)
|
||||
}
|
||||
if bl {
|
||||
v := col.mem.ChipRefer(chipbus.CXM0FB)
|
||||
v |= 0x40
|
||||
col.LastVideoCycle |= m0bl
|
||||
col.LastColorClock |= m0bl
|
||||
col.mem.ChipWrite(chipbus.CXM0FB, v)
|
||||
}
|
||||
}
|
||||
|
@ -215,26 +215,26 @@ func (col *Collisions) tick(p0, p1, m0, m1, bl, pf bool) {
|
|||
if p0 {
|
||||
v := col.mem.ChipRefer(chipbus.CXM1P)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= m1p0
|
||||
col.LastColorClock |= m1p0
|
||||
col.mem.ChipWrite(chipbus.CXM1P, v)
|
||||
}
|
||||
if p1 {
|
||||
v := col.mem.ChipRefer(chipbus.CXM1P)
|
||||
v |= 0x40
|
||||
col.LastVideoCycle |= m1p1
|
||||
col.LastColorClock |= m1p1
|
||||
col.mem.ChipWrite(chipbus.CXM1P, v)
|
||||
}
|
||||
|
||||
if pf {
|
||||
v := col.mem.ChipRefer(chipbus.CXM1FB)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= m1pf
|
||||
col.LastColorClock |= m1pf
|
||||
col.mem.ChipWrite(chipbus.CXM1FB, v)
|
||||
}
|
||||
if bl {
|
||||
v := col.mem.ChipRefer(chipbus.CXM1FB)
|
||||
v |= 0x40
|
||||
col.LastVideoCycle |= m1bl
|
||||
col.LastColorClock |= m1bl
|
||||
col.mem.ChipWrite(chipbus.CXM1FB, v)
|
||||
}
|
||||
}
|
||||
|
@ -243,13 +243,13 @@ func (col *Collisions) tick(p0, p1, m0, m1, bl, pf bool) {
|
|||
if pf {
|
||||
v := col.mem.ChipRefer(chipbus.CXP0FB)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= p0pf
|
||||
col.LastColorClock |= p0pf
|
||||
col.mem.ChipWrite(chipbus.CXP0FB, v)
|
||||
}
|
||||
if bl {
|
||||
v := col.mem.ChipRefer(chipbus.CXP0FB)
|
||||
v |= 0x40
|
||||
col.LastVideoCycle |= p0bl
|
||||
col.LastColorClock |= p0bl
|
||||
col.mem.ChipWrite(chipbus.CXP0FB, v)
|
||||
}
|
||||
}
|
||||
|
@ -258,13 +258,13 @@ func (col *Collisions) tick(p0, p1, m0, m1, bl, pf bool) {
|
|||
if pf {
|
||||
v := col.mem.ChipRefer(chipbus.CXP1FB)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= p1pf
|
||||
col.LastColorClock |= p1pf
|
||||
col.mem.ChipWrite(chipbus.CXP1FB, v)
|
||||
}
|
||||
if bl {
|
||||
v := col.mem.ChipRefer(chipbus.CXP1FB)
|
||||
v |= 0x40
|
||||
col.LastVideoCycle |= p1bl
|
||||
col.LastColorClock |= p1bl
|
||||
col.mem.ChipWrite(chipbus.CXP1FB, v)
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ func (col *Collisions) tick(p0, p1, m0, m1, bl, pf bool) {
|
|||
if bl && pf {
|
||||
v := col.mem.ChipRefer(chipbus.CXBLPF)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= blpf
|
||||
col.LastColorClock |= blpf
|
||||
col.mem.ChipWrite(chipbus.CXBLPF, v)
|
||||
}
|
||||
// no bit 6 for CXBLPF
|
||||
|
@ -280,14 +280,14 @@ func (col *Collisions) tick(p0, p1, m0, m1, bl, pf bool) {
|
|||
if p0 && p1 {
|
||||
v := col.mem.ChipRefer(chipbus.CXPPMM)
|
||||
v |= 0x80
|
||||
col.LastVideoCycle |= p0p1
|
||||
col.LastColorClock |= p0p1
|
||||
col.mem.ChipWrite(chipbus.CXPPMM, v)
|
||||
}
|
||||
|
||||
if m0 && m1 {
|
||||
v := col.mem.ChipRefer(chipbus.CXPPMM)
|
||||
v |= 0x40
|
||||
col.LastVideoCycle |= m0m1
|
||||
col.LastColorClock |= m0m1
|
||||
col.mem.ChipWrite(chipbus.CXPPMM, v)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -308,7 +308,7 @@ func (vd *Video) Pixel() {
|
|||
vd.Missile0.pixelCollision, vd.Missile1.pixelCollision,
|
||||
vd.Ball.pixelCollision, vd.Playfield.colorLatch)
|
||||
} else {
|
||||
vd.Collisions.LastVideoCycle.reset()
|
||||
vd.Collisions.LastColorClock.reset()
|
||||
}
|
||||
|
||||
// prioritisation of pixels:
|
||||
|
|
Loading…
Reference in a new issue