diff --git a/.gitignore b/.gitignore index a8a9d6b..2f51cb3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ frontend/a2fyne/a2fyne frontend/headless/headless frontend/*/snapshot.gif frontend/*/snapshot.png -frontend/*/printer.out \ No newline at end of file +frontend/*/printer.out +.DS_STORE diff --git a/README.md b/README.md index 2e7fb6f..dbcd3a5 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/apple2.go b/apple2.go index 0582be5..74f6cb2 100644 --- a/apple2.go +++ b/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() } } diff --git a/apple2Setup.go b/apple2Setup.go index 1cf1d65..4f3d364 100644 --- a/apple2Setup.go +++ b/apple2Setup.go @@ -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 diff --git a/cardDisk2.go b/cardDisk2.go index b6f7489..c75c24b 100644 --- a/cardDisk2.go +++ b/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 } diff --git a/cardDisk2Sequencer.go b/cardDisk2Sequencer.go index 8418562..5a94768 100644 --- a/cardDisk2Sequencer.go +++ b/cardDisk2Sequencer.go @@ -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: diff --git a/command.go b/command.go new file mode 100644 index 0000000..bdee80c --- /dev/null +++ b/command.go @@ -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) +} diff --git a/frontend/a2sdl/main.go b/frontend/a2sdl/main.go index bc567cc..c0aeebc 100644 --- a/frontend/a2sdl/main.go +++ b/frontend/a2sdl/main.go @@ -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) + } } } diff --git a/storage/WozSupportStatus.md b/storage/WozSupportStatus.md index 763adc6..7921e8b 100644 --- a/storage/WozSupportStatus.md +++ b/storage/WozSupportStatus.md @@ -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