2019-05-16 20:55:19 +00:00
|
|
|
package apple2
|
|
|
|
|
2019-05-17 21:28:20 +00:00
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
2019-05-16 20:55:19 +00:00
|
|
|
/*
|
|
|
|
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
|
2019-06-01 15:11:25 +00:00
|
|
|
Apple ][+, it is not considered. For the Plus it is often
|
2019-05-16 20:55:19 +00:00
|
|
|
refered as Language card but it is really a 16 KB Ram card,
|
|
|
|
|
|
|
|
|
2019-09-24 21:43:58 +00:00
|
|
|
"When RAM is deselected, the ROM on the Language card is selected for
|
2019-05-16 20:55:19 +00:00
|
|
|
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
|
2019-10-20 22:00:42 +00:00
|
|
|
writeState uint8
|
|
|
|
activeBank uint8
|
2019-05-16 20:55:19 +00:00
|
|
|
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
|
|
|
|
)
|
|
|
|
|
2019-05-18 14:43:51 +00:00
|
|
|
func (c *cardLanguage) assign(a *Apple2, slot int) {
|
2019-05-16 20:55:19 +00:00
|
|
|
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))
|
|
|
|
|
2019-10-20 22:00:42 +00:00
|
|
|
for i := uint8(0x0); i <= 0xf; i++ {
|
2019-05-16 20:55:19 +00:00
|
|
|
iCopy := i
|
2019-10-20 22:00:42 +00:00
|
|
|
c.addCardSoftSwitchR(iCopy, func(*ioC0Page) uint8 {
|
2019-10-20 11:31:57 +00:00
|
|
|
c.ssAction(iCopy, false)
|
2019-05-16 20:55:19 +00:00
|
|
|
return 0
|
2019-10-20 22:00:42 +00:00
|
|
|
}, "LANGCARDR")
|
|
|
|
c.addCardSoftSwitchW(iCopy, func(*ioC0Page, uint8) {
|
2019-10-20 11:31:57 +00:00
|
|
|
c.ssAction(iCopy, true)
|
2019-10-20 22:00:42 +00:00
|
|
|
}, "LANGCARDW")
|
2019-05-16 20:55:19 +00:00
|
|
|
}
|
2019-05-18 14:43:51 +00:00
|
|
|
|
|
|
|
c.cardBase.assign(a, slot)
|
|
|
|
c.applyState()
|
2019-05-16 20:55:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-20 22:00:42 +00:00
|
|
|
func (c *cardLanguage) ssAction(ss uint8, write bool) {
|
2019-05-16 20:55:19 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-10-20 11:31:57 +00:00
|
|
|
// Writing to the softswtich disables writes.
|
|
|
|
if write {
|
|
|
|
c.writeState = lcWriteDisabled
|
|
|
|
}
|
|
|
|
|
2019-05-16 20:55:19 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-05-17 21:28:20 +00:00
|
|
|
|
2019-10-05 23:26:00 +00:00
|
|
|
func (c *cardLanguage) save(w io.Writer) error {
|
|
|
|
err := binary.Write(w, binary.BigEndian, c.readState)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = binary.Write(w, binary.BigEndian, c.writeState)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = binary.Write(w, binary.BigEndian, c.activeBank)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = c.ramBankA.save(w)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = c.ramBankB.save(w)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = c.ramUpper.save(w)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.cardBase.save(w)
|
2019-05-17 21:28:20 +00:00
|
|
|
}
|
|
|
|
|
2019-10-05 23:26:00 +00:00
|
|
|
func (c *cardLanguage) load(r io.Reader) error {
|
|
|
|
err := binary.Read(r, binary.BigEndian, &c.readState)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = binary.Read(r, binary.BigEndian, &c.writeState)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = binary.Read(r, binary.BigEndian, &c.activeBank)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = c.ramBankA.load(r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = c.ramBankB.load(r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = c.ramUpper.load(r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-17 21:28:20 +00:00
|
|
|
|
|
|
|
c.applyState()
|
2019-10-05 23:26:00 +00:00
|
|
|
return c.cardBase.load(r)
|
2019-05-17 21:28:20 +00:00
|
|
|
}
|