diff --git a/keyboard/keyboard.go b/keyboard/keyboard.go index 861b2c9..db6bf9a 100644 --- a/keyboard/keyboard.go +++ b/keyboard/keyboard.go @@ -4,14 +4,14 @@ import ( "github.com/hajimehoshi/ebiten" ) -var ebitenAsciiMap map[ebiten.Key]uint8 -var shiftMap map[uint8]uint8 -var controlMap map[uint8]uint8 +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 controlMap map[uint8]uint8 // ebiten keys mapped to ASCII when control is pressed -var keyBoardData uint8 -var strobe uint8 -var previousKeysPressed map[uint8]bool -var capsLock bool +var keyBoardData uint8 // Contents of the $c000 address +var strobe uint8 // Contents of the $c010 address +var previousKeysPressed map[uint8]bool // Keep track of what keys have been pressed in the previous round +var capsLock bool // Is capslock down // Init the keyboard state and ebiten translation tables func Init() { @@ -190,12 +190,14 @@ func Init() { controlMap['~'] = 0x60 } -// Poll queries ebiten's keyboard state and transforms that into apple //e -// values in $c000 and $c010 +// Poll queries ebiten's keyboard state and transforms that into ASCII +// values in $c000 and $c010. Keypresses from the previous round have to be +// taken into account in order to detect if a single new key has been pressed. func Poll() { allKeysPressed := make(map[uint8]bool) newKeysPressed := make(map[uint8]bool) + // Query ebiten for all possible keys for k, v := range ebitenAsciiMap { if ebiten.IsKeyPressed(k) { allKeysPressed[v] = true @@ -222,6 +224,8 @@ func Poll() { } // Implicit else, one new key has been pressed + + // Get the key keys := []uint8{} for k := range newKeysPressed { keys = append(keys, k) @@ -229,11 +233,13 @@ func Poll() { key := keys[0] if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && key == 'c' { + // Toggle capslock capsLock = !capsLock } else { + // Normal case. Transform the ebiten key into ASCII + shift := ebiten.IsKeyPressed(ebiten.KeyShift) shift = shift || (capsLock && key >= 'a' && key <= 'z') - if shift { shiftedKey, present := shiftMap[key] if present { @@ -247,6 +253,7 @@ func Poll() { key = controlKey } } + keyBoardData = key | 0x80 strobe = keyBoardData } @@ -254,10 +261,12 @@ func Poll() { return } +// Read returns the data and strobe values from set from the Poll() call func Read() (uint8, uint8) { return keyBoardData, strobe } +// ResetStrobe clears the high bit in keyboardData func ResetStrobe() { keyBoardData &= 0x7f } diff --git a/system/system.go b/system/system.go index 2b05235..d6d2119 100644 --- a/system/system.go +++ b/system/system.go @@ -1,32 +1,36 @@ package system +// The system package is a dumping ground for globals that are shared between +// the packages. + const ( - CpuFrequency = 1023000 - AudioSampleRate = 44100 + CpuFrequency = 1023000 // 6402 CPU frequency in Hz + AudioSampleRate = 44100 // Audio sample rate in Hz ) var ( - PendingInterrupt bool - PendingNMI bool - RunningTests bool - RunningFunctionalTests bool - RunningInterruptTests bool - Cycles uint64 - FrameCycles uint64 - AudioChannel chan int16 - LastAudioValue int16 - LastAudioCycles uint64 - AudioAttenuationCounter uint64 + PendingInterrupt bool // Set when an interrupt has just happened + PendingNMI bool // Set when a non maskable interrupt has just happened + RunningTests bool // For testing + RunningFunctionalTests bool // For testing + RunningInterruptTests bool // For testing + Cycles uint64 // Total CPU cycles executed + FrameCycles uint64 // CPU cycles executed in the current frame + AudioChannel chan int16 // Audio channel + LastAudioValue int16 // + or - value of the current square wave + LastAudioCycles uint64 // Last CPU cycle when audio was sent to the channel + AudioAttenuationCounter uint64 // Counter to keep track of when the audio should be zeroed after inactivity ) +// DriveState has the state of the disk drive var DriveState struct { - Drive uint8 - Spinning bool - Phase int8 - Phases uint8 - BytePosition int - Q6 bool - Q7 bool + Drive uint8 // What drive we're using. Currently only 1 is implemented + Spinning bool // Is the motor spinning + Phase int8 // Phase of the stepper motor + Phases uint8 // the 4 lowest bits represent the 4 stepper motor magnet on/off states. + BytePosition int // Index of the position on the current track + Q6 bool // Q6 soft switch + Q7 bool // Q7 soft switch } // Init initializes the system-wide state diff --git a/utils/utils.go b/utils/utils.go index 9f13423..2fa6dbb 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -12,6 +12,7 @@ import ( "github.com/freewilll/apple2/system" ) +// ReadMemoryFromGzipFile just reads and uncompresses a gzip file func ReadMemoryFromGzipFile(filename string) (data []byte, err error) { f, err := os.Open(filename) if err != nil { @@ -29,6 +30,7 @@ func ReadMemoryFromGzipFile(filename string) (data []byte, err error) { return } +// DecodeCmdLineAddress decodes a 4 byte string hex address func DecodeCmdLineAddress(s *string) (result *uint16) { if *s != "" { breakAddressValue, err := hex.DecodeString(*s) @@ -50,6 +52,9 @@ func DecodeCmdLineAddress(s *string) (result *uint16) { return result } +// RunUntilBreakPoint runs the CPU until it either hits a breakpoint or a time +// has expired. An assertion is done at the end to ensure the breakpoint has +// been reached. func RunUntilBreakPoint(t *testing.T, breakAddress uint16, seconds int, showInstructions bool, message string) { fmt.Printf("Running until %#04x: %s \n", breakAddress, message) system.FrameCycles = 0 @@ -62,6 +67,7 @@ func RunUntilBreakPoint(t *testing.T, breakAddress uint16, seconds int, showInst } } +// Disassemble disassembles and prints the code in memory between start and end func Disassemble(start uint16, end uint16) { oldPC := cpu.State.PC