Boots DOS 3.3 using nib format

This commit is contained in:
Ivan Izaguirre 2019-03-05 00:00:12 +01:00
parent 6babe07ea6
commit cc45403b43
5 changed files with 116 additions and 42 deletions

View File

@ -32,9 +32,13 @@ func NewApple2(romFile string) *Apple2 {
} }
// AddDisk2 insterts a DiskII controller on slot 6 // 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 := newCardDisk2(diskRomFile)
d.cardBase.insert(a, 6) d.cardBase.insert(a, 6)
if diskImage != "" {
d.drive[0].loadDisk(diskImage)
}
} }
// Run starts the Apple2 emulation // Run starts the Apple2 emulation

View File

@ -2,7 +2,6 @@ package apple2
import ( import (
"bufio" "bufio"
"fmt"
"os" "os"
) )
@ -10,14 +9,29 @@ import (
https://applesaucefdc.com/woz/reference2/ https://applesaucefdc.com/woz/reference2/
Good explanation of the softswitches and the phases: Good explanation of the softswitches and the phases:
http://yesterbits.com/media/pubs/AppleOrchard/articles/disk-ii-part-1-1983-apr.pdf 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 { type cardDisk2 struct {
cardBase cardBase
phases [4]bool selected int // Only 0 and 1 supported
power [2]bool drive [2]cardDisk2Drive
selected int // Only 0 and 1 supported }
writeMode bool
type cardDisk2Drive struct {
isLoaded bool
currentPhase int
power bool
writeMode bool
halfTrack int
position int
data *[]uint8
} }
// type softSwitchR func(io *ioC0Page) uint8 // type softSwitchR func(io *ioC0Page) uint8
@ -27,73 +41,103 @@ func newCardDisk2(filename string) *cardDisk2 {
c.rom = loadCardRom(filename) c.rom = loadCardRom(filename)
// Phase control soft switches // 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++ { for i := 0; i < 4; i++ {
c.ssr[i<<1] = func(_ *ioC0Page) uint8 { func(phase int) {
fmt.Printf("DISKII: Phase %v off\n", i) c.ssr[phase<<1] = func(_ *ioC0Page) uint8 {
return 0 //fmt.Printf("DISKII: Phase %v off\n", phase)
} return 0
c.ssr[(i<<1)+1] = func(_ *ioC0Page) uint8 { }
fmt.Printf("DISKII: Phase %v on\n", i) c.ssr[(phase<<1)+1] = func(_ *ioC0Page) uint8 {
return 0 //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 // Other soft switches
c.ssr[0x8] = func(_ *ioC0Page) uint8 { c.ssr[0x8] = func(_ *ioC0Page) uint8 {
c.power[c.selected] = false c.drive[c.selected].power = false
fmt.Printf("DISKII: Disk %v is off\n", c.selected) //fmt.Printf("DISKII: Disk %v is off\n", c.selected)
return 0 return 0
} }
c.ssr[0x9] = func(_ *ioC0Page) uint8 { c.ssr[0x9] = func(_ *ioC0Page) uint8 {
c.power[c.selected] = true c.drive[c.selected].power = true
fmt.Printf("DISKII: Disk %v is on\n", c.selected) //fmt.Printf("DISKII: Disk %v is on\n", c.selected)
return 0 return 0
} }
c.ssr[0xA] = func(_ *ioC0Page) uint8 { c.ssr[0xA] = func(_ *ioC0Page) uint8 {
c.selected = 0 c.selected = 0
fmt.Printf("DISKII: Disk %v selected\n", c.selected) //fmt.Printf("DISKII: Disk %v selected\n", c.selected)
return 0 return 0
} }
c.ssr[0xB] = func(_ *ioC0Page) uint8 { c.ssr[0xB] = func(_ *ioC0Page) uint8 {
c.selected = 1 c.selected = 1
fmt.Printf("DISKII: Disk %v selected\n", c.selected) //fmt.Printf("DISKII: Disk %v selected\n", c.selected)
return 0 return 0
} }
var i uint8
// Q6L // Q6L
c.ssr[0xC] = func(_ *ioC0Page) uint8 { c.ssr[0xC] = func(_ *ioC0Page) uint8 {
fmt.Printf("DISKII: Reading\n") //fmt.Printf("DISKII: Reading\n")
i++ drive := &c.drive[c.selected]
return i 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) { 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 // Q6H
c.ssr[0xD] = func(_ *ioC0Page) uint8 { c.ssr[0xD] = func(_ *ioC0Page) uint8 {
c.writeMode = false c.drive[c.selected].writeMode = false
fmt.Printf("DISKII: Sense write protection\n") //fmt.Printf("DISKII: Sense write protection\n")
return 0 return 0
} }
// Q7L // Q7L
c.ssr[0xE] = func(_ *ioC0Page) uint8 { c.ssr[0xE] = func(_ *ioC0Page) uint8 {
c.writeMode = false c.drive[c.selected].writeMode = false
fmt.Printf("DISKII: Set read mode\n") //fmt.Printf("DISKII: Set read mode\n")
return 0 return 0
} }
// Q7H // Q7H
c.ssr[0xF] = func(_ *ioC0Page) uint8 { c.ssr[0xF] = func(_ *ioC0Page) uint8 {
c.writeMode = true c.drive[c.selected].writeMode = true
fmt.Printf("DISKII: Set write mode\n") //fmt.Printf("DISKII: Set write mode\n")
return 0 return 0
} }
// TODO: missing C, D, E, and F
return &c return &c
} }
@ -131,3 +175,28 @@ func loadCardRom(filename string) []memoryPage {
return memPages 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
}

View File

@ -72,7 +72,8 @@ func (p *ioC0Page) Peek(address uint8) uint8 {
//fmt.Printf("Peek on $C0%02x ", address) //fmt.Printf("Peek on $C0%02x ", address)
ss := p.softSwitchesR[address] ss := p.softSwitchesR[address]
if ss == nil { 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) 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) //fmt.Printf("Poke on $C0%02x with %02x ", address, value)
ss := p.softSwitchesW[address] ss := p.softSwitchesW[address]
if ss == nil { 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) ss(p, value)
} }

View File

@ -1,18 +1,16 @@
package apple2 package apple2
import "fmt"
type unassignedPage struct { type unassignedPage struct {
page uint8 page uint8
} }
func (p *unassignedPage) Peek(address uint8) 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) //panic(address)
return 0xdd return 0xdd
} }
func (p *unassignedPage) Poke(address uint8, value uint8) { 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) //panic(address)
} }

View File

@ -6,10 +6,11 @@ func main() {
//romFile := "apple2/romdumps/Apple2.rom" //romFile := "apple2/romdumps/Apple2.rom"
romFile := "apple2/romdumps/Apple2_Plus.rom" romFile := "apple2/romdumps/Apple2_Plus.rom"
//romFile := "apple2/romdumps/Apple2e.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 := apple2.NewApple2(romFile)
//a.AddDisk2(disk2RomFile) a.AddDisk2(disk2RomFile, diskImage)
a.Run(log) a.Run(log)
} }