diff --git a/apple2Setup.go b/apple2Setup.go index 879b7c6..09d5db1 100644 --- a/apple2Setup.go +++ b/apple2Setup.go @@ -191,6 +191,12 @@ func (a *Apple2) AddVidexCard(slot int) { a.softVideoSwitch = NewSoftVideoSwitch(c) } +// AddSwyftCard inserts a Swyft card in slot 3 +func (a *Apple2) AddSwyftCard() { + c := NewCardSwyft() + a.insertCard(c, 3) +} + // AddRGBCard inserts an RBG option to the Apple IIe 80 col 64KB card func (a *Apple2) AddRGBCard() { setupRGBCard(a) diff --git a/apple2Tester.go b/apple2Tester.go index f92ceb9..fdb2a4f 100644 --- a/apple2Tester.go +++ b/apple2Tester.go @@ -35,3 +35,7 @@ func (at *apple2Tester) run() { func (at *apple2Tester) getText() string { return screen.RenderTextModeString(at.a, false, false, false, at.a.isApple2e) } + +func (at *apple2Tester) getText80() string { + return screen.RenderTextModeString(at.a, true, false, false, at.a.isApple2e) +} diff --git a/apple2main.go b/apple2main.go index b25b5ee..8ef518e 100644 --- a/apple2main.go +++ b/apple2main.go @@ -18,7 +18,7 @@ func MainApple() *Apple2 { "slot for the disk driver. -1 for none.") diskImage := flag.String( "disk", - "/dos33.dsk", + defaultInternal, "file to load on the first disk drive") diskBImage := flag.String( "diskb", @@ -84,6 +84,10 @@ func MainApple() *Apple2 { "videxCardSlot", 3, "slot for the Videx Videoterm 80 columns card. For pre-2e models. -1 for none") + swyftCard := flag.Bool( + "swyftCard", + false, + "activate a Swyft Card in slot 3. Load the tutorial disk if none provided") nsc := flag.Int( "nsc", -1, @@ -169,6 +173,15 @@ func MainApple() *Apple2 { } } + // Resolve what is the default disk to use if not specified + if diskImageFinal == defaultInternal { + if *swyftCard { + diskImageFinal = "/SwyftWare_-_SwyftCard_Tutorial.woz" + } else { + diskImageFinal = "/dos33.dsk" + } + } + a := newApple2() a.setup(*cpuClock, *fastDisk) a.io.setTrace(*traceSS) @@ -232,6 +245,12 @@ func MainApple() *Apple2 { if *videxCardSlot > 0 { a.AddVidexCard(*videxCardSlot) } + if *swyftCard { + if !a.isApple2e { + panic("SwyftCard available only on Apple IIe or better") + } + a.AddSwyftCard() + } if *smartPortImage != "" { err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD) @@ -354,7 +373,7 @@ func initModel(a *Apple2, model string, romFile string, charRomFile string) { panic("Model not supported") } - // Load ROM if not loaded already + // Load ROM if romFile != "" { err := a.LoadRom(romFile) if err != nil { @@ -362,7 +381,7 @@ func initModel(a *Apple2, model string, romFile string, charRomFile string) { } } - // Load character generator if it loaded already + // Load character generator cg, err := newCharacterGenerator(charRomFile, charGenMap, a.isApple2e) if err != nil { panic(err) diff --git a/cardBase.go b/cardBase.go index 072c6a0..39b06ca 100644 --- a/cardBase.go +++ b/cardBase.go @@ -114,6 +114,16 @@ func (c *cardBase) addCardSoftSwitchW(address uint8, ss softSwitchW, name string c._sswName[address] = name } +func (c *cardBase) addCardSoftSwitchRW(address uint8, ss softSwitchR, name string) { + c._ssr[address] = ss + c._ssrName[address] = name + + c._ssw[address] = func(p *ioC0Page, _ uint8) { + ss(p) + } + c._sswName[address] = name +} + type softSwitches func(io *ioC0Page, address uint8, data uint8, write bool) uint8 func (c *cardBase) addCardSoftSwitches(sss softSwitches, name string) { diff --git a/cardSwyft.go b/cardSwyft.go new file mode 100644 index 0000000..327f667 --- /dev/null +++ b/cardSwyft.go @@ -0,0 +1,133 @@ +package izapple2 + +/* + Swyft card for Apple IIe + +See: + https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Interface%20Cards/Other/IAI%20SwyftCard/ + +*/ + +/* +"SwyftCard Hardware Theory of Operation". SwyftCard manual, page 98: + +The SwyftCard is a plug-in card for the Apple /Ie that operates in +slot 3. The card contains three integrated circuits which provide a +power-on reset circuit, storage for the SwyftCard program, and control +signals for the card. The card operates by asserting the Apple IIe bus +signal INH' which disables the built-in ROM and enables the SwyftCard +ROM. This permits the SwyftCard program to take over the system at +power-on and run the SwyftCard program. (Please refer to the +schematic.) + +The lM311 voltage comparator is connected to provide the power-on +reset function. When the Apple lie is first turned on, the power-on +reset circuit resets the PAL, turning on the SwyftCard and disabling +the Apple IIe internal ROM. The power-on reset circuit must be +provided because the existing Apple IIe reset function is used by +many Apple lie programs for a "warm start": if Apple lie reset always +started the SwyftCard, other programs could not use the "warm start." + +The 27128 PROM is used to store the SwyftCard program. The PROM +contains 16384 bytes which are mapped into the address space +$DOOO - $FFFF. Since the address space is only 12 Kbytes, there are +two 4 Kbyte sections of the PROM mapped into the address space +$DOOO-$DFFF. + +The card is controlled by the PAL. When the SwyftCard is active, the +PAL asserts the INH' signal, enables the PROM, and bank switches +the $DOOO-$DFFF address space. The card is controlled by two soft +switches. The soft switches are controlled by accessing the following +memory locations with either a read or a write operation. + + $COBO - SwyftCard active, Bank 1 + $COB1 - SwyftCard inactive, Bank 1 + $COB2 - SwyftCard active, Bank 2 + +When the power-on reset circuit asserts the RES signal on Pin 3 of the +PAL, the SwyftCard is made active in Bank 1. Accessing location +$COB1 deactivates the SwyftCard for normal Apple IIe operation. + +The INH' line is driven by a tri-state driver, so if another card in the +Apple /Ie asserts the IINH' signal there will not be a bus contention. +However, there will be a bus contention on the data bus if another card +attempts to control the bus while the SwyftCard is active. + +*/ + +// CardSwyft represents a Swyft card +type CardSwyft struct { + cardBase + bank2 bool + rom []uint8 +} + +// NewCardSwyft creates a new CardSwyft +func NewCardSwyft() *CardSwyft { + var c CardSwyft + c.name = "SwyftCard" + + // The Cx00 rom is not used. The card is expected to be installed in + // slot 3 of an Apple IIe with the 80 column firmware already present. + return &c +} + +func (c *CardSwyft) assign(a *Apple2, slot int) { + // Load main ROM replacement + data, _, err := LoadResource("/SwyftCard ROM.bin") + if err != nil { + // The resource should be internal and never fail + panic(err) + } + c.rom = data + + c.addCardSoftSwitchRW(0, func(*ioC0Page) uint8 { + a.mmu.inhibitROM(c) + c.bank2 = false + return 0x55 + }, "SWYFTONBANK1") + + c.addCardSoftSwitchRW(1, func(*ioC0Page) uint8 { + a.mmu.inhibitROM(nil) + c.bank2 = false + return 0x55 + }, "SWYFTOFFBANK1") + + c.addCardSoftSwitchRW(2, func(*ioC0Page) uint8 { + a.mmu.inhibitROM(c) + c.bank2 = true + return 0x55 + }, "SWYFTONBANK2") + + c.cardBase.assign(a, slot) + a.mmu.inhibitROM(c) +} + +func (c *CardSwyft) translateAddress(address uint16) uint16 { + /* + The four 4k sections of the 16k ROM image are mapped: + D000-DFFF (page 1) + D000-DFFF (page 2) + E000-EFFF + F000-FFFF + */ + if address >= 0xE000 { + return address - 0xE000 + 0x2000 + } + if !c.bank2 { + return address - 0xD000 + } + return address - 0xD000 + 0x1000 +} + +func (c *CardSwyft) peek(address uint16) uint8 { + return c.rom[c.translateAddress(address)] +} + +func (c *CardSwyft) poke(address uint16, value uint8) { + // Nothing +} + +func (c *CardSwyft) setBase(base uint16) { + // Nothing +} diff --git a/cardSwyft_test.go b/cardSwyft_test.go new file mode 100644 index 0000000..c310a50 --- /dev/null +++ b/cardSwyft_test.go @@ -0,0 +1,26 @@ +package izapple2 + +import ( + "strings" + "testing" +) + +func TestSwyftTutorial(t *testing.T) { + at := makeApple2Tester("2e") + at.a.AddSwyftCard() + err := at.a.AddDisk2(6, "/SwyftWare_-_SwyftCard_Tutorial.woz", "", nil) + if err != nil { + panic(err) + } + + at.terminateCondition = func(a *Apple2) bool { + return a.cpu.GetCycles() > 10_000_000 + } + at.run() + + text := at.getText80() + if !strings.Contains(text, "HOW TO USE SWYFTCARD") { + t.Errorf("Expected 'HOW TO USE SWYFTCARD', got '%s'", text) + } + +} diff --git a/memoryManager.go b/memoryManager.go index d6914a9..50142f6 100644 --- a/memoryManager.go +++ b/memoryManager.go @@ -33,15 +33,16 @@ type memoryManager struct { lcAltBank bool // Alternate // Configuration switches, Apple //e - altZeroPage bool // Use extra RAM from 0x0000 to 0x01ff. And additional language card block - altMainRAMActiveRead bool // Use extra RAM from 0x0200 to 0xbfff for read - altMainRAMActiveWrite bool // Use extra RAM from 0x0200 to 0xbfff for write - store80Active bool // Special pagination for text and graphics areas - slotC3ROMActive bool // Apple2e slot 3 ROM shadow - intCxROMActive bool // Apple2e slots internal ROM shadow - intC8ROMActive bool // C8Rom associated to the internal slot 3. Softswitch not directly accessible. See UtA2e 5-28 - activeSlot uint8 // Active slot owner of 0xc800 to 0xcfff - extendedRAMBlock uint8 // Block used for entended memory for RAMWorks cards + altZeroPage bool // Use extra RAM from 0x0000 to 0x01ff. And additional language card block + altMainRAMActiveRead bool // Use extra RAM from 0x0200 to 0xbfff for read + altMainRAMActiveWrite bool // Use extra RAM from 0x0200 to 0xbfff for write + store80Active bool // Special pagination for text and graphics areas + slotC3ROMActive bool // Apple2e slot 3 ROM shadow + intCxROMActive bool // Apple2e slots internal ROM shadow + intC8ROMActive bool // C8Rom associated to the internal slot 3. Softswitch not directly accessible. See UtA2e 5-28 + activeSlot uint8 // Active slot owner of 0xc800 to 0xcfff + extendedRAMBlock uint8 // Block used for entended memory for RAMWorks cards + mainROMinhibited memoryHandler // Alternative ROM from 0xd000 to 0xffff provided by a card with the INH signal. // Configuration switches, Base64A romPage uint8 // Active ROM page @@ -151,6 +152,12 @@ func (mmu *memoryManager) getVideoRAM(ext bool) *memoryRange { return mmu.physicalMainRAM } +func (mmu *memoryManager) inhibitROM(replacement memoryHandler) { + // If a card INH the ROM, it replaces the ROM and the LC RAM + mmu.mainROMinhibited = replacement + mmu.lastAddressPage = invalidAddressPage // Invalidate cache +} + func (mmu *memoryManager) accessRead(address uint16) memoryHandler { if address <= addressLimitZero { return mmu.getPhysicalMainRAM(mmu.altZeroPage) @@ -175,6 +182,9 @@ func (mmu *memoryManager) accessRead(address uint16) memoryHandler { if address <= addressLimitSlotsExtra { return mmu.accessCArea(address) } + if mmu.mainROMinhibited != nil { + return mmu.mainROMinhibited + } if mmu.lcActiveRead { return mmu.accessUpperRAMArea(address) } @@ -205,6 +215,9 @@ func (mmu *memoryManager) accessWrite(address uint16) memoryHandler { if address <= addressLimitSlotsExtra { return mmu.accessCArea(address) } + if mmu.mainROMinhibited != nil { + return mmu.mainROMinhibited + } if mmu.lcActiveWrite { return mmu.accessUpperRAMArea(address) } diff --git a/resources/SwyftCard ROM.bin b/resources/SwyftCard ROM.bin new file mode 100644 index 0000000..8d5adcf Binary files /dev/null and b/resources/SwyftCard ROM.bin differ diff --git a/resources/SwyftWare_-_SwyftCard_Tutorial.woz b/resources/SwyftWare_-_SwyftCard_Tutorial.woz new file mode 100644 index 0000000..f831ad5 Binary files /dev/null and b/resources/SwyftWare_-_SwyftCard_Tutorial.woz differ