Swyftcard support (#13)
This commit is contained in:
parent
fe15ce8c93
commit
1e32421c86
|
@ -191,6 +191,12 @@ func (a *Apple2) AddVidexCard(slot int) {
|
||||||
a.softVideoSwitch = NewSoftVideoSwitch(c)
|
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
|
// AddRGBCard inserts an RBG option to the Apple IIe 80 col 64KB card
|
||||||
func (a *Apple2) AddRGBCard() {
|
func (a *Apple2) AddRGBCard() {
|
||||||
setupRGBCard(a)
|
setupRGBCard(a)
|
||||||
|
|
|
@ -35,3 +35,7 @@ func (at *apple2Tester) run() {
|
||||||
func (at *apple2Tester) getText() string {
|
func (at *apple2Tester) getText() string {
|
||||||
return screen.RenderTextModeString(at.a, false, false, false, at.a.isApple2e)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ func MainApple() *Apple2 {
|
||||||
"slot for the disk driver. -1 for none.")
|
"slot for the disk driver. -1 for none.")
|
||||||
diskImage := flag.String(
|
diskImage := flag.String(
|
||||||
"disk",
|
"disk",
|
||||||
"<internal>/dos33.dsk",
|
defaultInternal,
|
||||||
"file to load on the first disk drive")
|
"file to load on the first disk drive")
|
||||||
diskBImage := flag.String(
|
diskBImage := flag.String(
|
||||||
"diskb",
|
"diskb",
|
||||||
|
@ -84,6 +84,10 @@ func MainApple() *Apple2 {
|
||||||
"videxCardSlot",
|
"videxCardSlot",
|
||||||
3,
|
3,
|
||||||
"slot for the Videx Videoterm 80 columns card. For pre-2e models. -1 for none")
|
"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 := flag.Int(
|
||||||
"nsc",
|
"nsc",
|
||||||
-1,
|
-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 = "<internal>/SwyftWare_-_SwyftCard_Tutorial.woz"
|
||||||
|
} else {
|
||||||
|
diskImageFinal = "<internal>/dos33.dsk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a := newApple2()
|
a := newApple2()
|
||||||
a.setup(*cpuClock, *fastDisk)
|
a.setup(*cpuClock, *fastDisk)
|
||||||
a.io.setTrace(*traceSS)
|
a.io.setTrace(*traceSS)
|
||||||
|
@ -232,6 +245,12 @@ func MainApple() *Apple2 {
|
||||||
if *videxCardSlot > 0 {
|
if *videxCardSlot > 0 {
|
||||||
a.AddVidexCard(*videxCardSlot)
|
a.AddVidexCard(*videxCardSlot)
|
||||||
}
|
}
|
||||||
|
if *swyftCard {
|
||||||
|
if !a.isApple2e {
|
||||||
|
panic("SwyftCard available only on Apple IIe or better")
|
||||||
|
}
|
||||||
|
a.AddSwyftCard()
|
||||||
|
}
|
||||||
|
|
||||||
if *smartPortImage != "" {
|
if *smartPortImage != "" {
|
||||||
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD)
|
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD)
|
||||||
|
@ -354,7 +373,7 @@ func initModel(a *Apple2, model string, romFile string, charRomFile string) {
|
||||||
panic("Model not supported")
|
panic("Model not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load ROM if not loaded already
|
// Load ROM
|
||||||
if romFile != "" {
|
if romFile != "" {
|
||||||
err := a.LoadRom(romFile)
|
err := a.LoadRom(romFile)
|
||||||
if err != nil {
|
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)
|
cg, err := newCharacterGenerator(charRomFile, charGenMap, a.isApple2e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
10
cardBase.go
10
cardBase.go
|
@ -114,6 +114,16 @@ func (c *cardBase) addCardSoftSwitchW(address uint8, ss softSwitchW, name string
|
||||||
c._sswName[address] = name
|
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
|
type softSwitches func(io *ioC0Page, address uint8, data uint8, write bool) uint8
|
||||||
|
|
||||||
func (c *cardBase) addCardSoftSwitches(sss softSwitches, name string) {
|
func (c *cardBase) addCardSoftSwitches(sss softSwitches, name string) {
|
||||||
|
|
|
@ -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("<internal>/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
|
||||||
|
}
|
|
@ -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, "<internal>/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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -33,15 +33,16 @@ type memoryManager struct {
|
||||||
lcAltBank bool // Alternate
|
lcAltBank bool // Alternate
|
||||||
|
|
||||||
// Configuration switches, Apple //e
|
// Configuration switches, Apple //e
|
||||||
altZeroPage bool // Use extra RAM from 0x0000 to 0x01ff. And additional language card block
|
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
|
altMainRAMActiveRead bool // Use extra RAM from 0x0200 to 0xbfff for read
|
||||||
altMainRAMActiveWrite bool // Use extra RAM from 0x0200 to 0xbfff for write
|
altMainRAMActiveWrite bool // Use extra RAM from 0x0200 to 0xbfff for write
|
||||||
store80Active bool // Special pagination for text and graphics areas
|
store80Active bool // Special pagination for text and graphics areas
|
||||||
slotC3ROMActive bool // Apple2e slot 3 ROM shadow
|
slotC3ROMActive bool // Apple2e slot 3 ROM shadow
|
||||||
intCxROMActive bool // Apple2e slots internal 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
|
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
|
activeSlot uint8 // Active slot owner of 0xc800 to 0xcfff
|
||||||
extendedRAMBlock uint8 // Block used for entended memory for RAMWorks cards
|
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
|
// Configuration switches, Base64A
|
||||||
romPage uint8 // Active ROM page
|
romPage uint8 // Active ROM page
|
||||||
|
@ -151,6 +152,12 @@ func (mmu *memoryManager) getVideoRAM(ext bool) *memoryRange {
|
||||||
return mmu.physicalMainRAM
|
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 {
|
func (mmu *memoryManager) accessRead(address uint16) memoryHandler {
|
||||||
if address <= addressLimitZero {
|
if address <= addressLimitZero {
|
||||||
return mmu.getPhysicalMainRAM(mmu.altZeroPage)
|
return mmu.getPhysicalMainRAM(mmu.altZeroPage)
|
||||||
|
@ -175,6 +182,9 @@ func (mmu *memoryManager) accessRead(address uint16) memoryHandler {
|
||||||
if address <= addressLimitSlotsExtra {
|
if address <= addressLimitSlotsExtra {
|
||||||
return mmu.accessCArea(address)
|
return mmu.accessCArea(address)
|
||||||
}
|
}
|
||||||
|
if mmu.mainROMinhibited != nil {
|
||||||
|
return mmu.mainROMinhibited
|
||||||
|
}
|
||||||
if mmu.lcActiveRead {
|
if mmu.lcActiveRead {
|
||||||
return mmu.accessUpperRAMArea(address)
|
return mmu.accessUpperRAMArea(address)
|
||||||
}
|
}
|
||||||
|
@ -205,6 +215,9 @@ func (mmu *memoryManager) accessWrite(address uint16) memoryHandler {
|
||||||
if address <= addressLimitSlotsExtra {
|
if address <= addressLimitSlotsExtra {
|
||||||
return mmu.accessCArea(address)
|
return mmu.accessCArea(address)
|
||||||
}
|
}
|
||||||
|
if mmu.mainROMinhibited != nil {
|
||||||
|
return mmu.mainROMinhibited
|
||||||
|
}
|
||||||
if mmu.lcActiveWrite {
|
if mmu.lcActiveWrite {
|
||||||
return mmu.accessUpperRAMArea(address)
|
return mmu.accessUpperRAMArea(address)
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue