mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-06-17 05:29:33 +00:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ad720f0527 | ||
|
9e89f0a23f | ||
|
cb355a17cd | ||
|
30e3a7503d | ||
|
a4ad599056 | ||
|
e20e355f9f | ||
|
9611993c85 | ||
|
cedb810400 |
|
@ -33,6 +33,7 @@ Portable emulator of an Apple II+ or //e. Written in Go.
|
||||||
- Brain Board II
|
- Brain Board II
|
||||||
- MultiROM card
|
- MultiROM card
|
||||||
- Dan ][ Controller card
|
- Dan ][ Controller card
|
||||||
|
- ProDOS ROM card
|
||||||
- Useful cards not emulating a real card
|
- Useful cards not emulating a real card
|
||||||
- Bootable SmartPort / ProDOS card with the following smartport devices:
|
- Bootable SmartPort / ProDOS card with the following smartport devices:
|
||||||
- Block device (hard disks)
|
- Block device (hard disks)
|
||||||
|
@ -240,6 +241,8 @@ The available cards are:
|
||||||
mouse: Mouse card implementation, does not emulate a real card, only the firmware behaviour
|
mouse: Mouse card implementation, does not emulate a real card, only the firmware behaviour
|
||||||
multirom: Multiple Image ROM card
|
multirom: Multiple Image ROM card
|
||||||
parallel: Card to dump to a file what would be printed to a parallel printer
|
parallel: Card to dump to a file what would be printed to a parallel printer
|
||||||
|
prodosromcard3: A bootable 4 MB ROM card by Ralle Palaveev
|
||||||
|
prodosromdrive: A bootable 1 MB solid state disk by Terence Boldt
|
||||||
saturn: RAM card with 128Kb, it's like 8 language cards
|
saturn: RAM card with 128Kb, it's like 8 language cards
|
||||||
smartport: SmartPort interface card
|
smartport: SmartPort interface card
|
||||||
softswitchlogger: Card to log softswitch accesses
|
softswitchlogger: Card to log softswitch accesses
|
||||||
|
@ -254,7 +257,7 @@ The available tracers are:
|
||||||
mli: Trace ProDOS MLI calls
|
mli: Trace ProDOS MLI calls
|
||||||
mos: Trace MOS calls with Applecorn skipping terminal IO
|
mos: Trace MOS calls with Applecorn skipping terminal IO
|
||||||
mosfull: Trace MOS calls with Applecorn
|
mosfull: Trace MOS calls with Applecorn
|
||||||
panicSS: Panic on unimplemented softswitches
|
panicss: Panic on unimplemented softswitches
|
||||||
ss: Trace sotfswiches calls
|
ss: Trace sotfswiches calls
|
||||||
ssreg: Trace sotfswiches registrations
|
ssreg: Trace sotfswiches registrations
|
||||||
ucsd: Trace UCSD system calls
|
ucsd: Trace UCSD system calls
|
||||||
|
|
10
apple2Run.go
10
apple2Run.go
|
@ -37,7 +37,7 @@ func (a *Apple2) Start(paused bool) {
|
||||||
for i := 0; i < cpuSpinLoops; i++ {
|
for i := 0; i < cpuSpinLoops; i++ {
|
||||||
// Conditional tracing
|
// Conditional tracing
|
||||||
//pc, _ := a.cpu.GetPCAndSP()
|
//pc, _ := a.cpu.GetPCAndSP()
|
||||||
//a.cpu.SetTrace(pc >= 0xc75e && pc < 0xc800)
|
//a.cpu.SetTrace(pc >= 0xc700 && pc < 0xc800)
|
||||||
|
|
||||||
// Execution
|
// Execution
|
||||||
a.cpu.ExecuteInstruction()
|
a.cpu.ExecuteInstruction()
|
||||||
|
@ -144,10 +144,16 @@ func (a *Apple2) dumpDebugInfo() {
|
||||||
0xee: "JVAFOLDL", // Apple Pascal
|
0xee: "JVAFOLDL", // Apple Pascal
|
||||||
0xef: "JVAFOLDH", // Apple Pascal
|
0xef: "JVAFOLDH", // Apple Pascal
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Page zero values:\n")
|
fmt.Printf("Page zero values:\n")
|
||||||
for _, k := range []int{0x36, 0x37, 0x38, 0x39, 0xe2, 0xe3, 0xec, 0xed, 0xee, 0xef} {
|
for _, k := range []int{0x36, 0x37, 0x38, 0x39, 0xe2, 0xe3, 0xec, 0xed, 0xee, 0xef} {
|
||||||
d := a.mmu.physicalMainRAM.data[k]
|
d := a.mmu.physicalMainRAM.data[k]
|
||||||
fmt.Printf(" %v(0x%x): 0x%02x\n", pageZeroSymbols[k], k, d)
|
fmt.Printf(" %v(0x%x): 0x%02x\n", pageZeroSymbols[k], k, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pc := uint16(0xc700)
|
||||||
|
for pc < 0xc800 {
|
||||||
|
line, newPc := a.cpu.DisasmInstruction(pc)
|
||||||
|
fmt.Println(line)
|
||||||
|
pc = newPc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
92
cardBase.go
92
cardBase.go
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
// Card represents an Apple II card to be inserted in a slot
|
// Card represents an Apple II card to be inserted in a slot
|
||||||
type Card interface {
|
type Card interface {
|
||||||
loadRom(data []uint8)
|
loadRom(data []uint8, layout cardRomLayout) error
|
||||||
assign(a *Apple2, slot int)
|
assign(a *Apple2, slot int)
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
|
@ -51,45 +51,79 @@ func (c *cardBase) reset() {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardBase) loadRomFromResource(resource string) error {
|
type cardRomLayout int
|
||||||
|
|
||||||
|
const (
|
||||||
|
cardRomSimple cardRomLayout = iota // The ROM is on the slot area, there can be more than one page
|
||||||
|
cardRomUpper // The ROM is on the full C800 area. The slot area copies C8xx
|
||||||
|
cardRomUpperHalfEnd // The ROM is on half of the C800 areas. The slot area copies CBxx
|
||||||
|
cardRomFull // The ROM is on the full Cxxx area, with pages for each slot position
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *cardBase) loadRomFromResource(resource string, layout cardRomLayout) error {
|
||||||
data, _, err := LoadResource(resource)
|
data, _, err := LoadResource(resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The resource should be internal and never fail
|
// The resource should be internal and never fail
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.loadRom(data)
|
err = c.loadRom(data, layout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardBase) loadRom(data []uint8) {
|
func (c *cardBase) loadRom(data []uint8, layout cardRomLayout) error {
|
||||||
if c.a != nil {
|
if c.a != nil {
|
||||||
panic("Assert failed. ROM must be loaded before inserting the card in the slot")
|
return fmt.Errorf("ROM must be loaded before inserting the card in the slot")
|
||||||
}
|
}
|
||||||
if len(data) == 0x100 {
|
switch layout {
|
||||||
// Just 256 bytes in Cs00
|
case cardRomSimple:
|
||||||
c.romCsxx = newMemoryRangeROM(0, data, "Slot ROM")
|
if len(data) == 0x100 {
|
||||||
} else if len(data) == 0x400 {
|
// Just 256 bytes in Cs00
|
||||||
// The file has C800 to CBFF for ROM
|
c.romCsxx = newMemoryRangeROM(0, data, "Slot ROM")
|
||||||
// The 256 bytes in Cx00 are copied from the last page in C800-CBFF
|
} else if len(data)%0x100 == 0 {
|
||||||
// Used on the Videx 80 columns card
|
// The ROM covers many 256 bytes pages of Csxx
|
||||||
c.romCsxx = newMemoryRangeROM(0, data[0x300:], "Slot ROM")
|
// Used on the Dan 2 controller card
|
||||||
c.romC8xx = newMemoryRangeROM(0xc800, data, "Slot C8 ROM")
|
c.romCsxx = newMemoryRangePagedROM(0, data, "Slot paged ROM", uint8(len(data)/0x100))
|
||||||
} else if len(data) == 0x800 {
|
} else {
|
||||||
// The file has C800 to CFFF
|
return fmt.Errorf("invalid ROM size for simple layout")
|
||||||
// The 256 bytes in Cx00 are copied from the first page in C800
|
}
|
||||||
c.romCsxx = newMemoryRangeROM(0, data, "Slot ROM")
|
case cardRomUpper:
|
||||||
c.romC8xx = newMemoryRangeROM(0xc800, data, "Slot C8 ROM")
|
if len(data) == 0x800 {
|
||||||
} else if len(data) == 0x1000 {
|
// The file has C800 to CFFF
|
||||||
// The file covers the full Cxxx range. Only showing the page
|
// The 256 bytes in Cx00 are copied from the first page in C800
|
||||||
// corresponding to the slot used.
|
c.romCsxx = newMemoryRangeROM(0, data, "Slot ROM")
|
||||||
c.romCxxx = newMemoryRangeROM(0xc000, data, "Slot ROM")
|
c.romC8xx = newMemoryRangeROM(0xc800, data, "Slot C8 ROM")
|
||||||
} else if len(data)%0x100 == 0 {
|
} else {
|
||||||
// The ROM covers many 256 bytes pages oc Csxx
|
return fmt.Errorf("invalid ROM size for upper layout")
|
||||||
// Used on the Dan 2 controller card
|
}
|
||||||
c.romCsxx = newMemoryRangePagedROM(0, data, "Slot paged ROM", uint8(len(data)/0x100))
|
case cardRomUpperHalfEnd:
|
||||||
} else {
|
if len(data) == 0x400 {
|
||||||
panic("Invalid ROM size")
|
// The file has C800 to CBFF for ROM
|
||||||
|
// The 256 bytes in Cx00 are copied from the last page in C800-CBFF
|
||||||
|
// Used on the Videx 80 columns card
|
||||||
|
c.romCsxx = newMemoryRangeROM(0, data[0x300:], "Slot ROM")
|
||||||
|
c.romC8xx = newMemoryRangeROM(0xc800, data, "Slot C8 ROM")
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid ROM size for upper half end layout")
|
||||||
|
}
|
||||||
|
case cardRomFull:
|
||||||
|
if len(data) == 0x1000 {
|
||||||
|
// The file covers the full Cxxx range. Only showing the page
|
||||||
|
// corresponding to the slot used.
|
||||||
|
c.romCxxx = newMemoryRangeROM(0xc000, data, "Slot ROM")
|
||||||
|
} else if len(data)%0x1000 == 0 {
|
||||||
|
// The ROM covers the full Cxxx range with several pages
|
||||||
|
c.romCxxx = newMemoryRangePagedROM(0xc000, data, "Slot paged ROM", uint8(len(data)/0x1000))
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid ROM size for full layout")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid card ROM layout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardBase) assign(a *Apple2, slot int) {
|
func (c *cardBase) assign(a *Apple2, slot int) {
|
||||||
|
|
|
@ -117,10 +117,8 @@ func (c *CardBrainBoard) updateState() {
|
||||||
((!c.dip56_lowerInA && !c.isBankB) || (!c.dip78_upperInB && c.isBankB))
|
((!c.dip56_lowerInA && !c.isBankB) || (!c.dip78_upperInB && c.isBankB))
|
||||||
|
|
||||||
if isMotherboardRomEnabled && !c.isMotherboardRomEnabled {
|
if isMotherboardRomEnabled && !c.isMotherboardRomEnabled {
|
||||||
fmt.Print("ROM: main")
|
|
||||||
c.a.mmu.inhibitROM(nil)
|
c.a.mmu.inhibitROM(nil)
|
||||||
} else if !isMotherboardRomEnabled && c.isMotherboardRomEnabled {
|
} else if !isMotherboardRomEnabled && c.isMotherboardRomEnabled {
|
||||||
fmt.Print("ROM: brain")
|
|
||||||
c.a.mmu.inhibitROM(c)
|
c.a.mmu.inhibitROM(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +157,3 @@ func (c *CardBrainBoard) peek(address uint16) uint8 {
|
||||||
func (c *CardBrainBoard) poke(address uint16, value uint8) {
|
func (c *CardBrainBoard) poke(address uint16, value uint8) {
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardBrainBoard) setBase(base uint16) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
|
|
@ -120,7 +120,3 @@ func (c *CardBrainBoardII) peek(address uint16) uint8 {
|
||||||
func (c *CardBrainBoardII) poke(address uint16, value uint8) {
|
func (c *CardBrainBoardII) poke(address uint16, value uint8) {
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardBrainBoardII) setBase(base uint16) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ const noCardName = "empty"
|
||||||
var commonParams = []paramSpec{
|
var commonParams = []paramSpec{
|
||||||
{"trace", "Enable debug messages", "false"},
|
{"trace", "Enable debug messages", "false"},
|
||||||
{"tracess", "Trace softswitches", "false"},
|
{"tracess", "Trace softswitches", "false"},
|
||||||
|
{"panicss", "Panic on unimplemented softswitches", "false"},
|
||||||
}
|
}
|
||||||
|
|
||||||
var cardFactory map[string]*cardBuilder
|
var cardFactory map[string]*cardBuilder
|
||||||
|
@ -51,6 +52,9 @@ func getCardFactory() map[string]*cardBuilder {
|
||||||
cardFactory["mouse"] = newCardMouseBuilder()
|
cardFactory["mouse"] = newCardMouseBuilder()
|
||||||
cardFactory["multirom"] = newMultiRomCardBuilder()
|
cardFactory["multirom"] = newMultiRomCardBuilder()
|
||||||
cardFactory["parallel"] = newCardParallelPrinterBuilder()
|
cardFactory["parallel"] = newCardParallelPrinterBuilder()
|
||||||
|
cardFactory["prodosromdrive"] = newCardProDOSRomDriveBuilder()
|
||||||
|
cardFactory["prodosromcard3"] = newCardProDOSRomCard3Builder()
|
||||||
|
//cardFactory["prodosnvramdrive"] = newCardProDOSNVRAMDriveBuilder()
|
||||||
cardFactory["saturn"] = newCardSaturnBuilder()
|
cardFactory["saturn"] = newCardSaturnBuilder()
|
||||||
cardFactory["smartport"] = newCardSmartPortStorageBuilder()
|
cardFactory["smartport"] = newCardSmartPortStorageBuilder()
|
||||||
cardFactory["swyftcard"] = newCardSwyftBuilder()
|
cardFactory["swyftcard"] = newCardSwyftBuilder()
|
||||||
|
@ -66,6 +70,20 @@ func availableCards() []string {
|
||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cb *cardBuilder) fullDefaultParams() map[string]string {
|
||||||
|
finalParams := make(map[string]string)
|
||||||
|
for _, commonParam := range commonParams {
|
||||||
|
finalParams[commonParam.name] = commonParam.defaultValue
|
||||||
|
}
|
||||||
|
if cb.defaultParams != nil {
|
||||||
|
for _, defaultParam := range *cb.defaultParams {
|
||||||
|
finalParams[defaultParam.name] = defaultParam.defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalParams
|
||||||
|
}
|
||||||
|
|
||||||
func setupCard(a *Apple2, slot int, paramString string) (Card, error) {
|
func setupCard(a *Apple2, slot int, paramString string) (Card, error) {
|
||||||
actualArgs := splitConfigurationString(paramString, ',')
|
actualArgs := splitConfigurationString(paramString, ',')
|
||||||
|
|
||||||
|
@ -83,16 +101,7 @@ func setupCard(a *Apple2, slot int, paramString string) (Card, error) {
|
||||||
return nil, fmt.Errorf("card %s requires an Apple IIe", builder.name)
|
return nil, fmt.Errorf("card %s requires an Apple IIe", builder.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
finalParams := make(map[string]string)
|
finalParams := builder.fullDefaultParams()
|
||||||
for _, commonParam := range commonParams {
|
|
||||||
finalParams[commonParam.name] = commonParam.defaultValue
|
|
||||||
}
|
|
||||||
if builder.defaultParams != nil {
|
|
||||||
for _, defaultParam := range *builder.defaultParams {
|
|
||||||
finalParams[defaultParam.name] = defaultParam.defaultValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i < len(actualArgs); i++ {
|
for i := 1; i < len(actualArgs); i++ {
|
||||||
actualArgSides := splitConfigurationString(actualArgs[i], '=')
|
actualArgSides := splitConfigurationString(actualArgs[i], '=')
|
||||||
actualArgName := strings.ToLower(actualArgSides[0])
|
actualArgName := strings.ToLower(actualArgSides[0])
|
||||||
|
@ -116,11 +125,14 @@ func setupCard(a *Apple2, slot int, paramString string) (Card, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common parameters
|
// Common parameters
|
||||||
traceSS := paramsGetBool(finalParams, "tracess")
|
if paramsGetBool(finalParams, "tracess") {
|
||||||
if traceSS {
|
|
||||||
a.io.traceSlot(slot)
|
a.io.traceSlot(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if paramsGetBool(finalParams, "panicss") {
|
||||||
|
a.io.panicNotImplementedSlot(slot)
|
||||||
|
}
|
||||||
|
|
||||||
debug := paramsGetBool(finalParams, "trace")
|
debug := paramsGetBool(finalParams, "trace")
|
||||||
|
|
||||||
card.setName(builder.name)
|
card.setName(builder.name)
|
||||||
|
|
17
cardBuilder_test.go
Normal file
17
cardBuilder_test.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCardBuilder(t *testing.T) {
|
||||||
|
cardFactory := getCardFactory()
|
||||||
|
for name, builder := range cardFactory {
|
||||||
|
if name != "prodosromdrive" && name != "prodosromcard3" && name != "prodosnvramdrive" {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
_, err := builder.buildFunc(builder.fullDefaultParams())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Exception building card '%s': %s", name, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -80,7 +80,7 @@ func newCardDan2ControllerBuilder() *cardBuilder {
|
||||||
if c.improved {
|
if c.improved {
|
||||||
romFilename = "<internal>/Apple2CardFirmwareImproved.bin"
|
romFilename = "<internal>/Apple2CardFirmwareImproved.bin"
|
||||||
}
|
}
|
||||||
err := c.loadRomFromResource(romFilename)
|
err := c.loadRomFromResource(romFilename, cardRomSimple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ func newCardDisk2Builder() *cardBuilder {
|
||||||
},
|
},
|
||||||
buildFunc: func(params map[string]string) (Card, error) {
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
var c CardDisk2
|
var c CardDisk2
|
||||||
err := c.loadRomFromResource("<internal>/DISK2.rom")
|
err := c.loadRomFromResource("<internal>/DISK2.rom", cardRomSimple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ func newCardDisk2SequencerBuilder() *cardBuilder {
|
||||||
},
|
},
|
||||||
buildFunc: func(params map[string]string) (Card, error) {
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
var c CardDisk2Sequencer
|
var c CardDisk2Sequencer
|
||||||
err := c.loadRomFromResource("<internal>/DISK2.rom")
|
err := c.loadRomFromResource("<internal>/DISK2.rom", cardRomSimple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ 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,
|
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
|
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.
|
Megabyte of RAM, the top nybble will be a meaningful part of the address.
|
||||||
|
|
||||||
|
Notes for RAMFactor:
|
||||||
|
- https://github.com/mamedev/mame/blob/master/src/devices/bus/a2bus/a2memexp.cpp
|
||||||
|
- ss 5 is for the ROM page, there are two.
|
||||||
|
- https://ae.applearchives.com/all_apple_iis/ramfactor/
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
memoryExpansionMask = 0x000fffff // 10 bits, 1MB
|
memoryExpansionMask = 0x000fffff // 10 bits, 1MB
|
||||||
|
@ -65,7 +70,7 @@ func newCardMemoryExpansionBuilder() *cardBuilder {
|
||||||
|
|
||||||
var c CardMemoryExpansion
|
var c CardMemoryExpansion
|
||||||
c.ram = make([]uint8, size*1024)
|
c.ram = make([]uint8, size*1024)
|
||||||
err = c.loadRomFromResource("<internal>/MemoryExpansionCard-341-0344a.bin")
|
err = c.loadRomFromResource("<internal>/MemoryExpansionCard-341-0344a.bin", cardRomFull)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,3 @@ func (c *MultiRomCard) peek(address uint16) uint8 {
|
||||||
func (c *MultiRomCard) poke(address uint16, value uint8) {
|
func (c *MultiRomCard) poke(address uint16, value uint8) {
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MultiRomCard) setBase(base uint16) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ func newCardParallelPrinterBuilder() *cardBuilder {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.file = f
|
c.file = f
|
||||||
err = c.loadRomFromResource("<internal>/Apple II Parallel Printer Interface Card ROM fixed.bin")
|
err = c.loadRomFromResource("<internal>/Apple II Parallel Printer Interface Card ROM fixed.bin", cardRomSimple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
133
cardProDOSRomCard3.go
Normal file
133
cardProDOSRomCard3.go
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ralle Palaveev's ProDOS-Romcard3
|
||||||
|
|
||||||
|
See:
|
||||||
|
https://github.com/rallepaqlaveev/ProDOS-Romcard3
|
||||||
|
|
||||||
|
Note that this card disables the C800-CFFF range only on writes to CFFF, not as most other cards that disable on reads and writes.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// CardProDOSRomCard3 is a Memory Expansion card
|
||||||
|
type CardProDOSRomCard3 struct {
|
||||||
|
cardBase
|
||||||
|
bank uint16
|
||||||
|
data []uint8
|
||||||
|
nvram bool
|
||||||
|
secondROMPage bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCardProDOSRomCard3Builder() *cardBuilder {
|
||||||
|
return &cardBuilder{
|
||||||
|
name: "ProDOS ROM Card 3",
|
||||||
|
description: "A bootable 4 MB ROM card by Ralle Palaveev",
|
||||||
|
defaultParams: &[]paramSpec{
|
||||||
|
{"image", "ROM image with the ProDOS volume", "https://github.com/rallepalaveev/ProDOS-Romcard3/raw/main/ProDOS-ROMCARD3_4MB_A2D.v1.4_v37.po"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
image := paramsGetPath(params, "image")
|
||||||
|
if image == "" {
|
||||||
|
return nil, fmt.Errorf("image required for the ProDOS ROM drive")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _, err := LoadResource(image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) != 4*1024*1024 {
|
||||||
|
return nil, fmt.Errorf("NVRAM image must be 4MB")
|
||||||
|
}
|
||||||
|
|
||||||
|
var c CardProDOSRomCard3
|
||||||
|
c.data = data
|
||||||
|
c.loadRom(data[0x200:0x300], cardRomSimple)
|
||||||
|
c.romC8xx = &c
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCardProDOSNVRAMDriveBuilder() *cardBuilder {
|
||||||
|
return &cardBuilder{
|
||||||
|
name: "ProDOS 4MB NVRAM DRive",
|
||||||
|
description: "A bootable 4 MB NVRAM card by Ralle Palaveev",
|
||||||
|
defaultParams: &[]paramSpec{
|
||||||
|
{"image", "ROM image with the ProDOS volume", ""},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
image := paramsGetPath(params, "image")
|
||||||
|
if image == "" {
|
||||||
|
return nil, fmt.Errorf("image required for the ProDOS ROM drive")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _, err := LoadResource(image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) != 4*1024*1024 && len(data) != 512*1024 {
|
||||||
|
return nil, fmt.Errorf("NVRAM image must be 512KB or 4MB")
|
||||||
|
}
|
||||||
|
|
||||||
|
var c CardProDOSRomCard3
|
||||||
|
c.data = data
|
||||||
|
c.loadRom(data[0x200:0x400], cardRomSimple)
|
||||||
|
c.romC8xx = &c
|
||||||
|
c.nvram = true
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CardProDOSRomCard3) assign(a *Apple2, slot int) {
|
||||||
|
|
||||||
|
// Set pointer position
|
||||||
|
c.addCardSoftSwitchW(0, func(value uint8) {
|
||||||
|
c.bank = uint16(value) | c.bank&0xff00
|
||||||
|
}, "BANKLO")
|
||||||
|
c.addCardSoftSwitchW(1, func(value uint8) {
|
||||||
|
c.bank = uint16(value)<<8 | c.bank&0xff
|
||||||
|
}, "BANKHI")
|
||||||
|
|
||||||
|
if c.nvram {
|
||||||
|
c.addCardSoftSwitchW(2, func(value uint8) {
|
||||||
|
if c.secondROMPage {
|
||||||
|
c.romCsxx.setPage(0)
|
||||||
|
} else {
|
||||||
|
c.romCsxx.setPage(1)
|
||||||
|
}
|
||||||
|
}, "?????")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.cardBase.assign(a, slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CardProDOSRomCard3) translateAddress(address uint16) int {
|
||||||
|
// An address from 0xC800 to 0xCFFF is mapped to the corresponding bank of the ROM
|
||||||
|
// There are 0x800 (2048) banks with 0x0800 (2048) bytes each
|
||||||
|
offset := address - 0xC800
|
||||||
|
pageAddress := int(c.bank&0x7FF) * 0x0800
|
||||||
|
|
||||||
|
//fmt.Printf("CardProDOSRomCard3.translateAddress: address=%04X, bank=%04X, offset=%04X, pageAddress=%08X\n", address, c.bank, offset, pageAddress)
|
||||||
|
|
||||||
|
return pageAddress + int(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CardProDOSRomCard3) peek(address uint16) uint8 {
|
||||||
|
if address&0xff == 0 {
|
||||||
|
fmt.Printf("CardProDOSRomCard3.peek: address=%04X\n", address)
|
||||||
|
}
|
||||||
|
return c.data[c.translateAddress(address)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CardProDOSRomCard3) poke(address uint16, value uint8) {
|
||||||
|
fmt.Printf("CardProDOSRomCard3.poke: address=%04X, value=%02X\n", address, value)
|
||||||
|
if c.nvram && address != 0xcfff {
|
||||||
|
c.data[c.translateAddress(address)] = value
|
||||||
|
}
|
||||||
|
}
|
73
cardProDOSRomDrive.go
Normal file
73
cardProDOSRomDrive.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Terence Boldt's ProDOS-ROM-Drive: A bootable 1 MB solid state disk for Apple ][ computers
|
||||||
|
|
||||||
|
Emulates version 4.0+
|
||||||
|
|
||||||
|
See:
|
||||||
|
https://github.com/tjboldt/ProDOS-ROM-Drive
|
||||||
|
https://github.com/Alex-Kw/ProDOS-ROM-Drive-Images
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// CardMemoryExpansion is a Memory Expansion card
|
||||||
|
type CardProDOSRomDrive struct {
|
||||||
|
cardBase
|
||||||
|
address uint16
|
||||||
|
data []uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
const proDOSRomDriveMask = 0xf_ffff // 1 MB mask
|
||||||
|
|
||||||
|
func newCardProDOSRomDriveBuilder() *cardBuilder {
|
||||||
|
return &cardBuilder{
|
||||||
|
name: "ProDOS ROM Drive",
|
||||||
|
description: "A bootable 1 MB solid state disk by Terence Boldt",
|
||||||
|
defaultParams: &[]paramSpec{
|
||||||
|
//{"image", "ROM image with the ProDOS volume", "https://github.com/tjboldt/ProDOS-ROM-Drive/raw/v4.0/Firmware/GamesWithFirmware.po"},
|
||||||
|
{"image", "ROM image with the ProDOS volume", "https://github.com/Alex-Kw/ProDOS-ROM-Drive-Images/raw/main/ProDOS_2.4.3_TJ.po"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
image := paramsGetPath(params, "image")
|
||||||
|
if image == "" {
|
||||||
|
return nil, fmt.Errorf("image required for the ProDOS ROM drive")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _, err := LoadResource(image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var c CardProDOSRomDrive
|
||||||
|
c.data = data
|
||||||
|
c.loadRom(data[0x300:0x400], cardRomSimple)
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CardProDOSRomDrive) assign(a *Apple2, slot int) {
|
||||||
|
|
||||||
|
// Set pointer position
|
||||||
|
c.addCardSoftSwitchW(0, func(value uint8) {
|
||||||
|
c.address = uint16(value) | c.address&0xff00
|
||||||
|
}, "LATCHLO")
|
||||||
|
c.addCardSoftSwitchW(1, func(value uint8) {
|
||||||
|
c.address = uint16(value)<<8 | c.address&0xff
|
||||||
|
}, "LATCHHI")
|
||||||
|
|
||||||
|
// Read data
|
||||||
|
for i := uint8(0x0); i <= 0xf; i++ {
|
||||||
|
iCopy := i
|
||||||
|
c.addCardSoftSwitchR(iCopy, func() uint8 {
|
||||||
|
offset := uint32(c.address)<<4 + uint32(iCopy)
|
||||||
|
offset &= proDOSRomDriveMask
|
||||||
|
return c.data[offset]
|
||||||
|
}, fmt.Sprintf("READ%X", iCopy))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.cardBase.assign(a, slot)
|
||||||
|
}
|
|
@ -112,7 +112,7 @@ func (c *CardSmartPort) AddDevice(device smartPortDevice) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardSmartPort) assign(a *Apple2, slot int) {
|
func (c *CardSmartPort) assign(a *Apple2, slot int) {
|
||||||
c.loadRom(buildHardDiskRom(slot))
|
c.loadRom(buildHardDiskRom(slot), cardRomSimple)
|
||||||
|
|
||||||
c.addCardSoftSwitchR(0, func() uint8 {
|
c.addCardSoftSwitchR(0, func() uint8 {
|
||||||
// Prodos entry point
|
// Prodos entry point
|
||||||
|
|
|
@ -135,7 +135,3 @@ func (c *CardSwyft) peek(address uint16) uint8 {
|
||||||
func (c *CardSwyft) poke(address uint16, value uint8) {
|
func (c *CardSwyft) poke(address uint16, value uint8) {
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardSwyft) setBase(base uint16) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ func newCardThunderClockPlusBuilder() *cardBuilder {
|
||||||
description: "Clock card",
|
description: "Clock card",
|
||||||
buildFunc: func(params map[string]string) (Card, error) {
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
var c CardThunderClockPlus
|
var c CardThunderClockPlus
|
||||||
err := c.loadRomFromResource("<internal>/ThunderclockPlusROM.bin")
|
err := c.loadRomFromResource("<internal>/ThunderclockPlusROM.bin", cardRomUpper)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ func newCardVidHDBuilder() *cardBuilder {
|
||||||
description: "Firmware signature of the VidHD card to trick Total Replay to use the SHR mode",
|
description: "Firmware signature of the VidHD card to trick Total Replay to use the SHR mode",
|
||||||
buildFunc: func(params map[string]string) (Card, error) {
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
var c CardVidHD
|
var c CardVidHD
|
||||||
c.loadRom(buildVidHDRom())
|
c.loadRom(buildVidHDRom(), cardRomSimple)
|
||||||
return &c, nil
|
return &c, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ func newCardVidexBuilder() *cardBuilder {
|
||||||
var c CardVidex
|
var c CardVidex
|
||||||
|
|
||||||
// The C800 area has ROM and RAM
|
// The C800 area has ROM and RAM
|
||||||
err := c.loadRomFromResource("<internal>/Videx Videoterm ROM 2.4.bin")
|
err := c.loadRomFromResource("<internal>/Videx Videoterm ROM 2.4.bin", cardRomUpperHalfEnd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -123,10 +123,6 @@ func (c *CardVidex) poke(address uint16, value uint8) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardVidex) setBase(base uint16) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
videxCharWidth = uint8(8)
|
videxCharWidth = uint8(8)
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,4 @@ board: 2e
|
||||||
rom: <internal>/Apple2e.rom
|
rom: <internal>/Apple2e.rom
|
||||||
charrom: <internal>/Apple IIe Video Unenhanced.bin
|
charrom: <internal>/Apple IIe Video Unenhanced.bin
|
||||||
s0: language
|
s0: language
|
||||||
s2: vidhd
|
|
||||||
s3: fastchip
|
|
||||||
s6: diskii,disk1=<internal>/dos33.dsk
|
s6: diskii,disk1=<internal>/dos33.dsk
|
|
@ -66,6 +66,8 @@ The available cards are:
|
||||||
mouse: Mouse card implementation, does not emulate a real card, only the firmware behaviour
|
mouse: Mouse card implementation, does not emulate a real card, only the firmware behaviour
|
||||||
multirom: Multiple Image ROM card
|
multirom: Multiple Image ROM card
|
||||||
parallel: Card to dump to a file what would be printed to a parallel printer
|
parallel: Card to dump to a file what would be printed to a parallel printer
|
||||||
|
prodosromcard3: A bootable 4 MB ROM card by Ralle Palaveev
|
||||||
|
prodosromdrive: A bootable 1 MB solid state disk by Terence Boldt
|
||||||
saturn: RAM card with 128Kb, it's like 8 language cards
|
saturn: RAM card with 128Kb, it's like 8 language cards
|
||||||
smartport: SmartPort interface card
|
smartport: SmartPort interface card
|
||||||
softswitchlogger: Card to log softswitch accesses
|
softswitchlogger: Card to log softswitch accesses
|
||||||
|
@ -80,7 +82,7 @@ The available tracers are:
|
||||||
mli: Trace ProDOS MLI calls
|
mli: Trace ProDOS MLI calls
|
||||||
mos: Trace MOS calls with Applecorn skipping terminal IO
|
mos: Trace MOS calls with Applecorn skipping terminal IO
|
||||||
mosfull: Trace MOS calls with Applecorn
|
mosfull: Trace MOS calls with Applecorn
|
||||||
panicSS: Panic on unimplemented softswitches
|
panicss: Panic on unimplemented softswitches
|
||||||
ss: Trace sotfswiches calls
|
ss: Trace sotfswiches calls
|
||||||
ssreg: Trace sotfswiches registrations
|
ssreg: Trace sotfswiches registrations
|
||||||
ucsd: Trace UCSD system calls
|
ucsd: Trace UCSD system calls
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
cd "$( dirname $0)"
|
cd "$( dirname $0)"
|
||||||
mkdir -p ${PWD}/build
|
mkdir -p ${PWD}/build
|
||||||
|
|
||||||
# MacOS builds
|
# MacOS ARM builds
|
||||||
echo "Building MacOS console frontend"
|
echo "Building MacOS console frontend"
|
||||||
CGO_ENABLED=1 go build -tags static -ldflags "-s -w" ../frontend/console
|
CGO_ENABLED=1 go build -tags static -ldflags "-s -w" ../frontend/console
|
||||||
mv console build/izapple2console_mac_arm64
|
mv console build/izapple2console_mac_arm64
|
||||||
|
@ -15,6 +15,19 @@ echo "Building MacOS Fyne frontend"
|
||||||
CGO_ENABLED=1 go build -tags static -ldflags "-s -w" ../frontend/a2fyne
|
CGO_ENABLED=1 go build -tags static -ldflags "-s -w" ../frontend/a2fyne
|
||||||
mv a2fyne build/izapple2fyne_mac_arm64
|
mv a2fyne build/izapple2fyne_mac_arm64
|
||||||
|
|
||||||
|
# MacOS x64 builds
|
||||||
|
echo "Building MacOS console frontend"
|
||||||
|
GOARCH=amd64 CGO_ENABLED=1 go build -tags static -ldflags "-s -w" ../frontend/console
|
||||||
|
mv console build/izapple2console_mac_amd64
|
||||||
|
|
||||||
|
echo "Building MacOS SDL frontend"
|
||||||
|
GOARCH=amd64 CGO_ENABLED=1 go build -tags static -ldflags "-s -w" ../frontend/a2sdl
|
||||||
|
mv a2sdl build/izapple2sdl_mac_amd64
|
||||||
|
|
||||||
|
echo "Building MacOS Fyne frontend"
|
||||||
|
GOARCH=amd64 CGO_ENABLED=1 go build -tags static -ldflags "-s -w" ../frontend/a2fyne
|
||||||
|
mv a2fyne build/izapple2fyne_mac_amd64∫
|
||||||
|
|
||||||
# Linux and Windows dockerized builds
|
# Linux and Windows dockerized builds
|
||||||
echo "Building docker container for the Linux and Windows builds"
|
echo "Building docker container for the Linux and Windows builds"
|
||||||
docker build . -t apple2builder --platform linux/amd64
|
docker build . -t apple2builder --platform linux/amd64
|
||||||
|
|
|
@ -151,6 +151,7 @@ var helpMessage = `
|
||||||
|
|
||||||
F1: Show/Hide help
|
F1: Show/Hide help
|
||||||
Ctrl-F2: Reset
|
Ctrl-F2: Reset
|
||||||
|
F4: Show/Hide CPU trace
|
||||||
F5: Fast/Normal speed
|
F5: Fast/Normal speed
|
||||||
Ctrl-F5: Show speed
|
Ctrl-F5: Show speed
|
||||||
F6: Next screen mode
|
F6: Next screen mode
|
||||||
|
@ -158,7 +159,6 @@ var helpMessage = `
|
||||||
F10: Next character set
|
F10: Next character set
|
||||||
Ctrl-F10: Show/Hide character set
|
Ctrl-F10: Show/Hide character set
|
||||||
Shift-F10: Show/Hide alternate text
|
Shift-F10: Show/Hide alternate text
|
||||||
F11: Show/Hide CPU trace
|
|
||||||
F12: Save screen snapshot
|
F12: Save screen snapshot
|
||||||
Pause: Pause the emulation
|
Pause: Pause the emulation
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,8 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) {
|
||||||
if ctrl {
|
if ctrl {
|
||||||
k.a.SendCommand(izapple2.CommandReset)
|
k.a.SendCommand(izapple2.CommandReset)
|
||||||
}
|
}
|
||||||
|
case sdl.K_F4:
|
||||||
|
k.a.SendCommand(izapple2.CommandToggleCPUTrace)
|
||||||
case sdl.K_F5:
|
case sdl.K_F5:
|
||||||
if ctrl {
|
if ctrl {
|
||||||
k.a.SendCommand(izapple2.CommandShowSpeed)
|
k.a.SendCommand(izapple2.CommandShowSpeed)
|
||||||
|
@ -123,8 +125,6 @@ func (k *sdlKeyboard) putKey(keyEvent *sdl.KeyboardEvent) {
|
||||||
} else {
|
} else {
|
||||||
k.a.SendCommand(izapple2.CommandNextCharGenPage)
|
k.a.SendCommand(izapple2.CommandNextCharGenPage)
|
||||||
}
|
}
|
||||||
case sdl.K_F11:
|
|
||||||
k.a.SendCommand(izapple2.CommandToggleCPUTrace)
|
|
||||||
case sdl.K_F12:
|
case sdl.K_F12:
|
||||||
fallthrough
|
fallthrough
|
||||||
case sdl.K_PRINTSCREEN:
|
case sdl.K_PRINTSCREEN:
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
fyne.io/fyne/v2 v2.1.4
|
fyne.io/fyne/v2 v2.1.4
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3
|
||||||
github.com/ivanizag/iz6502 v1.3.2
|
github.com/ivanizag/iz6502 v1.4.0
|
||||||
github.com/pkg/profile v1.7.0
|
github.com/pkg/profile v1.7.0
|
||||||
github.com/veandco/go-sdl2 v0.4.38
|
github.com/veandco/go-sdl2 v0.4.38
|
||||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -38,8 +38,8 @@ github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8I
|
||||||
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 h1:WzfWbQz/Ze8v6l++GGbGNFZnUShVpP/0xffCPLL+ax8=
|
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 h1:WzfWbQz/Ze8v6l++GGbGNFZnUShVpP/0xffCPLL+ax8=
|
||||||
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
github.com/ivanizag/iz6502 v1.3.2 h1:JQAxsGVXeerQc+L5wGpGPEgvX+yxLqpvm2Dx6aO7wGU=
|
github.com/ivanizag/iz6502 v1.4.0 h1:7eYygUkCPwFRH0tf2JSg1k+Sy27wwPi3ActuVNVv1Uc=
|
||||||
github.com/ivanizag/iz6502 v1.3.2/go.mod h1:h4gbw3IK6WCYawi00kBhQ4ACeQkGWgqbUeAgDaQpy6s=
|
github.com/ivanizag/iz6502 v1.4.0/go.mod h1:h4gbw3IK6WCYawi00kBhQ4ACeQkGWgqbUeAgDaQpy6s=
|
||||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||||
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
|
55
ioC0Page.go
55
ioC0Page.go
|
@ -5,20 +5,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ioC0Page struct {
|
type ioC0Page struct {
|
||||||
softSwitchesR [256]softSwitchR
|
softSwitchesR [256]softSwitchR
|
||||||
softSwitchesW [256]softSwitchW
|
softSwitchesW [256]softSwitchW
|
||||||
softSwitchesRName [256]string
|
softSwitchesRName [256]string
|
||||||
softSwitchesWName [256]string
|
softSwitchesWName [256]string
|
||||||
softSwitchesData [128]uint8
|
softSwitchesData [128]uint8
|
||||||
keyboard KeyboardProvider
|
keyboard KeyboardProvider
|
||||||
speaker SpeakerProvider
|
speaker SpeakerProvider
|
||||||
paddlesStrobeCycle uint64
|
paddlesStrobeCycle uint64
|
||||||
joysticks JoysticksProvider
|
joysticks JoysticksProvider
|
||||||
mouse MouseProvider
|
mouse MouseProvider
|
||||||
apple2 *Apple2
|
apple2 *Apple2
|
||||||
traceMask uint16 // A bit for each 16 softswitches
|
traceMask uint16 // A bit for each 16 softswitches
|
||||||
traceRegistrations bool
|
panicMask uint16 // A bit for each 16 softswitches
|
||||||
panicNotImplemented bool
|
traceRegistrations bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type softSwitchR func() uint8
|
type softSwitchR func() uint8
|
||||||
|
@ -65,7 +65,10 @@ func (p *ioC0Page) setTrace(trace bool) {
|
||||||
|
|
||||||
func (p *ioC0Page) traceSlot(slot int) {
|
func (p *ioC0Page) traceSlot(slot int) {
|
||||||
p.traceMask |= 1 << (8 + slot)
|
p.traceMask |= 1 << (8 + slot)
|
||||||
fmt.Printf("Slot %v traced %04x\n", slot, p.traceMask)
|
}
|
||||||
|
|
||||||
|
func (p *ioC0Page) panicNotImplementedSlot(slot int) {
|
||||||
|
p.panicMask |= 1 << (8 + slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) setTraceRegistrations(traceRegistrations bool) {
|
func (p *ioC0Page) setTraceRegistrations(traceRegistrations bool) {
|
||||||
|
@ -73,7 +76,11 @@ func (p *ioC0Page) setTraceRegistrations(traceRegistrations bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) setPanicNotImplemented(value bool) {
|
func (p *ioC0Page) setPanicNotImplemented(value bool) {
|
||||||
p.panicNotImplemented = value
|
if value {
|
||||||
|
p.panicMask = 0xffff
|
||||||
|
} else {
|
||||||
|
p.panicMask = 0x0000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) addSoftSwitchRW(address uint8, ss softSwitchR, name string) {
|
func (p *ioC0Page) addSoftSwitchRW(address uint8, ss softSwitchR, name string) {
|
||||||
|
@ -121,10 +128,16 @@ func (p *ioC0Page) setMouseProvider(m MouseProvider) {
|
||||||
|
|
||||||
func (p *ioC0Page) isTraced(address uint16) bool {
|
func (p *ioC0Page) isTraced(address uint16) bool {
|
||||||
ss := address & 0xff
|
ss := address & 0xff
|
||||||
return ss != 0xc000 && // Do not trace the spammy keyboard softswitch
|
return address != 0xc000 && // Do not trace the spammy keyboard softswitch
|
||||||
(p.traceMask&(1<<(ss>>4))) != 0
|
(p.traceMask&(1<<(ss>>4))) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ioC0Page) isPanicNotImplemented(address uint16) bool {
|
||||||
|
ss := address & 0xff
|
||||||
|
return ss != 0xc068 && // Ignore known IIGS softswitch
|
||||||
|
(p.panicMask&(1<<(ss>>4))) != 0
|
||||||
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) peek(address uint16) uint8 {
|
func (p *ioC0Page) peek(address uint16) uint8 {
|
||||||
pageAddress := uint8(address)
|
pageAddress := uint8(address)
|
||||||
ss := p.softSwitchesR[pageAddress]
|
ss := p.softSwitchesR[pageAddress]
|
||||||
|
@ -132,7 +145,7 @@ func (p *ioC0Page) peek(address uint16) uint8 {
|
||||||
if p.isTraced(address) {
|
if p.isTraced(address) {
|
||||||
fmt.Printf("Unknown softswitch on read to $%04x\n", address)
|
fmt.Printf("Unknown softswitch on read to $%04x\n", address)
|
||||||
}
|
}
|
||||||
if p.panicNotImplemented {
|
if p.isPanicNotImplemented(address) {
|
||||||
panic(fmt.Sprintf("Unknown softswitch on read to $%04x", address))
|
panic(fmt.Sprintf("Unknown softswitch on read to $%04x", address))
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
@ -152,7 +165,7 @@ func (p *ioC0Page) poke(address uint16, value uint8) {
|
||||||
if p.isTraced(address) {
|
if p.isTraced(address) {
|
||||||
fmt.Printf("Unknown softswitch on write $%02x to $%04x\n", value, address)
|
fmt.Printf("Unknown softswitch on write $%02x to $%04x\n", value, address)
|
||||||
}
|
}
|
||||||
if p.panicNotImplemented {
|
if p.isPanicNotImplemented(address) {
|
||||||
panic(fmt.Sprintf("Unknown softswitch on write to $%04x", address))
|
panic(fmt.Sprintf("Unknown softswitch on write to $%04x", address))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -164,10 +177,6 @@ func (p *ioC0Page) poke(address uint16, value uint8) {
|
||||||
ss(value)
|
ss(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ioC0Page) setBase(_ uint16) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
func ssFromBool(value bool) uint8 {
|
func ssFromBool(value bool) uint8 {
|
||||||
if value {
|
if value {
|
||||||
return ssOn
|
return ssOn
|
||||||
|
|
|
@ -68,7 +68,6 @@ const (
|
||||||
type memoryHandler interface {
|
type memoryHandler interface {
|
||||||
peek(uint16) uint8
|
peek(uint16) uint8
|
||||||
poke(uint16, uint8)
|
poke(uint16, uint8)
|
||||||
setBase(uint16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMemoryManager(a *Apple2) *memoryManager {
|
func newMemoryManager(a *Apple2) *memoryManager {
|
||||||
|
@ -105,7 +104,14 @@ func (mmu *memoryManager) accessCArea(address uint16) memoryHandler {
|
||||||
// Extra slot area reset
|
// Extra slot area reset
|
||||||
if address == ioC8Off {
|
if address == ioC8Off {
|
||||||
// Reset extra slot area owner
|
// Reset extra slot area owner
|
||||||
mmu.activeSlot = 0
|
|
||||||
|
// There is not really an activeSlot in c8xx, any card could be active
|
||||||
|
// we should check all of them and maybe have conflicts. As I don't do that I won't disable and
|
||||||
|
// just track teh last active card.
|
||||||
|
// This code is disabled because cards could have different logic for disabling. Most cards disable
|
||||||
|
// on access to 0xCFFF, but the ProDOS ROM card 3 disables only on writes and not on reads.
|
||||||
|
// mmu.activeSlot = 0
|
||||||
|
|
||||||
mmu.intC8ROMActive = false
|
mmu.intC8ROMActive = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,10 +141,6 @@ func (nsc *noSlotClockDS1216) poke(address uint16, value uint8) {
|
||||||
nsc.memory.poke(address, value)
|
nsc.memory.poke(address, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nsc *noSlotClockDS1216) setBase(base uint16) {
|
|
||||||
nsc.memory.setBase(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nsc *noSlotClockDS1216) loadTime() {
|
func (nsc *noSlotClockDS1216) loadTime() {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,14 @@ func LoadResource(filename string) ([]uint8, bool, error) {
|
||||||
filename = filename[1 : len(filename)-1]
|
filename = filename[1 : len(filename)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expand the tilde if prefixed by it
|
||||||
|
if strings.HasPrefix(filename, "~") {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err == nil {
|
||||||
|
filename = home + filename[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var writeable bool
|
var writeable bool
|
||||||
var file io.Reader
|
var file io.Reader
|
||||||
if isInternalResource(filename) {
|
if isInternalResource(filename) {
|
||||||
|
|
|
@ -64,8 +64,8 @@ func getTracerFactory() map[string]*traceBuilder {
|
||||||
description: "Trace sotfswiches registrations",
|
description: "Trace sotfswiches registrations",
|
||||||
connectFunc: func(a *Apple2) { a.io.setTraceRegistrations(true) },
|
connectFunc: func(a *Apple2) { a.io.setTraceRegistrations(true) },
|
||||||
}
|
}
|
||||||
tracerFactory["panicSS"] = &traceBuilder{
|
tracerFactory["panicss"] = &traceBuilder{
|
||||||
name: "panicSS",
|
name: "panicss",
|
||||||
description: "Panic on unimplemented softswitches",
|
description: "Panic on unimplemented softswitches",
|
||||||
connectFunc: func(a *Apple2) { a.io.setPanicNotImplemented(true) },
|
connectFunc: func(a *Apple2) { a.io.setPanicNotImplemented(true) },
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,6 +189,8 @@ func (t *traceProDOS) dumpMLIReturn() {
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Ok\n")
|
fmt.Printf("Ok\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.a.cpu.SetTrace(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +219,7 @@ func (t *traceProDOS) dumpDriverCall() {
|
||||||
if int(command) < len(proDosCommandNames) {
|
if int(command) < len(proDosCommandNames) {
|
||||||
commandName = proDosCommandNames[command]
|
commandName = proDosCommandNames[command]
|
||||||
}
|
}
|
||||||
fmt.Printf("\n Prodos driver $%04x command %02x-%s on unit $%x, block %v to $%04x ==> ", pc, command, commandName, unit, block, address)
|
fmt.Printf("\n Prodos driver $%04x command %02x-%s on unit $%x, block %v to/from $%04x ==> ", pc, command, commandName, unit, block, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
//lint:ignore U1000 unused but stays as reference
|
//lint:ignore U1000 unused but stays as reference
|
||||||
|
|
Loading…
Reference in New Issue
Block a user