mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-01-29 14:29:45 +00:00
Support for the 16KB language card
This commit is contained in:
parent
c4f7a7e709
commit
948dc6aab4
@ -53,7 +53,7 @@ func NewApple2(romFile string, charRomFile string, clockMhz float64,
|
||||
|
||||
// Set the io in 0xc000
|
||||
a.io = newIoC0Page(&a)
|
||||
a.mmu.setPage(0xc0, a.io)
|
||||
a.mmu.setPages(0xc0, 0xc0, a.io)
|
||||
|
||||
return &a
|
||||
}
|
||||
@ -70,6 +70,13 @@ func (a *Apple2) AddDisk2(slot int, diskRomFile string, diskImage string) {
|
||||
}
|
||||
}
|
||||
|
||||
// AddLanguageCard inserts a 16Kb card
|
||||
func (a *Apple2) AddLanguageCard(slot int) {
|
||||
d := newCardLanguage()
|
||||
d.cardBase.insert(a, slot)
|
||||
d.applyState()
|
||||
}
|
||||
|
||||
// ConfigureStdConsole uses stdin and stdout to interface with the Apple2
|
||||
func (a *Apple2) ConfigureStdConsole(stdinKeyboard bool, stdoutScreen bool) {
|
||||
if !stdinKeyboard && !stdoutScreen {
|
||||
|
@ -12,8 +12,8 @@ func (c *cardBase) insert(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.setPage(uint8(0xC0+slot), c.rom)
|
||||
c.rom.base = uint16(0xc000 + slot*0x100)
|
||||
a.mmu.setPagesRead(uint8(0xc0+slot), uint8(0xc0+slot), c.rom)
|
||||
}
|
||||
|
||||
for i := 0; i < 0x10; i++ {
|
||||
|
120
apple2/cardLanguage.go
Normal file
120
apple2/cardLanguage.go
Normal file
@ -0,0 +1,120 @@
|
||||
package apple2
|
||||
|
||||
/*
|
||||
Language card with 16 extra kb for the Apple ][ and ][+
|
||||
Manual: http://www.applelogic.org/files/LANGCARDMAN.pdf
|
||||
|
||||
The language card doesn't have ROM for Cx00. It would not
|
||||
be used in slot 0 anyway.
|
||||
|
||||
Note also that language cards for the Apple ][ had ROM on
|
||||
board to replace the main board F8 ROM with Autostart. That
|
||||
was not used/needed on the Apple ][+. As this emulates the
|
||||
Apple ][+, it is not considered. For the PLus it is often
|
||||
refered as Language card but it is really a 16 KB Ram card,
|
||||
|
||||
|
||||
"When RAM is deselected, the ROM on the LAnguage card is selected for
|
||||
the top 2K ($F800-$FFFF), and the ROM on the main board is selected
|
||||
for $D000-$F7FF.
|
||||
|
||||
Power on RESET initializes ROM to read mode and RAM to write mode,
|
||||
and selects the second 4K bank to map $D000-$DFFF."
|
||||
|
||||
*/
|
||||
|
||||
type cardLanguage struct {
|
||||
cardBase
|
||||
readState bool
|
||||
writeState int
|
||||
activeBank int
|
||||
ramBankA *memoryRange // First 4kb to map in 0xD000-0xDFFF
|
||||
ramBankB *memoryRange // Second 4kb to map in 0xD000-0xDFFF
|
||||
ramUpper *memoryRange // Upper 8kb to map in 0xE000-0xFFFF
|
||||
}
|
||||
|
||||
const (
|
||||
// Write enabling requires two sofstwitch accesses
|
||||
lcWriteDisabled = 0
|
||||
lcWriteHalfEnabled = 1
|
||||
lcWriteEnabled = 2
|
||||
)
|
||||
|
||||
func newCardLanguage() *cardLanguage {
|
||||
var c cardLanguage
|
||||
c.readState = false
|
||||
c.writeState = lcWriteEnabled
|
||||
c.activeBank = 1
|
||||
|
||||
c.ramBankA = newMemoryRange(0xd000, make([]uint8, 0x1000))
|
||||
c.ramBankB = newMemoryRange(0xd000, make([]uint8, 0x1000))
|
||||
c.ramUpper = newMemoryRange(0xe000, make([]uint8, 0x2000))
|
||||
|
||||
for i := 0x0; i <= 0xf; i++ {
|
||||
iCopy := i
|
||||
c.ssr[i] = func(*ioC0Page) uint8 {
|
||||
c.ssAction(iCopy)
|
||||
return 0
|
||||
}
|
||||
c.ssw[i] = func(*ioC0Page, uint8) {
|
||||
// Writing resets write count (from A2AUDIT)
|
||||
c.writeState = lcWriteDisabled
|
||||
}
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *cardLanguage) ssAction(ss int) {
|
||||
c.activeBank = (ss >> 3) & 1
|
||||
action := ss & 0x3
|
||||
switch action {
|
||||
case 0:
|
||||
// RAM read, no writes
|
||||
c.readState = true
|
||||
c.writeState = lcWriteDisabled
|
||||
case 1:
|
||||
// ROM read, RAM write
|
||||
c.readState = false
|
||||
c.writeState++
|
||||
case 2:
|
||||
// ROM read, no writes
|
||||
c.readState = false
|
||||
c.writeState = lcWriteDisabled
|
||||
case 3:
|
||||
//RAM read, RAM write
|
||||
c.readState = true
|
||||
c.writeState++
|
||||
}
|
||||
|
||||
if c.writeState > lcWriteEnabled {
|
||||
c.writeState = lcWriteEnabled
|
||||
}
|
||||
|
||||
c.applyState()
|
||||
}
|
||||
|
||||
func (c *cardLanguage) getActiveBank() *memoryRange {
|
||||
if c.activeBank == 0 {
|
||||
return c.ramBankA
|
||||
}
|
||||
return c.ramBankB
|
||||
}
|
||||
|
||||
func (c *cardLanguage) applyState() {
|
||||
mmu := c.a.mmu
|
||||
|
||||
if c.readState {
|
||||
mmu.setPagesRead(0xd0, 0xdf, c.getActiveBank())
|
||||
mmu.setPagesRead(0xe0, 0xff, c.ramUpper)
|
||||
} else {
|
||||
mmu.setPagesRead(0xd0, 0xff, mmu.physicalROM)
|
||||
}
|
||||
|
||||
if c.writeState == lcWriteEnabled {
|
||||
mmu.setPagesWrite(0xd0, 0xdf, c.getActiveBank())
|
||||
mmu.setPagesWrite(0xe0, 0xff, c.ramUpper)
|
||||
} else {
|
||||
mmu.setPagesWrite(0xd0, 0xff, nil)
|
||||
}
|
||||
|
||||
}
|
@ -81,7 +81,7 @@ func (p *ioC0Page) setSpeakerProvider(s SpeakerProvider) {
|
||||
}
|
||||
|
||||
func (p *ioC0Page) peek(address uint16) uint8 {
|
||||
//fmt.Printf("Peek on $C0%02x ", address)
|
||||
//fmt.Printf("Peek on $%02x\n", address)
|
||||
pageAddress := uint8(address)
|
||||
ss := p.softSwitchesR[pageAddress]
|
||||
if ss == nil {
|
||||
@ -94,7 +94,7 @@ func (p *ioC0Page) peek(address uint16) uint8 {
|
||||
}
|
||||
|
||||
func (p *ioC0Page) poke(address uint16, value uint8) {
|
||||
//fmt.Printf("Poke on $C0%02x with %02x ", address, value)
|
||||
//fmt.Printf("Poke on $%02x with %02x\n", address, value)
|
||||
pageAddress := uint8(address)
|
||||
ss := p.softSwitchesW[pageAddress]
|
||||
if ss == nil {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package apple2
|
||||
|
||||
import "io/ioutil"
|
||||
import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// See https://fabiensanglard.net/fd_proxy/prince_of_persia/Inside%20the%20Apple%20IIe.pdf
|
||||
// See https://i.stack.imgur.com/yn21s.gif
|
||||
@ -53,17 +55,31 @@ func (mmu *memoryManager) Poke(address uint16, value uint8) {
|
||||
mh.poke(address, value)
|
||||
}
|
||||
|
||||
func (mmu *memoryManager) setPage(index uint8, mh memoryHandler) {
|
||||
mmu.setPageRead(index, mh)
|
||||
mmu.setPageWrite(index, mh)
|
||||
func (mmu *memoryManager) setPages(begin uint8, end uint8, mh memoryHandler) {
|
||||
mmu.setPagesRead(begin, end, mh)
|
||||
mmu.setPagesWrite(begin, end, mh)
|
||||
}
|
||||
|
||||
func (mmu *memoryManager) setPageRead(index uint8, mh memoryHandler) {
|
||||
mmu.activeMemoryRead[index] = mh
|
||||
func (mmu *memoryManager) setPagesRead(begin uint8, end uint8, mh memoryHandler) {
|
||||
i := begin
|
||||
for {
|
||||
mmu.activeMemoryRead[i] = mh
|
||||
if i == end {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func (mmu *memoryManager) setPageWrite(index uint8, mh memoryHandler) {
|
||||
mmu.activeMemoryWrite[index] = mh
|
||||
func (mmu *memoryManager) setPagesWrite(begin uint8, end uint8, mh memoryHandler) {
|
||||
i := begin
|
||||
for {
|
||||
mmu.activeMemoryWrite[i] = mh
|
||||
if i == end {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// When 0xcfff is accessed the card expansion rom is unassigned
|
||||
@ -72,9 +88,7 @@ func (mmu *memoryManager) resetSlotExpansionRoms() {
|
||||
// Ignore if the Apple2 shadow ROM is active
|
||||
return
|
||||
}
|
||||
for i := uint8(0xc8); i < 0xd0; i++ {
|
||||
mmu.setPage(i, nil)
|
||||
}
|
||||
mmu.setPagesRead(0xc8, 0xcf, nil)
|
||||
}
|
||||
|
||||
func newMemoryManager(a *Apple2) *memoryManager {
|
||||
@ -84,9 +98,7 @@ func newMemoryManager(a *Apple2) *memoryManager {
|
||||
// Assign RAM from 0x0000 to 0xbfff, 48kb
|
||||
ram := make([]uint8, 0xc000)
|
||||
mmu.physicalMainRAM = newMemoryRange(0, ram)
|
||||
for i := 0; i < 0xc000; i = i + 0x100 {
|
||||
mmu.setPage(uint8(i>>8), mmu.physicalMainRAM)
|
||||
}
|
||||
mmu.setPages(0x00, 0xc0, mmu.physicalMainRAM)
|
||||
|
||||
return &mmu
|
||||
}
|
||||
@ -119,6 +131,6 @@ func (mmu *memoryManager) loadRom(filename string) {
|
||||
func (mmu *memoryManager) resetRomPaging() {
|
||||
// Assign the first 12kb of ROM from 0xd000 to 0xfff
|
||||
for i := 0x0000; i < 0x3000; i = i + 0x100 {
|
||||
mmu.setPageRead(uint8(0xd0+(i>>8)), mmu.physicalROM)
|
||||
mmu.setPagesRead(0xd0, 0xff, mmu.physicalROM)
|
||||
}
|
||||
}
|
||||
|
BIN
apple2/romdumps/LanguageCard_3410020F8.bin
Normal file
BIN
apple2/romdumps/LanguageCard_3410020F8.bin
Normal file
Binary file not shown.
@ -45,33 +45,19 @@ func getSoftSwitchExt(ioFlag uint8, dstValue uint8, action softSwitchExtAction)
|
||||
}
|
||||
|
||||
func softSwitchIntCxRomOn(io *ioC0Page) {
|
||||
mmu := io.apple2.mmu
|
||||
for i := 0x100; i < 0x1000; i = i + 0x100 {
|
||||
mmu.setPage(uint8(i>>8), mmu.physicalROMe)
|
||||
}
|
||||
io.apple2.mmu.setPagesRead(0xc1, 0xcf, io.apple2.mmu.physicalROMe)
|
||||
}
|
||||
|
||||
func softSwitchIntCxRomOff(io *ioC0Page) {
|
||||
// TODO restore all the ROM from the slot for 0xc1 to 0xc7
|
||||
mmu := io.apple2.mmu
|
||||
for i := 1; i < 16; i++ {
|
||||
mmu.setPage(uint8(0xc0+i), nil)
|
||||
}
|
||||
// TODO restore all the ROM from the slots for 0xc1 to 0xc7
|
||||
io.apple2.mmu.setPages(0xc1, 0xc7, nil)
|
||||
}
|
||||
|
||||
func softSwitchSlotC3RomOn(io *ioC0Page) {
|
||||
if io.isSoftSwitchActive(ioFlagIntCxRom) {
|
||||
return // Ignore if allt the Apple2 shadow ROM is active
|
||||
}
|
||||
// TODO restore the slot 3 ROM
|
||||
mmu := io.apple2.mmu
|
||||
mmu.setPage(0xC3, nil)
|
||||
io.apple2.mmu.setPages(0xc3, 0xc3, nil)
|
||||
}
|
||||
|
||||
func softSwitchSlotC3RomOff(io *ioC0Page) {
|
||||
if io.isSoftSwitchActive(ioFlagIntCxRom) {
|
||||
return // Ignore if alt the Apple2 shadow ROM is active
|
||||
}
|
||||
mmu := io.apple2.mmu
|
||||
mmu.setPageRead(0xC3, mmu.physicalROMe)
|
||||
io.apple2.mmu.setPagesRead(0xc3, 0xc3, io.apple2.mmu.physicalROMe)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user