mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-09-27 06:54:35 +00:00
Add proper extra cycle when adding an index cross boundaries on memory reads
This commit is contained in:
parent
9c89ad4a61
commit
95998ba37d
@ -61,10 +61,15 @@ func resolveSetValue(s *State, line []uint8, opcode opcode, value uint8) {
|
|||||||
// The value is in memory
|
// The value is in memory
|
||||||
address := resolveAddress(s, line, opcode)
|
address := resolveAddress(s, line, opcode)
|
||||||
s.mem.Poke(address, value)
|
s.mem.Poke(address, value)
|
||||||
|
|
||||||
|
// On writes, the possible extra cycle crossing page boundaries is
|
||||||
|
// always added and already accounted for.
|
||||||
|
s.extraCycleCrossingBoundaries = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
||||||
var address uint16
|
var address uint16
|
||||||
|
extraCycle := false
|
||||||
|
|
||||||
switch opcode.addressMode {
|
switch opcode.addressMode {
|
||||||
case modeZeroPage:
|
case modeZeroPage:
|
||||||
@ -76,18 +81,20 @@ func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
|||||||
case modeAbsolute:
|
case modeAbsolute:
|
||||||
address = getWordInLine(line)
|
address = getWordInLine(line)
|
||||||
case modeAbsoluteX:
|
case modeAbsoluteX:
|
||||||
address = getWordInLine(line) + uint16(s.reg.getX())
|
base := getWordInLine(line)
|
||||||
|
address, extraCycle = addOffset(base, s.reg.getX())
|
||||||
case modeAbsoluteY:
|
case modeAbsoluteY:
|
||||||
address = getWordInLine(line) + uint16(s.reg.getY())
|
base := getWordInLine(line)
|
||||||
|
address, extraCycle = addOffset(base, s.reg.getY())
|
||||||
case modeIndexedIndirectX:
|
case modeIndexedIndirectX:
|
||||||
addressAddress := uint8(line[1] + s.reg.getX())
|
addressAddress := line[1] + s.reg.getX()
|
||||||
address = getZeroPageWord(s.mem, addressAddress)
|
address = getZeroPageWord(s.mem, addressAddress)
|
||||||
case modeIndirect:
|
case modeIndirect:
|
||||||
addressAddress := getWordInLine(line)
|
addressAddress := getWordInLine(line)
|
||||||
address = getWord(s.mem, addressAddress)
|
address = getWord(s.mem, addressAddress)
|
||||||
case modeIndirectIndexedY:
|
case modeIndirectIndexedY:
|
||||||
address = getZeroPageWord(s.mem, line[1]) +
|
base := getZeroPageWord(s.mem, line[1])
|
||||||
uint16(s.reg.getY())
|
address, extraCycle = addOffset(base, s.reg.getY())
|
||||||
// 65c02 additions
|
// 65c02 additions
|
||||||
case modeIndirectZeroPage:
|
case modeIndirectZeroPage:
|
||||||
address = getZeroPageWord(s.mem, line[1])
|
address = getZeroPageWord(s.mem, line[1])
|
||||||
@ -96,17 +103,57 @@ func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
|||||||
address = getWord(s.mem, addressAddress)
|
address = getWord(s.mem, addressAddress)
|
||||||
case modeRelative:
|
case modeRelative:
|
||||||
// This assumes that PC is already pointing to the next instruction
|
// This assumes that PC is already pointing to the next instruction
|
||||||
address = s.reg.getPC() + uint16(int8(line[1])) // Note: line[1] is signed
|
base := s.reg.getPC()
|
||||||
|
address, extraCycle = addOffsetRelative(base, line[1])
|
||||||
case modeZeroPageAndRelative:
|
case modeZeroPageAndRelative:
|
||||||
// Two addressing modes combined. We refer to the second one, relative,
|
// Two addressing modes combined. We refer to the second one, relative,
|
||||||
// placed one byte after the zeropage reference
|
// placed one byte after the zeropage reference
|
||||||
address = s.reg.getPC() + uint16(int8(line[2])) // Note: line[2] is signed
|
base := s.reg.getPC()
|
||||||
|
address, extraCycle = addOffsetRelative(base, line[2])
|
||||||
default:
|
default:
|
||||||
panic("Assert failed. Missing addressing mode")
|
panic("Assert failed. Missing addressing mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if extraCycle {
|
||||||
|
s.extraCycleCrossingBoundaries = true
|
||||||
|
}
|
||||||
|
|
||||||
return address
|
return address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note: extra cycle on reads when crossing page boundaries.
|
||||||
|
|
||||||
|
Only for:
|
||||||
|
modeAbsoluteX
|
||||||
|
modeAbsoluteY
|
||||||
|
modeIndirectIndexedY
|
||||||
|
modeRelative
|
||||||
|
modeZeroPageAndRelative
|
||||||
|
That is when we add a 8 bit offset to a 16 bit base. The reason is
|
||||||
|
that if don't have a page crossing the CPU optimizes one cycle assuming
|
||||||
|
that the MSB addition won't change. If it does we spend this extra cycle.
|
||||||
|
|
||||||
|
Note that for writes we don't add a cycle in this case. There is no
|
||||||
|
optimization that could make a double write. The regular cycle count
|
||||||
|
is alwaus the same with no optimization.
|
||||||
|
*/
|
||||||
|
func addOffset(base uint16, offset uint8) (uint16, bool) {
|
||||||
|
dest := base + uint16(offset)
|
||||||
|
if (base & 0xff00) != (dest & 0xff00) {
|
||||||
|
return dest, true
|
||||||
|
}
|
||||||
|
return dest, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func addOffsetRelative(base uint16, offset uint8) (uint16, bool) {
|
||||||
|
dest := base + uint16(int8(offset))
|
||||||
|
if (base & 0xff00) != (dest & 0xff00) {
|
||||||
|
return dest, true
|
||||||
|
}
|
||||||
|
return dest, false
|
||||||
|
}
|
||||||
|
|
||||||
func lineString(line []uint8, opcode opcode) string {
|
func lineString(line []uint8, opcode opcode) string {
|
||||||
t := opcode.name
|
t := opcode.name
|
||||||
switch opcode.addressMode {
|
switch opcode.addressMode {
|
||||||
|
@ -73,18 +73,18 @@ var opcodes65c02Delta = [256]opcode{
|
|||||||
0x65: {"ADC", 2, 3, modeZeroPage, opADCAlt},
|
0x65: {"ADC", 2, 3, modeZeroPage, opADCAlt},
|
||||||
0x75: {"ADC", 2, 4, modeZeroPageX, opADCAlt},
|
0x75: {"ADC", 2, 4, modeZeroPageX, opADCAlt},
|
||||||
0x6D: {"ADC", 3, 4, modeAbsolute, opADCAlt},
|
0x6D: {"ADC", 3, 4, modeAbsolute, opADCAlt},
|
||||||
0x7D: {"ADC", 3, 4, modeAbsoluteX, opADCAlt},
|
0x7D: {"ADC", 3, 4, modeAbsoluteX, opADCAlt}, // Extra cycles
|
||||||
0x79: {"ADC", 3, 4, modeAbsoluteY, opADCAlt},
|
0x79: {"ADC", 3, 4, modeAbsoluteY, opADCAlt}, // Extra cycles
|
||||||
0x61: {"ADC", 2, 6, modeIndexedIndirectX, opADCAlt},
|
0x61: {"ADC", 2, 6, modeIndexedIndirectX, opADCAlt},
|
||||||
0x71: {"ADC", 2, 5, modeIndirectIndexedY, opADCAlt},
|
0x71: {"ADC", 2, 5, modeIndirectIndexedY, opADCAlt}, // Extra cycles
|
||||||
0xE9: {"SBC", 2, 2, modeImmediate, opSBCAlt},
|
0xE9: {"SBC", 2, 2, modeImmediate, opSBCAlt},
|
||||||
0xE5: {"SBC", 2, 3, modeZeroPage, opSBCAlt},
|
0xE5: {"SBC", 2, 3, modeZeroPage, opSBCAlt},
|
||||||
0xF5: {"SBC", 2, 4, modeZeroPageX, opSBCAlt},
|
0xF5: {"SBC", 2, 4, modeZeroPageX, opSBCAlt},
|
||||||
0xED: {"SBC", 3, 4, modeAbsolute, opSBCAlt},
|
0xED: {"SBC", 3, 4, modeAbsolute, opSBCAlt},
|
||||||
0xFD: {"SBC", 3, 4, modeAbsoluteX, opSBCAlt},
|
0xFD: {"SBC", 3, 4, modeAbsoluteX, opSBCAlt}, // Extra cycles
|
||||||
0xF9: {"SBC", 3, 4, modeAbsoluteY, opSBCAlt},
|
0xF9: {"SBC", 3, 4, modeAbsoluteY, opSBCAlt}, // Extra cycles
|
||||||
0xE1: {"SBC", 2, 6, modeIndexedIndirectX, opSBCAlt},
|
0xE1: {"SBC", 2, 6, modeIndexedIndirectX, opSBCAlt},
|
||||||
0xF1: {"SBC", 2, 5, modeIndirectIndexedY, opSBCAlt},
|
0xF1: {"SBC", 2, 5, modeIndirectIndexedY, opSBCAlt}, // Extra cycles
|
||||||
|
|
||||||
// Different cycle count
|
// Different cycle count
|
||||||
0x1e: {"ASL", 3, 6, modeAbsoluteX, buildOpShift(true, false)},
|
0x1e: {"ASL", 3, 6, modeAbsoluteX, buildOpShift(true, false)},
|
||||||
@ -105,7 +105,7 @@ var opcodes65c02Delta = [256]opcode{
|
|||||||
// New addressing options
|
// New addressing options
|
||||||
0x89: {"BIT", 2, 2, modeImmediate, opBIT},
|
0x89: {"BIT", 2, 2, modeImmediate, opBIT},
|
||||||
0x34: {"BIT", 2, 4, modeZeroPageX, opBIT},
|
0x34: {"BIT", 2, 4, modeZeroPageX, opBIT},
|
||||||
0x3c: {"BIT", 3, 4, modeAbsoluteX, opBIT},
|
0x3c: {"BIT", 3, 4, modeAbsoluteX, opBIT}, // Extra cycles
|
||||||
0x1a: {"INC", 1, 2, modeAccumulator, buildOpIncDec(true)},
|
0x1a: {"INC", 1, 2, modeAccumulator, buildOpIncDec(true)},
|
||||||
0x3a: {"DEC", 1, 2, modeAccumulator, buildOpIncDec(false)},
|
0x3a: {"DEC", 1, 2, modeAccumulator, buildOpIncDec(false)},
|
||||||
0x7c: {"JMP", 3, 6, modeAbsoluteIndexedIndirectX, opJMP},
|
0x7c: {"JMP", 3, 6, modeAbsoluteIndexedIndirectX, opJMP},
|
||||||
@ -115,7 +115,7 @@ var opcodes65c02Delta = [256]opcode{
|
|||||||
0x5a: {"PHY", 1, 3, modeImplicit, buildOpPush(regY)},
|
0x5a: {"PHY", 1, 3, modeImplicit, buildOpPush(regY)},
|
||||||
0xfa: {"PLX", 1, 4, modeImplicit, buildOpPull(regX)},
|
0xfa: {"PLX", 1, 4, modeImplicit, buildOpPull(regX)},
|
||||||
0x7a: {"PLY", 1, 4, modeImplicit, buildOpPull(regY)},
|
0x7a: {"PLY", 1, 4, modeImplicit, buildOpPull(regY)},
|
||||||
0x80: {"BRA", 2, 4, modeRelative, opJMP},
|
0x80: {"BRA", 2, 4, modeRelative, opJMP}, // Extra cycles
|
||||||
|
|
||||||
0x64: {"STZ", 2, 3, modeZeroPage, opSTZ},
|
0x64: {"STZ", 2, 3, modeZeroPage, opSTZ},
|
||||||
0x74: {"STZ", 2, 4, modeZeroPageX, opSTZ},
|
0x74: {"STZ", 2, 4, modeZeroPageX, opSTZ},
|
||||||
@ -130,22 +130,22 @@ var opcodes65c02Delta = [256]opcode{
|
|||||||
|
|
||||||
// Additional in Rockwell 65c02 and WDC 65c02?
|
// Additional in Rockwell 65c02 and WDC 65c02?
|
||||||
// They have a double addressing mode: zeropage and relative.
|
// They have a double addressing mode: zeropage and relative.
|
||||||
0x0f: {"BBR0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, false)},
|
0x0f: {"BBR0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, false)}, // Extra cycles
|
||||||
0x1f: {"BBR1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, false)},
|
0x1f: {"BBR1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, false)}, // Extra cycles
|
||||||
0x2f: {"BBR2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, false)},
|
0x2f: {"BBR2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, false)}, // Extra cycles
|
||||||
0x3f: {"BBR3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, false)},
|
0x3f: {"BBR3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, false)}, // Extra cycles
|
||||||
0x4f: {"BBR4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, false)},
|
0x4f: {"BBR4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, false)}, // Extra cycles
|
||||||
0x5f: {"BBR5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, false)},
|
0x5f: {"BBR5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, false)}, // Extra cycles
|
||||||
0x6f: {"BBR6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, false)},
|
0x6f: {"BBR6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, false)}, // Extra cycles
|
||||||
0x7f: {"BBR7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, false)},
|
0x7f: {"BBR7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, false)}, // Extra cycles
|
||||||
0x8f: {"BBS0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, true)},
|
0x8f: {"BBS0", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(0, true)}, // Extra cycles
|
||||||
0x9f: {"BBS1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, true)},
|
0x9f: {"BBS1", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(1, true)}, // Extra cycles
|
||||||
0xaf: {"BBS2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, true)},
|
0xaf: {"BBS2", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(2, true)}, // Extra cycles
|
||||||
0xbf: {"BBS3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, true)},
|
0xbf: {"BBS3", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(3, true)}, // Extra cycles
|
||||||
0xcf: {"BBS4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, true)},
|
0xcf: {"BBS4", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(4, true)}, // Extra cycles
|
||||||
0xdf: {"BBS5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, true)},
|
0xdf: {"BBS5", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(5, true)}, // Extra cycles
|
||||||
0xef: {"BBS6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, true)},
|
0xef: {"BBS6", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(6, true)}, // Extra cycles
|
||||||
0xff: {"BBS7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, true)},
|
0xff: {"BBS7", 3, 2, modeZeroPageAndRelative, buildOpBranchOnBit(7, true)}, // Extra cycles
|
||||||
|
|
||||||
0x07: {"RMB0", 2, 5, modeZeroPage, buildOpSetBit(0, false)},
|
0x07: {"RMB0", 2, 5, modeZeroPage, buildOpSetBit(0, false)},
|
||||||
0x17: {"RMB1", 2, 5, modeZeroPage, buildOpSetBit(1, false)},
|
0x17: {"RMB1", 2, 5, modeZeroPage, buildOpSetBit(1, false)},
|
||||||
|
@ -24,8 +24,9 @@ type State struct {
|
|||||||
mem Memory
|
mem Memory
|
||||||
cycles uint64
|
cycles uint64
|
||||||
|
|
||||||
extraCycle bool
|
extraCycleCrossingBoundaries bool
|
||||||
lineCache []uint8
|
extraCycleBranchTaken bool
|
||||||
|
lineCache []uint8
|
||||||
// We cache the allocation of a line to avoid a malloc per instruction. To be used only
|
// We cache the allocation of a line to avoid a malloc per instruction. To be used only
|
||||||
// by ExecuteInstruction(). 2x speedup on the emulation!!
|
// by ExecuteInstruction(). 2x speedup on the emulation!!
|
||||||
}
|
}
|
||||||
@ -79,10 +80,16 @@ func (s *State) ExecuteInstruction() {
|
|||||||
}
|
}
|
||||||
opcode.action(s, s.lineCache, opcode)
|
opcode.action(s, s.lineCache, opcode)
|
||||||
s.cycles += uint64(opcode.cycles)
|
s.cycles += uint64(opcode.cycles)
|
||||||
if s.extraCycle {
|
|
||||||
|
if s.extraCycleBranchTaken {
|
||||||
s.cycles++
|
s.cycles++
|
||||||
s.extraCycle = false
|
s.extraCycleBranchTaken = false
|
||||||
}
|
}
|
||||||
|
if s.extraCycleCrossingBoundaries {
|
||||||
|
s.cycles++
|
||||||
|
s.extraCycleCrossingBoundaries = false
|
||||||
|
}
|
||||||
|
|
||||||
if s.trace {
|
if s.trace {
|
||||||
fmt.Printf("%v, [%02x]\n", s.reg, s.lineCache[0:opcode.bytes])
|
fmt.Printf("%v, [%02x]\n", s.reg, s.lineCache[0:opcode.bytes])
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ func buildOpUpdateFlag(flag uint8, value bool) opFunc {
|
|||||||
func buildOpBranch(flag uint8, test bool) opFunc {
|
func buildOpBranch(flag uint8, test bool) opFunc {
|
||||||
return func(s *State, line []uint8, opcode opcode) {
|
return func(s *State, line []uint8, opcode opcode) {
|
||||||
if s.reg.getFlag(flag) == test {
|
if s.reg.getFlag(flag) == test {
|
||||||
s.extraCycle = true
|
s.extraCycleBranchTaken = true
|
||||||
address := resolveAddress(s, line, opcode)
|
address := resolveAddress(s, line, opcode)
|
||||||
s.reg.setPC(address)
|
s.reg.setPC(address)
|
||||||
}
|
}
|
||||||
@ -92,6 +92,7 @@ func buildOpBranchOnBit(bit uint8, test bool) opFunc {
|
|||||||
bitValue := ((value >> bit) & 1) == 1
|
bitValue := ((value >> bit) & 1) == 1
|
||||||
|
|
||||||
if bitValue == test {
|
if bitValue == test {
|
||||||
|
s.extraCycleBranchTaken = true
|
||||||
address := resolveAddress(s, line, opcode)
|
address := resolveAddress(s, line, opcode)
|
||||||
s.reg.setPC(address)
|
s.reg.setPC(address)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user