Show speed on screen

This commit is contained in:
Ivan Izaguirre 2024-11-01 21:35:20 +01:00
parent 4a8372ca90
commit 71ea4bd733
8 changed files with 68 additions and 22 deletions

View File

@ -34,11 +34,12 @@ type Apple2 struct {
cycleBreakpoint uint64 cycleBreakpoint uint64
breakPoint bool breakPoint bool
profile bool profile bool
showSpeed bool
paused bool paused bool
cpuTrace bool cpuTrace bool
forceCaps bool forceCaps bool
removableMediaDrives []drive removableMediaDrives []drive
currentFreqMHz float64
} }
// GetCards returns the array of inserted cards // GetCards returns the array of inserted cards
@ -75,6 +76,10 @@ func (a *Apple2) GetCycles() uint64 {
return a.cycles return a.cycles
} }
func (a *Apple2) GetCurrentFreqMHz() float64 {
return a.currentFreqMHz
}
// SetCycleBreakpoint sets a cycle number to pause the emulator. 0 to disable // SetCycleBreakpoint sets a cycle number to pause the emulator. 0 to disable
func (a *Apple2) SetCycleBreakpoint(cycle uint64) { func (a *Apple2) SetCycleBreakpoint(cycle uint64) {
a.cycleBreakpoint = cycle a.cycleBreakpoint = cycle

View File

@ -116,12 +116,11 @@ func (a *Apple2) Start(paused bool) {
} }
} }
if a.showSpeed && a.cycles-speedReferenceCycles > 1000000 { if a.cycles-speedReferenceCycles > 1000000 {
// Calculate speed in MHz every million cycles // Calculate speed in MHz every million cycles
newTime := time.Now() newTime := time.Now()
elapsedCycles := float64(a.cycles - speedReferenceCycles) elapsedCycles := float64(a.cycles - speedReferenceCycles)
freq := 1000.0 * elapsedCycles / float64(newTime.Sub(speedReferenceTime).Nanoseconds()) a.currentFreqMHz = 1000.0 * elapsedCycles / float64(newTime.Sub(speedReferenceTime).Nanoseconds())
fmt.Printf("Freq: %f Mhz\n", freq)
speedReferenceTime = newTime speedReferenceTime = newTime
speedReferenceCycles = a.cycles speedReferenceCycles = a.cycles
} }

View File

@ -77,7 +77,7 @@ func (a *Apple2) executeCommand(command command) {
a.cycleDurationNs = 0 a.cycleDurationNs = 0
} }
case CommandShowSpeed: case CommandShowSpeed:
a.showSpeed = !a.showSpeed fmt.Printf("Freq: %f Mhz\n", a.currentFreqMHz)
case CommandDumpDebugInfo: case CommandDumpDebugInfo:
a.dumpDebugInfo() a.dumpDebugInfo()
case CommandNextCharGenPage: case CommandNextCharGenPage:

View File

@ -18,7 +18,10 @@ type ebitenKeyboard struct {
showPages bool showPages bool
showCharGen bool showCharGen bool
showAltText bool showAltText bool
showFreq bool
screenMode int screenMode int
debug bool
} }
func newEbitenKeyBoard(a *izapple2.Apple2) *ebitenKeyboard { func newEbitenKeyBoard(a *izapple2.Apple2) *ebitenKeyboard {
@ -33,14 +36,18 @@ func newEbitenKeyBoard(a *izapple2.Apple2) *ebitenKeyboard {
func (k *ebitenKeyboard) update() { func (k *ebitenKeyboard) update() {
runes := ebiten.AppendInputChars(nil) runes := ebiten.AppendInputChars(nil)
if len(runes) > 0 { if len(runes) > 0 {
fmt.Println("Runes: ", string(runes)) if k.debug {
fmt.Println("Runes: ", string(runes))
}
k.putText(string(runes)) k.putText(string(runes))
} }
keys := inpututil.AppendJustPressedKeys(nil) keys := inpututil.AppendJustPressedKeys(nil)
for _, key := range keys { for _, key := range keys {
s := key.String() if k.debug {
fmt.Println("Key pressed: ", s) s := key.String()
fmt.Println("Key pressed: ", s)
}
k.putKey(key) k.putKey(key)
} }
} }
@ -115,7 +122,7 @@ func (k *ebitenKeyboard) putKey(key ebiten.Key) {
k.a.SendCommand(izapple2.CommandToggleCPUTrace) k.a.SendCommand(izapple2.CommandToggleCPUTrace)
case ebiten.KeyF5: case ebiten.KeyF5:
if ctrl { if ctrl {
k.a.SendCommand(izapple2.CommandShowSpeed) k.showFreq = !k.showFreq
} else { } else {
k.a.SendCommand(izapple2.CommandToggleSpeed) k.a.SendCommand(izapple2.CommandToggleSpeed)
} }

View File

@ -84,11 +84,7 @@ func (s *ebitenSpeaker) Read(buf []byte) (n int, err error) {
// Build wave // Build wave
const bytesPerSample = 8 // Two floats32, 4 bytes each, one for each channel const bytesPerSample = 8 // Two floats32, 4 bytes each, one for each channel
samples := len(buf) / bytesPerSample samples := len(buf) / bytesPerSample
//fmt.Printf("smples: %v\n", smples)
if len(s.pendingClicks) > 0 {
fmt.Printf("pendingClicks: %v\n", len(s.pendingClicks))
}
var i, r int var i, r int
level := s.lastLevel level := s.lastLevel
for p := 0; p < len(s.pendingClicks); p++ { for p := 0; p < len(s.pendingClicks); p++ {
@ -125,7 +121,6 @@ func (s *ebitenSpeaker) Read(buf []byte) (n int, err error) {
// If the buffer is empty lets stop the signal // If the buffer is empty lets stop the signal
if i == 0 && level != 0.0 { if i == 0 && level != 0.0 {
level = 0.0 level = 0.0
fmt.Printf("Speaker buffer empty, to zero.\n")
} }
// Complete the buffer if needed // Complete the buffer if needed

View File

@ -1,31 +1,42 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"image" "image"
"image/color"
"github.com/ivanizag/izapple2" "github.com/ivanizag/izapple2"
a_screen "github.com/ivanizag/izapple2/screen" a_screen "github.com/ivanizag/izapple2/screen"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
"github.com/hajimehoshi/ebiten/v2/text/v2"
"github.com/pkg/profile" "github.com/pkg/profile"
) )
type Game struct { type Game struct {
a *izapple2.Apple2 a *izapple2.Apple2
image *ebiten.Image image *ebiten.Image
keyboard *ebitenKeyboard keyboard *ebitenKeyboard
speaker *ebitenSpeaker speaker *ebitenSpeaker
fontSource *text.GoTextFaceSource
paused bool paused bool
title string title string
updates uint64
freq float64
} }
const ( const (
virtualWidth = 1128 virtualWidth = 1128
virtualHeight = 768 virtualHeight = 768
hudFontSize = 50
) )
var hudColor = color.RGBA{208, 241, 141, 255} // Yellow
func (g *Game) Update() error { func (g *Game) Update() error {
g.keyboard.update() g.keyboard.update()
g.speaker.update() g.speaker.update()
@ -39,7 +50,7 @@ func (g *Game) Update() error {
g.paused = g.a.IsPaused() g.paused = g.a.IsPaused()
} }
if !g.a.IsPaused() { if g.updates%3 == 0 && !g.a.IsPaused() { // 20 times per second
var img *image.RGBA var img *image.RGBA
vs := g.a.GetVideoSource() vs := g.a.GetVideoSource()
if g.keyboard.showHelp { if g.keyboard.showHelp {
@ -59,6 +70,11 @@ func (g *Game) Update() error {
} }
} }
if g.updates%60 == 0 { // Once per second
g.freq = g.a.GetCurrentFreqMHz()
}
g.updates++
return nil return nil
} }
@ -72,6 +88,18 @@ func (g *Game) Draw(screen *ebiten.Image) {
screen.DrawImage(g.image, op) screen.DrawImage(g.image, op)
} }
if g.keyboard.showFreq {
msg := fmt.Sprintf("%0.2f Hz, FPS %0.0f", g.freq, ebiten.ActualFPS())
op := &text.DrawOptions{}
op.GeoM.Translate(20, 20)
op.ColorScale.ScaleWithColor(hudColor)
text.Draw(screen, msg, &text.GoTextFace{
Source: g.fontSource,
Size: hudFontSize,
}, op)
}
} }
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
@ -101,16 +129,20 @@ func ebitenRun(a *izapple2.Apple2) {
title := "iz-" + a.Name + " (F1 for help)" title := "iz-" + a.Name + " (F1 for help)"
ebiten.SetWindowTitle(title) ebiten.SetWindowTitle(title)
go a.Run()
game := &Game{ game := &Game{
a: a, a: a,
keyboard: newEbitenKeyBoard(a), keyboard: newEbitenKeyBoard(a),
speaker: newEbitenSpeaker(), speaker: newEbitenSpeaker(),
} }
a.SetSpeakerProvider(game.speaker) a.SetSpeakerProvider(game.speaker)
var err error
game.fontSource, err = text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
if err != nil {
panic(err)
}
go a.Run()
if err := ebiten.RunGame(game); err != nil { if err := ebiten.RunGame(game); err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
} }
@ -140,3 +172,8 @@ side of the window to load a disk
Run izapple2 -h for more options Run izapple2 -h for more options
https://github.com/ivanizag/izapple2 https://github.com/ivanizag/izapple2
` `
/*
To test the WebAssembly version, run:
go run github.com/hajimehoshi/wasmserve@latest .
*/

1
go.mod
View File

@ -19,6 +19,7 @@ require (
github.com/ebitengine/hideconsole v1.0.0 // indirect github.com/ebitengine/hideconsole v1.0.0 // indirect
github.com/ebitengine/oto/v3 v3.3.1 // indirect github.com/ebitengine/oto/v3 v3.3.1 // indirect
github.com/ebitengine/purego v0.8.0 // indirect github.com/ebitengine/purego v0.8.0 // indirect
github.com/go-text/typesetting v0.2.0 // indirect
github.com/jezek/xgb v1.1.1 // indirect github.com/jezek/xgb v1.1.1 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect

2
go.sum
View File

@ -36,6 +36,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3 h1:nanQfMsOs3gnuKRm0E5jXWomedE/9YIFXdmHJNZYeqc= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3 h1:nanQfMsOs3gnuKRm0E5jXWomedE/9YIFXdmHJNZYeqc=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho=
github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=