diff --git a/cards/cards.go b/cards/cards.go index 0a05744..f8a71c3 100644 --- a/cards/cards.go +++ b/cards/cards.go @@ -1,6 +1,7 @@ package cards type Card interface { + Init() // Give the card a chance to initialize String() string // The name of the card, for debug/display purposes Read16(address byte) byte // Read from the $C0(8+slot)X addresses Write16(address byte, value byte) // Write to the $C0(8+slot)X addresses diff --git a/cards/disk.go b/cards/disk.go index 6afd5a6..d3fca0a 100644 --- a/cards/disk.go +++ b/cards/disk.go @@ -58,6 +58,9 @@ func (dc *DiskCard) String() string { } +func (dc *DiskCard) Init() { +} + func (dc *DiskCard) Slot() byte { return dc.slot } diff --git a/cards/firmware.go b/cards/firmware.go index d55508a..75adab3 100644 --- a/cards/firmware.go +++ b/cards/firmware.go @@ -5,18 +5,17 @@ import ( ) type FirmwareCard struct { - name string - rom [12288]byte - cm CardManager - slot byte - slotbit byte + name string + rom [12288]byte + cm CardManager + slot byte } func NewFirmwareCard(rom []byte, name string, slot byte, cm CardManager) (*FirmwareCard, error) { if len(rom) != 12288 { return nil, fmt.Errorf("Wrong size ROM: expected 12288, got %d", len(rom)) } - fc := &FirmwareCard{name: name, cm: cm, slot: slot, slotbit: 1 << slot} + fc := &FirmwareCard{name: name, cm: cm, slot: slot} copy(fc.rom[:], rom) return fc, nil } @@ -25,6 +24,9 @@ func (fc *FirmwareCard) String() string { return fmt.Sprintf("%s (slot %d)", fc.name, fc.slot) } +func (fc *FirmwareCard) Init() { +} + func (fc *FirmwareCard) Slot() byte { return fc.slot } @@ -36,10 +38,10 @@ func (fc *FirmwareCard) ROMDisabled() { func (fc *FirmwareCard) handleAccess(address byte) { if address%2 == 1 { // Card off - fc.cm.HandleROM(false, fc.slotbit) + fc.cm.HandleROM(false, fc.slot) } else { // Card on - fc.cm.HandleROM(true, fc.slotbit) + fc.cm.HandleROM(true, fc.slot) } } diff --git a/cards/language.go b/cards/language.go new file mode 100644 index 0000000..7f35b16 --- /dev/null +++ b/cards/language.go @@ -0,0 +1,142 @@ +package cards + +import ( + "fmt" +) + +type LanguageCard struct { + name string + rom [12288]byte + ram [16384]byte + cm CardManager + slot byte + slotbit byte + bank int // 1 = bank 1, 0 = bank 2 + ramread bool + ramwrite bool + readcount int +} + +func NewLanguageCard(rom []byte, name string, slot byte, cm CardManager) (*LanguageCard, error) { + if len(rom) != 12288 { + return nil, fmt.Errorf("Wrong size ROM: expected 12288, got %d", len(rom)) + } + lc := &LanguageCard{ + name: name, + cm: cm, + slot: slot, + slotbit: 1 << slot, + bank: 0, + ramread: false, + ramwrite: true, + readcount: 0, + } + copy(lc.rom[:], rom) + return lc, nil +} + +func (lc *LanguageCard) String() string { + return fmt.Sprintf("%s (slot %d)", lc.name, lc.slot) +} + +// Init: language card should always handle D000-FFFF accessess, since +// it contains either RAM or ROM. +func (lc *LanguageCard) Init() { + lc.cm.Handle12k(true, lc.slot) +} + +func (lc *LanguageCard) Slot() byte { + return lc.slot +} + +func (lc *LanguageCard) ROMDisabled() { + // Language card doesn't have a $C(8-F)xx ROM +} + +func (lc *LanguageCard) handleAccess(address byte, write bool) { + if write { + lc.readcount = 0 + } + + address &^= 4 + switch address &^ 8 { + case 0: + lc.ramread = true + lc.ramwrite = false + lc.readcount = 0 + case 1: + lc.ramread = false + if lc.readcount > 0 { + lc.ramwrite = true + } + if !write { + lc.readcount++ + } + case 2: + lc.ramread = false + lc.ramwrite = false + lc.readcount = 0 + case 3: + lc.ramread = true + if lc.readcount > 0 { + lc.ramwrite = true + } + if !write { + lc.readcount++ + } + } + lc.bank = int((address & 8) >> 3) + // fmt.Printf("ramread: %v, ramwrite: %v, bank: %d, readcount: %d\n", lc.ramread, lc.ramwrite, lc.bank, lc.readcount) +} + +func (lc *LanguageCard) Read16(address byte) byte { + // fmt.Printf("Read to %02xd: ", address) + lc.handleAccess(address, false) + return lc.cm.EmptyRead() +} + +func (lc *LanguageCard) Write16(address byte, value byte) { + // fmt.Printf("Write to %02xd: ", address) + lc.handleAccess(address, true) +} + +func (lc *LanguageCard) ramOffset(address uint16) uint16 { + if address < 0xE000 { + return address - 0xD000 + 0x1000*uint16(lc.bank) + } + return address - 0xE000 + 0x2000 +} + +func (lc *LanguageCard) Read(address uint16) byte { + // fmt.Printf("Read from %04x\n", address) + if address < 0xD000 { + panic(fmt.Sprintf("%s got read to $%04X (<$D000)", lc.String(), address)) + } + if lc.ramread { + return lc.ram[lc.ramOffset(address)] + } + return lc.rom[address-0xD000] +} + +func (lc *LanguageCard) Write(address uint16, value byte) { + // fmt.Printf("Write to %04x\n", address) + if lc.ramwrite { + lc.ram[lc.ramOffset(address)] = value + } +} + +func (lc *LanguageCard) Read256(address byte) byte { + return lc.cm.EmptyRead() +} + +func (lc *LanguageCard) Write256(address byte, value byte) { + // Language is ROM: do nothing +} + +func (lc *LanguageCard) WantTicker() bool { + return false +} + +func (lc *LanguageCard) Tick() { + // do nothing +} diff --git a/goapple2.go b/goapple2.go index a517e00..7621de0 100644 --- a/goapple2.go +++ b/goapple2.go @@ -95,6 +95,7 @@ func (a2 *Apple2) AddCard(card cards.Card) error { a2.cardTickerMask |= slotbit } a2.cards[slot] = card + card.Init() return nil } @@ -213,7 +214,7 @@ func (a2 *Apple2) Read(address uint16) byte { if address&0xF000 == 0xC000 { return a2.handleC00X(address, 0, false) } - if address >= 0xD000 && a2.cardRomMask > 0 { + if address >= 0xD000 && a2.card12kMask > 0 { if a2.card12kConflict { panic(fmt.Sprintf("More than one card trying to provide 12K ROM: Mask=$%02X", a2.card12kMask)) } @@ -235,7 +236,7 @@ func (a2 *Apple2) Write(address uint16, value byte) { // fmt.Printf("Write to 0x46: PC==$%04X\n", a2.cpu.PC()) // } if address >= 0xD000 { - if a2.cardRomMask > 0 { + if a2.card12kMask > 0 { if a2.card12kConflict { panic(fmt.Sprintf("More than one card trying to provide 12K ROM: Mask=$%02X", a2.card12kMask)) } @@ -344,9 +345,9 @@ func (a2 *Apple2) HandleROM(onOff bool, slot byte) { func (a2 *Apple2) Handle12k(onOff bool, slot byte) { if onOff { - a2.card12kMask |= slot + a2.card12kMask |= (1 << slot) } else { - a2.card12kMask &^= slot + a2.card12kMask &^= (1 << slot) } a2.card12kConflict = a2.card12kMask&(a2.card12kMask-1) > 0 if !onOff && !a2.card12kConflict && a2.card12kMask > 0 { diff --git a/shiny/shiny.go b/shiny/shiny.go index 9c4d163..bd02f14 100644 --- a/shiny/shiny.go +++ b/shiny/shiny.go @@ -8,7 +8,6 @@ import ( "image/color" "log" "os" - "runtime" "runtime/pprof" "golang.org/x/exp/shiny/driver" @@ -18,6 +17,7 @@ import ( "github.com/zellyn/goapple2" "github.com/zellyn/goapple2/cards" + "github.com/zellyn/goapple2/disk" "github.com/zellyn/goapple2/util" "github.com/zellyn/goapple2/videoscan" ) @@ -39,17 +39,17 @@ const ( func RunEmulator(s screen.Screen) { rom := util.ReadRomOrDie("../data/roms/apple2+.rom", 12288) charRom := util.ReadSmallCharacterRomOrDie("../data/roms/apple2-chars.rom") - intBasicRom := util.ReadRomOrDie("../data/roms/apple2.rom", 12288) + // intBasicRom := util.ReadRomOrDie("../data/roms/apple2.rom", 12288) util.ReadRomOrDie("../data/roms/Apple Disk II 16 Sector Interface Card ROM P5 - 341-0027.bin", 256) eventChan := make(chan (interface{})) - w, err := s.NewWindow(&screen.NewWindowOptions{Width: SCREEN_WIDTH, Height: SCREEN_HEIGHT}) + w, err := s.NewWindow(&screen.NewWindowOptions{Width: SCREEN_WIDTH * 2, Height: SCREEN_HEIGHT * 2}) if err != nil { log.Fatal(err) } defer w.Release() - winSize := image.Point{SCREEN_WIDTH, SCREEN_HEIGHT} + winSize := image.Point{SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2} b, err := s.NewBuffer(winSize) if err != nil { log.Fatal(err) @@ -59,37 +59,48 @@ func RunEmulator(s screen.Screen) { var a2 *goapple2.Apple2 oncePerFrame := func() { a2.Done = a2.Done || ProcessEvents(a2, w, eventChan) - runtime.Gosched() } plotter := ShinyPlotter{w, b, oncePerFrame} a2 = goapple2.NewApple2(plotter, rom, charRom) - firmwareCard, err := cards.NewFirmwareCard(intBasicRom, "Intbasic Firmware Card", 0, a2) - if err != nil { - panic(err) - } - if err := a2.AddCard(firmwareCard); err != nil { - log.Fatal(err) - } - /* - diskCardRom := util.ReadRomOrDie("../data/roms/Apple Disk II 16 Sector Interface Card ROM P5 - 341-0027.bin", 256) - diskCard, err := cards.NewDiskCard(diskCardRom, 6, a2) + firmwareCard, err := cards.NewFirmwareCard(intBasicRom, "Intbasic Firmware Card", 0, a2) if err != nil { panic(err) } - if err := a2.AddCard(diskCard); err != nil { + if err := a2.AddCard(firmwareCard); err != nil { log.Fatal(err) } */ + + languageCard, err := cards.NewLanguageCard(rom, "Language Card", 0, a2) + if err != nil { + panic(err) + } + if err := a2.AddCard(languageCard); err != nil { + log.Fatal(err) + } + + diskCardRom := util.ReadRomOrDie("../data/roms/Apple Disk II 16 Sector Interface Card ROM P5 - 341-0027.bin", 256) + diskCard, err := cards.NewDiskCard(diskCardRom, 6, a2) + if err != nil { + panic(err) + } + if err := a2.AddCard(diskCard); err != nil { + log.Fatal(err) + } // disk1, err := disk.DiskFromFile("../data/disks/spedtest.dsk", 0) // disk1, err := disk.DiskFromFile("../data/disks/dung_beetles.dsk", 0) // disk1, err := disk.DiskFromFile("../data/disks/chivalry.dsk", 0) // disk1, err := disk.DiskFromFile("../data/disks/wavynavy.dsk", 0) - // if err != nil { - // log.Fatal(err) - // } - // diskCard.LoadDisk(disk1, 0) + disk1, err := disk.DiskFromFile("/Users/zellyn/Documents/a2-disks/disks/Rescue_Raiders_1.2.dsk", 0) + // disk1, err := disk.DiskFromFile("/Users/zellyn/Development/go/src/github.com/zellyn/a2audit/floatbus/floatbus.dsk", 0) + // disk1, err := disk.DiskFromFile("/Users/zellyn/Development/go/src/github.com/zellyn/a2audit/audit/audit.dsk", 0) + // disk1, err := disk.DiskFromFile("/Users/zellyn/Development/go/src/github.com/zellyn/diskii/lib/supermon/testdata/chacha20.dsk", 0) + if err != nil { + log.Fatal(err) + } + diskCard.LoadDisk(disk1, 0) steps := *steplimit @@ -119,7 +130,7 @@ func RunEmulator(s screen.Screen) { a2.AddPCAction(0xBDAF, goapple2.PCAction{Type: goapple2.ActionDiskStatus}) - a2.AddPCAction(0xBDAF, goapple2.PCAction{Type: goapple2.ActionTrace, String: "on", + a2.AddPCAction(0x6000, goapple2.PCAction{Type: goapple2.ActionTrace, String: "on", Delay: 70}) a2.AddPCAction( @@ -129,7 +140,9 @@ func RunEmulator(s screen.Screen) { 0xBDAF, goapple2.PCAction{Type: goapple2.ActionDumpMem, String: "0xBDAF-goa2.bin", Delay: 68}) */ - go typeProgram(a2) + // a2.AddPCAction(0x6000, goapple2.PCAction{Type: goapple2.ActionTrace, String: "on"}) + + // go typeProgram(a2) go func() { for { @@ -143,7 +156,6 @@ func RunEmulator(s screen.Screen) { fmt.Println(err) break } - // runtime.Gosched() // So the keyboard-reading goroutines can run if steps > 0 { steps-- if steps == 0 { @@ -156,7 +168,12 @@ func RunEmulator(s screen.Screen) { func plot(x, y int, c color.RGBA, b screen.Buffer) { rgba := b.RGBA() - rgba.SetRGBA(x+BORDER_W, y+BORDER_H, c) + xx := (x + BORDER_W) * 2 + yy := (y + BORDER_H) * 2 + rgba.SetRGBA(xx, yy, c) + rgba.SetRGBA(xx+1, yy, c) + rgba.SetRGBA(xx, yy+1, c) + rgba.SetRGBA(xx+1, yy+1, c) /* x = x + BORDER_W