debugging disk operation

This commit is contained in:
Zellyn Hunter 2013-04-21 16:49:58 -07:00
parent 8f3ce4748b
commit 5e28969748
16 changed files with 4059 additions and 22 deletions

BIN
a2/a2 Executable file

Binary file not shown.

46
a2/disasm.go Normal file
View File

@ -0,0 +1,46 @@
package main
import (
"io/ioutil"
"log"
"os"
"github.com/gonuts/commander"
"github.com/zellyn/go6502/asm"
)
var cmdDisasm = &commander.Command{
Run: runDisasm,
UsageLine: "disasm [-a address] filename",
Short: "disassemble binary files",
Long: `
Disasm is a very simple disassembler for 6502 binary files.
`,
}
var disasmAddress uint // disasm -a flag
func init() {
cmdDisasm.Flag.UintVar(&disasmAddress, "a", 0, "The starting memory address.")
}
func runDisasm(cmd *commander.Command, args []string) {
if len(args) != 1 {
cmd.Usage()
return
}
bytes, err := ioutil.ReadFile(args[0])
if err != nil {
log.Fatal(err)
}
if len(bytes) > 0x10000 {
log.Fatalf("File %s is %04X bytes long, which is more than $10000.", args[0], len(bytes))
}
if int(disasmAddress)+len(bytes) > 0x10000 {
log.Fatalf("Starting address ($%04X) + file length ($%04X) = $%X, which is > $10000",
disasmAddress, len(bytes), int(disasmAddress)+len(bytes))
}
asm.DisasmBlock(bytes, uint16(disasmAddress), os.Stdout)
}

31
a2/main.go Normal file
View File

@ -0,0 +1,31 @@
package main
import (
"log"
"os"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
)
var a2cmd *commander.Commander
func init() {
a2cmd = &commander.Commander{
Name: os.Args[0],
Commands: []*commander.Command{
cmdDisasm,
},
Flag: flag.NewFlagSet("a2", flag.ExitOnError),
}
}
func main() {
if err := a2cmd.Flag.Parse(os.Args[1:]); err != nil {
log.Fatal(err)
}
args := a2cmd.Flag.Args()
if err := a2cmd.Run(args); err != nil {
log.Fatal(err)
}
}

View File

@ -10,6 +10,8 @@ type Card interface {
Write256(address byte, value byte) // Write to the $C(slot)XX addresses Write256(address byte, value byte) // Write to the $C(slot)XX addresses
Read(address uint16) byte // Read from any address ($C800-$FFFF) Read(address uint16) byte // Read from any address ($C800-$FFFF)
Write(address uint16, value byte) // Write to any address ($C800-$FFFF) Write(address uint16, value byte) // Write to any address ($C800-$FFFF)
WantTicker() bool
Tick()
} }
type CardManager interface { type CardManager interface {

View File

@ -8,6 +8,7 @@ import (
type Disk interface { type Disk interface {
Read() byte Read() byte
Skip(int)
Write(byte) Write(byte)
SetHalfTrack(byte) SetHalfTrack(byte)
HalfTrack() byte HalfTrack() byte
@ -16,14 +17,25 @@ type Disk interface {
Writeable() bool Writeable() bool
} }
const (
MODE_READ = 0
MODE_WRITE = 1
MODE_SHIFT = 0
MODE_LOAD = 2
)
type DiskCard struct { type DiskCard struct {
rom [256]byte rom [256]byte
cm CardManager cm CardManager
slot byte slot byte
slotbit byte slotbit byte
disks [2]Disk disks [2]Disk
active int active int
phases byte phases byte
mode byte
onOff bool
dataRegister byte
lastAccess int
} }
func NewDiskCard(rom []byte, slot byte, cm CardManager) (*DiskCard, error) { func NewDiskCard(rom []byte, slot byte, cm CardManager) (*DiskCard, error) {
@ -53,6 +65,9 @@ func (dc *DiskCard) ROMDisabled() {
} }
func (dc *DiskCard) handlePhase(phase byte, onOff bool) { func (dc *DiskCard) handlePhase(phase byte, onOff bool) {
if !dc.onOff {
return
}
phaseBit := byte(1 << phase) phaseBit := byte(1 << phase)
if onOff { if onOff {
dc.phases |= phaseBit dc.phases |= phaseBit
@ -98,15 +113,90 @@ func (dc *DiskCard) handleAccess(address byte) {
dc.handlePhase(phase, onOff) dc.handlePhase(phase, onOff)
return return
} }
switch address {
case 0x8:
dc.onOff = false
case 0x9:
dc.onOff = true
case 0xA, 0xB:
which := int(address & 1)
if dc.active != which {
dc.active = which
dc.handlePhase(0, dc.phases&1 == 1) // No change: force update
}
case 0xC, 0xD:
dc.mode = dc.mode&^2 | address&1<<2
case 0xE, 0xF:
dc.mode = dc.mode&^1 | address&1
}
} }
func (dc *DiskCard) Read16(address byte) byte { func (dc *DiskCard) Read16(address byte) byte {
dc.handleAccess(address) dc.handleAccess(address)
return dc.cm.EmptyRead() if address != 0xC && address != 0xE {
return 0xFF
}
if dc.onOff {
switch dc.mode {
case MODE_READ | MODE_SHIFT:
// Normal read
return dc.readOne()
case MODE_READ | MODE_LOAD:
// Check write-protect
if dc.disks[dc.active].Writeable() {
return 0x00
} else {
return 0xFF
}
case MODE_WRITE | MODE_SHIFT:
// Doesn't do anything in our simulation: just return last data
return dc.dataRegister
case MODE_WRITE | MODE_LOAD:
// Nonsense for reading: just return last data
return dc.dataRegister
}
}
return 0xFF
} }
func (dc *DiskCard) Write16(address byte, value byte) { func (dc *DiskCard) Write16(address byte, value byte) {
dc.handleAccess(address) dc.handleAccess(address)
if dc.onOff {
switch dc.mode {
case MODE_READ | MODE_SHIFT:
// Normal read
panic("Write while in read mode")
case MODE_READ | MODE_LOAD:
// Check write-protect
panic("Write while in check-write-protect mode")
case MODE_WRITE | MODE_SHIFT:
// Shifting data to disk
panic("Write while in shift mode")
case MODE_WRITE | MODE_LOAD:
if dc.disks[dc.active].Writeable() {
dc.writeOne(value)
}
}
}
}
func (dc *DiskCard) readOne() byte {
if dc.lastAccess < 4 {
return dc.dataRegister
}
disk := dc.disks[dc.active]
if dc.lastAccess > 300 {
disk.Skip(dc.lastAccess / 36)
}
dc.lastAccess = 0
dc.dataRegister = disk.Read()
return dc.dataRegister
}
func (dc *DiskCard) writeOne(value byte) {
disk := dc.disks[dc.active]
dc.dataRegister = value
disk.Write(value)
} }
func (dc *DiskCard) Read(address uint16) byte { func (dc *DiskCard) Read(address uint16) byte {
@ -128,3 +218,11 @@ func (dc *DiskCard) Write256(address byte, value byte) {
func (dc *DiskCard) LoadDisk(d Disk, which int) { func (dc *DiskCard) LoadDisk(d Disk, which int) {
dc.disks[which] = d dc.disks[which] = d
} }
func (dc *DiskCard) WantTicker() bool {
return true
}
func (dc *DiskCard) Tick() {
dc.lastAccess++
}

View File

@ -70,3 +70,11 @@ func (fc *FirmwareCard) Read256(address byte) byte {
func (fc *FirmwareCard) Write256(address byte, value byte) { func (fc *FirmwareCard) Write256(address byte, value byte) {
// Firmware is ROM: do nothing // Firmware is ROM: do nothing
} }
func (fc *FirmwareCard) WantTicker() bool {
return false
}
func (fc *FirmwareCard) Tick() {
// do nothing
}

View File

@ -96,12 +96,13 @@ func appendSyncs(target []byte, count int) []byte {
// append44 appends bytes using the 4-4 encoded format used for volume, track, sector, checksum. // append44 appends bytes using the 4-4 encoded format used for volume, track, sector, checksum.
func append44(target []byte, b byte) []byte { func append44(target []byte, b byte) []byte {
return append(target, 0xAA|b<<1, 0xAA|b) return append(target, 0xAA|(b>>1), 0xAA|b)
} }
// appendAddress appends the encoded sector address to the slice, and returns the resulting slice. // appendAddress appends the encoded sector address to the slice, and returns the resulting slice.
func appendAddress(target []byte, t, s, v byte) []byte { func appendAddress(target []byte, t, s, v byte) []byte {
target = append(target, 0xD5, 0xAA, 0x96) target = append(target, 0xD5, 0xAA, 0x96)
fmt.Println("s=", s)
target = append44(target, v) target = append44(target, v)
target = append44(target, t) target = append44(target, t)
target = append44(target, s) target = append44(target, s)

View File

@ -3,7 +3,11 @@ package disk
type Dummy byte type Dummy byte
func (v Dummy) Read() byte { func (v Dummy) Read() byte {
return 0xFF return 0x00
}
func (v Dummy) Skip(int) {
// pass
} }
func (v Dummy) Write(b byte) { func (v Dummy) Write(b byte) {

View File

@ -55,6 +55,11 @@ func (disk *Nybble) Read() byte {
return track[disk.position] return track[disk.position]
} }
func (disk *Nybble) Skip(amount int) {
track := disk.Tracks[disk.halfTrack/2]
disk.position = (disk.position + amount) % len(track)
}
func (disk *Nybble) Write(b byte) { func (disk *Nybble) Write(b byte) {
track := disk.Tracks[disk.halfTrack/2] track := disk.Tracks[disk.halfTrack/2]
disk.position = (disk.position + 1) % len(track) disk.position = (disk.position + 1) % len(track)

View File

@ -155,6 +155,59 @@ Apple II notes
| $F3 | 243 | FLASH MASK | | $F3 | 243 | FLASH MASK |
| $F9 | 249 | ROT | | $F9 | 249 | ROT |
* Page 00 DOS
[BAD 8-42]
|---------+-----+-----------------------------------------------|
| Address | Dec | Description |
|---------+-----+-----------------------------------------------|
| 24 | | Cursor horizontal |
| 26,27 | | Sector read buffer address (ROM) |
| 28,29 | | BASL/BASH (DOS) |
| 2A | | Segment merge counter (ROM,BOOT) |
| | | Scratch space (RWTS) |
| 2B | | BOOT slot*16 (ROM) |
| | | Scratch space (RWTS) |
| 2C | | Checksum from sector header (RWTS) |
| 2D | | Sector number form sector header (RWTS) |
| 2E | | Track number form sector header (RWTS) |
| 2F | | Volume number form sector header (RWTS) |
| 33 | | Prompt character (DOS) |
| 35 | | Drive number in high bit (RWTS) |
| 36,37 | | CSWL,CSWH (DOS) |
| 38,39 | | KSWL,KSWH (DOS) |
| 3C | | Workbyte (ROM) |
| | | Merge workbyte (BOOT) |
| | | Device characteristics table address (RWTS) |
| 3D | | Sector number (ROM) |
| | | Device characteristics table address (RWTS) |
| 3E,3F | | Address of ROM sector-read subroutine (BOOT) |
| | | Buffer address (RWTS) |
| 40,41 | | DOS image address (BOOT) |
| | | File buffer address (DOS) |
| 41 | | Format track counter (RWTS) |
| 42,43 | | Buffer address (DOS) |
| 44,45 | | Numeric operand (DOS) |
| 46,47 | | Scratch space (RWTS) |
| 48,49 | | IOB address (RWTS) |
| 4A,4B | | INTEGER BASIC LOMEM address (DOS) |
| | | Format diskette workspace (RWTS) |
| 4C,4D | | INTEGER BASIC HIMEM address (DOS) |
| 67,68 | | APPLESOFT BASIC PROGRAM START (DOS) |
| 69,6A | | APPLESOFT BASIC VARIABLES START (DOS) |
| 6F,70 | | APPLESOFT BASIC STRING START (DOS) |
| 73,74 | | APPLESOFT BASIC HIMEM address (DOS) |
| 76 | | APPLESOFT BASIC line number high (DOS) |
| AF,B0 | | APPLESOFT BASIC PROGRAM END (DOS) |
| CA,CB | | INTEGER BASIC PROGRAM START (DOS) |
| CC,CD | | INTEGER BASIC VARIABLES END (DOS) |
| D6 | | APPLESOFT BASIC PROGRAM protection flag (DOS) |
| D8,D9 | | INTEGER BASIC line number (DOS) |
| | | APPLESOFT BASIC ONERR (DOS) |
|---------+-----+-----------------------------------------------|
* Page 03 * Page 03
|-------------+--------------------------------------| |-------------+--------------------------------------|
@ -217,7 +270,7 @@ Apple II notes
- http://www.textfiles.com/apple/peeks.pokes.2 - http://www.textfiles.com/apple/peeks.pokes.2
- http://www.applefritter.com/node/24236 - http://www.applefritter.com/node/24236
- http://www.easy68k.com/paulrsm/6502/index.html - Information about firmware - http://www.easy68k.com/paulrsm/6502/index.html - Information about firmware
- http://www-personal.umich.edu/~mressl/a2dp/index.html - Apple II documentation project - http://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/ - Apple II documentation project
* Language card * Language card
Install in slot 0. Install in slot 0.
@ -665,3 +718,30 @@ If track 0 has >15 sync count, the rest of the tracks will have (sync count - 2)
tracksize 51024 tracksize 51024
(/ 51024 8) (/ 51024 8)
6378 6378
* System disk masters
https://github.com/cmosher01/Apple-II-System-Masters.git
http://beagle.applearchives.com/the_software/vintage_beagle_bros_softwar/prontodos.html
| 452f496e7c9c447052331f72f643117f | dos331/clean331sysmas.do |
| 452f496e7c9c447052331f72f643117f | dos331/original331sysmas.do |
| 452f496e7c9c447052331f72f643117f | dos331/stock331mastercreated.do |
| 5a72964c04e4a37bb8b13a471aa046de | dos331/stock331init.do |
| 5ed656da29221d891d056b6b576ae2ce | dos330/stock330init.do |
| 6987207d16a72c60de03d8d1c5e267f3 | dos332/clean332sysmas.do |
| 6987207d16a72c60de03d8d1c5e267f3 | dos332/original332sysmas.do |
| 6987207d16a72c60de03d8d1c5e267f3 | dos332/stock332mastercreated.do |
| 7df440bb6b3ddd8cbc44009f90a8d18f | dos330/clean330sysmas.do |
| 7df440bb6b3ddd8cbc44009f90a8d18f | dos330/original330sysmas.do |
| 7df440bb6b3ddd8cbc44009f90a8d18f | dos330/stock330mastercreated.do |
| 9f79e87fcbcb9885a47f2f462868833f | dos332/stock332init.do |
| 7df440bb6b3ddd8cbc44009f90a8d18f | Apple DOS 3.3 August 1980.dsk |
| 452f496e7c9c447052331f72f643117f | Apple DOS 3.3 January 1983.dsk |
| 92e8fc0c4e79e50f2d81a14cfa55b151 | DavidDOS.dsk |
| c17928ee915b9bf5c4041621fa8a0cb4 | DiversiDOS.dsk |
| 6390fc84edf64dd88682b920ff88bbfa | Prontodos/Prontodos.dsk |
| 5b9894778b6b97d56d1199ab13b0db66 | EsDos.dsk |
| db809f937c0de0a12cb70897ed9a72da | chivalry.dsk |

91
docs/chivalry-debug.org Normal file
View File

@ -0,0 +1,91 @@
* Debugging the Chivalry disk
Ascending memory references from $1F76.
** Disassembly of routine that is overwriting from $1F76
$73,$74
$77,$78
$5B
$5C
$5D
$62
$1F51: A0 00 LDY #$00
$1F53: B1 73 LDA ($73),Y
$1F55: 85 5C STA $5C
$1F57: C8 INY
$1F58: B1 73 LDA ($73),Y
$1F5A: 85 5B STA $5B
$1F5C: C8 INY
$1F5D: 18 CLC
$1F5E: 98 TYA
$1F5F: 65 73 ADC $73
$1F61: 85 73 STA $73
$1F63: A9 00 LDA #$00
$1F65: 65 74 ADC $74
$1F67: 85 74 STA $74
$1F69: 20 9C 1D JSR $1D9C
$1F6C: A2 00 LDX #$00
$1F6E: A0 00 LDY #$00
$1F70: A5 5C LDA $5C
$1F72: 85 5D STA $5D
$1F74: B1 73 LDA ($73),Y
$1F76: 91 77 STA ($77),Y
$1F78: C8 INY
$1F79: C6 5D DEC $5D
$1F7B: D0 F7 BNE $1F74
$1F7D: 18 CLC
$1F7E: 98 TYA
$1F7F: 65 73 ADC $73
$1F81: 85 73 STA $73
$1F83: A9 00 LDA #$00
$1F85: 65 74 ADC $74
$1F87: 85 74 STA $74
$1F89: E6 62 INC $62
$1F8B: C6 5B DEC $5B
$1F8D: D0 DA BNE $1F69
$1F8F: 60 RTS
** Routine it calls: $1D9C
$5E
$61
$62
$64
$77
$78
$1D9C: A5 62 LDA $62
$1D9E: AA TAX
$1D9F: 0A ASL
$1DA0: 0A ASL
$1DA1: 29 1C AND #$1C
$1DA3: 85 78 STA $78
$1DA5: 8A TXA
$1DA6: 6A ROR
$1DA7: AA TAX
$1DA8: 6A ROR
$1DA9: 6A ROR
$1DAA: 6A ROR
$1DAB: 08 PHP
$1DAC: 29 03 AND #$03
$1DAE: 05 78 ORA $78
$1DB0: 05 5E ORA $5E
$1DB2: 85 78 STA $78
$1DB4: 8A TXA
$1DB5: 29 60 AND #$60
$1DB7: 85 77 STA $77
$1DB9: 6A ROR
$1DBA: 28 PLP
$1DBB: 6A ROR
$1DBC: 29 98 AND #$98
$1DBE: 05 77 ORA $77
$1DC0: 85 77 STA $77
$1DC2: A6 61 LDX $61
$1DC4: BD D2 1D LDA $1DD2,X
$1DC7: 18 CLC
$1DC8: 65 77 ADC $77
$1DCA: 85 77 STA $77
$1DCC: BD 5E 1E LDA $1E5E,X
$1DCF: 85 64 STA $64
$1DD1: 60 RTS

3538
docs/disk-md5s.org Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,13 +2,30 @@ package goapple2
import ( import (
"fmt" "fmt"
"io/ioutil"
"log"
"path/filepath"
"strconv"
"time"
"github.com/zellyn/go6502/cpu" "github.com/zellyn/go6502/cpu"
"github.com/zellyn/goapple2/cards" "github.com/zellyn/goapple2/cards"
"github.com/zellyn/goapple2/videoscan" "github.com/zellyn/goapple2/videoscan"
) )
// Memory for the tests. Satisfies the cpu.Memory interface. type PCActionType int
const (
ActionDumpMem PCActionType = iota + 1
ActionLogRegisters
)
type PCAction struct {
Type PCActionType
String string
}
// Apple II struct
type Apple2 struct { type Apple2 struct {
mem [65536]byte mem [65536]byte
cpu cpu.Cpu cpu cpu.Cpu
@ -26,12 +43,15 @@ type Apple2 struct {
card12kMask byte card12kMask byte
card12kConflict bool "True if more than one card is handling the 12k ROM area" card12kConflict bool "True if more than one card is handling the 12k ROM area"
card12kHandler byte card12kHandler byte
cardTickerMask byte
pcActions map[uint16][]PCAction
} }
func NewApple2(p videoscan.Plotter, rom []byte, charRom [2048]byte) *Apple2 { func NewApple2(p videoscan.Plotter, rom []byte, charRom [2048]byte) *Apple2 {
a2 := Apple2{ a2 := Apple2{
// BUG(zellyn): this is not how the apple2 keyboard actually works // BUG(zellyn): this is not how the apple2 keyboard actually works
keys: make(chan byte, 16), keys: make(chan byte, 16),
pcActions: make(map[uint16][]PCAction),
} }
copy(a2.mem[len(a2.mem)-len(rom):len(a2.mem)], rom) copy(a2.mem[len(a2.mem)-len(rom):len(a2.mem)], rom)
a2.scanner = videoscan.NewScanner(&a2, p, charRom) a2.scanner = videoscan.NewScanner(&a2, p, charRom)
@ -47,6 +67,9 @@ func (a2 *Apple2) AddCard(card cards.Card) error {
return fmt.Errorf("Slot %d already has a card: %s", slot, a2.cards[slot]) return fmt.Errorf("Slot %d already has a card: %s", slot, a2.cards[slot])
} }
a2.cardMask |= slotbit a2.cardMask |= slotbit
if card.WantTicker() {
a2.cardTickerMask |= slotbit
}
a2.cards[slot] = card a2.cards[slot] = card
return nil return nil
} }
@ -75,20 +98,28 @@ func (a2 *Apple2) handleC00X(address uint16, value byte, write bool) byte {
} }
switch address { switch address {
case 0xC050: // GRAPHICS case 0xC050: // GRAPHICS
fmt.Printf("$%04X: GRAPHICS\n", a2.cpu.PC())
a2.scanner.SetGraphics(true) a2.scanner.SetGraphics(true)
case 0xC051: // TEXT case 0xC051: // TEXT
fmt.Printf("$%04X: NO GRAPHICS\n", a2.cpu.PC())
a2.scanner.SetGraphics(false) a2.scanner.SetGraphics(false)
case 0xC052: // NOMIX case 0xC052: // NOMIX
fmt.Printf("$%04X: NOMIX\n", a2.cpu.PC())
a2.scanner.SetMix(false) a2.scanner.SetMix(false)
case 0xC053: // MIX case 0xC053: // MIX
fmt.Printf("$%04X: MIX\n", a2.cpu.PC())
a2.scanner.SetMix(true) a2.scanner.SetMix(true)
case 0xC054: // PAGE 1 case 0xC054: // PAGE 1
fmt.Printf("$%04X: PAGE1\n", a2.cpu.PC())
a2.scanner.SetPage(1) a2.scanner.SetPage(1)
case 0xC055: // PAGE 2 case 0xC055: // PAGE 2
fmt.Printf("$%04X: PAGE2\n", a2.cpu.PC())
a2.scanner.SetPage(2) a2.scanner.SetPage(2)
case 0xC056: // LORES case 0xC056: // LORES
fmt.Printf("$%04X: LORES\n", a2.cpu.PC())
a2.scanner.SetHires(false) a2.scanner.SetHires(false)
case 0xC057: // HIRES case 0xC057: // HIRES
fmt.Printf("$%04X: HIRES\n", a2.cpu.PC())
a2.scanner.SetHires(true) a2.scanner.SetHires(true)
} }
} }
@ -197,12 +228,33 @@ func (a2 *Apple2) Keypress(key byte) {
a2.keys <- key | 0x80 a2.keys <- key | 0x80
} }
func (a2 *Apple2) AddPCAction(address uint16, action PCAction) {
a2.pcActions[address] = append(a2.pcActions[address], action)
}
func (a2 *Apple2) Step() error { func (a2 *Apple2) Step() error {
if actions, ok := a2.pcActions[a2.cpu.PC()]; ok {
for _, action := range actions {
switch action.Type {
case ActionDumpMem:
a2.DumpRAM(action.String, true)
case ActionLogRegisters:
a2.LogRegisters()
}
}
}
return a2.cpu.Step() return a2.cpu.Step()
} }
func (a2 *Apple2) Tick() { func (a2 *Apple2) Tick() {
a2.scanner.Scan1() a2.scanner.Scan1()
tickerMask := a2.cardTickerMask
for i := 0; i < 8 && tickerMask > 0; i++ {
if tickerMask&1 == 1 {
a2.cards[i].Tick()
}
tickerMask >>= 1
}
} }
func (a2 *Apple2) Quit() { func (a2 *Apple2) Quit() {
@ -245,3 +297,24 @@ func (a2 *Apple2) Handle12k(onOff bool, slot byte) {
} }
} }
} }
func (a2 *Apple2) LogRegisters() {
c := a2.cpu
log.Printf("Registers: PC=$%04X A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%02X=$%08b",
c.PC(), c.A(), c.X(), c.Y(), c.SP(), c.P(), c.P())
}
func (a2 *Apple2) DumpRAM(filename string, addTimestamp bool) error {
f := filename
if addTimestamp {
ts := "-" + strconv.FormatInt(time.Now().UnixNano(), 10)
if ext := filepath.Ext(filename); ext == "" {
f = filename + ts
} else {
dir, file := filepath.Split(filename[:len(filename)-len(ext)])
f = dir + file + ts + ext
}
}
log.Printf("Dumping RAM to %s", f)
a2.LogRegisters()
return ioutil.WriteFile(f, a2.mem[:0xC000], 0644)
}

View File

@ -261,6 +261,7 @@ func (s SdlPlotter) OncePerFrame() {
} }
func typeProgram(a2 *goapple2.Apple2) { func typeProgram(a2 *goapple2.Apple2) {
return
lines := []string{ lines := []string{
"10 GR", "10 GR",
"20 POKE -16302,0", "20 POKE -16302,0",
@ -272,6 +273,14 @@ func typeProgram(a2 *goapple2.Apple2) {
"80 NEXT", "80 NEXT",
"RUN", "RUN",
} }
lines = []string{
"10 HGR2",
"20 FOR I = 0 to 7",
"30 HCOLOR=7-I",
"40 HPLOT I*10, 0 TO 191 + I*10, 191",
"50 NEXT",
"RUN",
}
for _, line := range lines { for _, line := range lines {
for _, ch := range line { for _, ch := range line {
a2.Keypress(byte(ch)) a2.Keypress(byte(ch))
@ -315,10 +324,11 @@ func RunEmulator() {
log.Fatal(err) log.Fatal(err)
} }
disk1 := disk.NewNybble() disk1 := disk.NewNybble()
// if err = disk1.LoadDosDisk("../data/disks/spedtest.dsk"); err != nil {
// if err = disk1.LoadDosDisk("../data/disks/dung_beetles.dsk"); err != nil {
if err = disk1.LoadDosDisk("../data/disks/chivalry.dsk"); err != nil { if err = disk1.LoadDosDisk("../data/disks/chivalry.dsk"); err != nil {
log.Fatal(err) log.Fatal(err)
} }
disk1.SetHalfTrack(50)
diskCard.LoadDisk(disk1, 0) diskCard.LoadDisk(disk1, 0)
steps := *steplimit steps := *steplimit
@ -332,7 +342,10 @@ func RunEmulator() {
defer pprof.StopCPUProfile() defer pprof.StopCPUProfile()
} }
go typeProgram(a2) a2.AddPCAction(0xC6EB, goapple2.PCAction{goapple2.ActionDumpMem, "chivalry-dump.bin"})
a2.AddPCAction(0x1F76, goapple2.PCAction{goapple2.ActionDumpMem, "chivalry-dump.bin"})
// go typeProgram(a2)
for !a2.Done { for !a2.Done {
err := a2.Step() err := a2.Step()

View File

@ -96,15 +96,19 @@ func (s *Scanner) inc() {
var last [192][40]uint16 var last [192][40]uint16
func (s *Scanner) Scan1() { func (s *Scanner) Scan1() {
m := s.m.RamRead(s.address()) address := s.address()
if address >= 0xC000 {
fmt.Printf("\n\n\nWOAH! $%04X\n\n\n", address)
}
m := s.m.RamRead(address)
row, column := s.row(), s.column() row, column := s.row(), s.column()
_, _, _ = m, row, column _, _, _ = m, row, column
var data uint16 var data uint16
switch { switch {
case !s.graphics || (s.mix && s.lastFour): case !s.graphics || (s.mix && s.lastFour):
data = s.textData(m, row, column) data = s.textData(m, row)
case s.hires: case s.hires:
data = s.hiresData(m, row, column) data = s.hiresData(m)
default: // lores default: // lores
data = s.loresData(m, row, column) data = s.loresData(m, row, column)
} }
@ -112,6 +116,9 @@ func (s *Scanner) Scan1() {
if !s.hbl && !s.vbl { if !s.hbl && !s.vbl {
change := last[row][column] != (data | s.graphicsBit) change := last[row][column] != (data | s.graphicsBit)
if change || s.lastChange { if change || s.lastChange {
// if row <= 8 && column == 0 {
// fmt.Printf("%d,%d: RawData=%02X, Data=%04X\n", row, column, m, data)
// }
s.plotter.Plot(PlotData{ s.plotter.Plot(PlotData{
Row: byte(row), Row: byte(row),
Column: byte(column), Column: byte(column),
@ -218,7 +225,7 @@ func (s *Scanner) SetPage(page int) {
} }
} }
func (s *Scanner) textData(m byte, row int, column int) uint16 { func (s *Scanner) textData(m byte, row int) uint16 {
line := s.rom[int(m)*8+((row+800)%8)] line := s.rom[int(m)*8+((row+800)%8)]
// Invert if flash // Invert if flash
if (m^0x80)&line&s.flasher > 127 { if (m^0x80)&line&s.flasher > 127 {
@ -226,7 +233,7 @@ func (s *Scanner) textData(m byte, row int, column int) uint16 {
} }
line &= 0x7f // Mask out high bit line &= 0x7f // Mask out high bit
// Now it's just like hires data // Now it's just like hires data
return s.hiresData(line, row, column) return s.hiresData(line)
} }
// Double each bit to go from pixel info to color info // Double each bit to go from pixel info to color info
@ -249,11 +256,11 @@ var HIRES_DOUBLES = [128]uint16{
0x3FC0, 0x3FC3, 0x3FCC, 0x3FCF, 0x3FF0, 0x3FF3, 0x3FFC, 0x3FFF, 0x3FC0, 0x3FC3, 0x3FCC, 0x3FCF, 0x3FF0, 0x3FF3, 0x3FFC, 0x3FFF,
} }
func (s *Scanner) hiresData(m byte, row int, column int) uint16 { func (s *Scanner) hiresData(m byte) uint16 {
if m < 128 { if m < 128 {
return HIRES_DOUBLES[m] return HIRES_DOUBLES[m]
} }
return ((HIRES_DOUBLES[m&0x7f] << 1) & 0x3ff) | s.lastBit return ((HIRES_DOUBLES[m&0x7f] << 1) & 0x3fff) | s.lastBit
} }
func (s *Scanner) loresData(m byte, row int, column int) uint16 { func (s *Scanner) loresData(m byte, row int, column int) uint16 {

View File

@ -228,3 +228,43 @@ func TestSpecificLocationAddresses(t *testing.T) {
} }
} }
} }
func TestHiresConversion(t *testing.T) {
var m K64
var f fakePlotter
s := NewScanner(&m, &f, [2048]byte{})
s.SetMix(false)
s.SetGraphics(true)
s.SetHires(true)
hiresIn0 := []byte{0x03, 0x06, 0x0C, 0x18, 0x30}
hiresOut0 := []uint16{0xf, 0x3c, 0xf0, 0x3c0, 0xf00}
hiresIn1 := []byte{0x83, 0x86, 0x8C, 0x98, 0xB0}
hiresOut1 := []uint16{0x1e, 0x78, 0x1e0, 0x780, 0x1e00}
for i := range hiresIn0 {
s.lastBit = 0
out := s.hiresData(hiresIn0[i])
if out != hiresOut0[i] {
t.Errorf("Expected hiresData(0x%02X) to be %04X, got %04X", hiresIn0[i], hiresOut0[i], out)
}
s.lastBit = 1
out = s.hiresData(hiresIn0[i])
if out != hiresOut0[i] {
t.Errorf("Expected hiresData(0x%02X) to be %04X, got %04X", hiresIn0[i], hiresOut0[i], out)
}
}
for i := range hiresIn1 {
s.lastBit = 0
out := s.hiresData(hiresIn1[i])
if out != hiresOut1[i] {
t.Errorf("Expected hiresData(0x%02X) lb=0 to be %04X, got %04X", hiresIn1[i], hiresOut1[i], out)
}
s.lastBit = 1
out = s.hiresData(hiresIn1[i])
if out != hiresOut1[i]|1 {
t.Errorf("Expected hiresData(0x%02X) lb=1 to be %04X, got %04X", hiresIn1[i], hiresOut1[i]|1, out)
}
}
}