diff --git a/apple2/ansiConsoleFrontend.go b/apple2/ansiConsoleFrontend.go index 1b0b780..9451b46 100644 --- a/apple2/ansiConsoleFrontend.go +++ b/apple2/ansiConsoleFrontend.go @@ -42,7 +42,7 @@ func (fe *ansiConsoleFrontend) subscribeToTextPages() { const refreshDelayMs = 100 -func (fe *ansiConsoleFrontend) getKey() (key uint8, ok bool) { +func (fe *ansiConsoleFrontend) GetKey() (key uint8, ok bool) { stdinReader := func(c chan uint8) { reader := bufio.NewReader(os.Stdin) for { diff --git a/apple2/apple2.go b/apple2/apple2.go index cee7087..cb7d1c4 100644 --- a/apple2/apple2.go +++ b/apple2/apple2.go @@ -44,10 +44,12 @@ func (a *Apple2) AddDisk2(diskRomFile string, diskImage string) { } // Run starts the Apple2 emulation -func (a *Apple2) Run(log bool) { +func (a *Apple2) Run(log bool, consoleKeyboard bool) { // Init frontend fe := newAnsiConsoleFrontend(a) - a.io.setKeyboardProvider(fe) + if consoleKeyboard { + a.io.setKeyboardProvider(fe) + } if !log { go fe.textModeGoRoutine() } @@ -59,6 +61,11 @@ func (a *Apple2) Run(log bool) { } } +// SetKeyboardProvider attaches an external keyboard provider +func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) { + a.io.setKeyboardProvider(kb) +} + // LoadRom loads a binary file to the top of the memory. const ( apple2RomSize = 12 * 1024 diff --git a/apple2/ioC0Page.go b/apple2/ioC0Page.go index 3434418..3e50df2 100644 --- a/apple2/ioC0Page.go +++ b/apple2/ioC0Page.go @@ -8,15 +8,15 @@ type ioC0Page struct { softSwitchesR [256]softSwitchR softSwitchesW [256]softSwitchW softSwitchesData [128]uint8 - keyboard keyboardProvider + keyboard KeyboardProvider apple2 *Apple2 } type softSwitchR func(io *ioC0Page) uint8 type softSwitchW func(io *ioC0Page, value uint8) -type keyboardProvider interface { - getKey() (key uint8, ok bool) +type KeyboardProvider interface { + GetKey() (key uint8, ok bool) } // See https://www.kreativekorp.com/miscpages/a2info/iomemory.shtml @@ -64,7 +64,7 @@ func (p *ioC0Page) isSoftSwitchExtActive(ioFlag uint8) bool { return (p.softSwitchesData[ioFlag] & ssOn) == ssOn } -func (p *ioC0Page) setKeyboardProvider(kb keyboardProvider) { +func (p *ioC0Page) setKeyboardProvider(kb KeyboardProvider) { p.keyboard = kb } diff --git a/apple2/softSwitches2.go b/apple2/softSwitches2.go index 8f40d7d..e915852 100644 --- a/apple2/softSwitches2.go +++ b/apple2/softSwitches2.go @@ -95,7 +95,7 @@ func getSoftSwitch(ioFlag uint8, isSet bool) softSwitchR { func getKeySoftSwitch(io *ioC0Page) uint8 { strobed := (io.softSwitchesData[ioDataKeyboard] & (1 << 7)) == 0 if strobed && io.keyboard != nil { - if key, ok := io.keyboard.getKey(); ok { + if key, ok := io.keyboard.GetKey(); ok { io.softSwitchesData[ioDataKeyboard] = key + (1 << 7) } } diff --git a/apple2sdl/run.go b/apple2sdl/run.go new file mode 100644 index 0000000..5c0ab66 --- /dev/null +++ b/apple2sdl/run.go @@ -0,0 +1,44 @@ +package apple2sdl + +import ( + "github.com/veandco/go-sdl2/sdl" + + "go6502/apple2" +) + +// SDLRun starts the Apple2 emulator on SDL +func SDLRun(a *apple2.Apple2) { + window, renderer, err := sdl.CreateWindowAndRenderer(800, 600, sdl.WINDOW_SHOWN) + if err != nil { + panic("Failed to create window") + } + defer window.Destroy() + defer renderer.Destroy() + window.SetTitle("Apple2") + renderer.Clear() + renderer.Present() + + kp := newSDLKeyBoard() + a.SetKeyboardProvider(&kp) + go a.Run(false, false) + + running := true + for running { + for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { + switch t := event.(type) { + case *sdl.QuitEvent: + running = false + case *sdl.KeyboardEvent: + //fmt.Printf("[%d ms] Keyboard\ttype:%d\tsym:%c\tmodifiers:%d\tstate:%d\trepeat:%d\n", + // t.Timestamp, t.Type, t.Keysym.Sym, t.Keysym.Mod, t.State, t.Repeat) + kp.putKey(t) + case *sdl.TextInputEvent: + //fmt.Printf("[%d ms] TextInput\ttype:%d\texts:%s\n", + // t.Timestamp, t.Type, t.GetText()) + kp.putText(t) + } + } + sdl.Delay(1000 / 60) + } + +} diff --git a/apple2sdl/sdlKeyboard.go b/apple2sdl/sdlKeyboard.go new file mode 100644 index 0000000..351521b --- /dev/null +++ b/apple2sdl/sdlKeyboard.go @@ -0,0 +1,105 @@ +package apple2sdl + +import ( + "unicode/utf8" + + "github.com/veandco/go-sdl2/sdl" +) + +type sdlKeyboard struct { + keyChannel chan uint8 +} + +func newSDLKeyBoard() sdlKeyboard { + var k sdlKeyboard + k.keyChannel = make(chan uint8, 100) + return k +} + +func (k *sdlKeyboard) putText(textEvent *sdl.TextInputEvent) { + text := textEvent.GetText() + + for _, ch := range text { + // We will use computed text only for printable ASCII chars + if ch < ' ' || ch > '~' { + continue + } + + buf := make([]uint8, 1) + utf8.EncodeRune(buf, ch) + + k.putChar(buf[0]) + } +} + +func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) { + /* To get keys as understood by the Apple2 hardware run: + 10 A=PEEK(49152) + 20 B = A - 128 + 30 PRINT A, B + 40 GOTO 10 + */ + if keyEvent.Type != sdl.KEYDOWN { + // Process only key pushes + return + } + + key := keyEvent.Keysym + ctrl := key.Mod&sdl.KMOD_CTRL != 0 + + if ctrl { + if key.Sym >= 'a' && key.Sym <= 'z' { + k.putChar(uint8(key.Sym) - 97 + 1) + return + } + } + + result := uint8(0) + + switch key.Sym { + case sdl.K_ESCAPE: + result = 27 + case sdl.K_BACKSPACE: + result = 24 + case sdl.K_RETURN: + result = 13 + case sdl.K_RETURN2: + result = 13 + case sdl.K_LEFT: + if ctrl { + result = 31 // Base64A + } + result = 8 + case sdl.K_RIGHT: + result = 21 + + // Base64A clone particularities + case sdl.K_F2: + result = 127 + case sdl.K_UP: + result = 31 + case sdl.K_DOWN: + result = 10 + } + + // Missing values 91 to 95. Usually control for [\]^_ + // On the Base64A it's control for \]./ + + if result != 0 { + k.putChar(result) + } +} + +func (k *sdlKeyboard) putChar(ch uint8) { + k.keyChannel <- ch +} + +func (k *sdlKeyboard) GetKey() (key uint8, ok bool) { + select { + case key = <-k.keyChannel: + ok = true + default: + ok = false + } + return +} diff --git a/main.go b/main.go index ef14b68..8d3c11c 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,9 @@ package main -import "go6502/apple2" +import ( + "go6502/apple2" + "go6502/apple2sdl" +) func main() { //romFile := "apple2/romdumps/Apple2.rom" @@ -10,7 +13,12 @@ func main() { diskImage := "../dos33.dsk" log := false + sdl := true a := apple2.NewApple2(romFile) a.AddDisk2(disk2RomFile, diskImage) - a.Run(log) + if sdl { + apple2sdl.SDLRun(a) + } else { + a.Run(log, true) + } }