mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-04-19 23:37:11 +00:00
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func MainApple() *Apple2 {
|
||||
"slot for the disk driver. -1 for none.")
|
||||
diskImage := flag.String(
|
||||
"disk",
|
||||
"<internal>/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 = "<internal>/SwyftWare_-_SwyftCard_Tutorial.woz"
|
||||
} else {
|
||||
diskImageFinal = "<internal>/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)
|
||||
|
10
cardBase.go
10
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) {
|
||||
|
133
cardSwyft.go
Normal file
133
cardSwyft.go
Normal file
@ -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
|
||||
}
|
26
cardSwyft_test.go
Normal file
26
cardSwyft_test.go
Normal file
@ -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
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
BIN
resources/SwyftCard ROM.bin
Normal file
BIN
resources/SwyftCard ROM.bin
Normal file
Binary file not shown.
BIN
resources/SwyftWare_-_SwyftCard_Tutorial.woz
Normal file
BIN
resources/SwyftWare_-_SwyftCard_Tutorial.woz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user