mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-12-22 09:30:19 +00:00
Change diskettes with drag and drop
This commit is contained in:
parent
e38c4cf57b
commit
a89cf24fe7
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,4 +10,5 @@ frontend/a2fyne/a2fyne
|
||||
frontend/headless/headless
|
||||
frontend/*/snapshot.gif
|
||||
frontend/*/snapshot.png
|
||||
frontend/*/printer.out
|
||||
frontend/*/printer.out
|
||||
.DS_STORE
|
||||
|
@ -178,6 +178,8 @@ Line:
|
||||
- F12: Save a screen snapshot to a file `snapshot.png`
|
||||
- Pause: Pause the emulation
|
||||
|
||||
Drag and drop a diskette file on the left side of the window to change Drive 1; to the right side to change the disk on Drive 2.
|
||||
|
||||
Only valid on SDL mode
|
||||
|
||||
### Command line options
|
||||
|
99
apple2.go
99
apple2.go
@ -10,25 +10,26 @@ import (
|
||||
|
||||
// Apple2 represents all the components and state of the emulated machine
|
||||
type Apple2 struct {
|
||||
Name string
|
||||
cpu *iz6502.State
|
||||
mmu *memoryManager
|
||||
io *ioC0Page
|
||||
cg *CharacterGenerator
|
||||
cards [8]Card
|
||||
softVideoSwitch *SoftVideoSwitch
|
||||
isApple2e bool
|
||||
commandChannel chan int
|
||||
cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz
|
||||
fastMode bool
|
||||
fastRequestsCounter int32
|
||||
cycleBreakpoint uint64
|
||||
breakPoint bool
|
||||
profile bool
|
||||
showSpeed bool
|
||||
paused bool
|
||||
tracers []executionTracer
|
||||
forceCaps bool
|
||||
Name string
|
||||
cpu *iz6502.State
|
||||
mmu *memoryManager
|
||||
io *ioC0Page
|
||||
cg *CharacterGenerator
|
||||
cards [8]Card
|
||||
softVideoSwitch *SoftVideoSwitch
|
||||
isApple2e bool
|
||||
commandChannel chan command
|
||||
cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz
|
||||
fastMode bool
|
||||
fastRequestsCounter int32
|
||||
cycleBreakpoint uint64
|
||||
breakPoint bool
|
||||
profile bool
|
||||
showSpeed bool
|
||||
paused bool
|
||||
forceCaps bool
|
||||
tracers []executionTracer
|
||||
removableMediaDrives []drive
|
||||
}
|
||||
|
||||
type executionTracer interface {
|
||||
@ -90,7 +91,7 @@ func (a *Apple2) Start(paused bool) {
|
||||
for commandsPending {
|
||||
select {
|
||||
case command := <-a.commandChannel:
|
||||
switch command {
|
||||
switch command.getId() {
|
||||
case CommandKill:
|
||||
return
|
||||
case CommandPause:
|
||||
@ -192,58 +193,6 @@ func (a *Apple2) IsForceCaps() bool {
|
||||
return a.forceCaps
|
||||
}
|
||||
|
||||
const (
|
||||
// CommandToggleSpeed toggles cpu speed between full speed and actual Apple II speed
|
||||
CommandToggleSpeed = iota + 1
|
||||
// CommandShowSpeed toggles printinf the current freq in Mhz
|
||||
CommandShowSpeed
|
||||
// CommandDumpDebugInfo dumps useful info
|
||||
CommandDumpDebugInfo
|
||||
// CommandNextCharGenPage cycles the CharGen page if several
|
||||
CommandNextCharGenPage
|
||||
// CommandToggleCPUTrace toggle tracing of CPU execution
|
||||
CommandToggleCPUTrace
|
||||
// CommandKill stops the cpu execution loop
|
||||
CommandKill
|
||||
// CommandReset executes a 6502 reset
|
||||
CommandReset
|
||||
// CommandPauseUnpause allows the Pause button to freeze the emulator for a coffee break
|
||||
CommandPauseUnpause
|
||||
// CommandPause pauses the emulator
|
||||
CommandPause
|
||||
// CommandStart restarts the emulator
|
||||
CommandStart
|
||||
)
|
||||
|
||||
// SendCommand enqueues a command to the emulator thread
|
||||
func (a *Apple2) SendCommand(command int) {
|
||||
a.commandChannel <- command
|
||||
}
|
||||
|
||||
func (a *Apple2) executeCommand(command int) {
|
||||
switch command {
|
||||
case CommandToggleSpeed:
|
||||
if a.cycleDurationNs == 0 {
|
||||
//fmt.Println("Slow")
|
||||
a.cycleDurationNs = 1000.0 / CPUClockMhz
|
||||
} else {
|
||||
//fmt.Println("Fast")
|
||||
a.cycleDurationNs = 0
|
||||
}
|
||||
case CommandShowSpeed:
|
||||
a.showSpeed = !a.showSpeed
|
||||
case CommandDumpDebugInfo:
|
||||
a.dumpDebugInfo()
|
||||
case CommandNextCharGenPage:
|
||||
a.cg.nextPage()
|
||||
fmt.Printf("Chargen page %v\n", a.cg.page)
|
||||
case CommandToggleCPUTrace:
|
||||
a.cpu.SetTrace(!a.cpu.GetTrace())
|
||||
case CommandReset:
|
||||
a.reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Apple2) RequestFastMode() {
|
||||
// Note: if the fastMode is shorter than maxWaitDuration, there won't be any gain.
|
||||
if a.fastMode {
|
||||
@ -258,10 +207,8 @@ func (a *Apple2) ReleaseFastMode() {
|
||||
}
|
||||
|
||||
func (a *Apple2) executionTrace() {
|
||||
if a.tracers != nil {
|
||||
for _, v := range a.tracers {
|
||||
v.inspect()
|
||||
}
|
||||
for _, v := range a.tracers {
|
||||
v.inspect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ func newApple2() *Apple2 {
|
||||
}
|
||||
|
||||
func (a *Apple2) setup(clockMhz float64, fastMode bool) {
|
||||
a.commandChannel = make(chan int, 100)
|
||||
a.commandChannel = make(chan command, 100)
|
||||
a.fastMode = fastMode
|
||||
|
||||
if clockMhz <= 0 {
|
||||
@ -52,13 +52,13 @@ func setApple2eEnhanced(a *Apple2) {
|
||||
}
|
||||
|
||||
func (a *Apple2) addTracer(tracer executionTracer) {
|
||||
if a.tracers == nil {
|
||||
a.tracers = make([]executionTracer, 0)
|
||||
}
|
||||
|
||||
a.tracers = append(a.tracers, tracer)
|
||||
}
|
||||
|
||||
func (a *Apple2) registerRemovableMediaDrive(d drive) {
|
||||
a.removableMediaDrives = append(a.removableMediaDrives, d)
|
||||
}
|
||||
|
||||
func (a *Apple2) insertCard(c Card, slot int) {
|
||||
c.assign(a, slot)
|
||||
a.cards[slot] = c
|
||||
@ -97,19 +97,17 @@ func (a *Apple2) AddDisk2(slot int, diskImage, diskBImage string, trackTracer tr
|
||||
a.insertCard(c, slot)
|
||||
|
||||
if diskImage != "" {
|
||||
diskette, err := LoadDiskette(diskImage)
|
||||
err := c.drive[0].insertDiskette(diskImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.drive[0].insertDiskette(diskImage, diskette)
|
||||
}
|
||||
|
||||
if diskBImage != "" {
|
||||
diskette, err := LoadDiskette(diskBImage)
|
||||
err := c.drive[1].insertDiskette(diskBImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.drive[1].insertDiskette(diskImage, diskette)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
17
cardDisk2.go
17
cardDisk2.go
@ -36,6 +36,10 @@ type CardDisk2 struct {
|
||||
trackTracer trackTracer
|
||||
}
|
||||
|
||||
type drive interface {
|
||||
insertDiskette(path string) error
|
||||
}
|
||||
|
||||
type cardDisk2Drive struct {
|
||||
name string
|
||||
diskette storage.Diskette
|
||||
@ -77,6 +81,9 @@ func (c *CardDisk2) reset() {
|
||||
}
|
||||
|
||||
func (c *CardDisk2) assign(a *Apple2, slot int) {
|
||||
a.registerRemovableMediaDrive(&c.drive[0])
|
||||
a.registerRemovableMediaDrive(&c.drive[1])
|
||||
|
||||
// Q1, Q2, Q3 and Q4 phase control soft switches,
|
||||
for i := uint8(0); i < 4; i++ {
|
||||
phase := i
|
||||
@ -225,7 +232,13 @@ func (c *CardDisk2) processQ6Q7(in uint8) {
|
||||
*/
|
||||
}
|
||||
|
||||
func (d *cardDisk2Drive) insertDiskette(name string, dt storage.Diskette) {
|
||||
func (d *cardDisk2Drive) insertDiskette(name string) error {
|
||||
diskette, err := LoadDiskette(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.name = name
|
||||
d.diskette = dt
|
||||
d.diskette = diskette
|
||||
return nil
|
||||
}
|
||||
|
@ -80,6 +80,9 @@ func (c *CardDisk2Sequencer) reset() {
|
||||
}
|
||||
|
||||
func (c *CardDisk2Sequencer) assign(a *Apple2, slot int) {
|
||||
a.registerRemovableMediaDrive(&c.drive[0])
|
||||
a.registerRemovableMediaDrive(&c.drive[1])
|
||||
|
||||
c.addCardSoftSwitches(func(address uint8, data uint8, write bool) uint8 {
|
||||
/*
|
||||
Slot card pins to SN74LS259 latch mapping:
|
||||
|
106
command.go
Normal file
106
command.go
Normal file
@ -0,0 +1,106 @@
|
||||
package izapple2
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
// CommandToggleSpeed toggles cpu speed between full speed and actual Apple II speed
|
||||
CommandToggleSpeed = iota + 1
|
||||
// CommandShowSpeed toggles printinf the current freq in Mhz
|
||||
CommandShowSpeed
|
||||
// CommandDumpDebugInfo dumps useful info
|
||||
CommandDumpDebugInfo
|
||||
// CommandNextCharGenPage cycles the CharGen page if several
|
||||
CommandNextCharGenPage
|
||||
// CommandToggleCPUTrace toggle tracing of CPU execution
|
||||
CommandToggleCPUTrace
|
||||
// CommandKill stops the cpu execution loop
|
||||
CommandKill
|
||||
// CommandReset executes a 6502 reset
|
||||
CommandReset
|
||||
// CommandPauseUnpause allows the Pause button to freeze the emulator for a coffee break
|
||||
CommandPauseUnpause
|
||||
// CommandPause pauses the emulator
|
||||
CommandPause
|
||||
// CommandStart restarts the emulator
|
||||
CommandStart
|
||||
// CommandComplex for commands that use a struct with parameters
|
||||
CommandComplex
|
||||
)
|
||||
|
||||
type command interface {
|
||||
getId() int
|
||||
}
|
||||
|
||||
type commandSimple struct {
|
||||
id int
|
||||
}
|
||||
|
||||
type commandLoadDisk struct {
|
||||
drive int
|
||||
path string
|
||||
}
|
||||
|
||||
func (c *commandSimple) getId() int {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *commandLoadDisk) getId() int {
|
||||
return CommandComplex
|
||||
}
|
||||
|
||||
func (a *Apple2) queueCommand(c command) {
|
||||
a.commandChannel <- c
|
||||
}
|
||||
|
||||
// SendCommand enqueues a command to the emulator thread
|
||||
func (a *Apple2) SendCommand(commandId int) {
|
||||
var c commandSimple
|
||||
c.id = commandId
|
||||
a.queueCommand(&c)
|
||||
}
|
||||
|
||||
func (a *Apple2) SendLoadDisk(drive int, path string) {
|
||||
var c commandLoadDisk
|
||||
c.drive = drive
|
||||
c.path = path
|
||||
a.queueCommand(&c)
|
||||
}
|
||||
|
||||
func (a *Apple2) executeCommand(command command) {
|
||||
switch command.getId() {
|
||||
case CommandToggleSpeed:
|
||||
if a.cycleDurationNs == 0 {
|
||||
//fmt.Println("Slow")
|
||||
a.cycleDurationNs = 1000.0 / CPUClockMhz
|
||||
} else {
|
||||
//fmt.Println("Fast")
|
||||
a.cycleDurationNs = 0
|
||||
}
|
||||
case CommandShowSpeed:
|
||||
a.showSpeed = !a.showSpeed
|
||||
case CommandDumpDebugInfo:
|
||||
a.dumpDebugInfo()
|
||||
case CommandNextCharGenPage:
|
||||
a.cg.nextPage()
|
||||
fmt.Printf("Chargen page %v\n", a.cg.page)
|
||||
case CommandToggleCPUTrace:
|
||||
a.cpu.SetTrace(!a.cpu.GetTrace())
|
||||
case CommandReset:
|
||||
a.reset()
|
||||
case CommandComplex:
|
||||
switch t := command.(type) {
|
||||
case *commandLoadDisk:
|
||||
err := a.changeDisk(t.drive, t.path)
|
||||
if err != nil {
|
||||
fmt.Printf("Could no load file %v\n%v\n", t.path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Apple2) changeDisk(unit int, path string) error {
|
||||
if unit < len(a.removableMediaDrives) {
|
||||
return a.removableMediaDrives[unit].insertDiskette(path)
|
||||
}
|
||||
return fmt.Errorf("Unit %v not defined", unit)
|
||||
}
|
@ -52,6 +52,7 @@ func sdlRun(a *izapple2.Apple2) {
|
||||
|
||||
go a.Run()
|
||||
|
||||
var x int32
|
||||
paused := false
|
||||
running := true
|
||||
for running {
|
||||
@ -73,9 +74,18 @@ func sdlRun(a *izapple2.Apple2) {
|
||||
w, h := window.GetSize()
|
||||
j.putMouseMotionEvent(t, w, h)
|
||||
m.putMouseMotionEvent(t, w, h)
|
||||
x = t.X
|
||||
case *sdl.MouseButtonEvent:
|
||||
j.putMouseButtonEvent(t)
|
||||
m.putMouseButtonEvent(t)
|
||||
case *sdl.DropEvent:
|
||||
switch t.Type {
|
||||
case sdl.DROPFILE:
|
||||
w, _ := window.GetSize()
|
||||
drive := int(2 * x / w)
|
||||
fmt.Printf("Loading '%s' in drive %v\n", t.File, drive+1)
|
||||
a.SendLoadDisk(drive, t.File)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
- Wings of Fury: Working
|
||||
- Stickybear Town Builder: Working
|
||||
- Optimal bit timing of WOZ 2.0
|
||||
- Border Zone: **Unknown, there is no UI to swap disks**
|
||||
- Border Zone: Slow (optimal bit timing not implemented)
|
||||
- 4am on Slack (2021-06-29)
|
||||
- Mr Do: Working
|
||||
- Wavy Navy: Working
|
||||
@ -68,7 +68,7 @@
|
||||
- Wings of Fury: ***Not working***
|
||||
- Stickybear Town Builder: Working
|
||||
- Optimal bit timing of WOZ 2.0
|
||||
- Border Zone: **Unknown, there is no UI to swap disks**
|
||||
- Border Zone: Slow (optimal bit timing not implemented)
|
||||
- 4am on Slack (2021-06-29)
|
||||
- Mr Do: ***Not working***
|
||||
- Wavy Navy: Working
|
||||
|
Loading…
Reference in New Issue
Block a user