mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-02 20:18:20 -04:00
corrected/improved how BX instruction is implemented
This commit is contained in:
parent
ce19219205
commit
4d8c3c94fd
|
@ -115,6 +115,12 @@ type ARMState struct {
|
|||
// the yield reason explains the reason for why the ARM execution ended
|
||||
yield coprocessor.CoProcYield
|
||||
|
||||
// the expectedReturnAddress is the address that the program will return to
|
||||
// at the end of the program's execution. if the PC is ever set to this
|
||||
// value (as a result of a BX or BLX instruction) then the ARM will yield
|
||||
// with the YieldProgramEnded type
|
||||
expectedReturnAddress uint32
|
||||
|
||||
// the area the PC covers. once assigned we'll assume that the program
|
||||
// never reads outside this area. the value is assigned on reset()
|
||||
programMemory *[]uint8
|
||||
|
@ -417,6 +423,7 @@ func (arm *ARM) resetRegisters() {
|
|||
preResetPC := arm.state.registers[rPC]
|
||||
arm.state.registers[rSP], arm.state.registers[rLR], arm.state.registers[rPC] = arm.mem.ResetVectors()
|
||||
arm.state.stackFrame = arm.state.registers[rSP]
|
||||
arm.state.expectedReturnAddress = (arm.state.registers[rLR] + 2) & 0xfffffffe
|
||||
|
||||
// set executingPC to be two behind the current value in the PC register
|
||||
arm.state.executingPC = arm.state.registers[rPC] - 2
|
||||
|
@ -508,7 +515,11 @@ func (arm *ARM) logYield() {
|
|||
if arm.state.yield.Type.Normal() {
|
||||
return
|
||||
}
|
||||
logger.Logf("ARM7", "%s: %s", arm.state.yield.Type.String(), arm.state.yield.Error)
|
||||
if arm.state.yield.Error != nil {
|
||||
logger.Logf("ARM7", "%s: %s", arm.state.yield.Type.String(), arm.state.yield.Error.Error())
|
||||
} else {
|
||||
logger.Logf("ARM7", "%s: no specific error", arm.state.yield.Type.String())
|
||||
}
|
||||
|
||||
// extended memory logging
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package arm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
|
||||
|
@ -929,90 +928,81 @@ func (arm *ARM) decodeThumbHiRegisterOps(opcode uint16) decodeFunction {
|
|||
case architecture.ARMv7_M:
|
||||
if opcode&0x0080 == 0x0080 {
|
||||
// "A7.7.19 BLX (register)" in "ARMv7-M"
|
||||
target := arm.state.registers[srcReg]
|
||||
nextPC := arm.state.registers[rPC] - 2
|
||||
arm.state.registers[rLR] = nextPC | 0x01
|
||||
if target&0x01 == 0x00 {
|
||||
// cannot switch to ARM mode in the ARMv7-M architecture
|
||||
arm.state.yield.Type = coprocessor.YieldUndefinedBehaviour
|
||||
arm.state.yield.Error = errors.New("cannot switch to ARM mode in ARMv7-M architecture")
|
||||
}
|
||||
arm.state.registers[rPC] = (target + 2) & 0xfffffffe
|
||||
} else {
|
||||
// "A7.7.20 BX " in "ARMv7-M"
|
||||
target := arm.state.registers[srcReg]
|
||||
if target&0x01 == 0x00 {
|
||||
// cannot switch to ARM mode in the ARMv7-M architecture
|
||||
arm.state.yield.Type = coprocessor.YieldUndefinedBehaviour
|
||||
arm.state.yield.Error = errors.New("cannot switch to ARM mode in ARMv7-M architecture")
|
||||
}
|
||||
arm.state.registers[rPC] = (target + 2) & 0xfffffffe
|
||||
}
|
||||
arm.state.registers[rPC] = (arm.state.registers[srcReg] + 2) & 0xfffffffe
|
||||
|
||||
// "7.6 Data Operations" in "ARM7TDMI-S Technical Reference Manual r4p3"
|
||||
// - fillPipeline() will be called if necessary
|
||||
return nil
|
||||
case architecture.ARM7TDMI:
|
||||
thumbMode := arm.state.registers[srcReg]&0x01 == 0x01
|
||||
|
||||
var newPC uint32
|
||||
|
||||
// "ARM7TDMI Data Sheet" page 5-15:
|
||||
//
|
||||
// "If R15 is used as an operand, the value will be the address of the instruction + 4 with
|
||||
// bit 0 cleared. Executing a BX PC in THUMB state from a non-word aligned address
|
||||
// will result in unpredictable execution."
|
||||
if srcReg == rPC {
|
||||
newPC = arm.state.registers[rPC] + 2
|
||||
arm.state.registers[rPC] = arm.state.registers[rPC] + 2
|
||||
} else {
|
||||
newPC = (arm.state.registers[srcReg] & 0x7ffffffe) + 2
|
||||
arm.state.registers[rPC] = (arm.state.registers[srcReg] + 2) & 0xfffffffe
|
||||
}
|
||||
}
|
||||
|
||||
if thumbMode {
|
||||
arm.state.registers[rPC] = newPC
|
||||
|
||||
// "7.6 Data Operations" in "ARM7TDMI-S Technical Reference Manual r4p3"
|
||||
// - fillPipeline() will be called if necessary
|
||||
return nil
|
||||
}
|
||||
|
||||
// switch to ARM mode. emulate function call.
|
||||
res, err := arm.hook.ARMinterrupt(arm.state.registers[rPC]-4, arm.state.registers[2], arm.state.registers[3])
|
||||
if err != nil {
|
||||
arm.state.yield.Type = coprocessor.YieldExecutionError
|
||||
arm.state.yield.Error = err
|
||||
return nil
|
||||
}
|
||||
|
||||
// if ARMinterrupt returns false this indicates that the
|
||||
// function at the quoted program counter is not recognised and
|
||||
// has nothing to do with the cartridge mapping. at this point
|
||||
// we can assume that the main() function call is done and we
|
||||
// can return to the VCS emulation.
|
||||
if !res.InterruptServiced {
|
||||
arm.state.yield.Type = coprocessor.YieldProgramEnded
|
||||
arm.state.yield.Error = nil
|
||||
// "7.6 Data Operations" in "ARM7TDMI-S Technical Reference Manual r4p3"
|
||||
// - interrupted
|
||||
return nil
|
||||
}
|
||||
|
||||
// ARM function updates the ARM registers
|
||||
if res.SaveResult {
|
||||
arm.state.registers[res.SaveRegister] = res.SaveValue
|
||||
}
|
||||
|
||||
// the end of the emulated function will have an operation that
|
||||
// switches back to thumb mode, and copies the link register to the
|
||||
// program counter. we need to emulate that too.
|
||||
arm.state.registers[rPC] = arm.state.registers[rLR] + 2
|
||||
|
||||
// add cycles used by the ARM program
|
||||
arm.armInterruptCycles(res)
|
||||
|
||||
// if the low bit of the src register is set then the target is
|
||||
// using the thumb instruction set and we can return immediately
|
||||
if arm.state.registers[srcReg]&0x01 == 0x01 {
|
||||
// "7.6 Data Operations" in "ARM7TDMI-S Technical Reference Manual r4p3"
|
||||
// - fillPipeline() will be called if necessary
|
||||
return nil
|
||||
}
|
||||
|
||||
// if the PC is now the same as the expected return address then the
|
||||
// ARM program has ended and we can yield with the YieldProgramEnded
|
||||
// type
|
||||
if arm.state.registers[rPC] == arm.state.expectedReturnAddress {
|
||||
arm.state.yield.Type = coprocessor.YieldProgramEnded
|
||||
arm.state.yield.Error = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// NOTE: the remainder of the instruction handles ARM interrupts.
|
||||
// note that this hasn't been tested with ARMv7_M processor types.
|
||||
// this type of processor does not have 32bit instructions so may
|
||||
// work a bit differerntly
|
||||
|
||||
// check if we have an emulated implementation for the code starting
|
||||
// at this address
|
||||
res, err := arm.hook.ARMinterrupt(arm.state.registers[rPC]-4, arm.state.registers[2], arm.state.registers[3])
|
||||
if err != nil {
|
||||
arm.state.yield.Type = coprocessor.YieldExecutionError
|
||||
arm.state.yield.Error = err
|
||||
return nil
|
||||
}
|
||||
|
||||
// if ARMinterrupt returns false this indicates that the
|
||||
// function at the quoted program counter is not recognised and
|
||||
// has nothing to do with the cartridge mapping. at this point
|
||||
// we can assume that the main() function call is done and we
|
||||
// can return to the VCS emulation.
|
||||
if !res.InterruptServiced {
|
||||
arm.state.yield.Type = coprocessor.YieldExecutionError
|
||||
arm.state.yield.Error = fmt.Errorf("ARMinterrupt() not serviced for address %08x", arm.state.registers[rPC]-4)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ARM function updates the ARM registers
|
||||
if res.SaveResult {
|
||||
arm.state.registers[res.SaveRegister] = res.SaveValue
|
||||
}
|
||||
|
||||
// the end of the emulated function will have an operation that
|
||||
// switches back to thumb mode, and copies the link register to the
|
||||
// program counter. we need to emulate that too.
|
||||
arm.state.registers[rPC] = arm.state.registers[rLR] + 2
|
||||
|
||||
// add cycles used by the ARM program
|
||||
arm.armInterruptCycles(res)
|
||||
|
||||
// "7.6 Data Operations" in "ARM7TDMI-S Technical Reference Manual r4p3"
|
||||
// - fillPipeline() will be called if necessary
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in a new issue