169 lines
3.5 KiB
Go
169 lines
3.5 KiB
Go
// Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"errors"
|
|
"flag"
|
|
"time"
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
|
// "image/color"
|
|
)
|
|
|
|
const IdleTicks = 1000
|
|
|
|
var consoleChan chan byte
|
|
var cpu CPU
|
|
var mem Mem
|
|
var uart UART
|
|
var framebuffer Framebuffer
|
|
var sdspi SDSPI
|
|
var irqc IRQC
|
|
|
|
var Terminated = errors.New("terminated")
|
|
|
|
var idleCounter int = IdleTicks
|
|
|
|
func idle(canGoIdle bool) {
|
|
if canGoIdle {
|
|
if idleCounter > 0 { idleCounter -= 1 }
|
|
} else {
|
|
if idleCounter != IdleTicks { idleCounter = IdleTicks }
|
|
}
|
|
}
|
|
|
|
type Game struct{
|
|
debug bool
|
|
x,y int
|
|
stepsPerFrame int
|
|
lastFrameDuration time.Duration
|
|
}
|
|
|
|
func (g *Game) Update() error {
|
|
startTime := time.Now()
|
|
|
|
framebuffer.startFrame()
|
|
for i := 0; i < g.stepsPerFrame; i++ {
|
|
err := cpu.step()
|
|
if err != nil {
|
|
log.Printf("Stopped by error at PC %08X",cpu.PC)
|
|
log.Print(err)
|
|
return Terminated
|
|
}
|
|
|
|
if cpu.stopped { return Terminated }
|
|
|
|
if idleCounter == 0 { break }
|
|
}
|
|
g.lastFrameDuration = time.Since(startTime)
|
|
|
|
if inpututil.IsKeyJustReleased(ebiten.KeyF12) {
|
|
g.debug = !g.debug
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *Game) Draw(screen *ebiten.Image) {
|
|
screen.DrawImage(framebuffer.framebuffer, nil)
|
|
|
|
if g.debug {
|
|
buf := fmt.Sprintf("PC: %08X FP: %08X RP: %08X ESP: %2X %v",
|
|
cpu.PC, cpu.FP, cpu.RP, cpu.ESP, g.lastFrameDuration)
|
|
ebitenutil.DebugPrint(screen, buf)
|
|
}
|
|
|
|
/*
|
|
screen.Set(g.x, g.y, color.RGBA{255,0,0,0})
|
|
screen.Set(g.x, g.y+1, color.RGBA{0,255,0,0})
|
|
screen.Set(g.x, g.y+2, color.RGBA{0,255,255,0})
|
|
screen.Set(g.x, g.y+3, color.RGBA{255,255,255,0})
|
|
g.x += 1
|
|
if g.x > 319 { g.x = 0 }
|
|
*/
|
|
|
|
// if idleCounter == 0 { ebitenutil.DebugPrint(screen, "idle") }
|
|
}
|
|
|
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
|
return 640, 400
|
|
}
|
|
|
|
func main() {
|
|
var codefile string = ""
|
|
|
|
addrPtr := flag.Int("a",0,"starting address")
|
|
tracePtr := flag.Bool("t",false,"trace")
|
|
cardImgPtr := flag.String("i", "sdcard.img", "SD card image file")
|
|
flag.Parse()
|
|
if len(flag.Args()) > 0 {
|
|
codefile = flag.Args()[0]
|
|
} else {
|
|
codefile = "rommon.prog"
|
|
}
|
|
|
|
log.SetFlags(0)
|
|
oldState, err := SetRawConsole()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer RestoreConsole(oldState)
|
|
|
|
cpu.initialize()
|
|
mem.initialize(4096 * 1024 / 4)
|
|
|
|
uart.cpu = &cpu
|
|
mem.attachIO(&uart, 0)
|
|
|
|
err = sdspi.openImage(*cardImgPtr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer sdspi.closeImage()
|
|
//sdspi.debug = true
|
|
mem.attachIO(&sdspi, 1)
|
|
|
|
framebuffer.initialize()
|
|
mem.attachIO(&framebuffer, 2)
|
|
|
|
irqc.initialize()
|
|
mem.attachIO(&irqc, 3)
|
|
|
|
cpu.mem = &mem
|
|
cpu.PC = word(*addrPtr)
|
|
|
|
if codefile != "" {
|
|
mem.loadFromFile(codefile, *addrPtr)
|
|
}
|
|
|
|
consoleChan = make(chan byte)
|
|
uart.consoleChan = make(chan byte)
|
|
|
|
ebiten.SetWindowSize(800, 600)
|
|
ebiten.SetWindowTitle("Tridora Framebuffer")
|
|
|
|
g := Game{}
|
|
cpu.trace = *tracePtr
|
|
g.stepsPerFrame = 166666
|
|
// g.stepsPerFrame = 1
|
|
|
|
go func(ch chan byte) {
|
|
for {
|
|
buf := make([] byte,1);
|
|
n, err := ConsoleRead(buf)
|
|
if err != nil {
|
|
fmt.Println("read error on stdin, closing channel")
|
|
close(ch)
|
|
return
|
|
}
|
|
if n > 0 {ch <- buf[0] }
|
|
}
|
|
}(uart.consoleChan)
|
|
|
|
if err := ebiten.RunGame(&g); err != Terminated && err != nil {
|
|
log.Panic(err)
|
|
}
|
|
}
|