Made golint happy
This commit is contained in:
parent
01adea08ba
commit
ca7f2d2e04
12
README.md
12
README.md
|
@ -82,4 +82,14 @@ Assemble the tests
|
||||||
* 80 column card
|
* 80 column card
|
||||||
* 48k aux memory
|
* 48k aux memory
|
||||||
* double hires
|
* double hires
|
||||||
* joystick
|
* joystick
|
||||||
|
|
||||||
|
## Coding standards
|
||||||
|
|
||||||
|
Use `gofmt` to ensure standard go style consistency
|
||||||
|
|
||||||
|
go fmt $(go list ./... | grep -v /vendor/)
|
||||||
|
|
||||||
|
Use `golint` to ensure Google's style consistency
|
||||||
|
|
||||||
|
golint $(go list ./...)
|
||||||
|
|
|
@ -65,7 +65,7 @@ func update(screen *ebiten.Image) error {
|
||||||
exitAtBreak := true // Die if a BRK instruction is seen
|
exitAtBreak := true // Die if a BRK instruction is seen
|
||||||
|
|
||||||
// Run for 1/60 of a second, the duration of an ebiten frame
|
// Run for 1/60 of a second, the duration of an ebiten frame
|
||||||
cpu.Run(*showInstructions, breakAddress, exitAtBreak, *disableFirmwareWait, system.CpuFrequency/60)
|
cpu.Run(*showInstructions, breakAddress, exitAtBreak, *disableFirmwareWait, system.CPUFrequency/60)
|
||||||
|
|
||||||
// Process any audio speaker clicks from this frame
|
// Process any audio speaker clicks from this frame
|
||||||
audio.ForwardToFrameCycle()
|
audio.ForwardToFrameCycle()
|
||||||
|
|
|
@ -18,10 +18,11 @@ func Click() {
|
||||||
func attenuate(sample int16) int16 {
|
func attenuate(sample int16) int16 {
|
||||||
if system.AudioAttenuationCounter == 0 {
|
if system.AudioAttenuationCounter == 0 {
|
||||||
return 0
|
return 0
|
||||||
} else {
|
|
||||||
system.AudioAttenuationCounter--
|
|
||||||
return sample
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
system.AudioAttenuationCounter--
|
||||||
|
return sample
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForwardToFrameCycle calculates how many audio samples need to be written to
|
// ForwardToFrameCycle calculates how many audio samples need to be written to
|
||||||
|
@ -29,7 +30,7 @@ func attenuate(sample int16) int16 {
|
||||||
// flush and shove them into the channel.
|
// flush and shove them into the channel.
|
||||||
func ForwardToFrameCycle() {
|
func ForwardToFrameCycle() {
|
||||||
// 1023000/44100=23.19 cycles per audio sample
|
// 1023000/44100=23.19 cycles per audio sample
|
||||||
cyclesPerAudioSample := system.CpuFrequency / float64(system.AudioSampleRate)
|
cyclesPerAudioSample := system.CPUFrequency / float64(system.AudioSampleRate)
|
||||||
|
|
||||||
// Should be about 1023000/60=17050
|
// Should be about 1023000/60=17050
|
||||||
elapsedCycles := system.FrameCycles - system.LastAudioCycles
|
elapsedCycles := system.FrameCycles - system.LastAudioCycles
|
||||||
|
|
|
@ -10,11 +10,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
audioContext *ebiten_audio.Context // Ebitem audio context
|
audioContext *ebiten_audio.Context // Ebitem audio context
|
||||||
player *ebiten_audio.Player // Ebitem stream player
|
player *ebiten_audio.Player // Ebitem stream player
|
||||||
firstAudio bool // True at startup
|
firstAudio bool // True at startup
|
||||||
Mute bool // Mute
|
)
|
||||||
ClickWhenDriveHeadMoves bool // Click speaker when the drive head moves
|
|
||||||
|
var (
|
||||||
|
// Mute ensures no samples are output
|
||||||
|
Mute bool
|
||||||
|
|
||||||
|
// ClickWhenDriveHeadMoves makes the speaker click once every time the stepper motor magnets change
|
||||||
|
ClickWhenDriveHeadMoves bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// The streaming code is based on the ebiten sinewave example
|
// The streaming code is based on the ebiten sinewave example
|
||||||
|
|
|
@ -11,9 +11,9 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func assertMemoryConfiguration(t *testing.T, address uint16, upperRamReadOnly bool, upperReadMappedToROM bool, d000Bank int) {
|
func assertMemoryConfiguration(t *testing.T, address uint16, upperRAMReadOnly bool, upperReadMappedToROM bool, d000Bank int) {
|
||||||
mmu.WriteMemory(address, 0x00)
|
mmu.WriteMemory(address, 0x00)
|
||||||
assert.Equal(t, upperRamReadOnly, mmu.UpperRamReadOnly)
|
assert.Equal(t, upperRAMReadOnly, mmu.UpperRAMReadOnly)
|
||||||
assert.Equal(t, upperReadMappedToROM, mmu.UpperReadMappedToROM)
|
assert.Equal(t, upperReadMappedToROM, mmu.UpperReadMappedToROM)
|
||||||
assert.Equal(t, d000Bank, mmu.D000Bank)
|
assert.Equal(t, d000Bank, mmu.D000Bank)
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ func TestBankSwitching(t *testing.T) {
|
||||||
|
|
||||||
// Set d000 RAM to bank 1, RAM to read only and attempt writes
|
// Set d000 RAM to bank 1, RAM to read only and attempt writes
|
||||||
mmu.SetD000Bank(1)
|
mmu.SetD000Bank(1)
|
||||||
mmu.SetUpperRamReadOnly(true)
|
mmu.SetUpperRAMReadOnly(true)
|
||||||
assert.Equal(t, uint8(0xfd), mmu.PhysicalMemory.MainMemory[0xc000]) // bank #1 RAM
|
assert.Equal(t, uint8(0xfd), mmu.PhysicalMemory.MainMemory[0xc000]) // bank #1 RAM
|
||||||
assert.Equal(t, uint8(0xfe), mmu.PhysicalMemory.MainMemory[0xd000]) // bank #2 RAM
|
assert.Equal(t, uint8(0xfe), mmu.PhysicalMemory.MainMemory[0xd000]) // bank #2 RAM
|
||||||
mmu.WriteMemory(0xd000, 0x01) // attempt to write to read only RAM
|
mmu.WriteMemory(0xd000, 0x01) // attempt to write to read only RAM
|
||||||
|
@ -77,7 +77,7 @@ func TestBankSwitching(t *testing.T) {
|
||||||
assert.Equal(t, uint8(0xff), mmu.PhysicalMemory.MainMemory[0xffff]) // top of RAM is unchanged
|
assert.Equal(t, uint8(0xff), mmu.PhysicalMemory.MainMemory[0xffff]) // top of RAM is unchanged
|
||||||
|
|
||||||
// Set RAM to write and write to it
|
// Set RAM to write and write to it
|
||||||
mmu.SetUpperRamReadOnly(false)
|
mmu.SetUpperRAMReadOnly(false)
|
||||||
mmu.WriteMemory(0xd000, 0xfc) // write to RAM
|
mmu.WriteMemory(0xd000, 0xfc) // write to RAM
|
||||||
mmu.WriteMemory(0xffff, 0xfb) // write to RAM
|
mmu.WriteMemory(0xffff, 0xfb) // write to RAM
|
||||||
assert.Equal(t, uint8(0xfc), mmu.PhysicalMemory.MainMemory[0xc000]) // bank #1 RAM has been updated
|
assert.Equal(t, uint8(0xfc), mmu.PhysicalMemory.MainMemory[0xc000]) // bank #1 RAM has been updated
|
||||||
|
|
|
@ -24,7 +24,7 @@ func testBellCycles(delay int) {
|
||||||
exitAtBreak := false
|
exitAtBreak := false
|
||||||
disableFirmwareWait := false
|
disableFirmwareWait := false
|
||||||
cpu.State.PC = 0x800
|
cpu.State.PC = 0x800
|
||||||
cpu.Run(showInstructions, &breakAddress, exitAtBreak, disableFirmwareWait, system.CpuFrequency*1000)
|
cpu.Run(showInstructions, &breakAddress, exitAtBreak, disableFirmwareWait, system.CPUFrequency*1000)
|
||||||
|
|
||||||
// See http://apple2.org.za/gswv/a2zine/GS.WorldView/Resources/USEFUL.TABLES/WAIT.DELAY.CR.txt
|
// See http://apple2.org.za/gswv/a2zine/GS.WorldView/Resources/USEFUL.TABLES/WAIT.DELAY.CR.txt
|
||||||
expectedCycles := (26 + 27*delay + 5*delay*delay) / 2
|
expectedCycles := (26 + 27*delay + 5*delay*delay) / 2
|
||||||
|
|
89
cpu/cpu.go
89
cpu/cpu.go
|
@ -9,16 +9,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CpuFlagC byte = 1 << iota // 0x01 carry
|
cpuFlagC byte = 1 << iota // 0x01 carry
|
||||||
CpuFlagZ // 0x02 zero
|
cpuFlagZ // 0x02 zero
|
||||||
CpuFlagI // 0x04 interrupt disable
|
cpuFlagI // 0x04 interrupt disable
|
||||||
CpuFlagD // 0x08 decimal mode
|
cpuFlagD // 0x08 decimal mode
|
||||||
CpuFlagB // 0x10 break
|
cpuFlagB // 0x10 break
|
||||||
CpuFlagR // 0x20 reserved (unused)
|
cpuFlagR // 0x20 reserved (unused)
|
||||||
CpuFlagV // 0x40 overflow
|
cpuFlagV // 0x40 overflow
|
||||||
CpuFlagN // 0x80 sign/negative
|
cpuFlagN // 0x80 sign/negative
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// State contains the CPU state
|
||||||
var State struct {
|
var State struct {
|
||||||
A uint8 // accumulator
|
A uint8 // accumulator
|
||||||
X uint8 // X register
|
X uint8 // X register
|
||||||
|
@ -40,70 +41,70 @@ func Init() {
|
||||||
State.A = 0
|
State.A = 0
|
||||||
State.X = 0
|
State.X = 0
|
||||||
State.Y = 0
|
State.Y = 0
|
||||||
State.P = CpuFlagR | CpuFlagB | CpuFlagZ
|
State.P = cpuFlagR | cpuFlagB | cpuFlagZ
|
||||||
State.SP = 0xff
|
State.SP = 0xff
|
||||||
}
|
}
|
||||||
|
|
||||||
// setC sets the carry flag
|
// setC sets the carry flag
|
||||||
func setC(value bool) {
|
func setC(value bool) {
|
||||||
if value {
|
if value {
|
||||||
State.P |= CpuFlagC
|
State.P |= cpuFlagC
|
||||||
} else {
|
} else {
|
||||||
State.P &= ^CpuFlagC
|
State.P &= ^cpuFlagC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setV sets the overflow flag
|
// setV sets the overflow flag
|
||||||
func setV(value bool) {
|
func setV(value bool) {
|
||||||
if value {
|
if value {
|
||||||
State.P |= CpuFlagV
|
State.P |= cpuFlagV
|
||||||
} else {
|
} else {
|
||||||
State.P &= ^CpuFlagV
|
State.P &= ^cpuFlagV
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setN sets the sign/negative flag if the value is negative (>=0x80)
|
// setN sets the sign/negative flag if the value is negative (>=0x80)
|
||||||
func setN(value uint8) {
|
func setN(value uint8) {
|
||||||
if (value & 0x80) != 0 {
|
if (value & 0x80) != 0 {
|
||||||
State.P |= CpuFlagN
|
State.P |= cpuFlagN
|
||||||
} else {
|
} else {
|
||||||
State.P &= ^CpuFlagN
|
State.P &= ^cpuFlagN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setZ sets the zero flag if the value is zero
|
// setZ sets the zero flag if the value is zero
|
||||||
func setZ(value uint8) {
|
func setZ(value uint8) {
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
State.P |= CpuFlagZ
|
State.P |= cpuFlagZ
|
||||||
} else {
|
} else {
|
||||||
State.P &= ^CpuFlagZ
|
State.P &= ^cpuFlagZ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isC() bool {
|
func isC() bool {
|
||||||
return (State.P & CpuFlagC) != 0
|
return (State.P & cpuFlagC) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func isZ() bool {
|
func isZ() bool {
|
||||||
return (State.P & CpuFlagZ) != 0
|
return (State.P & cpuFlagZ) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func isD() bool {
|
func isD() bool {
|
||||||
return (State.P & CpuFlagD) != 0
|
return (State.P & cpuFlagD) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func isV() bool {
|
func isV() bool {
|
||||||
return (State.P & CpuFlagV) != 0
|
return (State.P & cpuFlagV) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func isN() bool {
|
func isN() bool {
|
||||||
return (State.P & CpuFlagN) != 0
|
return (State.P & cpuFlagN) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// push8 pushes an 8 bit value to the stack
|
// push8 pushes an 8 bit value to the stack
|
||||||
func push8(value uint8) {
|
func push8(value uint8) {
|
||||||
mmu.WritePageTable[mmu.StackPage][State.SP] = value
|
mmu.WritePageTable[mmu.StackPage][State.SP] = value
|
||||||
State.SP -= 1
|
State.SP--
|
||||||
State.SP &= 0xff
|
State.SP &= 0xff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ func push16(value uint16) {
|
||||||
|
|
||||||
// pop8 pulls an 8 bit value from the stack
|
// pop8 pulls an 8 bit value from the stack
|
||||||
func pop8() uint8 {
|
func pop8() uint8 {
|
||||||
State.SP += 1
|
State.SP++
|
||||||
State.SP &= 0xff
|
State.SP &= 0xff
|
||||||
return mmu.ReadPageTable[mmu.StackPage][State.SP]
|
return mmu.ReadPageTable[mmu.StackPage][State.SP]
|
||||||
}
|
}
|
||||||
|
@ -153,7 +154,7 @@ func branch(doBranch bool) {
|
||||||
// The number of cycles depends on if a page boundary was crossed
|
// The number of cycles depends on if a page boundary was crossed
|
||||||
samePage := (State.PC & 0xff00) == (relativeAddress & 0xff00)
|
samePage := (State.PC & 0xff00) == (relativeAddress & 0xff00)
|
||||||
if samePage {
|
if samePage {
|
||||||
system.FrameCycles += 1
|
system.FrameCycles++
|
||||||
} else {
|
} else {
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
}
|
}
|
||||||
|
@ -479,7 +480,7 @@ func postProcessShift(addressMode byte, address uint16, value uint8) {
|
||||||
switch addressMode {
|
switch addressMode {
|
||||||
case amAccumulator:
|
case amAccumulator:
|
||||||
State.A = value
|
State.A = value
|
||||||
State.PC += 1
|
State.PC++
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
case amZeroPage:
|
case amZeroPage:
|
||||||
mmu.WriteMemory(address, value)
|
mmu.WriteMemory(address, value)
|
||||||
|
@ -523,27 +524,27 @@ func postProcessIncDec(addressMode byte) {
|
||||||
|
|
||||||
func brk() {
|
func brk() {
|
||||||
push16(State.PC + 2)
|
push16(State.PC + 2)
|
||||||
State.P |= CpuFlagB
|
State.P |= cpuFlagB
|
||||||
push8(State.P)
|
push8(State.P)
|
||||||
State.P |= CpuFlagI
|
State.P |= cpuFlagI
|
||||||
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
|
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
|
||||||
system.FrameCycles += 7
|
system.FrameCycles += 7
|
||||||
}
|
}
|
||||||
|
|
||||||
func irq() {
|
func irq() {
|
||||||
push16(State.PC)
|
push16(State.PC)
|
||||||
State.P &= ^CpuFlagB
|
State.P &= ^cpuFlagB
|
||||||
push8(State.P)
|
push8(State.P)
|
||||||
State.P |= CpuFlagI
|
State.P |= cpuFlagI
|
||||||
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
|
State.PC = uint16(mmu.ReadMemory(0xffff))<<8 + uint16(mmu.ReadMemory(0xfffe))
|
||||||
system.FrameCycles += 7
|
system.FrameCycles += 7
|
||||||
}
|
}
|
||||||
|
|
||||||
func nmi() {
|
func nmi() {
|
||||||
push16(State.PC)
|
push16(State.PC)
|
||||||
State.P &= ^CpuFlagB
|
State.P &= ^cpuFlagB
|
||||||
push8(State.P)
|
push8(State.P)
|
||||||
State.P |= CpuFlagI
|
State.P |= cpuFlagI
|
||||||
State.PC = uint16(mmu.ReadMemory(0xfffb))<<8 + uint16(mmu.ReadMemory(0xfffa))
|
State.PC = uint16(mmu.ReadMemory(0xfffb))<<8 + uint16(mmu.ReadMemory(0xfffa))
|
||||||
system.FrameCycles += 7
|
system.FrameCycles += 7
|
||||||
}
|
}
|
||||||
|
@ -572,7 +573,7 @@ func Run(showInstructions bool, breakAddress *uint16, exitAtBreak bool, disableF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle an IRQ f there is one pending and interrupts are enabled
|
// Handle an IRQ f there is one pending and interrupts are enabled
|
||||||
if system.PendingInterrupt && ((State.P & CpuFlagI) == 0) {
|
if system.PendingInterrupt && ((State.P & cpuFlagI) == 0) {
|
||||||
irq()
|
irq()
|
||||||
system.PendingInterrupt = false
|
system.PendingInterrupt = false
|
||||||
continue
|
continue
|
||||||
|
@ -761,23 +762,23 @@ func Run(showInstructions bool, breakAddress *uint16, exitAtBreak bool, disableF
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
case 0x58: // CLI
|
case 0x58: // CLI
|
||||||
State.P &= ^CpuFlagI
|
State.P &= ^cpuFlagI
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
case 0x78: // SEI
|
case 0x78: // SEI
|
||||||
State.P |= CpuFlagI
|
State.P |= cpuFlagI
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
case 0xb8: // CLV
|
case 0xb8: // CLV
|
||||||
State.P &= ^CpuFlagV
|
State.P &= ^cpuFlagV
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
case 0xd8: // CLD
|
case 0xd8: // CLD
|
||||||
State.P &= ^CpuFlagD
|
State.P &= ^cpuFlagD
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
case 0xf8: //SED
|
case 0xf8: //SED
|
||||||
State.P |= CpuFlagD
|
State.P |= cpuFlagD
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 2
|
system.FrameCycles += 2
|
||||||
|
|
||||||
|
@ -795,12 +796,12 @@ func Run(showInstructions bool, breakAddress *uint16, exitAtBreak bool, disableF
|
||||||
case 0x08: // PHP
|
case 0x08: // PHP
|
||||||
// From http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit#the_B_flag_and_the_various_mechanisms
|
// From http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit#the_B_flag_and_the_various_mechanisms
|
||||||
// software instructions BRK & PHP will push the B flag as being 1.
|
// software instructions BRK & PHP will push the B flag as being 1.
|
||||||
push8(State.P | CpuFlagB)
|
push8(State.P | cpuFlagB)
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 3
|
system.FrameCycles += 3
|
||||||
case 0x28: // PLP
|
case 0x28: // PLP
|
||||||
// CpuFlagR is always supposed to be 1
|
// cpuFlagR is always supposed to be 1
|
||||||
State.P = pop8() | CpuFlagR
|
State.P = pop8() | cpuFlagR
|
||||||
State.PC++
|
State.PC++
|
||||||
system.FrameCycles += 4
|
system.FrameCycles += 4
|
||||||
case 0xea:
|
case 0xea:
|
||||||
|
@ -810,7 +811,7 @@ func Run(showInstructions bool, breakAddress *uint16, exitAtBreak bool, disableF
|
||||||
case 0x00: // BRK
|
case 0x00: // BRK
|
||||||
brk()
|
brk()
|
||||||
case 0x40: // RTI
|
case 0x40: // RTI
|
||||||
State.P = pop8() | CpuFlagR
|
State.P = pop8() | cpuFlagR
|
||||||
value := pop16()
|
value := pop16()
|
||||||
State.PC = value
|
State.PC = value
|
||||||
system.FrameCycles += 6
|
system.FrameCycles += 6
|
||||||
|
@ -845,7 +846,7 @@ func Run(showInstructions bool, breakAddress *uint16, exitAtBreak bool, disableF
|
||||||
address, value := preProcessShift(addressMode)
|
address, value := preProcessShift(addressMode)
|
||||||
value16 := uint16(value)
|
value16 := uint16(value)
|
||||||
value16 <<= 1
|
value16 <<= 1
|
||||||
if (State.P & CpuFlagC) != 0 {
|
if (State.P & cpuFlagC) != 0 {
|
||||||
value16 |= 0x01
|
value16 |= 0x01
|
||||||
}
|
}
|
||||||
setC((value16 & 0x100) != 0)
|
setC((value16 & 0x100) != 0)
|
||||||
|
@ -856,7 +857,7 @@ func Run(showInstructions bool, breakAddress *uint16, exitAtBreak bool, disableF
|
||||||
case 0x6a, 0x66, 0x76, 0x6e, 0x7e: // ROR
|
case 0x6a, 0x66, 0x76, 0x6e, 0x7e: // ROR
|
||||||
address, value := preProcessShift(addressMode)
|
address, value := preProcessShift(addressMode)
|
||||||
value16 := uint16(value)
|
value16 := uint16(value)
|
||||||
if (State.P & CpuFlagC) != 0 {
|
if (State.P & cpuFlagC) != 0 {
|
||||||
value16 |= 0x100
|
value16 |= 0x100
|
||||||
}
|
}
|
||||||
setC((value16 & 0x01) != 0)
|
setC((value16 & 0x01) != 0)
|
||||||
|
|
16
cpu/debug.go
16
cpu/debug.go
|
@ -29,14 +29,14 @@ func printInstruction(instruction string, showRegisters bool) {
|
||||||
State.P,
|
State.P,
|
||||||
)
|
)
|
||||||
|
|
||||||
printFlag(State.P, CpuFlagN, "n")
|
printFlag(State.P, cpuFlagN, "n")
|
||||||
printFlag(State.P, CpuFlagV, "v")
|
printFlag(State.P, cpuFlagV, "v")
|
||||||
fmt.Print("-") // CpuFlagR flag that's always 1
|
fmt.Print("-") // cpuFlagR flag that's always 1
|
||||||
printFlag(State.P, CpuFlagB, "b")
|
printFlag(State.P, cpuFlagB, "b")
|
||||||
printFlag(State.P, CpuFlagD, "d")
|
printFlag(State.P, cpuFlagD, "d")
|
||||||
printFlag(State.P, CpuFlagI, "i")
|
printFlag(State.P, cpuFlagI, "i")
|
||||||
printFlag(State.P, CpuFlagZ, "z")
|
printFlag(State.P, cpuFlagZ, "z")
|
||||||
printFlag(State.P, CpuFlagC, "c")
|
printFlag(State.P, cpuFlagC, "c")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
|
|
|
@ -311,6 +311,7 @@ func initOpCodes() {
|
||||||
opCodes[0xFF] = opCode{mnemonic: "???", addressingMode: addressingModes[amExpansion]}
|
opCodes[0xFF] = opCode{mnemonic: "???", addressingMode: addressingModes[amExpansion]}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitInstructionDecoder initializes tables used for the instruction decoding
|
||||||
func InitInstructionDecoder() {
|
func InitInstructionDecoder() {
|
||||||
initAddressingModes()
|
initAddressingModes()
|
||||||
initOpCodes()
|
initOpCodes()
|
||||||
|
|
|
@ -123,7 +123,7 @@ func InitDiskImage() {
|
||||||
resetsectorWriteState()
|
resetsectorWriteState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitDiskImage reads a disk image from file
|
// ReadDiskImage reads a disk image from file
|
||||||
func ReadDiskImage(path string) {
|
func ReadDiskImage(path string) {
|
||||||
imagePath = path
|
imagePath = path
|
||||||
|
|
||||||
|
@ -329,7 +329,7 @@ func decodeAddressField(data []uint8) addressField {
|
||||||
return af
|
return af
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a byte from the disk head and spin the disk along
|
// ReadTrackData reads a byte from the disk head and spins the disk along
|
||||||
func ReadTrackData() (result uint8) {
|
func ReadTrackData() (result uint8) {
|
||||||
result = trackData[system.DriveState.BytePosition]
|
result = trackData[system.DriveState.BytePosition]
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ func WriteTrackData(value uint8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sectorWriteState.RawData[sectorWriteState.RawDataPosition] = value
|
sectorWriteState.RawData[sectorWriteState.RawDataPosition] = value
|
||||||
sectorWriteState.RawDataPosition += 1
|
sectorWriteState.RawDataPosition++
|
||||||
|
|
||||||
// Check for address prologue
|
// Check for address prologue
|
||||||
if sectorWriteState.RawDataPosition > 2 && sectorWriteState.RawData[sectorWriteState.RawDataPosition-3] == 0xd5 &&
|
if sectorWriteState.RawDataPosition > 2 && sectorWriteState.RawData[sectorWriteState.RawDataPosition-3] == 0xd5 &&
|
||||||
|
@ -393,7 +393,7 @@ func WriteTrackData(value uint8) {
|
||||||
|
|
||||||
} else if sectorWriteState.State == receivingData {
|
} else if sectorWriteState.State == receivingData {
|
||||||
sectorWriteState.RawData[sectorWriteState.RawDataPosition] = value
|
sectorWriteState.RawData[sectorWriteState.RawDataPosition] = value
|
||||||
sectorWriteState.RawDataPosition += 1
|
sectorWriteState.RawDataPosition++
|
||||||
|
|
||||||
if sectorWriteState.RawDataPosition == 0x56+0x100 {
|
if sectorWriteState.RawDataPosition == 0x56+0x100 {
|
||||||
// We have the full sector data
|
// We have the full sector data
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ebitenAsciiMap map[ebiten.Key]uint8 // ebiten keys mapped to ASCII
|
var ebitenASCIIMap map[ebiten.Key]uint8 // ebiten keys mapped to ASCII
|
||||||
var shiftMap map[uint8]uint8 // ebiten keys mapped to ASCII when shift is pressed
|
var shiftMap map[uint8]uint8 // ebiten keys mapped to ASCII when shift is pressed
|
||||||
var controlMap map[uint8]uint8 // ebiten keys mapped to ASCII when control is pressed
|
var controlMap map[uint8]uint8 // ebiten keys mapped to ASCII when control is pressed
|
||||||
|
|
||||||
|
@ -19,68 +19,68 @@ func Init() {
|
||||||
strobe = 0
|
strobe = 0
|
||||||
capsLock = true
|
capsLock = true
|
||||||
|
|
||||||
ebitenAsciiMap = make(map[ebiten.Key]uint8)
|
ebitenASCIIMap = make(map[ebiten.Key]uint8)
|
||||||
shiftMap = make(map[uint8]uint8)
|
shiftMap = make(map[uint8]uint8)
|
||||||
controlMap = make(map[uint8]uint8)
|
controlMap = make(map[uint8]uint8)
|
||||||
previousKeysPressed = make(map[uint8]bool)
|
previousKeysPressed = make(map[uint8]bool)
|
||||||
|
|
||||||
ebitenAsciiMap[ebiten.KeyLeft] = 8
|
ebitenASCIIMap[ebiten.KeyLeft] = 8
|
||||||
ebitenAsciiMap[ebiten.KeyTab] = 9
|
ebitenASCIIMap[ebiten.KeyTab] = 9
|
||||||
ebitenAsciiMap[ebiten.KeyDown] = 10
|
ebitenASCIIMap[ebiten.KeyDown] = 10
|
||||||
ebitenAsciiMap[ebiten.KeyUp] = 11
|
ebitenASCIIMap[ebiten.KeyUp] = 11
|
||||||
ebitenAsciiMap[ebiten.KeyEnter] = 13
|
ebitenASCIIMap[ebiten.KeyEnter] = 13
|
||||||
ebitenAsciiMap[ebiten.KeyRight] = 21
|
ebitenASCIIMap[ebiten.KeyRight] = 21
|
||||||
ebitenAsciiMap[ebiten.KeyEscape] = 27
|
ebitenASCIIMap[ebiten.KeyEscape] = 27
|
||||||
ebitenAsciiMap[ebiten.KeyDelete] = 127
|
ebitenASCIIMap[ebiten.KeyDelete] = 127
|
||||||
|
|
||||||
ebitenAsciiMap[ebiten.Key0] = '0'
|
ebitenASCIIMap[ebiten.Key0] = '0'
|
||||||
ebitenAsciiMap[ebiten.Key1] = '1'
|
ebitenASCIIMap[ebiten.Key1] = '1'
|
||||||
ebitenAsciiMap[ebiten.Key2] = '2'
|
ebitenASCIIMap[ebiten.Key2] = '2'
|
||||||
ebitenAsciiMap[ebiten.Key3] = '3'
|
ebitenASCIIMap[ebiten.Key3] = '3'
|
||||||
ebitenAsciiMap[ebiten.Key4] = '4'
|
ebitenASCIIMap[ebiten.Key4] = '4'
|
||||||
ebitenAsciiMap[ebiten.Key5] = '5'
|
ebitenASCIIMap[ebiten.Key5] = '5'
|
||||||
ebitenAsciiMap[ebiten.Key6] = '6'
|
ebitenASCIIMap[ebiten.Key6] = '6'
|
||||||
ebitenAsciiMap[ebiten.Key7] = '7'
|
ebitenASCIIMap[ebiten.Key7] = '7'
|
||||||
ebitenAsciiMap[ebiten.Key8] = '8'
|
ebitenASCIIMap[ebiten.Key8] = '8'
|
||||||
ebitenAsciiMap[ebiten.Key9] = '9'
|
ebitenASCIIMap[ebiten.Key9] = '9'
|
||||||
ebitenAsciiMap[ebiten.KeyA] = 'a'
|
ebitenASCIIMap[ebiten.KeyA] = 'a'
|
||||||
ebitenAsciiMap[ebiten.KeyB] = 'b'
|
ebitenASCIIMap[ebiten.KeyB] = 'b'
|
||||||
ebitenAsciiMap[ebiten.KeyC] = 'c'
|
ebitenASCIIMap[ebiten.KeyC] = 'c'
|
||||||
ebitenAsciiMap[ebiten.KeyD] = 'd'
|
ebitenASCIIMap[ebiten.KeyD] = 'd'
|
||||||
ebitenAsciiMap[ebiten.KeyE] = 'e'
|
ebitenASCIIMap[ebiten.KeyE] = 'e'
|
||||||
ebitenAsciiMap[ebiten.KeyF] = 'f'
|
ebitenASCIIMap[ebiten.KeyF] = 'f'
|
||||||
ebitenAsciiMap[ebiten.KeyG] = 'g'
|
ebitenASCIIMap[ebiten.KeyG] = 'g'
|
||||||
ebitenAsciiMap[ebiten.KeyH] = 'h'
|
ebitenASCIIMap[ebiten.KeyH] = 'h'
|
||||||
ebitenAsciiMap[ebiten.KeyI] = 'i'
|
ebitenASCIIMap[ebiten.KeyI] = 'i'
|
||||||
ebitenAsciiMap[ebiten.KeyJ] = 'j'
|
ebitenASCIIMap[ebiten.KeyJ] = 'j'
|
||||||
ebitenAsciiMap[ebiten.KeyK] = 'k'
|
ebitenASCIIMap[ebiten.KeyK] = 'k'
|
||||||
ebitenAsciiMap[ebiten.KeyL] = 'l'
|
ebitenASCIIMap[ebiten.KeyL] = 'l'
|
||||||
ebitenAsciiMap[ebiten.KeyM] = 'm'
|
ebitenASCIIMap[ebiten.KeyM] = 'm'
|
||||||
ebitenAsciiMap[ebiten.KeyN] = 'n'
|
ebitenASCIIMap[ebiten.KeyN] = 'n'
|
||||||
ebitenAsciiMap[ebiten.KeyO] = 'o'
|
ebitenASCIIMap[ebiten.KeyO] = 'o'
|
||||||
ebitenAsciiMap[ebiten.KeyP] = 'p'
|
ebitenASCIIMap[ebiten.KeyP] = 'p'
|
||||||
ebitenAsciiMap[ebiten.KeyQ] = 'q'
|
ebitenASCIIMap[ebiten.KeyQ] = 'q'
|
||||||
ebitenAsciiMap[ebiten.KeyR] = 'r'
|
ebitenASCIIMap[ebiten.KeyR] = 'r'
|
||||||
ebitenAsciiMap[ebiten.KeyS] = 's'
|
ebitenASCIIMap[ebiten.KeyS] = 's'
|
||||||
ebitenAsciiMap[ebiten.KeyT] = 't'
|
ebitenASCIIMap[ebiten.KeyT] = 't'
|
||||||
ebitenAsciiMap[ebiten.KeyU] = 'u'
|
ebitenASCIIMap[ebiten.KeyU] = 'u'
|
||||||
ebitenAsciiMap[ebiten.KeyV] = 'v'
|
ebitenASCIIMap[ebiten.KeyV] = 'v'
|
||||||
ebitenAsciiMap[ebiten.KeyW] = 'w'
|
ebitenASCIIMap[ebiten.KeyW] = 'w'
|
||||||
ebitenAsciiMap[ebiten.KeyX] = 'x'
|
ebitenASCIIMap[ebiten.KeyX] = 'x'
|
||||||
ebitenAsciiMap[ebiten.KeyY] = 'y'
|
ebitenASCIIMap[ebiten.KeyY] = 'y'
|
||||||
ebitenAsciiMap[ebiten.KeyZ] = 'z'
|
ebitenASCIIMap[ebiten.KeyZ] = 'z'
|
||||||
ebitenAsciiMap[ebiten.KeyApostrophe] = '\''
|
ebitenASCIIMap[ebiten.KeyApostrophe] = '\''
|
||||||
ebitenAsciiMap[ebiten.KeyBackslash] = '\\'
|
ebitenASCIIMap[ebiten.KeyBackslash] = '\\'
|
||||||
ebitenAsciiMap[ebiten.KeyComma] = ','
|
ebitenASCIIMap[ebiten.KeyComma] = ','
|
||||||
ebitenAsciiMap[ebiten.KeyEqual] = '='
|
ebitenASCIIMap[ebiten.KeyEqual] = '='
|
||||||
ebitenAsciiMap[ebiten.KeyGraveAccent] = '`'
|
ebitenASCIIMap[ebiten.KeyGraveAccent] = '`'
|
||||||
ebitenAsciiMap[ebiten.KeyLeftBracket] = '['
|
ebitenASCIIMap[ebiten.KeyLeftBracket] = '['
|
||||||
ebitenAsciiMap[ebiten.KeyMinus] = '-'
|
ebitenASCIIMap[ebiten.KeyMinus] = '-'
|
||||||
ebitenAsciiMap[ebiten.KeyPeriod] = '.'
|
ebitenASCIIMap[ebiten.KeyPeriod] = '.'
|
||||||
ebitenAsciiMap[ebiten.KeyRightBracket] = ']'
|
ebitenASCIIMap[ebiten.KeyRightBracket] = ']'
|
||||||
ebitenAsciiMap[ebiten.KeySemicolon] = ';'
|
ebitenASCIIMap[ebiten.KeySemicolon] = ';'
|
||||||
ebitenAsciiMap[ebiten.KeySlash] = '/'
|
ebitenASCIIMap[ebiten.KeySlash] = '/'
|
||||||
ebitenAsciiMap[ebiten.KeySpace] = ' '
|
ebitenASCIIMap[ebiten.KeySpace] = ' '
|
||||||
|
|
||||||
shiftMap['1'] = '!'
|
shiftMap['1'] = '!'
|
||||||
shiftMap['2'] = '@'
|
shiftMap['2'] = '@'
|
||||||
|
@ -198,7 +198,7 @@ func Poll() {
|
||||||
newKeysPressed := make(map[uint8]bool)
|
newKeysPressed := make(map[uint8]bool)
|
||||||
|
|
||||||
// Query ebiten for all possible keys
|
// Query ebiten for all possible keys
|
||||||
for k, v := range ebitenAsciiMap {
|
for k, v := range ebitenASCIIMap {
|
||||||
if ebiten.IsKeyPressed(k) {
|
if ebiten.IsKeyPressed(k) {
|
||||||
allKeysPressed[v] = true
|
allKeysPressed[v] = true
|
||||||
|
|
||||||
|
|
269
mmu/io.go
269
mmu/io.go
|
@ -14,83 +14,83 @@ import (
|
||||||
// https://github.com/cmosher01/Apple-II-Platform/blob/master/asminclude/iorom.asm
|
// https://github.com/cmosher01/Apple-II-Platform/blob/master/asminclude/iorom.asm
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KEYBOARD = 0xC000 // keyboard data (latched) (RD-only)
|
mKEYBOARD = 0xC000 // keyboard data (latched) (RD-only)
|
||||||
CLR80COL = 0xC000 // use 80-column memory mapping (WR-only)
|
mCLR80COL = 0xC000 // use 80-column memory mapping (WR-only)
|
||||||
SET80COL = 0xC001
|
mSET80COL = 0xC001
|
||||||
CLRAUXRD = 0xC002 // read from auxilliary 48K
|
mCLRAUXRD = 0xC002 // read from auxilliary 48K
|
||||||
SETAUXRD = 0xC003
|
mSETAUXRD = 0xC003
|
||||||
CLRAUXWR = 0xC004 // write to auxilliary 48K
|
mCLRAUXWR = 0xC004 // write to auxilliary 48K
|
||||||
SETAUXWR = 0xC005
|
mSETAUXWR = 0xC005
|
||||||
CLRCXROM = 0xC006 // use external slot ROM
|
mCLRCXROM = 0xC006 // use external slot ROM
|
||||||
SETCXROM = 0xC007
|
mSETCXROM = 0xC007
|
||||||
CLRAUXZP = 0xC008 // use auxilliary ZP, stack, & LC
|
mCLRAUXZP = 0xC008 // use auxilliary ZP, stack, & LC
|
||||||
SETAUXZP = 0xC009
|
mSETAUXZP = 0xC009
|
||||||
CLRC3ROM = 0xC00A // use external slot C3 ROM
|
mCLRC3ROM = 0xC00A // use external slot C3 ROM
|
||||||
SETC3ROM = 0xC00B
|
mSETC3ROM = 0xC00B
|
||||||
CLR80VID = 0xC00C // use 80-column display mode
|
mCLR80VID = 0xC00C // use 80-column display mode
|
||||||
SET80VID = 0xC00D
|
mSET80VID = 0xC00D
|
||||||
CLRALTCH = 0xC00E // use alternate character set ROM
|
mCLRALTCH = 0xC00E // use alternate character set ROM
|
||||||
SETALTCH = 0xC00F
|
mSETALTCH = 0xC00F
|
||||||
STROBE = 0xC010 // strobe (unlatch) keyboard data
|
mSTROBE = 0xC010 // strobe (unlatch) keyboard data
|
||||||
|
|
||||||
RDLCBNK2 = 0xC011 // reading from LC bank $Dx 2
|
mRDLCBNK2 = 0xC011 // reading from LC bank $Dx 2
|
||||||
RDLCRAM = 0xC012 // reading from LC RAM
|
mRDLCRAM = 0xC012 // reading from LC RAM
|
||||||
RDRAMRD = 0xC013 // reading from auxilliary 48K
|
mRDRAMRD = 0xC013 // reading from auxilliary 48K
|
||||||
RDRAMWR = 0xC014 // writing to auxilliary 48K
|
mRDRAMWR = 0xC014 // writing to auxilliary 48K
|
||||||
RDCXROM = 0xC015 // using external slot ROM
|
mRDCXROM = 0xC015 // using external slot ROM
|
||||||
RDAUXZP = 0xC016 // using auxilliary ZP, stack, & LC
|
mRDAUXZP = 0xC016 // using auxilliary ZP, stack, & LC
|
||||||
RDC3ROM = 0xC017 // using external slot C3 ROM
|
mRDC3ROM = 0xC017 // using external slot C3 ROM
|
||||||
RD80COL = 0xC018 // using 80-column memory mapping
|
mRD80COL = 0xC018 // using 80-column memory mapping
|
||||||
RDVBLBAR = 0xC019 // not VBL (VBL signal low)
|
mRDVBLBAR = 0xC019 // not VBL (VBL signal low)
|
||||||
RDTEXT = 0xC01A // using text mode
|
mRDTEXT = 0xC01A // using text mode
|
||||||
RDMIXED = 0xC01B // using mixed mode
|
mRDMIXED = 0xC01B // using mixed mode
|
||||||
RDPAGE2 = 0xC01C // using text/graphics page2
|
mRDPAGE2 = 0xC01C // using text/graphics page2
|
||||||
RDHIRES = 0xC01D // using Hi-res graphics mode
|
mRDHIRES = 0xC01D // using Hi-res graphics mode
|
||||||
RDALTCH = 0xC01E // using alternate character set ROM
|
mRDALTCH = 0xC01E // using alternate character set ROM
|
||||||
RD80VID = 0xC01F // using 80-column display mode
|
mRD80VID = 0xC01F // using 80-column display mode
|
||||||
SPEAKER = 0xC030 // toggle speaker diaphragm
|
mSPEAKER = 0xC030 // toggle speaker diaphragm
|
||||||
|
|
||||||
CLRTEXT = 0xC050 // enable text-only mode
|
mCLRTEXT = 0xC050 // enable text-only mode
|
||||||
SETTEXT = 0xC051
|
mSETTEXT = 0xC051
|
||||||
CLRMIXED = 0xC052 // enable graphics/text mixed mode
|
mCLRMIXED = 0xC052 // enable graphics/text mixed mode
|
||||||
SETMIXED = 0xC053
|
mSETMIXED = 0xC053
|
||||||
TXTPAGE1 = 0xC054 // select page1/2 (or page1/1x)
|
mTXTPAGE1 = 0xC054 // select page1/2 (or page1/1x)
|
||||||
TXTPAGE2 = 0xC055
|
mTXTPAGE2 = 0xC055
|
||||||
CLRHIRES = 0xC056 // enable Hi-res graphics
|
mCLRHIRES = 0xC056 // enable Hi-res graphics
|
||||||
SETHIRES = 0xC057
|
mSETHIRES = 0xC057
|
||||||
|
|
||||||
SETAN0 = 0xC058 // 4-bit annunciator inputs
|
mSETAN0 = 0xC058 // 4-bit annunciator inputs
|
||||||
CLRAN0 = 0xC059
|
mCLRAN0 = 0xC059
|
||||||
SETAN1 = 0xC05A
|
mSETAN1 = 0xC05A
|
||||||
CLRAN1 = 0xC05B
|
mCLRAN1 = 0xC05B
|
||||||
SETAN2 = 0xC05C
|
mSETAN2 = 0xC05C
|
||||||
CLRAN2 = 0xC05D
|
mCLRAN2 = 0xC05D
|
||||||
SETAN3 = 0xC05E
|
mSETAN3 = 0xC05E
|
||||||
CLRAN3 = 0xC05F
|
mCLRAN3 = 0xC05F
|
||||||
|
|
||||||
OPNAPPLE = 0xC061 // open apple (command) key data
|
mOPNAPPLE = 0xC061 // open apple (command) key data
|
||||||
CLSAPPLE = 0xC062 // closed apple (option) key data
|
mCLSAPPLE = 0xC062 // closed apple (option) key data
|
||||||
STATEREG = 0xC068 // Has no effect on //e
|
mSTATEREG = 0xC068 // Has no effect on //e
|
||||||
|
|
||||||
PDLTRIG = 0xC070 // trigger paddles
|
mPDLTRIG = 0xC070 // trigger paddles
|
||||||
|
|
||||||
// Slot 6 Drive IO
|
// Slot 6 Drive IO
|
||||||
S6CLRDRVP0 = 0xC0E0 // stepper phase 0 (Q0)
|
mS6CLRDRVP0 = 0xC0E0 // stepper phase 0 (Q0)
|
||||||
S6SETDRVP0 = 0xC0E1 //
|
mS6SETDRVP0 = 0xC0E1 //
|
||||||
S6CLRDRVP1 = 0xC0E2 // stepper phase 1 (Q1)
|
mS6CLRDRVP1 = 0xC0E2 // stepper phase 1 (Q1)
|
||||||
S6SETDRVP1 = 0xC0E3 //
|
mS6SETDRVP1 = 0xC0E3 //
|
||||||
S6CLRDRVP2 = 0xC0E4 // stepper phase 2 (Q2)
|
mS6CLRDRVP2 = 0xC0E4 // stepper phase 2 (Q2)
|
||||||
S6SETDRVP2 = 0xC0E5 //
|
mS6SETDRVP2 = 0xC0E5 //
|
||||||
S6CLRDRVP3 = 0xC0E6 // stepper phase 3 (Q3)
|
mS6CLRDRVP3 = 0xC0E6 // stepper phase 3 (Q3)
|
||||||
S6SETDRVP3 = 0xC0E7 //
|
mS6SETDRVP3 = 0xC0E7 //
|
||||||
S6MOTOROFF = 0xC0E8 // drive motor (Q4)
|
mS6MOTOROFF = 0xC0E8 // drive motor (Q4)
|
||||||
S6MOTORON = 0xC0E9 //
|
mS6MOTORON = 0xC0E9 //
|
||||||
S6SELDRV1 = 0xC0EA // drive select (Q5)
|
mS6SELDRV1 = 0xC0EA // drive select (Q5)
|
||||||
S6SELDRV2 = 0xC0EB //
|
mS6SELDRV2 = 0xC0EB //
|
||||||
S6Q6L = 0xC0EC // read (Q6)
|
mS6Q6L = 0xC0EC // read (Q6)
|
||||||
S6Q6H = 0xC0ED // WP sense
|
mS6Q6H = 0xC0ED // WP sense
|
||||||
S6Q7L = 0xC0EE // WP sense/read (Q7)
|
mS6Q7L = 0xC0EE // WP sense/read (Q7)
|
||||||
S6Q7H = 0xC0EF // write
|
mS6Q7H = 0xC0EF // write
|
||||||
)
|
)
|
||||||
|
|
||||||
// VideoState has 3 booleans which determine the video configuration:
|
// VideoState has 3 booleans which determine the video configuration:
|
||||||
|
@ -106,6 +106,7 @@ var VideoState struct {
|
||||||
Mixed bool
|
Mixed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitIO resets all IO states
|
||||||
func InitIO() {
|
func InitIO() {
|
||||||
// Empty slots that aren't yet implemented
|
// Empty slots that aren't yet implemented
|
||||||
emptySlot(3)
|
emptySlot(3)
|
||||||
|
@ -138,81 +139,81 @@ func readWrite(address uint16, isRead bool) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch address {
|
switch address {
|
||||||
case CLRAUXRD:
|
case mCLRAUXRD:
|
||||||
SetFakeAuxMemoryRead(false)
|
SetFakeAuxMemoryRead(false)
|
||||||
return true
|
return true
|
||||||
case SETAUXRD:
|
case mSETAUXRD:
|
||||||
SetFakeAuxMemoryRead(true)
|
SetFakeAuxMemoryRead(true)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case CLRAUXWR:
|
case mCLRAUXWR:
|
||||||
SetFakeAuxMemoryWrite(false)
|
SetFakeAuxMemoryWrite(false)
|
||||||
return true
|
return true
|
||||||
case SETAUXWR:
|
case mSETAUXWR:
|
||||||
SetFakeAuxMemoryWrite(true)
|
SetFakeAuxMemoryWrite(true)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case CLRAUXZP:
|
case mCLRAUXZP:
|
||||||
SetFakeAltZP(false)
|
SetFakeAltZP(false)
|
||||||
return true
|
return true
|
||||||
case SETAUXZP:
|
case mSETAUXZP:
|
||||||
SetFakeAltZP(true)
|
SetFakeAltZP(true)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case CLR80VID:
|
case mCLR80VID:
|
||||||
SetCol80(false)
|
SetCol80(false)
|
||||||
return true
|
return true
|
||||||
case SET80VID:
|
case mSET80VID:
|
||||||
SetCol80(true)
|
SetCol80(true)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case TXTPAGE1:
|
case mTXTPAGE1:
|
||||||
SetPage2(false)
|
SetPage2(false)
|
||||||
return true
|
return true
|
||||||
case TXTPAGE2:
|
case mTXTPAGE2:
|
||||||
SetPage2(true)
|
SetPage2(true)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case CLRTEXT:
|
case mCLRTEXT:
|
||||||
VideoState.TextMode = false
|
VideoState.TextMode = false
|
||||||
return true
|
return true
|
||||||
case SETTEXT:
|
case mSETTEXT:
|
||||||
VideoState.TextMode = true
|
VideoState.TextMode = true
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case CLRMIXED:
|
case mCLRMIXED:
|
||||||
VideoState.Mixed = false
|
VideoState.Mixed = false
|
||||||
return true
|
return true
|
||||||
case SETMIXED:
|
case mSETMIXED:
|
||||||
VideoState.Mixed = true
|
VideoState.Mixed = true
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case CLRHIRES:
|
case mCLRHIRES:
|
||||||
VideoState.HiresMode = false
|
VideoState.HiresMode = false
|
||||||
return true
|
return true
|
||||||
case SETHIRES:
|
case mSETHIRES:
|
||||||
VideoState.HiresMode = true
|
VideoState.HiresMode = true
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case CLR80COL:
|
case mCLR80COL:
|
||||||
if !isRead {
|
if !isRead {
|
||||||
SetStore80(false)
|
SetStore80(false)
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
case SET80COL:
|
return false
|
||||||
|
|
||||||
|
case mSET80COL:
|
||||||
SetStore80(true)
|
SetStore80(true)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case STATEREG:
|
case mSTATEREG:
|
||||||
// Ignore not implemented memory management reg
|
// Ignore not implemented memory management reg
|
||||||
return true
|
return true
|
||||||
|
|
||||||
// Drive stepper motor phase change
|
// Drive stepper motor phase change
|
||||||
case S6CLRDRVP0, S6SETDRVP0, S6CLRDRVP1, S6SETDRVP1, S6CLRDRVP2, S6SETDRVP2, S6CLRDRVP3, S6SETDRVP3:
|
case mS6CLRDRVP0, mS6SETDRVP0, mS6CLRDRVP1, mS6SETDRVP1, mS6CLRDRVP2, mS6SETDRVP2, mS6CLRDRVP3, mS6SETDRVP3:
|
||||||
magnet := (address - S6CLRDRVP0) / 2
|
magnet := (address - mS6CLRDRVP0) / 2
|
||||||
on := ((address - S6CLRDRVP0) % 2) == 1
|
on := ((address - mS6CLRDRVP0) % 2) == 1
|
||||||
if !on {
|
if !on {
|
||||||
// Turn off the magnet in Phases
|
// Turn off the magnet in Phases
|
||||||
system.DriveState.Phases &= ^(1 << magnet)
|
system.DriveState.Phases &= ^(1 << magnet)
|
||||||
|
@ -225,10 +226,10 @@ func readWrite(address uint16, isRead bool) bool {
|
||||||
// Move head if a neighboring magnet is on and all others are off
|
// Move head if a neighboring magnet is on and all others are off
|
||||||
direction := int8(0)
|
direction := int8(0)
|
||||||
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+1)&3))) != 0 {
|
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+1)&3))) != 0 {
|
||||||
direction += 1
|
direction++
|
||||||
}
|
}
|
||||||
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+3)&3))) != 0 {
|
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+3)&3))) != 0 {
|
||||||
direction -= 1
|
direction--
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the head
|
// Move the head
|
||||||
|
@ -251,37 +252,37 @@ func readWrite(address uint16, isRead bool) bool {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case S6MOTOROFF:
|
case mS6MOTOROFF:
|
||||||
system.DriveState.Spinning = false
|
system.DriveState.Spinning = false
|
||||||
return true
|
return true
|
||||||
case S6MOTORON:
|
case mS6MOTORON:
|
||||||
system.DriveState.Spinning = true
|
system.DriveState.Spinning = true
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case S6SELDRV1:
|
case mS6SELDRV1:
|
||||||
system.DriveState.Drive = 1
|
system.DriveState.Drive = 1
|
||||||
return true
|
return true
|
||||||
case S6SELDRV2:
|
case mS6SELDRV2:
|
||||||
system.DriveState.Drive = 2
|
system.DriveState.Drive = 2
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case S6Q6L:
|
case mS6Q6L:
|
||||||
if !isRead {
|
if !isRead {
|
||||||
system.DriveState.Q6 = false
|
system.DriveState.Q6 = false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
case S6Q6H:
|
case mS6Q6H:
|
||||||
if isRead {
|
if isRead {
|
||||||
system.DriveState.Q6 = true
|
system.DriveState.Q6 = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case S6Q7L:
|
case mS6Q7L:
|
||||||
system.DriveState.Q7 = false
|
system.DriveState.Q7 = false
|
||||||
return true
|
return true
|
||||||
case S6Q7H:
|
case mS6Q7H:
|
||||||
system.DriveState.Q7 = true
|
system.DriveState.Q7 = true
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
@ -299,64 +300,60 @@ func ReadIO(address uint16) uint8 {
|
||||||
|
|
||||||
switch address {
|
switch address {
|
||||||
|
|
||||||
case KEYBOARD, STROBE:
|
case mKEYBOARD, mSTROBE:
|
||||||
keyBoardData, strobe := keyboard.Read()
|
keyBoardData, strobe := keyboard.Read()
|
||||||
if address == KEYBOARD {
|
if address == mKEYBOARD {
|
||||||
return keyBoardData
|
return keyBoardData
|
||||||
} else {
|
|
||||||
keyboard.ResetStrobe()
|
|
||||||
return strobe
|
|
||||||
}
|
}
|
||||||
|
keyboard.ResetStrobe()
|
||||||
|
return strobe
|
||||||
|
|
||||||
case RDRAMRD, RDRAMWR, RDAUXZP:
|
case mRDRAMRD, mRDRAMWR, mRDAUXZP:
|
||||||
panic("Read/write aux memory not implemented")
|
panic("Read/write aux memory not implemented")
|
||||||
return 0x0d
|
return 0x0d
|
||||||
|
|
||||||
case RDCXROM:
|
case mRDCXROM:
|
||||||
if UsingExternalSlotRom {
|
if UsingExternalSlotRom {
|
||||||
return 0x8d
|
return 0x8d
|
||||||
} else {
|
|
||||||
return 0x0d
|
|
||||||
}
|
}
|
||||||
|
return 0x0d
|
||||||
|
|
||||||
case RD80VID:
|
case mRD80VID:
|
||||||
// using 80-column display mode not implemented
|
// using 80-column display mode not implemented
|
||||||
return 0x0d
|
return 0x0d
|
||||||
|
|
||||||
case RDPAGE2:
|
case mRDPAGE2:
|
||||||
if Page2 {
|
if Page2 {
|
||||||
return 0x8d
|
return 0x8d
|
||||||
} else {
|
|
||||||
return 0x0d
|
|
||||||
}
|
}
|
||||||
|
return 0x0d
|
||||||
|
|
||||||
// 4-bit annunciator inputs
|
// 4-bit annunciator inputs
|
||||||
case SETAN0, CLRAN0, SETAN1, CLRAN1, SETAN2, CLRAN2, SETAN3, CLRAN3:
|
case mSETAN0, mCLRAN0, mSETAN1, mCLRAN1, mSETAN2, mCLRAN2, mSETAN3, mCLRAN3:
|
||||||
// Annunciators not implemented
|
// Annunciators not implemented
|
||||||
|
|
||||||
case OPNAPPLE:
|
case mOPNAPPLE:
|
||||||
// Open apple key not implemented
|
// Open apple key not implemented
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
case CLSAPPLE:
|
case mCLSAPPLE:
|
||||||
// Closed apple key not implemented
|
// Closed apple key not implemented
|
||||||
|
|
||||||
case RD80COL:
|
case mRD80COL:
|
||||||
if Store80 {
|
if Store80 {
|
||||||
return 0x8d
|
return 0x8d
|
||||||
} else {
|
|
||||||
return 0x0d
|
|
||||||
}
|
}
|
||||||
|
return 0x0d
|
||||||
|
|
||||||
case RDALTCH:
|
case mRDALTCH:
|
||||||
// RDALTCH not implemented, but it's also used, so don't fail on it.
|
// RDALTCH not implemented, but it's also used, so don't fail on it.
|
||||||
return 0x0d
|
return 0x0d
|
||||||
|
|
||||||
case SPEAKER:
|
case mSPEAKER:
|
||||||
audio.Click()
|
audio.Click()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
case S6Q6L:
|
case mS6Q6L:
|
||||||
// A read from disk
|
// A read from disk
|
||||||
return disk.ReadTrackData()
|
return disk.ReadTrackData()
|
||||||
|
|
||||||
|
@ -367,7 +364,7 @@ func ReadIO(address uint16) uint8 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadIO does a write in the $c000-$c0ff area
|
// WriteIO does a write in the $c000-$c0ff area
|
||||||
func WriteIO(address uint16, value uint8) {
|
func WriteIO(address uint16, value uint8) {
|
||||||
// Try the generic readWrite and return if it has handled the write
|
// Try the generic readWrite and return if it has handled the write
|
||||||
if readWrite(address, false) {
|
if readWrite(address, false) {
|
||||||
|
@ -376,29 +373,29 @@ func WriteIO(address uint16, value uint8) {
|
||||||
|
|
||||||
switch address {
|
switch address {
|
||||||
|
|
||||||
case STROBE:
|
case mSTROBE:
|
||||||
keyboard.ResetStrobe()
|
keyboard.ResetStrobe()
|
||||||
|
|
||||||
case CLRCXROM:
|
case mCLRCXROM:
|
||||||
MapFirstHalfOfIO()
|
MapFirstHalfOfIO()
|
||||||
case SETCXROM:
|
case mSETCXROM:
|
||||||
MapSecondHalfOfIO()
|
MapSecondHalfOfIO()
|
||||||
|
|
||||||
case CLRALTCH:
|
case mCLRALTCH:
|
||||||
return
|
return
|
||||||
case SETALTCH:
|
case mSETALTCH:
|
||||||
panic("SETALTCH not implemented")
|
panic("SETALTCH not implemented")
|
||||||
|
|
||||||
case CLR80COL:
|
case mCLR80COL:
|
||||||
// CLR80COL not implemented
|
// CLR80COL not implemented
|
||||||
return
|
return
|
||||||
|
|
||||||
case CLRC3ROM:
|
case mCLRC3ROM:
|
||||||
// CLRC3ROM not implemented
|
// CLRC3ROM not implemented
|
||||||
case SETC3ROM:
|
case mSETC3ROM:
|
||||||
// SETC3ROM not implemented
|
// SETC3ROM not implemented
|
||||||
|
|
||||||
case S6Q6H:
|
case mS6Q6H:
|
||||||
// A write to disk
|
// A write to disk
|
||||||
disk.WriteTrackData(value)
|
disk.WriteTrackData(value)
|
||||||
|
|
||||||
|
|
67
mmu/mmu.go
67
mmu/mmu.go
|
@ -7,8 +7,11 @@ import (
|
||||||
"github.com/freewilll/apple2/system"
|
"github.com/freewilll/apple2/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const RomPath = "apple2e.rom" // So far only one ROM is supported and it's loaded at startup
|
// RomPath is a hardcoded path to an Apple //e ROM file that's loaded at startup
|
||||||
const StackPage = 1 // The 6502 stack is at 0x100
|
const RomPath = "apple2e.rom"
|
||||||
|
|
||||||
|
// StackPage is the location of the 6504 stack
|
||||||
|
const StackPage = 1
|
||||||
|
|
||||||
// PhysicalMemory contains all the unmapped memory, ROM and RAM
|
// PhysicalMemory contains all the unmapped memory, ROM and RAM
|
||||||
var PhysicalMemory struct {
|
var PhysicalMemory struct {
|
||||||
|
@ -18,8 +21,10 @@ var PhysicalMemory struct {
|
||||||
RomC2 [0x1000]uint8 // Second half of IO ROM
|
RomC2 [0x1000]uint8 // Second half of IO ROM
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page tables for read & write
|
// ReadPageTable is the page table for reads
|
||||||
var ReadPageTable [0x100][]uint8
|
var ReadPageTable [0x100][]uint8
|
||||||
|
|
||||||
|
// WritePageTable is the page table for writes
|
||||||
var WritePageTable [0x100][]uint8
|
var WritePageTable [0x100][]uint8
|
||||||
|
|
||||||
// Memory mapping states
|
// Memory mapping states
|
||||||
|
@ -27,7 +32,7 @@ var (
|
||||||
D000Bank int // one maps to $c000, two maps to $d000
|
D000Bank int // one maps to $c000, two maps to $d000
|
||||||
UsingExternalSlotRom bool // Which IO ROM is being used
|
UsingExternalSlotRom bool // Which IO ROM is being used
|
||||||
UpperReadMappedToROM bool // Do reads go to the RAM or ROM
|
UpperReadMappedToROM bool // Do reads go to the RAM or ROM
|
||||||
UpperRamReadOnly bool // Is the upper RAM read only
|
UpperRAMReadOnly bool // Is the upper RAM read only
|
||||||
FakeAuxMemoryRead bool // Aux memory isn't implemented
|
FakeAuxMemoryRead bool // Aux memory isn't implemented
|
||||||
FakeAuxMemoryWrite bool // Aux memory isn't implemented
|
FakeAuxMemoryWrite bool // Aux memory isn't implemented
|
||||||
FakeAltZP bool // Aux memory isn't implemented
|
FakeAltZP bool // Aux memory isn't implemented
|
||||||
|
@ -37,7 +42,7 @@ var (
|
||||||
Page2 bool // Main memory Page2 is selected
|
Page2 bool // Main memory Page2 is selected
|
||||||
)
|
)
|
||||||
|
|
||||||
// Make page tables for current RAM, ROM and IO configuration
|
// ApplyMemoryConfiguration creates the page tables for current RAM, ROM and IO configuration
|
||||||
func ApplyMemoryConfiguration() {
|
func ApplyMemoryConfiguration() {
|
||||||
// Map main RAM for read/write
|
// Map main RAM for read/write
|
||||||
for i := 0x0; i < 0xc0; i++ {
|
for i := 0x0; i < 0xc0; i++ {
|
||||||
|
@ -65,7 +70,7 @@ func ApplyMemoryConfiguration() {
|
||||||
ReadPageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
ReadPageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
||||||
}
|
}
|
||||||
|
|
||||||
if UpperRamReadOnly {
|
if UpperRAMReadOnly {
|
||||||
WritePageTable[i] = nil
|
WritePageTable[i] = nil
|
||||||
} else {
|
} else {
|
||||||
WritePageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
WritePageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
||||||
|
@ -78,7 +83,7 @@ func ApplyMemoryConfiguration() {
|
||||||
if !UpperReadMappedToROM {
|
if !UpperReadMappedToROM {
|
||||||
ReadPageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
ReadPageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
||||||
}
|
}
|
||||||
if UpperRamReadOnly {
|
if UpperRAMReadOnly {
|
||||||
WritePageTable[i] = nil
|
WritePageTable[i] = nil
|
||||||
} else {
|
} else {
|
||||||
WritePageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
WritePageTable[i] = PhysicalMemory.MainMemory[base : base+0x100]
|
||||||
|
@ -93,13 +98,13 @@ func ApplyMemoryConfiguration() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map 0xc100-0xcfff for reading from RomC1
|
// MapFirstHalfOfIO maps 0xc100-0xcfff for reading from RomC1
|
||||||
func MapFirstHalfOfIO() {
|
func MapFirstHalfOfIO() {
|
||||||
UsingExternalSlotRom = false
|
UsingExternalSlotRom = false
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map 0xc100-0xcfff for reading from RomC2
|
// MapSecondHalfOfIO map 0xc100-0xcfff for reading from RomC2
|
||||||
func MapSecondHalfOfIO() {
|
func MapSecondHalfOfIO() {
|
||||||
UsingExternalSlotRom = true
|
UsingExternalSlotRom = true
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
|
@ -131,61 +136,66 @@ func loadApple2eROM() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitApple2eROM loads the ROM and inits the ROM page tables
|
||||||
func InitApple2eROM() {
|
func InitApple2eROM() {
|
||||||
loadApple2eROM()
|
loadApple2eROM()
|
||||||
MapFirstHalfOfIO() // Map 0xc100-0xcfff for reading
|
MapFirstHalfOfIO() // Map 0xc100-0xcfff for reading
|
||||||
InitROM() // Map 0xd000-0xffff for reading
|
InitROM() // Map 0xd000-0xffff for reading
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set upper memory area for reading from ROM
|
// InitROM sets the upper memory area for reading from ROM
|
||||||
func InitROM() {
|
func InitROM() {
|
||||||
UpperReadMappedToROM = true
|
UpperReadMappedToROM = true
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUpperReadMappedToROM sets the upper area so that reads are done from the ROM if true or RAM if false
|
||||||
func SetUpperReadMappedToROM(value bool) {
|
func SetUpperReadMappedToROM(value bool) {
|
||||||
UpperReadMappedToROM = value
|
UpperReadMappedToROM = value
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetUpperRamReadOnly(value bool) {
|
// SetUpperRAMReadOnly sets the upper RAM area to read only
|
||||||
UpperRamReadOnly = value
|
func SetUpperRAMReadOnly(value bool) {
|
||||||
|
UpperRAMReadOnly = value
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set d000 bank to map to $c000 or $d000 in the physical memory
|
// SetD000Bank sets the $d000 bank to map to $c000 or $d000 in the physical memory
|
||||||
func SetD000Bank(value int) {
|
func SetD000Bank(value int) {
|
||||||
D000Bank = value
|
D000Bank = value
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aux memory hasn't been implemented. If aux memory is selected, and a read
|
// SetFakeAuxMemoryRead sets an internal state to fake aux memory reads. Aux memory hasn't been implemented. If aux memory is selected, and a read
|
||||||
// is attempted, then nonsense must be returned.
|
// is attempted, then nonsense must be returned.
|
||||||
func SetFakeAuxMemoryRead(value bool) {
|
func SetFakeAuxMemoryRead(value bool) {
|
||||||
FakeAuxMemoryRead = value
|
FakeAuxMemoryRead = value
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aux memory hasn't been implemented. If aux memory is selected, and a write
|
// SetFakeAuxMemoryWrite sets an internal state to fake aux memory writes. Aux memory hasn't been implemented. If aux memory is selected, and a write
|
||||||
// is attempted, then it must be ignored.
|
// is attempted, then it must be ignored.
|
||||||
func SetFakeAuxMemoryWrite(value bool) {
|
func SetFakeAuxMemoryWrite(value bool) {
|
||||||
FakeAuxMemoryWrite = value
|
FakeAuxMemoryWrite = value
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alternate zero page isn't implemented
|
// SetFakeAltZP sets an internal state to fake a missing alternate zero page. Alternate zero page isn't implemented
|
||||||
func SetFakeAltZP(value bool) {
|
func SetFakeAltZP(value bool) {
|
||||||
FakeAltZP = value
|
FakeAltZP = value
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 80 column card isn't implemented
|
// SetCol80 sets an internal state to fake a missing 80 column card
|
||||||
func SetCol80(value bool) {
|
func SetCol80(value bool) {
|
||||||
Col80 = value
|
Col80 = value
|
||||||
// No changes are needed when this is toggled
|
// No changes are needed when this is toggled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page switching is only implemented for the main memory
|
// SetPage2 sets page1/page2 in text, lores or hires. This only works if the
|
||||||
|
// 80 column card is disabled since the 80 columns card has not yet nbeen
|
||||||
|
// implemented.
|
||||||
func SetPage2(value bool) {
|
func SetPage2(value bool) {
|
||||||
// If the 80 column card is enabled, then this toggles aux memory
|
// If the 80 column card is enabled, then this toggles aux memory
|
||||||
// Otherwise, page1/page2 is toggled in the main memory
|
// Otherwise, page1/page2 is toggled in the main memory
|
||||||
|
@ -197,7 +207,7 @@ func SetPage2(value bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 80 column card isn't implemented
|
// SetStore80 sets an internal state to fake a missing 80 column card
|
||||||
func SetStore80(value bool) {
|
func SetStore80(value bool) {
|
||||||
Store80 = value
|
Store80 = value
|
||||||
FakePage2 = value
|
FakePage2 = value
|
||||||
|
@ -206,7 +216,7 @@ func SetStore80(value bool) {
|
||||||
|
|
||||||
// InitRAM sets all default RAM memory settings and resets the page tables
|
// InitRAM sets all default RAM memory settings and resets the page tables
|
||||||
func InitRAM() {
|
func InitRAM() {
|
||||||
UpperRamReadOnly = false
|
UpperRAMReadOnly = false
|
||||||
D000Bank = 2
|
D000Bank = 2
|
||||||
FakeAuxMemoryRead = false // Aux memory isn't implemented
|
FakeAuxMemoryRead = false // Aux memory isn't implemented
|
||||||
FakeAuxMemoryWrite = false // Aux memory isn't implemented
|
FakeAuxMemoryWrite = false // Aux memory isn't implemented
|
||||||
|
@ -217,22 +227,23 @@ func InitRAM() {
|
||||||
ApplyMemoryConfiguration()
|
ApplyMemoryConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WipeRAM wipes all the physical RAM
|
||||||
func WipeRAM() {
|
func WipeRAM() {
|
||||||
for i := 0; i < 0x10000; i++ {
|
for i := 0; i < 0x10000; i++ {
|
||||||
PhysicalMemory.MainMemory[i] = 0
|
PhysicalMemory.MainMemory[i] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMemoryMode is used to set UpperRamReadOnly, UpperReadMappedToROM and D000Bank number
|
// SetMemoryMode is used to set UpperRAMReadOnly, UpperReadMappedToROM and D000Bank number
|
||||||
func SetMemoryMode(mode uint8) {
|
func SetMemoryMode(mode uint8) {
|
||||||
// mode corresponds to a read/write to $c080 with
|
// mode corresponds to a read/write to $c080 with
|
||||||
// $c080 mode=$00
|
// $c080 mode=$00
|
||||||
// $c08f mode=$0f
|
// $c08f mode=$0f
|
||||||
|
|
||||||
if (mode & 1) == 0 {
|
if (mode & 1) == 0 {
|
||||||
UpperRamReadOnly = true
|
UpperRAMReadOnly = true
|
||||||
} else {
|
} else {
|
||||||
UpperRamReadOnly = false
|
UpperRAMReadOnly = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((mode & 2) >> 1) ^ (mode & 1)) == 0 {
|
if (((mode & 2) >> 1) ^ (mode & 1)) == 0 {
|
||||||
|
@ -266,18 +277,18 @@ func ReadMemory(address uint16) uint8 {
|
||||||
if address >= 0x200 {
|
if address >= 0x200 {
|
||||||
// Return nothingness
|
// Return nothingness
|
||||||
return uint8(0x00)
|
return uint8(0x00)
|
||||||
} else {
|
|
||||||
if FakeAltZP {
|
|
||||||
return uint8(0x00)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if FakeAltZP {
|
||||||
|
return uint8(0x00)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implicit else, we're reading the main non-IO RAM
|
// Implicit else, we're reading the main non-IO RAM
|
||||||
return ReadPageTable[address>>8][address&0xff]
|
return ReadPageTable[address>>8][address&0xff]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadMemory writes to the ROM or RAM page table
|
// WriteMemory writes to the ROM or RAM page table
|
||||||
func WriteMemory(address uint16, value uint8) {
|
func WriteMemory(address uint16, value uint8) {
|
||||||
if (address >= 0xc000) && (address < 0xc100) {
|
if (address >= 0xc000) && (address < 0xc100) {
|
||||||
WriteIO(address, value)
|
WriteIO(address, value)
|
||||||
|
|
|
@ -4,22 +4,46 @@ package system
|
||||||
// the packages.
|
// the packages.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CpuFrequency = 1023000 // 6402 CPU frequency in Hz
|
// CPUFrequency is the 6502 CPU frequency in Hz
|
||||||
AudioSampleRate = 44100 // Audio sample rate in Hz
|
CPUFrequency = 1023000
|
||||||
|
|
||||||
|
// AudioSampleRate is the audio sample rate in Hz
|
||||||
|
AudioSampleRate = 44100
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PendingInterrupt bool // Set when an interrupt has just happened
|
// PendingInterrupt is set when an interrupt has just happened
|
||||||
PendingNMI bool // Set when a non maskable interrupt has just happened
|
PendingInterrupt bool
|
||||||
RunningTests bool // For testing
|
|
||||||
RunningFunctionalTests bool // For testing
|
// PendingNMI is set when a non maskable interrupt has just happened
|
||||||
RunningInterruptTests bool // For testing
|
PendingNMI bool
|
||||||
Cycles uint64 // Total CPU cycles executed
|
|
||||||
FrameCycles uint64 // CPU cycles executed in the current frame
|
// RunningTests is set when tests are running
|
||||||
AudioChannel chan int16 // Audio channel
|
RunningTests bool
|
||||||
LastAudioValue int16 // + or - value of the current square wave
|
|
||||||
LastAudioCycles uint64 // Last CPU cycle when audio was sent to the channel
|
// RunningFunctionalTests is set when functional tests are running
|
||||||
AudioAttenuationCounter uint64 // Counter to keep track of when the audio should be zeroed after inactivity
|
RunningFunctionalTests bool
|
||||||
|
|
||||||
|
// RunningInterruptTests is set when interupt tests are running
|
||||||
|
RunningInterruptTests bool
|
||||||
|
|
||||||
|
// Cycles is the total CPU cycles executed
|
||||||
|
Cycles uint64
|
||||||
|
|
||||||
|
// FrameCycles is the CPU cycles executed in the current frame
|
||||||
|
FrameCycles uint64
|
||||||
|
|
||||||
|
// AudioChannel is the audio channel used to produce and consume audio samples between the cpu and audio packages
|
||||||
|
AudioChannel chan int16
|
||||||
|
|
||||||
|
// LastAudioValue is the + or - value of the current square wave
|
||||||
|
LastAudioValue int16
|
||||||
|
|
||||||
|
// LastAudioCycles is the ast CPU cycle when audio was sent to the channel
|
||||||
|
LastAudioCycles uint64
|
||||||
|
|
||||||
|
// AudioAttenuationCounter is a counter to keep track of when the audio should be zeroed after inactivity
|
||||||
|
AudioAttenuationCounter uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
// DriveState has the state of the disk drive
|
// DriveState has the state of the disk drive
|
||||||
|
@ -40,7 +64,7 @@ func Init() {
|
||||||
LastAudioValue = 0x2000
|
LastAudioValue = 0x2000
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle a write to a magic test address that triggers an interrupt and/or an NMI
|
// WriteInterruptTestOpenCollector handles a write to a magic test address that triggers an interrupt and/or an NMI
|
||||||
func WriteInterruptTestOpenCollector(address uint16, oldValue uint8, value uint8) {
|
func WriteInterruptTestOpenCollector(address uint16, oldValue uint8, value uint8) {
|
||||||
oldInterrupt := (oldValue & 0x1) == 0x1
|
oldInterrupt := (oldValue & 0x1) == 0x1
|
||||||
oldNMI := (oldValue & 0x2) == 0x2
|
oldNMI := (oldValue & 0x2) == 0x2
|
||||||
|
|
|
@ -60,7 +60,7 @@ func RunUntilBreakPoint(t *testing.T, breakAddress uint16, seconds int, showInst
|
||||||
system.LastAudioCycles = 0
|
system.LastAudioCycles = 0
|
||||||
exitAtBreak := false
|
exitAtBreak := false
|
||||||
disableFirmwareWait := false
|
disableFirmwareWait := false
|
||||||
cpu.Run(showInstructions, &breakAddress, exitAtBreak, disableFirmwareWait, uint64(system.CpuFrequency*seconds))
|
cpu.Run(showInstructions, &breakAddress, exitAtBreak, disableFirmwareWait, uint64(system.CPUFrequency*seconds))
|
||||||
if cpu.State.PC != breakAddress {
|
if cpu.State.PC != breakAddress {
|
||||||
t.Fatalf("Did not reach breakpoint at %04x. Got to %04x", breakAddress, cpu.State.PC)
|
t.Fatalf("Did not reach breakpoint at %04x. Got to %04x", breakAddress, cpu.State.PC)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ScreenSizeFactor = 1 // Factor by which the whole screen is resized
|
// ScreenSizeFactor is the fFactor by which the whole screen is resized
|
||||||
textVideoMemory = 0x400 // Base location of page 1 text video memory
|
ScreenSizeFactor = 1
|
||||||
flashFrames = 11 // Number of frames when FLASH mode is toggled
|
|
||||||
|
textVideoMemory = 0x400 // Base location of page 1 text video memory
|
||||||
|
flashFrames = 11 // Number of frames when FLASH mode is toggled
|
||||||
)
|
)
|
||||||
|
|
||||||
// drawTextLoresByte is a function definition used for mixed text/lores rendering
|
// drawTextLoresByte is a function definition used for mixed text/lores rendering
|
||||||
|
@ -25,7 +27,9 @@ var (
|
||||||
flashCounter int // Counter used for flashing characters on the text screen
|
flashCounter int // Counter used for flashing characters on the text screen
|
||||||
flashOn bool // Are we currently flashing?
|
flashOn bool // Are we currently flashing?
|
||||||
loresSquares [16]*ebiten.Image // Colored blocks for lores rendering
|
loresSquares [16]*ebiten.Image // Colored blocks for lores rendering
|
||||||
ShowFPS bool // Show FPS in corner?
|
|
||||||
|
// ShowFPS determines if the FPS is shown in the corner of the video
|
||||||
|
ShowFPS bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// initTextCharMap initializes the text character map
|
// initTextCharMap initializes the text character map
|
||||||
|
@ -130,7 +134,7 @@ func drawText(screen *ebiten.Image, x int, y int, value uint8) error {
|
||||||
// drawLores draws two colored lores squares at the equivalent text location x,y.
|
// drawLores draws two colored lores squares at the equivalent text location x,y.
|
||||||
func drawLores(screen *ebiten.Image, x int, y int, value uint8) error {
|
func drawLores(screen *ebiten.Image, x int, y int, value uint8) error {
|
||||||
// Convert the 8 bit value to two 4 bit values
|
// Convert the 8 bit value to two 4 bit values
|
||||||
var values [2]uint8 = [2]uint8{value & 0xf, value >> 4}
|
var values = [2]uint8{value & 0xf, value >> 4}
|
||||||
|
|
||||||
// Render top & bottom squares
|
// Render top & bottom squares
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
|
|
Loading…
Reference in New Issue