Apple II Memory Expansion Card with up to 1024KB

This commit is contained in:
Ivan Izaguirre 2020-03-08 23:39:25 +01:00
parent 4d3d199d33
commit 35b8bb032b
7 changed files with 185 additions and 39 deletions

View File

@ -19,7 +19,8 @@ Portable emulator of an Apple II+ or //e. Written in Go.
- Emulated extension cards:
- DiskII controller
- 16Kb Language Card
- 256Kb Saturn RAM
- 256Kb Saturn RAM
- 1Mb Memory Expansion Card
- ThunderClock Plus real time clock
- Bootable hard disk card
- Apple //e 80 columns with 64Kb extra RAM
@ -118,8 +119,8 @@ Line:
- F5: Toggle speed between real and fastest
- Ctrl-F5: Show current speed in Mhz
- F6: Toggle between NTSC color TV and green phosphor monochrome monitor
- F7: Save current state to disk
- F8: Restore state from disk
- F7: Save current state to disk (incomplete)
- F8: Restore state from disk (incomplete)
- F10: Cycle character generator codepages. Only if the character generator ROM has more than one 2Kb page.
- F11: Toggle on and off the trace to console of the CPU execution
- F12: Save a screen snapshot to a file `snapshot.png`
@ -139,6 +140,8 @@ Only valid on SDL mode
rom file for the disk drive controller (default "<internal>/DISK2.rom")
-dumpChars
shows the character map
-fastChipSlot int
slot for the FASTChip accelerator card, -1 for none (default 3)
-fastDisk
set fast mode when the disks are spinning (default true)
-hd string
@ -147,6 +150,8 @@ Only valid on SDL mode
slot for the hard drive if present. -1 for none. (default -1)
-languageCardSlot int
slot for the 16kb language card. -1 for none
-memoryExpSlot int
slot for the Memory Expansion card with 1GB. -1 for none (default 4)
-mhz float
cpu speed in Mhz, use 0 for full speed. Use F5 to toggle. (default 1.0227142857142857)
-model string
@ -162,7 +167,7 @@ Only valid on SDL mode
-saturnCardSlot int
slot for the 256kb Saturn card. -1 for none (default -1)
-thunderClockCardSlot int
slot for the ThunderClock Plus card. -1 for none (default 4)
slot for the ThunderClock Plus card. -1 for none (default 5)
-traceCpu
dump to the console the CPU execution. Use F11 to toggle.
-traceHD

View File

@ -154,6 +154,18 @@ func (a *Apple2) AddSaturnCard(slot int) {
a.insertCard(&cardSaturn{}, slot)
}
// AddMemoryExpansionCard inserts an Apple II Memory Expansion card with 1GB
func (a *Apple2) AddMemoryExpansionCard(slot int, romFile string) error {
var c cardMemoryExpansion
data, err := loadResource(romFile)
if err != nil {
return err
}
c.loadRom(data)
a.insertCard(&c, slot)
return nil
}
// AddThunderClockPlusCard inserts a ThunderClock Plus clock card
func (a *Apple2) AddThunderClockPlusCard(slot int, romFile string) error {
var c cardThunderClockPlus

View File

@ -61,9 +61,13 @@ func MainApple() *Apple2 {
"fastChipSlot",
3,
"slot for the FASTChip accelerator card, -1 for none")
memoryExpansionCardSlot := flag.Int(
"memoryExpSlot",
4,
"slot for the Memory Expansion card with 1GB. -1 for none")
thunderClockCardSlot := flag.Int(
"thunderClockCardSlot",
4,
5,
"slot for the ThunderClock Plus card. -1 for none")
mono := flag.Bool(
"mono",
@ -199,8 +203,16 @@ func MainApple() *Apple2 {
if *saturnCardSlot >= 0 {
a.AddSaturnCard(*saturnCardSlot)
}
if *memoryExpansionCardSlot >= 0 {
err := a.AddMemoryExpansionCard(*memoryExpansionCardSlot,
"<internal>/MemoryExpansionCard-341-0344a.bin")
if err != nil {
panic(err)
}
}
if *thunderClockCardSlot > 0 {
err := a.AddThunderClockPlusCard(*thunderClockCardSlot, "<internal>/ThunderclockPlusROM.bin")
err := a.AddThunderClockPlusCard(*thunderClockCardSlot,
"<internal>/ThunderclockPlusROM.bin")
if err != nil {
panic(err)
}

View File

@ -12,8 +12,9 @@ type card interface {
type cardBase struct {
a *Apple2
rom *memoryRange
romExtra *memoryRange
romCsxx *memoryRange
romC8xx *memoryRange
romCxxx *memoryRange
slot int
_ssr [16]softSwitchR
_ssw [16]softSwitchW
@ -25,25 +26,39 @@ func (c *cardBase) loadRom(data []uint8) {
if c.a != nil {
panic("Assert failed. Rom must be loaded before inserting the card in the slot")
}
if len(data) >= 0x100 {
c.rom = newMemoryRange(0, data)
}
if len(data) >= 0x800 {
c.romExtra = newMemoryRange(0, data)
if len(data) == 0x100 {
// Just 256 bytes in Cs00
c.romCsxx = newMemoryRange(0, data)
} 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 = newMemoryRange(0, data)
c.romC8xx = newMemoryRange(0xc800, data)
} else if len(data) == 0x1000 {
// The file covers the full Cxxx range. Only showing the page
// corresponding to the slot used.
c.romCxxx = newMemoryRange(0xc000, data)
} else {
panic("Invalid ROM size")
}
}
func (c *cardBase) assign(a *Apple2, slot int) {
c.a = a
c.slot = slot
if slot != 0 && c.rom != nil {
c.rom.base = uint16(0xc000 + slot*0x100)
a.mmu.setCardROM(slot, c.rom)
}
if slot != 0 && c.romExtra != nil {
c.romExtra.base = uint16(0xc800)
a.mmu.setCardROMExtra(slot, c.romExtra)
if slot != 0 {
if c.romCsxx != nil {
// Relocate to the assigned slot
c.romCsxx.base = uint16(0xc000 + slot*0x100)
a.mmu.setCardROM(slot, c.romCsxx)
}
if c.romC8xx != nil {
a.mmu.setCardROMExtra(slot, c.romC8xx)
}
if c.romCxxx != nil {
a.mmu.setCardROM(slot, c.romCxxx)
a.mmu.setCardROMExtra(slot, c.romCxxx)
}
}
for i := 0; i < 0x10; i++ {

View File

@ -71,7 +71,7 @@ func (c *cardInOut) assign(a *Apple2, slot int) {
}
}
c.rom = newMemoryRange(0xC200, data[0:255])
c.romCsxx = newMemoryRange(0xC200, data[0:255])
if slot != 2 {
// To make ifwork on other slots, patch C2, A0 and A1

94
cardMemoryExpansion.go Normal file
View File

@ -0,0 +1,94 @@
package apple2
/*
Apple II Memory Expansion Card
See:
http://www.apple-iigs.info/doc/fichiers/a2me.pdf
http://ae.applearchives.com/files/RamFactor_Manual_1.5.pdf
http://www.1000bit.it/support/manuali/apple/technotes/memx/tn.memx.1.html
There is a self test in ROM, address Cs0A.
From the RamFactor docs:
The RamFactor card has five addressable registers, which are addressed
according to the slot number the card is in:
$C080+slot * 16low byte of RAM address
$C081+slot * 16middle byte of RAM address
$C082+slot * 16high byte of RAM address
$C083+slot * 16data at addressed location
$C08F+slot * 16Firmware Bank Select
After power up or Control-Reset, the registers on the card are all in a
disabled state. They will be enabled by addressing any address in the firmware
page $Cs00-CsFF.
The three address bytes can be both written into and read from. If the card
has one Megabyte or less, reading the high address byte will always return a
value in the range $F0-FF. The top nybble can be any value when you write it,
but it will always be F when you read it. If the card has more than one
Megabyte of RAM, the top nybble will be a meaningful part of the address.
*/
const (
memoryExpansionSize256 = 256 * 1024
memoryExpansionSize512 = 512 * 1024
memoryExpansionSize768 = 768 * 1024
memoryExpansionSize1024 = 1024 * 1024
memoryExpansionMask = 0x000fffff // 10 bits, 1MB
)
type cardMemoryExpansion struct {
ram [memoryExpansionSize1024]uint8
index int
cardBase
}
func (c *cardMemoryExpansion) assign(a *Apple2, slot int) {
// Read pointer position
c.addCardSoftSwitchR(0, func(*ioC0Page) uint8 {
return uint8(c.index)
}, "MEMORYEXLOR")
c.addCardSoftSwitchR(1, func(*ioC0Page) uint8 {
return uint8(c.index >> 8)
}, "MEMORYEXMIR")
c.addCardSoftSwitchR(2, func(*ioC0Page) uint8 {
// Top nibble returned is 0xf
return uint8(c.index>>16) | 0xf0
}, "MEMORYEXHIR")
// Set pointer position
c.addCardSoftSwitchW(0, func(_ *ioC0Page, value uint8) {
c.index = (c.index &^ 0xff) + int(value)
}, "MEMORYEXLOW")
c.addCardSoftSwitchW(1, func(_ *ioC0Page, value uint8) {
c.index = (c.index &^ 0xff00) + int(value)<<8
}, "MEMORYEXMIW")
c.addCardSoftSwitchW(2, func(_ *ioC0Page, value uint8) {
// Only lo nibble is used
c.index = (c.index &^ 0xff0000) + int(value&0x0f)<<16
}, "MEMORYEXHIW")
// Read data
c.addCardSoftSwitchR(3, func(*ioC0Page) uint8 {
var value uint8
if c.index < len(c.ram) {
value = c.ram[c.index]
} else {
value = 0xde // Ram socket not populated
}
c.index = (c.index + 1) & memoryExpansionMask
return value
}, "MEMORYEXR")
// Write data
c.addCardSoftSwitchW(3, func(_ *ioC0Page, value uint8) {
if c.index < len(c.ram) {
c.ram[c.index] = value
}
c.index = (c.index + 1) & memoryExpansionMask
}, "MEMORYEXW")
c.cardBase.assign(a, slot)
}

File diff suppressed because one or more lines are too long