diff --git a/cards/disk.go b/cards/disk.go new file mode 100644 index 0000000..a5a5264 --- /dev/null +++ b/cards/disk.go @@ -0,0 +1,130 @@ +package cards + +import ( + "fmt" + + "github.com/zellyn/goapple2/disk" +) + +type Disk interface { + Read() byte + Write(byte) + SetHalfTrack(byte) + HalfTrack() byte + SetVolume(byte) + Volume() byte + Writeable() bool +} + +type DiskCard struct { + rom [256]byte + cm CardManager + slot byte + slotbit byte + disks [2]Disk + active int + phases byte +} + +func NewDiskCard(rom []byte, slot byte, cm CardManager) (*DiskCard, error) { + if len(rom) != 256 { + return nil, fmt.Errorf("Wrong size ROM: expected 256, got %d", len(rom)) + } + dc := &DiskCard{ + cm: cm, + slot: slot, + slotbit: 1 << slot, + disks: [2]Disk{disk.NewDummy(disk.DEFAULT_VOLUME), disk.NewDummy(disk.DEFAULT_VOLUME)}, + } + copy(dc.rom[:], rom) + return dc, nil +} + +func (dc *DiskCard) String() string { + return fmt.Sprintf("Disk Card (slot %d)", dc.slot) +} + +func (dc *DiskCard) Slot() byte { + return dc.slot +} + +func (dc *DiskCard) ROMDisabled() { + // Disk card doesn't have a $C(8-F)xx ROM +} + +func (dc *DiskCard) handlePhase(phase byte, onOff bool) { + phaseBit := byte(1 << phase) + if onOff { + dc.phases |= phaseBit + } else { + dc.phases &^= phaseBit + } + + disk := dc.disks[dc.active] + newTrack := int(disk.HalfTrack()) + switch dc.phases { + case 1: + newTrack = (newTrack + 1) / 4 * 4 + case 2: + newTrack = (newTrack/4)*4 + 1 + case 4: + if newTrack != 0 { + newTrack = (newTrack-1)/4*4 + 2 + } + case 8: + if newTrack < 2 { + newTrack = 0 + } else { + newTrack = (newTrack-2)/4*4 + 3 + } + default: + return + } + if newTrack < 0 { + newTrack = 0 + } + if newTrack > 68 { + newTrack = 68 + } + if disk.HalfTrack() != byte(newTrack) { + disk.SetHalfTrack(byte(newTrack)) + } +} + +func (dc *DiskCard) handleAccess(address byte) { + if address < 8 { + phase := address / 2 + onOff := (address & 1) == 1 + dc.handlePhase(phase, onOff) + return + } +} + +func (dc *DiskCard) Read16(address byte) byte { + dc.handleAccess(address) + return dc.cm.EmptyRead() +} + +func (dc *DiskCard) Write16(address byte, value byte) { + dc.handleAccess(address) +} + +func (dc *DiskCard) Read(address uint16) byte { + panic(fmt.Sprintf("%s got read to $%04X", dc.String(), address)) +} + +func (dc *DiskCard) Write(address uint16, value byte) { + panic(fmt.Sprintf("%s got write to $%04X", dc.String(), address)) +} + +func (dc *DiskCard) Read256(address byte) byte { + return dc.rom[address] +} + +func (dc *DiskCard) Write256(address byte, value byte) { + // Firmware is ROM: do nothing +} + +func (dc *DiskCard) LoadDisk(d Disk, which int) { + dc.disks[which] = d +} diff --git a/disk/convert.go b/disk/convert.go new file mode 100644 index 0000000..a2e813d --- /dev/null +++ b/disk/convert.go @@ -0,0 +1,171 @@ +package disk + +import ( + "fmt" +) + +// Dos33writeTable is the list of valid DOS 3.3 bytes. +// See [UtA2 9-27 Disk Data Formats]. +var Dos33writeTable = [64]byte{ + 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, + 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, + 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3, + 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, + 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, + 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, + 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, +} + +// Dos33LogicalToPhysicalSectorMap maps logical sector numbers to physical ones. +// See [UtA2 9-42 - Read Routines]. +var Dos33LogicalToPhysicalSectorMap = []byte{ + 0x00, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x01, + 0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x0F, +} + +// Dos33PhysicalToLogicalSectorMap maps physical sector numbers to logical ones. +// See [UtA2 9-42 - Read Routines]. +var Dos33PhysicalToLogicalSectorMap = []byte{ + 0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04, + 0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F, +} + +// ProDosLogicalToPhysicalSectorMap maps logical sector numbers to pysical ones. +// See [UtA2e 9-43 - Sectors vs. Blocks]. +var ProDosLogicalToPhysicalSectorMap = []byte{ + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, + 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, +} + +// ProDosPhysicalToLogicalSectorMap maps physical sector numbers to logical ones. +// See [UtA2e 9-43 - Sectors vs. Blocks]. +var ProDosPhysicalToLogicalSectorMap = []byte{ + 0x00, 0x08, 0x01, 0x09, 0x02, 0x0A, 0x03, 0x0B, + 0x04, 0x0C, 0x05, 0x0D, 0x06, 0x0E, 0x07, 0x0F, +} + +// PreNybble converts from 256 8-bit bytes to 342 6-bit "nybbles". +// The equivalent DOS routine, PRE.NYBBLE, would put the 256 MSB-6's into one buffer, +// and the 86 LSB-2's into a separate one, but we place the 86 after the 256. +// See http://www.txbobsc.com/aal/1981/aal8106.html#a5 +func PreNybble(source []byte) (target [342]byte) { + if len(source) != 256 { + panic(fmt.Sprintf("PreNybble expects 256 bytes as input, got %d", len(source))) + } + y := 2 + for i := 0; i < 3; i++ { + for x := 0; x < 86; x++ { + // DEY + y = (y + 0xff) & 0xff + a := source[y] + target[y] = a >> 2 + target[x+256] = (target[x+256]<<2 + a&2>>1 + a&1<<1) & 0x3F + } + } + return target +} + +// PostNybble converts from 342 6-bit "nybbles to 255 8-bit bytes. +// The equivalent DOS routine, POST.NYBBLE, would read the 256 MSB-6's from one buffer, +// and the 86 LSB-2's from a separate one, but we place the 86 after the 256. +// See http://www.txbobsc.com/aal/1981/aal8106.html#a5 +func PostNybble(source []byte) (target [256]byte) { + if len(source) != 342 { + panic(fmt.Sprintf("PostNybble expects 342 bytes as input, got %d", len(source))) + } + + x := 0 + for y := 0; y < 256; y++ { + // DEX + x = (x + 85) % 86 + a := source[256+x] + source[256+x] = a >> 2 + target[y] = source[y]<<2 + a&2>>1 + a&1<<1 + } + return target +} + +// appendSyncs appends the given number of sync bytes to the slice, and returns the resulting slice. +func appendSyncs(target []byte, count int) []byte { + for i := 0; i < count; i++ { + target = append(target, 0xFF) + } + return target +} + +// append44 appends bytes using the 4-4 encoded format used for volume, track, sector, checksum. +func append44(target []byte, b byte) []byte { + return append(target, 0xAA|b<<1, 0xAA|b) +} + +// appendAddress appends the encoded sector address to the slice, and returns the resulting slice. +func appendAddress(target []byte, t, s, v byte) []byte { + target = append(target, 0xD5, 0xAA, 0x96) + target = append44(target, v) + target = append44(target, t) + target = append44(target, s) + target = append44(target, v^t^s) + target = append(target, 0xDE, 0xAA, 0xEB) + return target +} + +// appendData appends the encoded sector data to the slice, and returns the resulting slice. +func appendData(target, source []byte) []byte { + target = append(target, 0xD5, 0xAA, 0xAD) + nybbles := PreNybble(source) + checksum := byte(0) + for i := 341; i >= 256; i-- { + target = append(target, Dos33writeTable[nybbles[i]^checksum]) + checksum = nybbles[i] + } + for i := 0; i < 256; i++ { + target = append(target, Dos33writeTable[nybbles[i]^checksum]) + checksum = nybbles[i] + } + target = append(target, Dos33writeTable[checksum]) + target = append(target, 0xDE, 0xAA, 0xEB) + return target +} + +// dosToNybbleSector appends a single 256-byte DOS sector in .nyb format, and returns the slice. +func dosToNybbleSector(target, source []byte, t, s, v byte, preSync, intraSync int) []byte { + if len(source) != 256 { + panic(fmt.Sprintf("dosToNybbleSector expects 256 bytes, got %d", len(source))) + } + target = appendSyncs(target, preSync) + target = appendAddress(target, t, s, v) + target = appendSyncs(target, intraSync) + target = appendData(target, source) + return target +} + +// dos16ToNybbleTrack converts a 16-sector dos image of a track to a nybblized track. +func dos16ToNybbleTrack(source []byte, t, v byte, preSync, intraSync int, sectorOrder []byte) []byte { + trackSync := NYBBLE_TRACK_BYTES - 16*(preSync+NYBBLE_ADDRESS_BYTES+intraSync+NYBBLE_DATA_BYTES) + target := make([]byte, 0, NYBBLE_TRACK_BYTES) + target = appendSyncs(target, trackSync) + for s := byte(0); s < 16; s++ { + start := 256 * int(sectorOrder[s]) + target = dosToNybbleSector(target, source[start:start+256], t, s, v, preSync, intraSync) + } + if len(target) != NYBBLE_TRACK_BYTES { + panic(fmt.Sprintf("Tracks should be %d bytes, go %d", NYBBLE_TRACK_BYTES, len(target))) + } + return target +} + +func dos16ToNybbleTracks(source []byte, v byte, preSync, intraSync int, sectorOrder []byte) (tracks [][]byte, err error) { + if preSync+intraSync > MAX_PRE_INTRA_SYNC { + return nil, fmt.Errorf("preSync(%d) + intraSync(%d) cannot be more than %d bytes", + preSync, intraSync, MAX_PRE_INTRA_SYNC) + } + + for t := byte(0); t < 35; t++ { + start := DOS_TRACK_BYTES * int(t) + trackBytes := source[start : start+DOS_TRACK_BYTES] + track := dos16ToNybbleTrack(trackBytes, t, v, preSync, intraSync, sectorOrder) + tracks = append(tracks, track) + } + return tracks, nil +} diff --git a/disk/disk.go b/disk/disk.go new file mode 100644 index 0000000..a9db7fa --- /dev/null +++ b/disk/disk.go @@ -0,0 +1,14 @@ +package disk + +const ( + DOS_DISK_BYTES = 143360 // 35 tracks * 16 sectors * 256 bytes + DOS_TRACK_BYTES = DOS_DISK_BYTES / 35 + NYBBLE_DISK_BYTES = 232960 + NYBBLE_TRACK_BYTES = NYBBLE_DISK_BYTES / 35 + NYBBLE_ADDRESS_BYTES = 14 + NYBBLE_DATA_BYTES = 349 + DEFAULT_VOLUME = 254 + DEFAULT_PRESYNC = 20 + DEFAULT_INTRASYNC = 8 + MAX_PRE_INTRA_SYNC = (NYBBLE_TRACK_BYTES / 16) - NYBBLE_ADDRESS_BYTES - NYBBLE_DATA_BYTES +) diff --git a/disk/disk_test.go b/disk/disk_test.go new file mode 100644 index 0000000..f93aa11 --- /dev/null +++ b/disk/disk_test.go @@ -0,0 +1,32 @@ +package disk + +import ( + "testing" +) + +func TestPreNybble(t *testing.T) { + var source [256]byte + for i := range source { + source[i] = byte(i) + } + target := PreNybble(source[:]) + for i := range target { + if target[i] > 0x3F { + t.Errorf("target[%d] too large: 0x%02X", i, target[i]) + } + } +} + +func TestPrePostNybble(t *testing.T) { + var source [256]byte + for i := range source { + source[i] = byte(i) + } + target := PreNybble(source[:]) + sourceCheck := PostNybble(target[:]) + for i := range source { + if source[i] != sourceCheck[i] { + t.Errorf("source, sourceCheck differ at %d: 0x%02X != 0x%02X", i, source[i], sourceCheck[i]) + } + } +} diff --git a/disk/dummy.go b/disk/dummy.go new file mode 100644 index 0000000..4ae4c02 --- /dev/null +++ b/disk/dummy.go @@ -0,0 +1,35 @@ +package disk + +type Dummy byte + +func (v Dummy) Read() byte { + return 0xFF +} + +func (v Dummy) Write(b byte) { + // pass +} + +func (v Dummy) SetHalfTrack(t byte) { + // pass +} + +func (v Dummy) HalfTrack() byte { + return 0 +} + +func (v Dummy) SetVolume(byte) { + // pass +} + +func (v Dummy) Volume() byte { + return byte(v) +} + +func (v Dummy) Writeable() bool { + return false +} + +func NewDummy(v byte) Dummy { + return Dummy(v) +} diff --git a/disk/nybble.go b/disk/nybble.go new file mode 100644 index 0000000..1afd738 --- /dev/null +++ b/disk/nybble.go @@ -0,0 +1,84 @@ +package disk + +import ( + "fmt" + "io/ioutil" + "strings" +) + +type Nybble struct { + Tracks [][]byte + volume byte + halfTrack byte + position int + writeable bool +} + +func NewNybble() *Nybble { + nd := Nybble{ + volume: DEFAULT_VOLUME, + } + return &nd +} + +func (disk *Nybble) LoadDosDisk(filename string) error { + var sectorOrder []byte + switch { + case strings.HasSuffix(filename, ".dsk"): + sectorOrder = Dos33PhysicalToLogicalSectorMap + case strings.HasSuffix(filename, ".do"): + sectorOrder = Dos33PhysicalToLogicalSectorMap + case strings.HasSuffix(filename, ".po"): + sectorOrder = ProDosPhysicalToLogicalSectorMap + default: + return fmt.Errorf("Unknown suffix (not .dsk, .do, or .po): %s", filename) + } + + bytes, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + if len(bytes) != DOS_DISK_BYTES { + return fmt.Errorf("Disk images should be %d bytes, got %d: %s", DOS_DISK_BYTES, len(bytes), filename) + } + tracks, err := dos16ToNybbleTracks(bytes, disk.Volume(), DEFAULT_PRESYNC, DEFAULT_INTRASYNC, sectorOrder) + if err != nil { + return err + } + disk.Tracks = tracks + return nil +} + +func (disk *Nybble) Read() byte { + track := disk.Tracks[disk.halfTrack/2] + disk.position = (disk.position + 1) % len(track) + return track[disk.position] +} + +func (disk *Nybble) Write(b byte) { + track := disk.Tracks[disk.halfTrack/2] + disk.position = (disk.position + 1) % len(track) + if disk.writeable { + track[disk.position] = b + } +} + +func (disk *Nybble) SetHalfTrack(halfTrack byte) { + disk.halfTrack = halfTrack +} + +func (disk *Nybble) HalfTrack() byte { + return disk.halfTrack +} + +func (disk *Nybble) SetVolume(volume byte) { + disk.volume = volume +} + +func (disk *Nybble) Volume() byte { + return disk.volume +} + +func (disk *Nybble) Writeable() bool { + return disk.writeable +} diff --git a/docs/apple2.org b/docs/apple2.org index 215f14c..6fa0a14 100644 --- a/docs/apple2.org +++ b/docs/apple2.org @@ -485,3 +485,183 @@ F8 jumper not jumpered: never use F8 ROM on card: use motherboard. * Questions ** Color Can a previously rendered dot change colors? Gray? +** Disk +I thought ProDOS only allowed a read syncing leader from $10 to +$1A. But the nybblized disk format seems to have 48 spare bytes for +each sector. +(- 416 14 5 6 1 342) 48 + +* Disk formats +** dsk/do - DOS 3.3 order +35 tracks * 16 sectors * 256 bytes = 143,360 +** po - ProDOS order +** nib +232,960 bytes + +(* 35 16 342) +191,520 + +(* 35 16 (+ 342 74)) +232,960 bytes + +Extra bytes per track: +1184 (74 PER SECTOR) + + +https://code.google.com/p/twoapple/source/browse/src/peripheral/diskii.d + +ADDR: 13 + PROLOGUE: 3 D5,AA,96 + VOLUME: 2 + TRACK: 2 + SECTOR: 2 + CHECK: 2 + EPILOGUE: 2 DE,AA + +DATAFIELD: 349 + PROLOGUE: 3 D5,AA,AD + DATA: 343 + EPILOGUE: 3 DE,AA,EB + +SECTOR: + ADDR: 13 + GAP2: 6 + DATAFIELD: 349 + GAP3: 45 + +(+ 13 6 349 45) +413 + +# 48 byte gap, 413 per sector: (+ 48 (* 16 413)) 6656 + + +http://sourceforge.net/p/java-ace/code/285/tree/jace/src/jace/hardware/FloppyDisk.java +- 15 junk bytes +- Address block: 14 + - D5,AA,96 - 3 + - Volume - 2 + - Track - 2 + - Sector - 2 + - Checksum - 2 + - DE,AA,EB - 3 +- 4 junk bytes +- Data block 349 + - D5,AA,AD 3 + - Data: 342 + - Checksum: 1 + - DE,AA,EB - 3 +- 34 junk bytes + +(+ 15 14 4 349 34) +416 + + +Writing out: +http://sourceforge.net/p/java-ace/code/285/tree/jace/src/jace/hardware/FloppyDisk.java#l260 + +- Look for D5,AA,96 +- Verify track +- Read sector +- Look for DE,AA +- Look for D5,AA,AD +- +- Look for DE,AA,EB + +http://mrob.com/apple2/extract.txt + +https://code.google.com/p/openemulator/source/browse/trunk/libdiskimage/DIApple525DiskStorage.cpp#590 +- 128 sync bytes for track 0, 20 for others. +- address field + - (+ 3 8 3) 14 +- 8 sync bytes +- data field + - (+ 3 86 256 1 3) 349 + +(+ 108 (* 20 16) (* 16 (+ 14 8 349))) +6364 + +6656 + + +* Listings +* RWTS listing +http://www.txbobsc.com/aal/1981/aal8109.html#a5 + +* DOS 3.3 boot sector +http://www.txbobsc.com/aal/1981/aal8108.html#a9 + +* Volume numbers +DOS 3.3: Default = 254 +Prodos: not used. [[https://groups.google.com/d/msg/comp.sys.apple2/YjoOR7gza1w/-2-HcNLLucwJ][1?]] + +* Sector orders + +** DOS 3.3 +[[ftp://ftp.apple.asimov.net/pub/apple_II/documentation/source_code/Apple2DOS33CSourceListing.pdf][Logical to physical sector mapping source]] + +Logical to physical +0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F +0,D,B,9,7,5,3,1,E,C,A,8,6,4,2,F + +Physical to logical: +0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F +0,7,E,6,D,5,C,4,B,3,A,2,9,1,8,F + +When writing nybblized track 1, we write track 1 in address header, but read from image track sector 7. + +logical 4 <==> physical 7 + +** Prodos +[[http://sourceforge.net/p/java-ace/code/285/tree/jace/src/jace/hardware/FloppyDisk.java#l40][Physical to logical - JACE]] + +Logical to Physical: +0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F +0,2,4,6,8,A,C,E,1,3,5,7,9,B,D,F + +Physical to logical: +0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F +0,8,1,9,2,A,3,B,4,C,5,D,6,E,7,F + +* Formatting... +_Prodos note_: tolerance of read syncing leader is $10 to $1A, so drive +speed must be close to 300rpm. + +Syncing leader: 5-40 FFs +Address +Gap 50 +Gap 53 +Syncing leader: 5 FFs + + +Guess 40 FFs. +Write 128 FF40's before Sector 0, 40 FF40s before the others. +Write sectors in order (physical sectors). + +Sync count reduced by 2s until it reaches 16, then by ones until it reaches 5. +If track 0 has >15 sync count, the rest of the tracks will have (sync count - 2). + + + + + + +* Byte counts + +| | Jace | Twoapple | OpenEmulator-Byte | OpenEmulator-Cycle | +|-------------+------------------------+---------------------+----------------------+-----------------------------| +| Pre-track-0 | | 48 | 108 | (* 108 40) 4320 | +| Pre-sync | 15 | | 20 | (+ (* 19 40) 32) 792 | +| Address | 14 | 13 | 14 | (+ (* 13 32) 16) 432 | +| Intra-sync | 4 | 6 | 8 | (+ (* 7 40) 36) 316 | +| Data | 349 | 349 | (+ 3 86 256 3 1) 349 | (* 349 32) 11168 | +| Post-sync | 34 | 45 | 0 | 0 | +| | (+ 15 14 4 349 34) 416 | (+ 13 6 349 45) 413 | (+ 20 14 8 349) 391 | (+ 792 432 316 11168) 12708 | +| | (* 416 16) 6656 | (* 413 16) 6608 | (* 16 391) 6256 | (* 16 12708) 203328 | +| | 6656 | (+ 6608 48) 6656 | (+ 6256 108) 6364 | (+ 203328 4320) 207648 | +| | | | | (/ 207648 4) 51912 | + + + +tracksize 51024 +(/ 51024 8) +6378 diff --git a/goapple2.go b/goapple2.go index 2f787e4..bd04300 100644 --- a/goapple2.go +++ b/goapple2.go @@ -56,39 +56,41 @@ func (a2 *Apple2) handleCardRom(address uint16, value byte, write bool) byte { } func (a2 *Apple2) handleC00X(address uint16, value byte, write bool) byte { - switch address & 0xC0F0 { - // $C00X: Read keyboard - case 0xC000: - if a2.key&0x80 == 0 { - select { - case key := <-a2.keys: - a2.key = key - default: + if address < 0xC080 { + switch address & 0xC0F0 { + // $C00X: Read keyboard + case 0xC000: + if a2.key&0x80 == 0 { + select { + case key := <-a2.keys: + a2.key = key + default: + } } + return a2.key + // $C01X: Reset keyboard + case 0xC010: + a2.key &= 0x7F + return a2.EmptyRead() + } + switch address { + case 0xC050: // GRAPHICS + a2.scanner.SetGraphics(true) + case 0xC051: // TEXT + a2.scanner.SetGraphics(false) + case 0xC052: // NOMIX + a2.scanner.SetMix(false) + case 0xC053: // MIX + a2.scanner.SetMix(true) + case 0xC054: // PAGE 1 + a2.scanner.SetPage(1) + case 0xC055: // PAGE 2 + a2.scanner.SetPage(2) + case 0xC056: // LORES + a2.scanner.SetHires(false) + case 0xC057: // HIRES + a2.scanner.SetHires(true) } - return a2.key - // $C01X: Reset keyboard - case 0xC010: - a2.key &= 0x7F - return a2.EmptyRead() - } - switch address { - case 0xC050: // GRAPHICS - a2.scanner.SetGraphics(true) - case 0xC051: // TEXT - a2.scanner.SetGraphics(false) - case 0xC052: // NOMIX - a2.scanner.SetMix(false) - case 0xC053: // MIX - a2.scanner.SetMix(true) - case 0xC054: // PAGE 1 - a2.scanner.SetPage(1) - case 0xC055: // PAGE 2 - a2.scanner.SetPage(2) - case 0xC056: // LORES - a2.scanner.SetHires(false) - case 0xC057: // HIRES - a2.scanner.SetHires(true) } if address < 0xC080 { diff --git a/sdl/sdl.go b/sdl/sdl.go index 64359f6..248e2a7 100644 --- a/sdl/sdl.go +++ b/sdl/sdl.go @@ -13,6 +13,7 @@ import ( "github.com/0xe2-0x9a-0x9b/Go-SDL/sdl" "github.com/zellyn/goapple2" "github.com/zellyn/goapple2/cards" + "github.com/zellyn/goapple2/disk" "github.com/zellyn/goapple2/util" "github.com/zellyn/goapple2/videoscan" ) @@ -33,12 +34,10 @@ const ( func plot(x, y uint, color uint32, screen *sdl.Surface) { x = x + BORDER_W y = y + BORDER_H - screen.Lock() pixels := uintptr(screen.Pixels) offset := uintptr(y*uint(screen.Pitch) + x*(SCREEN_BPP/8)) addr := pixels + offset *(*uint32)(unsafe.Pointer(addr)) = color - screen.Unlock() } func Init() (screen *sdl.Surface, err error) { @@ -255,7 +254,9 @@ func (s SdlPlotter) Plot(pd videoscan.PlotData) { } func (s SdlPlotter) OncePerFrame() { + s.screen.Unlock() s.screen.Flip() + s.screen.Lock() s.oncePerFrame() } @@ -305,6 +306,21 @@ func RunEmulator() { log.Fatal(err) } + diskCardRom := util.ReadRomOrDie("../data/roms/Apple Disk II 16 Sector Interface Card ROM P5 - 341-0027.bin") + diskCard, err := cards.NewDiskCard(diskCardRom, 6, a2) + if err != nil { + panic(err) + } + if err := a2.AddCard(diskCard); err != nil { + log.Fatal(err) + } + disk1 := disk.NewNybble() + if err = disk1.LoadDosDisk("../data/disks/chivalry.dsk"); err != nil { + log.Fatal(err) + } + disk1.SetHalfTrack(50) + diskCard.LoadDisk(disk1, 0) + steps := *steplimit if *cpuprofile != "" {