mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
o CPU
- reworked index bug handling in CPU - fixed slo opcode defintion (wrong addressing mode specified) o debugger - added BUG option to CPU command - debugger now optionally prints CPU BUG messages on the step it occurs
This commit is contained in:
parent
f735c06fc3
commit
ca33e408f1
9 changed files with 96 additions and 26 deletions
|
@ -70,7 +70,7 @@ const cmdHelp = "HELP"
|
|||
var commandTemplate = []string{
|
||||
cmdBall,
|
||||
cmdBreak + " [%S %N|%N] {& %S %N|& %N}",
|
||||
cmdCPU + " (SET [PC|A|X|Y|SP] [%N])",
|
||||
cmdCPU + " (SET [PC|A|X|Y|SP] [%N]|BUG (ON|OFF))",
|
||||
cmdCartridge + " (ANALYSIS|BANK %N)",
|
||||
cmdClear + " [BREAKS|TRAPS|WATCHES|ALL]",
|
||||
cmdDebuggerState,
|
||||
|
@ -752,6 +752,22 @@ func (dbg *Debugger) enactCommand(tokens *commandline.Tokens, interactive bool)
|
|||
|
||||
reg.Load(v)
|
||||
|
||||
case "BUG":
|
||||
option, _ := tokens.Get()
|
||||
|
||||
switch strings.ToUpper(option) {
|
||||
case "ON":
|
||||
dbg.reportCPUBugs = true
|
||||
case "OFF":
|
||||
dbg.reportCPUBugs = false
|
||||
}
|
||||
|
||||
if dbg.reportCPUBugs {
|
||||
dbg.print(console.StyleFeedback, "CPU bug reporting: ON")
|
||||
} else {
|
||||
dbg.print(console.StyleFeedback, "CPU bug reporting: OFF")
|
||||
}
|
||||
|
||||
default:
|
||||
// already caught by command line ValidateTokens()
|
||||
}
|
||||
|
|
|
@ -91,6 +91,11 @@ type Debugger struct {
|
|||
// machineInfoVerbose controls the verbosity of commands that echo machine state
|
||||
machineInfoVerbose bool
|
||||
|
||||
// whether to display the triggering of a known CPU bug. these are bugs
|
||||
// that are known about in the emulated hardware but which might catch an
|
||||
// unwary programmer by surprise
|
||||
reportCPUBugs bool
|
||||
|
||||
// input loop fields. we're storing these here because inputLoop can be
|
||||
// called from within another input loop (via a video step callback) and we
|
||||
// want these properties to persist (when a video step input loop has
|
||||
|
@ -291,6 +296,11 @@ func (dbg *Debugger) videoCycle(result *result.Instruction) error {
|
|||
if result.Defn.Effect == definitions.Flow || result.Defn.Effect == definitions.Subroutine || result.Defn.Effect == definitions.Interrupt {
|
||||
return nil
|
||||
}
|
||||
|
||||
// display information about any CPU bugs that may have been triggered
|
||||
if dbg.reportCPUBugs && result.Bug != "" {
|
||||
dbg.print(console.StyleMachineInfo, result.Bug)
|
||||
}
|
||||
}
|
||||
|
||||
dbg.breakMessages = dbg.breakpoints.check(dbg.breakMessages)
|
||||
|
|
|
@ -50,7 +50,7 @@ var messages = map[Errno]string{
|
|||
// cpu
|
||||
UnimplementedInstruction: "cpu error: unimplemented instruction (%0#x) at (%#04x)",
|
||||
InvalidOpcode: "cpu error: invalid opcode (%#04x)",
|
||||
InvalidResult: "cpu error: %s: %s",
|
||||
InvalidResult: "cpu error: %s",
|
||||
ProgramCounterCycled: "cpu error: program counter cycled back to 0x0000",
|
||||
InvalidOperationMidInstruction: "cpu error: invalid operation mid-instruction (%s)",
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ func main() {
|
|||
runner := func() error {
|
||||
err := dbg.Start(term, *initScript, modeFlags.Arg(0))
|
||||
if err != nil {
|
||||
return errors.NewFormattedError(errors.PerformanceError, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -216,7 +216,11 @@ func main() {
|
|||
os.Exit(2)
|
||||
}
|
||||
} else {
|
||||
runner()
|
||||
err := runner()
|
||||
if err != nil {
|
||||
fmt.Printf("* %s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
default:
|
||||
fmt.Printf("* too many arguments for %s mode\n", mode)
|
||||
|
|
|
@ -471,12 +471,16 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction) error)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result.InstructionData = indirectAddress
|
||||
adder := register.NewAnonRegister(indirectAddress, 8)
|
||||
adder.Add(mc.X, false)
|
||||
address = adder.ToUint16()
|
||||
|
||||
// !!TODO: zero page index bug
|
||||
// handle zero page index bug
|
||||
if (uint16(indirectAddress)+mc.X.ToUint16())&0xff00 != uint16(indirectAddress)&0xff00 {
|
||||
result.Bug = fmt.Sprintf("zero page index bug")
|
||||
}
|
||||
|
||||
// +1 cycle
|
||||
err = mc.endCycle()
|
||||
|
@ -497,7 +501,10 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction) error)
|
|||
adder.Add(mc.Y, false)
|
||||
address = adder.ToUint16()
|
||||
|
||||
// !!TODO: zero page index bug
|
||||
// handle zero page index bug
|
||||
if (uint16(indirectAddress)+mc.Y.ToUint16())&0xff00 != uint16(indirectAddress)&0xff00 {
|
||||
result.Bug = fmt.Sprintf("zero page index bug")
|
||||
}
|
||||
|
||||
// +1 cycle
|
||||
err = mc.endCycle()
|
||||
|
@ -515,7 +522,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction) error)
|
|||
}
|
||||
result.InstructionData = indirectAddress
|
||||
|
||||
// implement NMOS 6502 Indirect JMP bug
|
||||
// handle indirect addressing JMP bug
|
||||
if indirectAddress&0x00ff == 0x00ff {
|
||||
result.Bug = fmt.Sprintf("indirect addressing bug (JMP bug)")
|
||||
|
||||
|
@ -530,6 +537,10 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction) error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// in this bug path, the lower byte of the indirect address is on a
|
||||
// page boundary. because of the bug we must read high byte of JMP
|
||||
// address from the zero byte of the same page (rather than the
|
||||
// zero byte of the next page)
|
||||
hi, err := mc.mem.Read(indirectAddress & 0xff00)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -573,8 +584,8 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction) error)
|
|||
adder := register.NewAnonRegister(mc.X, 8)
|
||||
adder.Add(indirectAddress, false)
|
||||
|
||||
// indirect addressing / page boundary bug
|
||||
if uint16(indirectAddress)+mc.X.ToUint16() > 0x00ff {
|
||||
// note whether indirect addressing / page boundary bug has occurred
|
||||
if (uint16(indirectAddress)+mc.X.ToUint16())&0xff00 != uint16(indirectAddress)&0xff00 {
|
||||
result.Bug = fmt.Sprintf("indirect addressing bug")
|
||||
}
|
||||
|
||||
|
@ -1296,9 +1307,13 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction) error)
|
|||
mc.Status.Sign = mc.A.IsNegative()
|
||||
|
||||
case "slo":
|
||||
var r *register.Register
|
||||
r = register.NewAnonRegister(value, mc.A.Size())
|
||||
mc.Status.Carry = r.ROL(mc.Status.Carry)
|
||||
// the slo opcode starts off with an ASL operation
|
||||
// all versions of this opcode are RMW so we always work with
|
||||
// the anonymous register
|
||||
r := register.NewAnonRegister(value, mc.A.Size())
|
||||
mc.Status.Carry = r.ASL()
|
||||
mc.Status.Zero = r.IsZero()
|
||||
mc.Status.Sign = r.IsNegative()
|
||||
value = r.ToUint8()
|
||||
mc.A.ORA(value)
|
||||
mc.Status.Zero = mc.A.IsZero()
|
||||
|
@ -1311,16 +1326,16 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction) error)
|
|||
|
||||
// for RMW instructions: write altered value back to memory
|
||||
if defn.Effect == definitions.RMW {
|
||||
err = mc.write8Bit(address, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
// +1 cycle
|
||||
err = mc.endCycle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = mc.write8Bit(address, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// finalise result
|
||||
|
|
|
@ -261,5 +261,5 @@
|
|||
0xcb, sax, 2, IMMEDIATE, False
|
||||
0x6b, arr, 2, IMMEDIATE, False
|
||||
|
||||
0x03, slo, 8, INDEXED_ZERO_PAGE_X, False, RMW # aso
|
||||
0x03, slo, 8, PRE_INDEX_INDIRECT, False, RMW # aso
|
||||
0x07, slo, 5, ZERO_PAGE, False, RMW # aso
|
||||
|
|
Can't render this file because it contains an unexpected character in line 16 and column 5.
|
|
@ -8,7 +8,7 @@ return []*InstructionDefinition{
|
|||
&InstructionDefinition{ObjectCode:0x0, Mnemonic:"BRK", Bytes:1, Cycles:7, AddressingMode:0, PageSensitive:false, Effect:5},
|
||||
&InstructionDefinition{ObjectCode:0x1, Mnemonic:"ORA", Bytes:2, Cycles:6, AddressingMode:6, PageSensitive:false, Effect:0},
|
||||
nil,
|
||||
&InstructionDefinition{ObjectCode:0x3, Mnemonic:"slo", Bytes:2, Cycles:8, AddressingMode:10, PageSensitive:false, Effect:2},
|
||||
&InstructionDefinition{ObjectCode:0x3, Mnemonic:"slo", Bytes:2, Cycles:8, AddressingMode:6, PageSensitive:false, Effect:2},
|
||||
&InstructionDefinition{ObjectCode:0x4, Mnemonic:"dop", Bytes:2, Cycles:3, AddressingMode:4, PageSensitive:false, Effect:0},
|
||||
&InstructionDefinition{ObjectCode:0x5, Mnemonic:"ORA", Bytes:2, Cycles:3, AddressingMode:4, PageSensitive:false, Effect:0},
|
||||
&InstructionDefinition{ObjectCode:0x6, Mnemonic:"ASL", Bytes:2, Cycles:5, AddressingMode:4, PageSensitive:false, Effect:2},
|
||||
|
|
|
@ -33,19 +33,34 @@ func (result Instruction) IsValid() error {
|
|||
if result.Bug == "" {
|
||||
if result.Defn.AddressingMode == definitions.Relative {
|
||||
if result.ActualCycles != result.Defn.Cycles && result.ActualCycles != result.Defn.Cycles+1 && result.ActualCycles != result.Defn.Cycles+2 {
|
||||
msg := fmt.Sprintf("number of cycles wrong (%d instead of %d, %d or %d)", result.ActualCycles, result.Defn.Cycles, result.Defn.Cycles+1, result.Defn.Cycles+2)
|
||||
return errors.NewFormattedError(errors.InvalidResult, msg, result)
|
||||
msg := fmt.Sprintf("number of cycles wrong for opcode %#02x [%s] (%d instead of %d, %d or %d)",
|
||||
result.Defn.ObjectCode,
|
||||
result.Defn.Mnemonic,
|
||||
result.ActualCycles,
|
||||
result.Defn.Cycles,
|
||||
result.Defn.Cycles+1,
|
||||
result.Defn.Cycles+2)
|
||||
return errors.NewFormattedError(errors.InvalidResult, msg)
|
||||
}
|
||||
} else {
|
||||
if result.Defn.PageSensitive {
|
||||
if result.PageFault && result.ActualCycles != result.Defn.Cycles && result.ActualCycles != result.Defn.Cycles+1 {
|
||||
msg := fmt.Sprintf("number of cycles wrong (actual %d instead of %d or %d)", result.ActualCycles, result.Defn.Cycles, result.Defn.Cycles+1)
|
||||
return errors.NewFormattedError(errors.InvalidResult, msg, result)
|
||||
msg := fmt.Sprintf("number of cycles wrong for opcode %#02x [%s] (%d instead of %d, %d)",
|
||||
result.Defn.ObjectCode,
|
||||
result.Defn.Mnemonic,
|
||||
result.ActualCycles,
|
||||
result.Defn.Cycles,
|
||||
result.Defn.Cycles+1)
|
||||
return errors.NewFormattedError(errors.InvalidResult, msg)
|
||||
}
|
||||
} else {
|
||||
if result.ActualCycles != result.Defn.Cycles {
|
||||
msg := fmt.Sprintf("number of cycles wrong (actual %d instead of %d)", result.ActualCycles, result.Defn.Cycles)
|
||||
return errors.NewFormattedError(errors.InvalidResult, msg, result)
|
||||
msg := fmt.Sprintf("number of cycles wrong for opcode %#02x [%s] (%d instead of %d)",
|
||||
result.Defn.ObjectCode,
|
||||
result.Defn.Mnemonic,
|
||||
result.ActualCycles,
|
||||
result.Defn.Cycles)
|
||||
return errors.NewFormattedError(errors.InvalidResult, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,10 +310,20 @@ func (vcs *VCS) Run(continueCheck func() (bool, error)) error {
|
|||
|
||||
cont := true
|
||||
for cont {
|
||||
_, err = vcs.CPU.ExecuteInstruction(videoCycle)
|
||||
var r *result.Instruction
|
||||
r, err = vcs.CPU.ExecuteInstruction(videoCycle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check validity of result
|
||||
if r != nil {
|
||||
err = r.IsValid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cont, err = continueCheck()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue