2018-05-08 18:32:55 +00:00
|
|
|
package main
|
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
// Main emulator executable. This integrates all the components of the emulator
|
|
|
|
// and contains the ebiten main loop.
|
|
|
|
|
2018-05-08 18:32:55 +00:00
|
|
|
import (
|
|
|
|
"flag"
|
2018-05-14 09:33:49 +00:00
|
|
|
"fmt"
|
2018-05-26 22:08:38 +00:00
|
|
|
"os"
|
2018-05-08 21:18:49 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
"github.com/hajimehoshi/ebiten"
|
|
|
|
|
2019-11-02 13:33:05 +00:00
|
|
|
"github.com/freewilll/apple2-go/audio"
|
|
|
|
"github.com/freewilll/apple2-go/cpu"
|
|
|
|
"github.com/freewilll/apple2-go/disk"
|
|
|
|
"github.com/freewilll/apple2-go/keyboard"
|
|
|
|
"github.com/freewilll/apple2-go/mmu"
|
|
|
|
"github.com/freewilll/apple2-go/system"
|
|
|
|
"github.com/freewilll/apple2-go/utils"
|
|
|
|
"github.com/freewilll/apple2-go/video"
|
2018-05-08 21:18:49 +00:00
|
|
|
)
|
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
var (
|
|
|
|
showInstructions *bool // Display all instructions as they are executed
|
|
|
|
disableFirmwareWait *bool // Disable the WAIT function at $fca8
|
2019-11-10 12:32:16 +00:00
|
|
|
disableDosDelay *bool // Disable DOS delay functions
|
2018-05-27 21:58:19 +00:00
|
|
|
breakAddress *uint16 // Break address from the command line
|
2019-11-25 21:46:39 +00:00
|
|
|
scale float64 // Scale
|
2018-05-27 21:58:19 +00:00
|
|
|
|
2019-11-25 19:38:58 +00:00
|
|
|
resetKeysDown bool // Keep track of ctrl-alt-R key down state
|
|
|
|
fpsKeysDown bool // Keep track of ctrl-alt-F key down state
|
|
|
|
monochromeKeysDown bool // Keep track of ctrl-alt-M key down state
|
2018-05-27 21:58:19 +00:00
|
|
|
)
|
2018-05-09 09:57:04 +00:00
|
|
|
|
2018-05-14 09:33:49 +00:00
|
|
|
// checkSpecialKeys checks
|
|
|
|
// - ctrl-alt-R has been pressed. Releasing the R does a warm reset
|
|
|
|
// - ctrl-alt-F has been pressed, toggling FPS display
|
|
|
|
func checkSpecialKeys() {
|
2018-05-27 21:58:19 +00:00
|
|
|
// Check for ctrl-alt-R, and if released, do a warm CPU reset
|
2018-05-09 09:57:04 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && ebiten.IsKeyPressed(ebiten.KeyR) {
|
|
|
|
resetKeysDown = true
|
|
|
|
} else if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && !ebiten.IsKeyPressed(ebiten.KeyR) && resetKeysDown {
|
|
|
|
resetKeysDown = false
|
2018-05-14 21:49:35 +00:00
|
|
|
cpu.Reset()
|
2018-05-09 09:57:04 +00:00
|
|
|
} else {
|
|
|
|
resetKeysDown = false
|
|
|
|
}
|
2018-05-14 09:33:49 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
// Check for ctrl-alt-F and toggle FPS display
|
2018-05-14 09:33:49 +00:00
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && ebiten.IsKeyPressed(ebiten.KeyF) {
|
|
|
|
fpsKeysDown = true
|
|
|
|
} else if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && !ebiten.IsKeyPressed(ebiten.KeyF) && fpsKeysDown {
|
|
|
|
fpsKeysDown = false
|
|
|
|
video.ShowFPS = !video.ShowFPS
|
|
|
|
} else {
|
|
|
|
fpsKeysDown = false
|
|
|
|
}
|
2019-11-25 19:38:58 +00:00
|
|
|
|
|
|
|
// Check for ctrl-alt-M and toggle FPS display
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && ebiten.IsKeyPressed(ebiten.KeyM) {
|
|
|
|
monochromeKeysDown = true
|
|
|
|
} else if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyAlt) && !ebiten.IsKeyPressed(ebiten.KeyM) && monochromeKeysDown {
|
|
|
|
monochromeKeysDown = false
|
|
|
|
video.Monochrome = !video.Monochrome
|
|
|
|
} else {
|
|
|
|
monochromeKeysDown = false
|
|
|
|
}
|
2018-05-09 09:57:04 +00:00
|
|
|
}
|
2018-05-08 21:18:49 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
// update is the main ebiten loop
|
2018-05-08 21:18:49 +00:00
|
|
|
func update(screen *ebiten.Image) error {
|
2018-05-14 09:33:49 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
checkSpecialKeys() // Poll the keyboard and check for R and F keys
|
|
|
|
|
2019-11-25 19:38:58 +00:00
|
|
|
if !(fpsKeysDown || monochromeKeysDown) {
|
|
|
|
keyboard.Poll() // Convert ebiten's keyboard state to an interal value
|
|
|
|
}
|
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
system.FrameCycles = 0 // Reset cycles processed this frame
|
|
|
|
system.LastAudioCycles = 0 // Reset processed audio cycles
|
|
|
|
exitAtBreak := true // Die if a BRK instruction is seen
|
|
|
|
|
|
|
|
// Run for 1/60 of a second, the duration of an ebiten frame
|
2019-11-10 12:32:16 +00:00
|
|
|
cpu.Run(*showInstructions, breakAddress, exitAtBreak, *disableFirmwareWait, *disableDosDelay, system.CPUFrequency/60)
|
2018-05-27 21:58:19 +00:00
|
|
|
|
|
|
|
// Process any audio speaker clicks from this frame
|
2018-05-14 09:33:49 +00:00
|
|
|
audio.ForwardToFrameCycle()
|
2018-05-27 21:58:19 +00:00
|
|
|
|
|
|
|
// Updated the cycle accounting
|
2018-05-14 09:33:49 +00:00
|
|
|
system.Cycles += system.FrameCycles
|
2018-05-09 09:57:04 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
// Finally render the screen
|
2018-05-12 17:55:36 +00:00
|
|
|
return video.DrawScreen(screen)
|
2018-05-08 21:18:49 +00:00
|
|
|
}
|
|
|
|
|
2018-05-08 18:32:55 +00:00
|
|
|
func main() {
|
2018-05-26 22:08:38 +00:00
|
|
|
var Usage = func() {
|
|
|
|
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n\n", os.Args[0])
|
|
|
|
fmt.Fprintf(flag.CommandLine.Output(), "Synopsis: %s [disk image file]\n\n", os.Args[0])
|
|
|
|
fmt.Fprintf(flag.CommandLine.Output(), "Options\n")
|
|
|
|
flag.PrintDefaults()
|
|
|
|
}
|
|
|
|
flag.Usage = Usage
|
|
|
|
|
2018-05-08 21:18:49 +00:00
|
|
|
showInstructions = flag.Bool("show-instructions", false, "Show instructions code while running")
|
2018-05-11 14:57:42 +00:00
|
|
|
disableFirmwareWait = flag.Bool("disable-wait", false, "Ignore JSRs to firmware wait at $FCA8")
|
2019-11-10 12:32:16 +00:00
|
|
|
disableDosDelay = flag.Bool("disable-dos-delay", false, "Ignore DOS ARM move and motor on waits")
|
2018-05-11 14:57:42 +00:00
|
|
|
breakAddressString := flag.String("break", "", "Break on address")
|
2018-05-14 09:33:49 +00:00
|
|
|
mute := flag.Bool("mute", false, "Mute sound")
|
2019-11-25 21:46:39 +00:00
|
|
|
scale := flag.Float64("scale", 2, "Video scale")
|
2018-05-19 09:55:41 +00:00
|
|
|
clickWhenDriveHeadMoves := flag.Bool("drive-head-click", false, "Click speaker when drive head moves")
|
2018-05-08 18:32:55 +00:00
|
|
|
flag.Parse()
|
|
|
|
|
2018-05-11 14:57:42 +00:00
|
|
|
breakAddress = utils.DecodeCmdLineAddress(breakAddressString)
|
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
cpu.InitInstructionDecoder() // Init the instruction decoder data structures
|
|
|
|
mmu.InitRAM() // Set all switches to bootup values and initialize the page tables
|
|
|
|
mmu.InitApple2eROM() // Load the ROM and init page tables
|
|
|
|
mmu.InitIO() // Init slots, video and disk image statuses
|
2018-05-11 14:57:42 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
// If there is a disk image on the command line, load it
|
|
|
|
diskImages := flag.Args()
|
|
|
|
if len(diskImages) > 0 {
|
|
|
|
disk.ReadDiskImage(diskImages[0])
|
2018-05-11 14:57:42 +00:00
|
|
|
}
|
2018-05-08 18:32:55 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
cpu.Init() // Init the CPU registers, interrupts and disable testing code
|
|
|
|
keyboard.Init() // Init the keyboard state and ebiten translation tables
|
|
|
|
video.Init() // Init the video data structures used for rendering
|
|
|
|
audio.InitEbiten() // Initialize the audio sets up the ebiten output stream
|
|
|
|
|
2018-05-14 09:33:49 +00:00
|
|
|
audio.Mute = *mute
|
2018-05-19 09:55:41 +00:00
|
|
|
audio.ClickWhenDriveHeadMoves = *clickWhenDriveHeadMoves
|
2018-05-08 21:18:49 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
system.Init() // Initialize the system-wide state
|
|
|
|
cpu.SetColdStartReset() // Prepare memory to ensure a cold reset
|
|
|
|
cpu.Reset() // Set the CPU and memory states so that a next call to cpu.Run() calls the firmware reset code
|
|
|
|
|
|
|
|
// Start the ebiten main loop
|
|
|
|
ebiten.SetRunnableInBackground(true)
|
2019-11-25 21:46:39 +00:00
|
|
|
ebiten.Run(update, 560, 384, *scale, "Apple //e")
|
2018-05-17 12:13:10 +00:00
|
|
|
|
2018-05-27 21:58:19 +00:00
|
|
|
// The main loop has ended, flush any data to the disk image if any writes have been done.
|
2018-05-26 22:23:35 +00:00
|
|
|
disk.FlushImage()
|
2018-05-08 18:32:55 +00:00
|
|
|
}
|