From 758e4f79bbac37add052e8851473a6ca360152bc Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Wed, 14 Oct 2020 00:26:47 +0200 Subject: [PATCH] Card refactor and card panels --- apple2.go | 2 +- apple2Setup.go | 32 ++-- cardBase.go | 17 +- cardHardDisk.go | 342 +++++++++++++++++++--------------- cardThunderClockPlus.go | 21 ++- cardVidHD.go | 14 +- izapple2fyne/fyneJoysticks.go | 2 - izapple2fyne/panelCard.go | 30 +++ izapple2fyne/panelDevices.go | 12 +- izapple2fyne/panelJoystick.go | 1 - 10 files changed, 287 insertions(+), 186 deletions(-) create mode 100644 izapple2fyne/panelCard.go diff --git a/apple2.go b/apple2.go index bc3d62e..01673c6 100644 --- a/apple2.go +++ b/apple2.go @@ -14,7 +14,7 @@ type Apple2 struct { mmu *memoryManager io *ioC0Page cg *CharacterGenerator - cards [8]card + cards [8]Card isApple2e bool commandChannel chan int cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz diff --git a/apple2Setup.go b/apple2Setup.go index 45f1d39..1b8a892 100644 --- a/apple2Setup.go +++ b/apple2Setup.go @@ -55,11 +55,16 @@ func setApple2eEnhanced(a *Apple2) { addApple2ESoftSwitches(a.io) } -func (a *Apple2) insertCard(c card, slot int) { +func (a *Apple2) insertCard(c Card, slot int) { c.assign(a, slot) a.cards[slot] = c } +// GetCards returns the array of inserted cards +func (a *Apple2) GetCards() [8]Card { + return a.cards +} + const ( apple2RomSize = 12 * 1024 apple2eRomSize = 16 * 1024 @@ -113,24 +118,20 @@ func (a *Apple2) AddDisk2(slot int, diskRomFile string, diskImage, diskBImage st // AddSmartPortDisk adds a smart port card and image func (a *Apple2) AddSmartPortDisk(slot int, hdImage string, trace bool) error { - var c cardHardDisk - c.setTrace(trace) - c.loadRom(buildHardDiskRom(slot)) - a.insertCard(&c, slot) - - hd, err := openBlockDisk(hdImage) + c := NewCardHardDisk() + c.trace = trace + err := c.LoadImage(hdImage) if err != nil { return err } - c.addDisk(hd) + a.insertCard(c, slot) return nil } // AddVidHD adds a card with the signature of VidHD func (a *Apple2) AddVidHD(slot int) { - var c cardVidHD - c.loadRom(buildVidHDRom()) - a.insertCard(&c, slot) + c := NewCardVidHD() + a.insertCard(c, slot) } // AddFastChip adds a card with the signature of VidHD @@ -164,13 +165,8 @@ func (a *Apple2) AddMemoryExpansionCard(slot int, romFile string) error { // AddThunderClockPlusCard inserts a ThunderClock Plus clock card func (a *Apple2) AddThunderClockPlusCard(slot int, romFile string) error { - var c cardThunderClockPlus - data, err := loadResource(romFile) - if err != nil { - return err - } - c.loadRom(data) - a.insertCard(&c, slot) + c := NewCardThunderClockPlus() + a.insertCard(c, slot) return nil } diff --git a/cardBase.go b/cardBase.go index d85cb2d..f17796f 100644 --- a/cardBase.go +++ b/cardBase.go @@ -1,12 +1,17 @@ package izapple2 -type card interface { +// Card represents an Apple II card to be inserted in a slot +type Card interface { loadRom(data []uint8) assign(a *Apple2, slot int) + + GetName() string + GetInfo() map[string]string } type cardBase struct { a *Apple2 + name string romCsxx *memoryRangeROM romC8xx *memoryRangeROM romCxxx *memoryRangeROM @@ -17,6 +22,14 @@ type cardBase struct { _sswName [16]string } +func (c *cardBase) GetName() string { + return c.name +} + +func (c *cardBase) GetInfo() map[string]string { + return nil +} + func (c *cardBase) loadRom(data []uint8) { if c.a != nil { panic("Assert failed. Rom must be loaded before inserting the card in the slot") @@ -27,7 +40,7 @@ func (c *cardBase) loadRom(data []uint8) { } else if len(data) == 0x800 { // The file has C800 to C8FF // The 256 bytes in Cx00 are copied from the first page in C800 - c.romCsxx = newMemoryRangeROM(0, data, "Slor ROM") + c.romCsxx = newMemoryRangeROM(0, data, "Slot ROM") c.romC8xx = newMemoryRangeROM(0xc800, data, "Slot C8 ROM") } else if len(data) == 0x1000 { // The file covers the full Cxxx range. Only showing the page diff --git a/cardHardDisk.go b/cardHardDisk.go index 852c6c5..6e75418 100644 --- a/cardHardDisk.go +++ b/cardHardDisk.go @@ -1,6 +1,9 @@ package izapple2 -import "fmt" +import ( + "fmt" + "strconv" +) /* To implement a hard drive we just have to support boot from #PR7 and the PRODOS expextations. @@ -14,11 +17,193 @@ See: */ -type cardHardDisk struct { +// CardHardDisk represents a SmartPort card +type CardHardDisk struct { cardBase + + filename string + trace bool + + config CardHardDiskConfig disk *blockDisk mliParams uint16 - trace bool +} + +// CardHardDiskConfig represents a SmartPort card configuration +type CardHardDiskConfig struct { + Filename string + Trace bool +} + +// NewCardHardDisk creates a new SmartPort card +func NewCardHardDisk() *CardHardDisk { + var c CardHardDisk + c.name = "Smartport Card" + return &c +} + +// GetInfo return smartport info +func (c *CardHardDisk) GetInfo() map[string]string { + info := make(map[string]string) + info["filename"] = c.filename + info["trace"] = strconv.FormatBool(c.trace) + return info +} + +// LoadImage loads a disk image +func (c *CardHardDisk) LoadImage(filename string) error { + hd, err := openBlockDisk(filename) + if err != nil { + return err + } + c.disk = hd + c.filename = filename + return nil +} + +const ( + proDosDeviceCommandStatus = 0 + proDosDeviceCommandRead = 1 + proDosDeviceCommandWrite = 2 + proDosDeviceCommandFormat = 3 +) + +const ( + proDosDeviceNoError = uint8(0) + proDosDeviceErrorIO = uint8(0x27) + proDosDeviceErrorNoDevice = uint8(0x28) + proDosDeviceErrorWriteProtected = uint8(0x2b) +) + +func (c *CardHardDisk) assign(a *Apple2, slot int) { + c.loadRom(buildHardDiskRom(slot)) + + c.addCardSoftSwitchR(0, func(*ioC0Page) uint8 { + // Prodos entry point + command := a.mmu.Peek(0x42) + unit := a.mmu.Peek(0x43) + address := uint16(a.mmu.Peek(0x44)) + uint16(a.mmu.Peek(0x45))<<8 + block := uint16(a.mmu.Peek(0x46)) + uint16(a.mmu.Peek(0x47))<<8 + if c.config.Trace { + fmt.Printf("[CardHardDisk] Prodos command %v on slot %v, unit $%x, block %v to $%x.\n", command, slot, unit, block, address) + } + + switch command { + case proDosDeviceCommandStatus: + return c.status(unit, address) + case proDosDeviceCommandRead: + return c.readBlock(block, address) + case proDosDeviceCommandWrite: + return c.writeBlock(block, address) + default: + // Prodos device command not supported + return proDosDeviceErrorIO + } + }, "HDCOMMAND") + c.addCardSoftSwitchR(1, func(*ioC0Page) uint8 { + // Blocks available, low byte + return uint8(c.disk.blocks) + }, "HDBLOCKSLO") + c.addCardSoftSwitchR(2, func(*ioC0Page) uint8 { + // Blocks available, high byte + return uint8(c.disk.blocks >> 8) + }, "HDBLOCKHI") + + c.addCardSoftSwitchR(3, func(*ioC0Page) uint8 { + // Smart port entry point + command := c.a.mmu.Peek(c.mliParams + 1) + paramsAddress := uint16(c.a.mmu.Peek(c.mliParams+2)) + uint16(c.a.mmu.Peek(c.mliParams+3))<<8 + unit := a.mmu.Peek(paramsAddress + 1) + address := uint16(a.mmu.Peek(paramsAddress+2)) + uint16(a.mmu.Peek(paramsAddress+3))<<8 + block := uint16(a.mmu.Peek(paramsAddress+4)) + uint16(a.mmu.Peek(paramsAddress+5))<<8 + if c.config.Trace { + fmt.Printf("[CardHardDisk] Smart port command %v on slot %v, unit $%x, block %v to $%x.\n", command, slot, unit, block, address) + } + + switch command { + case proDosDeviceCommandStatus: + return c.status(unit, address) + case proDosDeviceCommandRead: + return c.readBlock(block, address) + case proDosDeviceCommandWrite: + return c.writeBlock(block, address) + default: + // Smartport device command not supported + return proDosDeviceErrorIO + } + }, "HDSMARTPORT") + c.addCardSoftSwitchW(4, func(_ *ioC0Page, value uint8) { + c.mliParams = (c.mliParams & 0xff00) + uint16(value) + if c.config.Trace { + fmt.Printf("[CardHardDisk] Smart port LO: 0x%x.\n", c.mliParams) + } + }, "HDSMARTPORTLO") + c.addCardSoftSwitchW(5, func(_ *ioC0Page, value uint8) { + c.mliParams = (c.mliParams & 0x00ff) + (uint16(value) << 8) + if c.config.Trace { + fmt.Printf("[CardHardDisk] Smart port HI: 0x%x.\n", c.mliParams) + } + }, "HDSMARTPORTHI") + + c.cardBase.assign(a, slot) +} + +func (c *CardHardDisk) readBlock(block uint16, dest uint16) uint8 { + if c.config.Trace { + fmt.Printf("[CardHardDisk] Read block %v into $%x.\n", block, dest) + } + + data, err := c.disk.read(uint32(block)) + if err != nil { + return proDosDeviceErrorIO + } + // Byte by byte transfer to memory using the full Poke code path + for i := uint16(0); i < uint16(proDosBlockSize); i++ { + c.a.mmu.Poke(dest+i, data[i]) + } + + return proDosDeviceNoError +} + +func (c *CardHardDisk) writeBlock(block uint16, source uint16) uint8 { + if c.config.Trace { + fmt.Printf("[CardHardDisk] Write block %v from $%x.\n", block, source) + } + + if c.disk.readOnly { + return proDosDeviceErrorWriteProtected + } + + // Byte by byte transfer from memory using the full Peek code path + buf := make([]uint8, proDosBlockSize) + for i := uint16(0); i < uint16(proDosBlockSize); i++ { + buf[i] = c.a.mmu.Peek(source + i) + } + + err := c.disk.write(uint32(block), buf) + if err != nil { + return proDosDeviceErrorIO + } + + return proDosDeviceNoError +} + +func (c *CardHardDisk) status(unit uint8, dest uint16) uint8 { + if c.config.Trace { + fmt.Printf("[CardHardDisk] Status for %v into $%x.\n", unit, dest) + } + + // See http://www.1000bit.it/support/manuali/apple/technotes/smpt/tn.smpt.2.html + c.a.mmu.Poke(dest+0, 0x02) // One device + c.a.mmu.Poke(dest+1, 0xff) // No interrupt + c.a.mmu.Poke(dest+2, 0x00) + c.a.mmu.Poke(dest+3, 0x00) // Unknown manufacturer + c.a.mmu.Poke(dest+4, 0x01) + c.a.mmu.Poke(dest+5, 0x00) // Versión 1.0 final + c.a.mmu.Poke(dest+6, 0x00) + c.a.mmu.Poke(dest+7, 0x00) // Reserved + + return proDosDeviceNoError } func buildHardDiskRom(slot int) []uint8 { @@ -98,154 +283,3 @@ func buildHardDiskRom(slot int) []uint8 { return data } - -const ( - proDosDeviceCommandStatus = 0 - proDosDeviceCommandRead = 1 - proDosDeviceCommandWrite = 2 - proDosDeviceCommandFormat = 3 -) - -const ( - proDosDeviceNoError = uint8(0) - proDosDeviceErrorIO = uint8(0x27) - proDosDeviceErrorNoDevice = uint8(0x28) - proDosDeviceErrorWriteProtected = uint8(0x2b) -) - -func (c *cardHardDisk) assign(a *Apple2, slot int) { - c.addCardSoftSwitchR(0, func(*ioC0Page) uint8 { - // Prodos entry point - command := a.mmu.Peek(0x42) - unit := a.mmu.Peek(0x43) - address := uint16(a.mmu.Peek(0x44)) + uint16(a.mmu.Peek(0x45))<<8 - block := uint16(a.mmu.Peek(0x46)) + uint16(a.mmu.Peek(0x47))<<8 - if c.trace { - fmt.Printf("[CardHardDisk] Prodos command %v on slot %v, unit $%x, block %v to $%x.\n", command, slot, unit, block, address) - } - - switch command { - case proDosDeviceCommandStatus: - return c.status(unit, address) - case proDosDeviceCommandRead: - return c.readBlock(block, address) - case proDosDeviceCommandWrite: - return c.writeBlock(block, address) - default: - // Prodos device command not supported - return proDosDeviceErrorIO - } - }, "HDCOMMAND") - c.addCardSoftSwitchR(1, func(*ioC0Page) uint8 { - // Blocks available, low byte - return uint8(c.disk.blocks) - }, "HDBLOCKSLO") - c.addCardSoftSwitchR(2, func(*ioC0Page) uint8 { - // Blocks available, high byte - return uint8(c.disk.blocks >> 8) - }, "HDBLOCKHI") - - c.addCardSoftSwitchR(3, func(*ioC0Page) uint8 { - // Smart port entry point - command := c.a.mmu.Peek(c.mliParams + 1) - paramsAddress := uint16(c.a.mmu.Peek(c.mliParams+2)) + uint16(c.a.mmu.Peek(c.mliParams+3))<<8 - unit := a.mmu.Peek(paramsAddress + 1) - address := uint16(a.mmu.Peek(paramsAddress+2)) + uint16(a.mmu.Peek(paramsAddress+3))<<8 - block := uint16(a.mmu.Peek(paramsAddress+4)) + uint16(a.mmu.Peek(paramsAddress+5))<<8 - if c.trace { - fmt.Printf("[CardHardDisk] Smart port command %v on slot %v, unit $%x, block %v to $%x.\n", command, slot, unit, block, address) - } - - switch command { - case proDosDeviceCommandStatus: - return c.status(unit, address) - case proDosDeviceCommandRead: - return c.readBlock(block, address) - case proDosDeviceCommandWrite: - return c.writeBlock(block, address) - default: - // Smartport device command not supported - return proDosDeviceErrorIO - } - }, "HDSMARTPORT") - c.addCardSoftSwitchW(4, func(_ *ioC0Page, value uint8) { - c.mliParams = (c.mliParams & 0xff00) + uint16(value) - if c.trace { - fmt.Printf("[CardHardDisk] Smart port LO: 0x%x.\n", c.mliParams) - } - }, "HDSMARTPORTLO") - c.addCardSoftSwitchW(5, func(_ *ioC0Page, value uint8) { - c.mliParams = (c.mliParams & 0x00ff) + (uint16(value) << 8) - if c.trace { - fmt.Printf("[CardHardDisk] Smart port HI: 0x%x.\n", c.mliParams) - } - }, "HDSMARTPORTHI") - - c.cardBase.assign(a, slot) -} - -func (c *cardHardDisk) readBlock(block uint16, dest uint16) uint8 { - if c.trace { - fmt.Printf("[CardHardDisk] Read block %v into $%x.\n", block, dest) - } - - data, err := c.disk.read(uint32(block)) - if err != nil { - return proDosDeviceErrorIO - } - // Byte by byte transfer to memory using the full Poke code path - for i := uint16(0); i < uint16(proDosBlockSize); i++ { - c.a.mmu.Poke(dest+i, data[i]) - } - - return proDosDeviceNoError -} - -func (c *cardHardDisk) writeBlock(block uint16, source uint16) uint8 { - if c.trace { - fmt.Printf("[CardHardDisk] Write block %v from $%x.\n", block, source) - } - - if c.disk.readOnly { - return proDosDeviceErrorWriteProtected - } - - // Byte by byte transfer from memory using the full Peek code path - buf := make([]uint8, proDosBlockSize) - for i := uint16(0); i < uint16(proDosBlockSize); i++ { - buf[i] = c.a.mmu.Peek(source + i) - } - - err := c.disk.write(uint32(block), buf) - if err != nil { - return proDosDeviceErrorIO - } - - return proDosDeviceNoError -} - -func (c *cardHardDisk) status(unit uint8, dest uint16) uint8 { - if c.trace { - fmt.Printf("[CardHardDisk] Status for %v into $%x.\n", unit, dest) - } - - // See http://www.1000bit.it/support/manuali/apple/technotes/smpt/tn.smpt.2.html - c.a.mmu.Poke(dest+0, 0x02) // One device - c.a.mmu.Poke(dest+1, 0xff) // No interrupt - c.a.mmu.Poke(dest+2, 0x00) - c.a.mmu.Poke(dest+3, 0x00) // Unknown manufacturer - c.a.mmu.Poke(dest+4, 0x01) - c.a.mmu.Poke(dest+5, 0x00) // Versión 1.0 final - c.a.mmu.Poke(dest+6, 0x00) - c.a.mmu.Poke(dest+7, 0x00) // Reserved - - return proDosDeviceNoError -} - -func (c *cardHardDisk) addDisk(disk *blockDisk) { - c.disk = disk -} - -func (c *cardHardDisk) setTrace(trace bool) { - c.trace = trace -} diff --git a/cardThunderClockPlus.go b/cardThunderClockPlus.go index bb8f9dc..f640234 100644 --- a/cardThunderClockPlus.go +++ b/cardThunderClockPlus.go @@ -19,12 +19,27 @@ uPD1990AC hookup: bit 7 = data out */ -type cardThunderClockPlus struct { - microPD1990ac +// CardThunderClockPlus represents a ThunderClock+ card +type CardThunderClockPlus struct { cardBase + microPD1990ac } -func (c *cardThunderClockPlus) assign(a *Apple2, slot int) { +// NewCardThunderClockPlus creates a new CardThunderClockPlus +func NewCardThunderClockPlus() *CardThunderClockPlus { + var c CardThunderClockPlus + c.name = "ThunderClock+ Card" + + data, err := loadResource("/ThunderclockPlusROM.bin") + if err != nil { + panic(err) + } + c.loadRom(data) + + return &c +} + +func (c *CardThunderClockPlus) assign(a *Apple2, slot int) { c.addCardSoftSwitchR(0, func(*ioC0Page) uint8 { bit := c.microPD1990ac.out() // Get the next data bit from uPD1990AC on the MSB diff --git a/cardVidHD.go b/cardVidHD.go index 9ca5d7c..bd2766f 100644 --- a/cardVidHD.go +++ b/cardVidHD.go @@ -8,10 +8,18 @@ See: http://www.applelogic.org/files/GSHARDWAREREF.pdf, page 89 */ -type cardVidHD struct { +// CardVidHD represents a VidHD card +type CardVidHD struct { cardBase } +// NewCardVidHD creates a new VidHD card +func NewCardVidHD() *CardVidHD { + var c CardVidHD + c.name = "VidHD Card" + return &c +} + func buildVidHDRom() []uint8 { data := make([]uint8, 256) @@ -26,7 +34,9 @@ const ( ioDataNewVideo uint8 = 0x29 ) -func (c *cardVidHD) assign(a *Apple2, slot int) { +func (c *CardVidHD) assign(a *Apple2, slot int) { + c.loadRom(buildVidHDRom()) + // The softswitches are outside the card reserved ss a.io.addSoftSwitchR(0x22, notImplementedSoftSwitchR, "VIDHD-TBCOLOR") a.io.addSoftSwitchW(0x22, notImplementedSoftSwitchW, "VIDHD-TBCOLOR") diff --git a/izapple2fyne/fyneJoysticks.go b/izapple2fyne/fyneJoysticks.go index fe525b4..96fbe1c 100644 --- a/izapple2fyne/fyneJoysticks.go +++ b/izapple2fyne/fyneJoysticks.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "runtime" "time" @@ -109,6 +108,5 @@ func (j *joysticks) ReadPaddle(i int) (uint8, bool) { if info != nil { value = info.paddles[i%2] } - fmt.Printf("%v, %v, %v\n", i, j.info[0], value) return value, value != unplugged } diff --git a/izapple2fyne/panelCard.go b/izapple2fyne/panelCard.go new file mode 100644 index 0000000..436c7bf --- /dev/null +++ b/izapple2fyne/panelCard.go @@ -0,0 +1,30 @@ +package main + +import ( + "strconv" + + "fyne.io/fyne" + "fyne.io/fyne/widget" + "github.com/ivanizag/izapple2" +) + +type panelCard struct { + w fyne.Widget +} + +func newPanelCard(slot int, card izapple2.Card) *panelCard { + var pc panelCard + + form := widget.NewForm() + form.Append("slot", widget.NewLabel(strconv.Itoa(slot))) + + info := card.GetInfo() + if info != nil { + for k, v := range info { + form.Append(k, widget.NewLabel(v)) + } + } + + pc.w = widget.NewGroup(card.GetName(), form) + return &pc +} diff --git a/izapple2fyne/panelDevices.go b/izapple2fyne/panelDevices.go index c1c60c9..12225b3 100644 --- a/izapple2fyne/panelDevices.go +++ b/izapple2fyne/panelDevices.go @@ -1,23 +1,29 @@ package main import ( - "fyne.io/fyne" "fyne.io/fyne/widget" ) type panelDevices struct { s *state - w fyne.Widget + w *widget.Box joystick *panelJoystick } func newPanelDevices(s *state) *panelDevices { var pd panelDevices pd.s = s + pd.w = widget.NewVBox() pd.joystick = newPanelJoystick() + pd.w.Append(pd.joystick.w) - pd.w = widget.NewVBox(pd.joystick.w) + var cards = s.a.GetCards() + for i, card := range cards { + if card != nil && card.GetName() != "" { + pd.w.Append(newPanelCard(i, card).w) + } + } return &pd } diff --git a/izapple2fyne/panelJoystick.go b/izapple2fyne/panelJoystick.go index 1020266..65457e8 100644 --- a/izapple2fyne/panelJoystick.go +++ b/izapple2fyne/panelJoystick.go @@ -18,7 +18,6 @@ func newPanelJoystick() *panelJoystick { pj.labelJoy1 = widget.NewLabel("") pj.labelJoy2 = widget.NewLabel("") - widget.NewForm() pj.w = widget.NewGroup( "Joysticks", widget.NewForm(