mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-09-10 03:54:30 +00:00
Apple II Memory Expansion Card with up to 1024KB
This commit is contained in:
parent
4d3d199d33
commit
35b8bb032b
13
README.md
13
README.md
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
45
cardBase.go
45
cardBase.go
@ -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++ {
|
||||
|
@ -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
94
cardMemoryExpansion.go
Normal 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
Loading…
Reference in New Issue
Block a user