RAMWorks card support for up to 16MB RAM

This commit is contained in:
Ivan Izaguirre 2020-08-10 20:52:17 +02:00
parent ede3b65257
commit 65f013e9b3
10 changed files with 144 additions and 59 deletions

View File

@ -19,7 +19,8 @@ Portable emulator of an Apple II+ or //e. Written in Go.
- DiskII controller
- 16Kb Language Card
- 256Kb Saturn RAM
- 1Mb Memory Expansion Card
- 1Mb Memory Expansion Card (slinky)
- RAMWorks style expansion Card (up to 16MB additional) (Apple //e only)
- ThunderClock Plus real time clock
- Bootable hard disk card
- Apple //e 80 columns with 64Kb extra RAM and optional RGB modes
@ -34,8 +35,9 @@ Portable emulator of an Apple II+ or //e. Written in Go.
- Double-Width High-Resolution graphics (Apple //e only)
- Super High Resolution (VidHD only)
- Mixed mode
- RGB card text 40 columns with 16 colors for foreground and background
- RGB card text 40 columns with 16 colors for foreground and background (mixable)
- RGB card mode 11, mono 560x192
- RGB card mode 12, ntsc 160*192
- RGB card mode 13, ntsc 140*192 (regular DHGR)
- RGB card mode 14, mix of modes 11 and 13 on the fly
- Displays:
@ -61,7 +63,7 @@ No installation required. [Download](https://github.com/ivanizag/apple2/releases
Execute without parameters to have an emulated Apple //e Enhanced with 128kb booting DOS 3.3 ready to run Applesoft:
``` shell
``` terminal
casa@servidor:~$ ./apple2sdl
```
@ -71,7 +73,7 @@ casa@servidor:~$ ./apple2sdl
Download a DSK or WOZ file or use an URL ([Asimov](https://www.apple.asimov.net/images/) is an excellent source) with the `-disk` parameter:
``` shell
``` terminal
casa@servidor:~$ ./apple2sdl -disk "https://www.apple.asimov.net/images/games/action/karateka/karateka (includes intro).dsk"
```
@ -82,7 +84,7 @@ casa@servidor:~$ ./apple2sdl -disk "https://www.apple.asimov.net/images/games/ac
Download the excellent [Total Replay](https://archive.org/details/TotalReplay) compilation by
[a2-4am](https://github.com/a2-4am/4cade). Run it with the `-hd` parameter:
``` shell
``` terminal
casa@servidor:~$ ./apple2sdl -hd "Total Replay v3.0.2mg"
```
@ -94,7 +96,7 @@ Displays super hi-res box art as seen with the VidHD card.
To run text mode right on the terminal without the SDL2 dependency, use `apple2console`. It runs on the console using ANSI escape codes. Input is sent to the emulated Apple II one line at a time:
``` shell
``` terminal
casa@servidor:~$ ./apple2console -model 2plus
############################################
@ -145,7 +147,7 @@ Only valid on SDL mode
### Command line options
``` shell
```terminal
-charRom string
rom file for the character generator (default "<default>")
-disk string
@ -180,6 +182,8 @@ Only valid on SDL mode
panic if a not implemented softswitch is used
-profile
generate profile trace to analyse with pprof
-ramworks int
memory to use with RAMWorks card, 0 for no card, max is 16384 (default 8192)
-rgb
emulate the RGB modes of the 80col RGB card for DHGR (default true)
-rom string
@ -212,7 +216,7 @@ The only dependency is having a working Go installation on any platform.
Run:
``` shell
``` terminal
go get github.com/ivanizag/apple2/apple2console
go build github.com/ivanizag/apple2/apple2console
```
@ -223,7 +227,7 @@ Besides having a working Go installation, install the SDL2 developer files. Vali
Run:
``` shell
``` terminal
go get github.com/ivanizag/apple2/apple2sdl
go build github.com/ivanizag/apple2/apple2sdl
```
@ -232,7 +236,7 @@ go build github.com/ivanizag/apple2/apple2sdl
To create executables for Linux and Windows without installing Go, SDL2 or the Windows cross compilation toosl, run:
``` shell
``` terminal
cd docker
./build.sh
```

View File

@ -24,7 +24,7 @@ func newApple2e() *Apple2 {
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewNMOS6502(a.mmu)
a.io = newIoC0Page(&a)
a.mmu.InitRAMalt()
a.mmu.initExtendedRAM(1)
addApple2SoftSwitches(a.io)
addApple2ESoftSwitches(a.io)
@ -37,7 +37,7 @@ func newApple2eEnhanced() *Apple2 {
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewCMOS65c02(a.mmu)
a.io = newIoC0Page(&a)
a.mmu.InitRAMalt()
a.mmu.initExtendedRAM(1)
addApple2SoftSwitches(a.io)
addApple2ESoftSwitches(a.io)
@ -194,6 +194,10 @@ func (a *Apple2) AddRGBCard() {
setupRGBCard(a)
}
func (a *Apple2) AddRAMWorks(banks int) {
setupRAMWorksCard(a, banks)
}
// AddCardLogger inserts a fake card that logs accesses
func (a *Apple2) AddCardLogger(slot int) {
a.insertCard(&cardLogger{}, slot)

View File

@ -69,6 +69,10 @@ func MainApple() *Apple2 {
"memoryExpSlot",
4,
"slot for the Memory Expansion card with 1GB. -1 for none")
ramWorksKb := flag.Int(
"ramworks",
8192,
"memory to use with RAMWorks card, 0 for no card, max is 16384")
thunderClockCardSlot := flag.Int(
"thunderClockCardSlot",
5,
@ -253,6 +257,13 @@ func MainApple() *Apple2 {
}
}
if *ramWorksKb != 0 {
if *ramWorksKb%64 != 0 {
panic("Ramworks size must be a multiple of 64")
}
a.AddRAMWorks(*ramWorksKb / 64)
}
if *rgbCard {
a.AddRGBCard()
}

View File

@ -22,7 +22,7 @@ Modes by RGB flags 1 and 2:
0-0: 560*192 mono
1-1: 140*192 ntsc
0-1: Mixed mode
1-0: 160*192 ntsc (not supported)
1-0: 160*192 ntsc
*/

34
cardRamWorks.go Normal file
View File

@ -0,0 +1,34 @@
package apple2
/*
RAMWorks style card on the Apple IIe aus slot.
https://patents.google.com/patent/US4601018
https://ae.applearchives.com/apple_e/ramworks_iii/ramworks_iii_basic_manual_1.pdf
Diagnostics disks:
https://ae.applearchives.com/apple_e/ramworks_iii/ramworks_diagnostics.zip
It's is like the extra 64kb on an Apple IIe 80col 64kb card, but with up to 256 banks
*/
func setupRAMWorksCard(a *Apple2, banks int) {
a.mmu.initExtendedRAM(banks)
ssr := func(_ *ioC0Page) uint8 {
return a.mmu.extendedRAMBlock
}
ssw := func(_ *ioC0Page, value uint8) {
a.mmu.setExtendedRAMActiveBlock(value)
}
// Does not have a slot assigned
a.io.addSoftSwitchR(0x71, ssr, "RAMWORKSR")
a.io.addSoftSwitchR(0x73, ssr, "RAMWORKSR")
a.io.addSoftSwitchR(0x75, ssr, "RAMWORKSR")
a.io.addSoftSwitchR(0x77, ssr, "RAMWORKSR")
a.io.addSoftSwitchW(0x71, ssw, "RAMWORKSW")
a.io.addSoftSwitchW(0x73, ssw, "RAMWORKSW")
a.io.addSoftSwitchW(0x75, ssw, "RAMWORKSW")
a.io.addSoftSwitchW(0x77, ssw, "RAMWORKSW")
}

View File

@ -114,7 +114,7 @@ func (c *cardSaturn) ssAction(ss uint8) {
}
func (c *cardSaturn) applyState() {
c.a.mmu.setLanguageRAMBlock(c.activeBlock)
c.a.mmu.setLanguageRAMActiveBlock(c.activeBlock)
c.a.mmu.setLanguageRAM(c.readState, c.writeState == lcWriteEnabled, c.altBank)
}

View File

@ -12,19 +12,23 @@ type memoryManager struct {
apple2 *Apple2
// Main RAM area: 0x0000 to 0xbfff
physicalMainRAM *memoryRange // 0x0000 to 0xbfff, Up to 48 Kb
physicalMainRAMAlt *memoryRange // 0x0000 to 0xbfff, Up to 48 Kb. Additional
physicalMainRAM *memoryRange // 0x0000 to 0xbfff, Up to 48 Kb
// Slots area: 0xc000 to 0xcfff
cardsROM [8]memoryHandler //0xcs00 to 0xcsff. 256 bytes for each card
cardsROM [8]memoryHandler //0xcs00 to 0xcSff. 256 bytes for each card
cardsROMExtra [8]memoryHandler // 0xc800 to 0xcfff. 2048 bytes for each card
physicalROMe memoryHandler // 0xc100 to 0xcfff, Zero or 4kb in the Apple2e
// Upper area: 0xd000 to 0xffff
physicalROM [4]memoryHandler // 0xd000 to 0xffff, 12 Kb. Up to four banks
physicalDRAM []memoryHandler // 0xd000 to 0xdfff, 4KB. Up to 8 banks.
physicalDAltRAM []memoryHandler // 0xd000 to 0xdfff, 4KB. Up to 8 banks.
physicalEFRAM []memoryHandler // 0xe000 to 0xffff, 8KB. Up to 8 banks.
// Upper area ROM: 0xd000 to 0xffff
physicalROM [4]memoryHandler // 0xd000 to 0xffff, 12 Kb. Up to four
// Language card upper area RAM: 0xd000 to 0xffff. One bank for regular LC cards, up to 8 with Saturn
physicalLangRAM []*memoryRange // 0xd000 to 0xffff, 12KB. Up to 8 banks.
physicalLangAltRAM []*memoryRange // 0xd000 to 0xdfff, 4KB. Up to 8 banks.
// Extended RAM: 0x0000 to 0xffff (with 4Kb moved from 0xc000 to 0xd000 alt). One bank for extended Apple 2e card, up to 256 with RamWorks
physicalExtRAM []*memoryRange // 0x0000 to 0xffff. 60Kb, 0xc000 to 0xcfff not used. Up to 256 banks
physicalExtAltRAM []*memoryRange // 0xd000 to 0xdfff, 4Kb. Up to 256 banks.
// Configuration switches, Language cards
lcSelectedBlock uint8 // Language card block selected. Usually, allways 0. But Saturn has 8
@ -40,6 +44,7 @@ type memoryManager struct {
slotC3ROMActive bool // Apple2e slot 3 ROM shadow
intCxROMActive bool // Apple2e slots internal ROM shadow
activeSlot uint8 // Active slot owner of 0xc800 to 0xcfff
extendedRAMBlock uint8 // Block used for entended memory for RAMWorks cards
// Configuration switches, Base64A
romPage uint8 // Active ROM page
@ -97,23 +102,35 @@ func (mmu *memoryManager) accessCArea(address uint16) memoryHandler {
return mmu.cardsROMExtra[mmu.activeSlot]
}
func (mmu *memoryManager) accessLCArea(address uint16) memoryHandler {
block := mmu.lcSelectedBlock
func (mmu *memoryManager) accessUpperRAMArea(address uint16) memoryHandler {
if mmu.altZeroPage {
block = 1
}
if address <= addressLimitDArea {
if mmu.lcAltBank {
return mmu.physicalDAltRAM[block]
// Use extended RAM
block := mmu.extendedRAMBlock
if mmu.lcAltBank && address <= addressLimitDArea {
return mmu.physicalExtAltRAM[block]
}
return mmu.physicalDRAM[block]
return mmu.physicalExtRAM[mmu.extendedRAMBlock]
}
return mmu.physicalEFRAM[block]
// Use language card
block := mmu.lcSelectedBlock
if mmu.lcAltBank && address <= addressLimitDArea {
return mmu.physicalLangAltRAM[block]
}
return mmu.physicalLangRAM[block]
}
func (mmu *memoryManager) getPhysicalMainRAM(alt bool) *memoryRange {
if alt {
return mmu.physicalMainRAMAlt
func (mmu *memoryManager) getPhysicalMainRAM(ext bool) memoryHandler {
if ext {
return mmu.physicalExtRAM[mmu.extendedRAMBlock]
}
return mmu.physicalMainRAM
}
func (mmu *memoryManager) getVideoRAM(ext bool) *memoryRange {
if ext {
// The video memory uses the first extended RAM block, even with RAMWorks
return mmu.physicalExtRAM[0]
}
return mmu.physicalMainRAM
}
@ -142,7 +159,7 @@ func (mmu *memoryManager) accessRead(address uint16) memoryHandler {
return mmu.accessCArea(address)
}
if mmu.lcActiveRead {
return mmu.accessLCArea(address)
return mmu.accessUpperRAMArea(address)
}
return mmu.physicalROM[mmu.romPage]
}
@ -171,7 +188,7 @@ func (mmu *memoryManager) accessWrite(address uint16) memoryHandler {
return mmu.accessCArea(address)
}
if mmu.lcActiveWrite {
return mmu.accessLCArea(address)
return mmu.accessUpperRAMArea(address)
}
return mmu.physicalROM[mmu.romPage]
}
@ -205,19 +222,24 @@ func (mmu *memoryManager) setCardROMExtra(slot int, mh memoryHandler) {
mmu.cardsROMExtra[slot] = mh
}
func (mmu *memoryManager) initLanguageRAM(groups int) {
mmu.physicalDRAM = make([]memoryHandler, groups)
mmu.physicalDAltRAM = make([]memoryHandler, groups)
mmu.physicalEFRAM = make([]memoryHandler, groups)
for i := 0; i < groups; i++ {
mmu.physicalDRAM[i] = newMemoryRange(0xd000, make([]uint8, 0x1000))
mmu.physicalDAltRAM[i] = newMemoryRange(0xd000, make([]uint8, 0x1000))
mmu.physicalEFRAM[i] = newMemoryRange(0xe000, make([]uint8, 0x2000))
func (mmu *memoryManager) initLanguageRAM(groups uint8) {
// Apple II+ language card or Saturn (up to 8 groups)
mmu.physicalLangRAM = make([]*memoryRange, groups)
mmu.physicalLangAltRAM = make([]*memoryRange, groups)
for i := uint8(0); i < groups; i++ {
mmu.physicalLangRAM[i] = newMemoryRange(0xd000, make([]uint8, 0x3000))
mmu.physicalLangAltRAM[i] = newMemoryRange(0xd000, make([]uint8, 0x1000))
}
}
func (mmu *memoryManager) InitRAMalt() {
mmu.physicalMainRAMAlt = newMemoryRange(0, make([]uint8, 0xc000))
func (mmu *memoryManager) initExtendedRAM(groups int) {
// Apple IIe 80 col card with 64Kb style RAM or RAMWorks (up to 256 banks)
mmu.physicalExtRAM = make([]*memoryRange, groups)
mmu.physicalExtAltRAM = make([]*memoryRange, groups)
for i := 0; i < groups; i++ {
mmu.physicalExtRAM[i] = newMemoryRange(0, make([]uint8, 0x10000))
mmu.physicalExtAltRAM[i] = newMemoryRange(0xd000, make([]uint8, 0x1000))
}
}
// Memory configuration
@ -235,11 +257,19 @@ func (mmu *memoryManager) setLanguageRAM(readActive bool, writeActive bool, altB
mmu.lcAltBank = altBank
}
func (mmu *memoryManager) setLanguageRAMBlock(block uint8) {
block = block % uint8(len(mmu.physicalDRAM))
func (mmu *memoryManager) setLanguageRAMActiveBlock(block uint8) {
block = block % uint8(len(mmu.physicalLangRAM))
mmu.lcSelectedBlock = block
}
func (mmu *memoryManager) setExtendedRAMActiveBlock(block uint8) {
if int(block) >= len(mmu.physicalExtRAM) {
// How does the real hardware reacts?
block = 0
}
mmu.extendedRAMBlock = block
}
// TODO: complete save and load
func (mmu *memoryManager) save(w io.Writer) error {
err := mmu.physicalMainRAM.save(w)

View File

@ -31,7 +31,7 @@ func getHiResLine(a *Apple2, line int, isSecondPage bool, auxMem bool) []uint8 {
}
address += getHiResLineOffset(line)
return a.mmu.getPhysicalMainRAM(auxMem).subRange(address, address+hiResLineBytes)
return a.mmu.getVideoRAM(auxMem).subRange(address, address+hiResLineBytes)
}
func snapshotHiResModeMono(a *Apple2, isSecondPage bool, light color.Color) *image.RGBA {

View File

@ -24,8 +24,10 @@ func snapshotSuperHiResMode(a *Apple2) *image.RGBA {
size := image.Rect(0, 0, shrWidth, shrHeight)
img := image.NewRGBA(size)
videoRAM := a.mmu.getVideoRAM(true)
// Load the palettes
paletteMem := a.mmu.physicalMainRAMAlt.subRange(shrColorPalettesAddress, shrColorPalettesAddressEnd)
paletteMem := videoRAM.subRange(shrColorPalettesAddress, shrColorPalettesAddressEnd)
colors := make([]color.Color, palettesCount)
iMem := 0
for i := 0; i < palettesCount; i++ {
@ -46,13 +48,13 @@ func snapshotSuperHiResMode(a *Apple2) *image.RGBA {
// Build the lines
for y := 0; y < shrHeight; y++ {
controlByte := a.mmu.physicalMainRAMAlt.peek(shrScanLineControlAddress + uint16(y))
controlByte := videoRAM.peek(shrScanLineControlAddress + uint16(y))
is640Wide := (controlByte & 0x80) != 0
isColorFill := (controlByte & 0x20) != 0
paletteIndex := (controlByte & 0x0f) << 4
lineAddress := shrPixelDataAddress + uint16(shrWidthBytes*y)
lineBytes := a.mmu.physicalMainRAMAlt.subRange(lineAddress, uint16(lineAddress+shrWidthBytes))
lineBytes := videoRAM.subRange(lineAddress, uint16(lineAddress+shrWidthBytes))
if is640Wide {
// Line is 640 pixels, two bits per pixel

View File

@ -20,7 +20,7 @@ const (
)
func snapshotText40Mode(a *Apple2, isSecondPage bool, light color.Color) *image.RGBA {
text := getTextFromMemory(a.mmu.physicalMainRAM, isSecondPage)
text := getTextFromMemory(a.mmu.getVideoRAM(false), isSecondPage)
return renderTextMode(a, text, nil /*colorMap*/, light)
}
@ -30,13 +30,13 @@ func snapshotText80Mode(a *Apple2, isSecondPage bool, light color.Color) *image.
}
func snapshotText40RGBMode(a *Apple2, isSecondPage bool) *image.RGBA {
text := getTextFromMemory(a.mmu.physicalMainRAM, isSecondPage)
colorMap := getTextFromMemory(a.mmu.physicalMainRAMAlt, isSecondPage)
text := getTextFromMemory(a.mmu.getVideoRAM(false), isSecondPage)
colorMap := getTextFromMemory(a.mmu.getVideoRAM(true), isSecondPage)
return renderTextMode(a, text, colorMap, nil)
}
func snapshotText40RGBModeColors(a *Apple2, isSecondPage bool) *image.RGBA {
colorMap := getTextFromMemory(a.mmu.physicalMainRAMAlt, isSecondPage)
colorMap := getTextFromMemory(a.mmu.getVideoRAM(true), isSecondPage)
return renderTextMode(a, nil /*text*/, colorMap, nil)
}
@ -49,8 +49,8 @@ func getTextCharOffset(col int, line int) uint16 {
}
func getText80FromMemory(a *Apple2, isSecondPage bool) []uint8 {
text40Columns := getTextFromMemory(a.mmu.physicalMainRAM, isSecondPage)
text40ColumnsAlt := getTextFromMemory(a.mmu.physicalMainRAMAlt, isSecondPage)
text40Columns := getTextFromMemory(a.mmu.getVideoRAM(false), isSecondPage)
text40ColumnsAlt := getTextFromMemory(a.mmu.getVideoRAM(true), isSecondPage)
// Merge the two 40 cols to return 80 cols
text80Columns := make([]uint8, 2*len(text40Columns))
@ -165,7 +165,7 @@ func DumpTextModeAnsi(a *Apple2) string {
if is80Columns {
text = getText80FromMemory(a, isSecondPage)
} else {
text = getTextFromMemory(a.mmu.physicalMainRAM, isSecondPage)
text = getTextFromMemory(a.mmu.getVideoRAM(false), isSecondPage)
}
columns := len(text) / textLines