goapple2/texty/texty.go

123 lines
2.8 KiB
Go

// Simplest possible Apple II that will possibly boot. ~ (tilde) to quit.
package main
import (
"fmt"
"time"
"github.com/nsf/termbox-go"
"github.com/zellyn/goapple2"
"github.com/zellyn/goapple2/util"
"github.com/zellyn/goapple2/videoscan"
)
// Mapping of screen bytes to character values
var AppleChars = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?"
// Translate to termbox
func translateToTermbox(value byte) (char rune, fg, bg termbox.Attribute) {
// BUG(zellyn): change this to return char, MODE_ENUM.
ch := rune(AppleChars[value&0x3F])
if value&0x80 > 0 {
return ch, termbox.ColorGreen, termbox.ColorBlack
}
return ch, termbox.ColorGreen, termbox.ColorBlack + termbox.AttrReverse
}
func termboxToAppleKeyboard(ev termbox.Event) (key byte, err error) {
if ev.Key > 0 && ev.Key <= 32 {
return byte(ev.Key), nil
}
if ev.Ch >= '!' && ev.Ch <= 'Z' || ev.Ch == '^' {
return byte(ev.Ch), nil
}
if ev.Ch >= 'a' && ev.Ch <= 'z' {
return byte(ev.Ch - 'a' + 'A'), nil
}
switch ev.Key {
case termbox.KeyBackspace2:
return 8, nil // backspace
case termbox.KeyArrowLeft:
return 8, nil // left arrow
case termbox.KeyArrowRight:
return 21, nil // right arrow
}
return 0, fmt.Errorf("hi")
}
// func Write(address uint16, value byte) {
// a2.mem[address] = value
// if address >= 0x0400 && address < 0x0800 {
// offset := int(address - 0x0400)
// count := offset & 0x7f
// if count <= 119 {
// x := count % 40
// segment := offset / 128
// which40 := count / 40
// y := which40*8 + segment
// ch, fg, bg := translateToTermbox(value)
// termbox.SetCell(x+1, y+1, ch, fg, bg)
// termbox.Flush()
// }
// }
// }
func ProcessEvents(events chan termbox.Event, a2 *goapple2.Apple2) bool {
select {
case ev := <-events:
if ev.Type == termbox.EventKey && ev.Ch == '~' {
return true
}
if ev.Type == termbox.EventKey {
if key, err := termboxToAppleKeyboard(ev); err == nil {
a2.Keypress(key | 0x80)
}
}
default:
}
return false
}
type TextPlotter int
func (p TextPlotter) Plot(data videoscan.PlotData) {
y := int(data.Row / 8)
x := int(data.Column)
value := data.RawData
ch, fg, bg := translateToTermbox(value)
termbox.SetCell(x+1, y+1, ch, fg, bg)
if x == 39 && data.Row == 191 {
termbox.Flush()
}
}
// Run the emulator
func RunEmulator() {
rom := util.ReadRomOrDie("../data/roms/apple2+.rom")
plotter := TextPlotter(0)
a2 := goapple2.NewApple2(plotter, rom)
if err := termbox.Init(); err != nil {
panic(err)
}
events := make(chan termbox.Event)
go func() {
for {
events <- termbox.PollEvent()
}
}()
for !ProcessEvents(events, a2) {
err := a2.Step()
if err != nil {
fmt.Println(err)
break
}
time.Sleep(1 * time.Nanosecond) // So the keyboard-reading goroutines can run
}
termbox.Close()
}
func main() {
RunEmulator()
}