Made golint happy

This commit is contained in:
Will Angenent 2018-05-28 17:31:52 +01:00
parent 01adea08ba
commit ca7f2d2e04
16 changed files with 370 additions and 315 deletions

View File

@ -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 ./...)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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("")

View File

@ -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()

View File

@ -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

View File

@ -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
View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)
} }

View File

@ -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++ {