From cc45403b43ff6103a5900a155bf98f4bd94a96ec Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Tue, 5 Mar 2019 00:00:12 +0100 Subject: [PATCH] Boots DOS 3.3 using nib format --- apple2/apple2.go | 6 +- apple2/cardDisk2.go | 133 +++++++++++++++++++++++++++++---------- apple2/ioC0Page.go | 6 +- apple2/unassignedPage.go | 6 +- main.go | 7 ++- 5 files changed, 116 insertions(+), 42 deletions(-) diff --git a/apple2/apple2.go b/apple2/apple2.go index 26e4a3d..8b78734 100644 --- a/apple2/apple2.go +++ b/apple2/apple2.go @@ -32,9 +32,13 @@ func NewApple2(romFile string) *Apple2 { } // AddDisk2 insterts a DiskII controller on slot 6 -func (a *Apple2) AddDisk2(diskRomFile string) { +func (a *Apple2) AddDisk2(diskRomFile string, diskImage string) { d := newCardDisk2(diskRomFile) d.cardBase.insert(a, 6) + + if diskImage != "" { + d.drive[0].loadDisk(diskImage) + } } // Run starts the Apple2 emulation diff --git a/apple2/cardDisk2.go b/apple2/cardDisk2.go index 49f146d..931526c 100644 --- a/apple2/cardDisk2.go +++ b/apple2/cardDisk2.go @@ -2,7 +2,6 @@ package apple2 import ( "bufio" - "fmt" "os" ) @@ -10,14 +9,29 @@ import ( https://applesaucefdc.com/woz/reference2/ Good explanation of the softswitches and the phases: http://yesterbits.com/media/pubs/AppleOrchard/articles/disk-ii-part-1-1983-apr.pdf + +35 tracks, 16 sectors, 256 bytes +NIB: 35 tracks 6656 bytes, 232960 bytes + */ +const maxHalfTrack = 68 +const bytesPerTrack = 6656 +const nibImageSize = 35 * bytesPerTrack type cardDisk2 struct { cardBase - phases [4]bool - power [2]bool - selected int // Only 0 and 1 supported - writeMode bool + selected int // Only 0 and 1 supported + drive [2]cardDisk2Drive +} + +type cardDisk2Drive struct { + isLoaded bool + currentPhase int + power bool + writeMode bool + halfTrack int + position int + data *[]uint8 } // type softSwitchR func(io *ioC0Page) uint8 @@ -27,73 +41,103 @@ func newCardDisk2(filename string) *cardDisk2 { c.rom = loadCardRom(filename) // Phase control soft switches + // Lazy emulation. It only checks for phases on and move the head + // up or down depending on the previous phase. for i := 0; i < 4; i++ { - c.ssr[i<<1] = func(_ *ioC0Page) uint8 { - fmt.Printf("DISKII: Phase %v off\n", i) - return 0 - } - c.ssr[(i<<1)+1] = func(_ *ioC0Page) uint8 { - fmt.Printf("DISKII: Phase %v on\n", i) - return 0 - } + func(phase int) { + c.ssr[phase<<1] = func(_ *ioC0Page) uint8 { + //fmt.Printf("DISKII: Phase %v off\n", phase) + return 0 + } + c.ssr[(phase<<1)+1] = func(_ *ioC0Page) uint8 { + //fmt.Printf("DISKII: Phase %v on\n", phase) + halfTrack := c.drive[c.selected].halfTrack + delta := (phase - c.drive[c.selected].currentPhase + 4) % 4 + switch delta { + case 1: // Up + halfTrack++ + case 2: // Illegal, let's say up + halfTrack++ + case 3: // Down + halfTrack-- + case 0: // No chamge + } + + if halfTrack > maxHalfTrack { + halfTrack = maxHalfTrack + } else if halfTrack < 0 { + halfTrack = 0 + } + c.drive[c.selected].halfTrack = halfTrack + c.drive[c.selected].currentPhase = phase + //fmt.Printf("DISKII: Current halftrack is %v\n", halfTrack) + return 0 + } + }(i) } // Other soft switches c.ssr[0x8] = func(_ *ioC0Page) uint8 { - c.power[c.selected] = false - fmt.Printf("DISKII: Disk %v is off\n", c.selected) + c.drive[c.selected].power = false + //fmt.Printf("DISKII: Disk %v is off\n", c.selected) return 0 } c.ssr[0x9] = func(_ *ioC0Page) uint8 { - c.power[c.selected] = true - fmt.Printf("DISKII: Disk %v is on\n", c.selected) + c.drive[c.selected].power = true + //fmt.Printf("DISKII: Disk %v is on\n", c.selected) return 0 } c.ssr[0xA] = func(_ *ioC0Page) uint8 { c.selected = 0 - fmt.Printf("DISKII: Disk %v selected\n", c.selected) + //fmt.Printf("DISKII: Disk %v selected\n", c.selected) return 0 } c.ssr[0xB] = func(_ *ioC0Page) uint8 { c.selected = 1 - fmt.Printf("DISKII: Disk %v selected\n", c.selected) + //fmt.Printf("DISKII: Disk %v selected\n", c.selected) return 0 } - var i uint8 // Q6L c.ssr[0xC] = func(_ *ioC0Page) uint8 { - fmt.Printf("DISKII: Reading\n") - i++ - return i + //fmt.Printf("DISKII: Reading\n") + drive := &c.drive[c.selected] + if drive.isLoaded { + track := drive.halfTrack / 2 + data := *drive.data + value := data[track*bytesPerTrack+drive.position] + //fmt.Printf("DISKII: Reading value 0x%02v from track %v, position %v\n", value, track, drive.position) + drive.position = (drive.position + 1) % bytesPerTrack + return value + } else { + return 0 + } } c.ssw[0xC] = func(_ *ioC0Page, value uint8) { - fmt.Printf("DISKII: Writing the value 0x%02x\n", value) + //fmt.Printf("DISKII: Writing the value 0x%02x\n", value) } // Q6H c.ssr[0xD] = func(_ *ioC0Page) uint8 { - c.writeMode = false - fmt.Printf("DISKII: Sense write protection\n") + c.drive[c.selected].writeMode = false + //fmt.Printf("DISKII: Sense write protection\n") return 0 } // Q7L c.ssr[0xE] = func(_ *ioC0Page) uint8 { - c.writeMode = false - fmt.Printf("DISKII: Set read mode\n") + c.drive[c.selected].writeMode = false + //fmt.Printf("DISKII: Set read mode\n") return 0 } // Q7H c.ssr[0xF] = func(_ *ioC0Page) uint8 { - c.writeMode = true - fmt.Printf("DISKII: Set write mode\n") + c.drive[c.selected].writeMode = true + //fmt.Printf("DISKII: Set write mode\n") return 0 } - // TODO: missing C, D, E, and F - return &c } @@ -131,3 +175,28 @@ func loadCardRom(filename string) []memoryPage { return memPages } + +func (d *cardDisk2Drive) loadDisk(filename string) { + f, err := os.Open(filename) + if err != nil { + panic(err) + } + defer f.Close() + + stats, statsErr := f.Stat() + if statsErr != nil { + panic(err) + } + + size := stats.Size() + if size != nibImageSize { + panic("Disk size with nib format has to be 232960 bytes") + } + + bytes := make([]uint8, size) + buf := bufio.NewReader(f) + buf.Read(bytes) + + d.data = &bytes + d.isLoaded = true +} diff --git a/apple2/ioC0Page.go b/apple2/ioC0Page.go index 189a050..3434418 100644 --- a/apple2/ioC0Page.go +++ b/apple2/ioC0Page.go @@ -72,7 +72,8 @@ func (p *ioC0Page) Peek(address uint8) uint8 { //fmt.Printf("Peek on $C0%02x ", address) ss := p.softSwitchesR[address] if ss == nil { - panic(fmt.Sprintf("Unknown softswitch on read to 0xC0%02x", address)) + //panic(fmt.Sprintf("Unknown softswitch on read to 0xC0%02x", address)) + return 0 } return ss(p) @@ -82,7 +83,8 @@ func (p *ioC0Page) Poke(address uint8, value uint8) { //fmt.Printf("Poke on $C0%02x with %02x ", address, value) ss := p.softSwitchesW[address] if ss == nil { - panic(fmt.Sprintf("Unknown softswitch on write to 0xC0%02x", address)) + //panic(fmt.Sprintf("Unknown softswitch on write to 0xC0%02x", address)) + return } ss(p, value) } diff --git a/apple2/unassignedPage.go b/apple2/unassignedPage.go index 8cc5a4b..fb63590 100644 --- a/apple2/unassignedPage.go +++ b/apple2/unassignedPage.go @@ -1,18 +1,16 @@ package apple2 -import "fmt" - type unassignedPage struct { page uint8 } func (p *unassignedPage) Peek(address uint8) uint8 { - fmt.Printf("Read on address 0x%02x%02x\n", p.page, address) + //fmt.Printf("Read on address 0x%02x%02x\n", p.page, address) //panic(address) return 0xdd } func (p *unassignedPage) Poke(address uint8, value uint8) { - fmt.Printf("Write on address 0x%02x%02x\n", p.page, address) + //fmt.Printf("Write on address 0x%02x%02x\n", p.page, address) //panic(address) } diff --git a/main.go b/main.go index eed5368..e1054be 100644 --- a/main.go +++ b/main.go @@ -6,10 +6,11 @@ func main() { //romFile := "apple2/romdumps/Apple2.rom" romFile := "apple2/romdumps/Apple2_Plus.rom" //romFile := "apple2/romdumps/Apple2e.rom" - //disk2RomFile := "apple2/romdumps/DISK2.rom" + disk2RomFile := "apple2/romdumps/DISK2.rom" + diskImage := "../dos33.nib" - log := true + log := false a := apple2.NewApple2(romFile) - //a.AddDisk2(disk2RomFile) + a.AddDisk2(disk2RomFile, diskImage) a.Run(log) }