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
Read(address uint16) byte // Read from any address ($C800-$FFFF)
Write(address uint16, value byte) // Write to any address ($C800-$FFFF)
WantTicker() bool
Tick()
}
type CardManager interface {

View File

@ -8,6 +8,7 @@ import (
type Disk interface {
Read() byte
Skip(int)
Write(byte)
SetHalfTrack(byte)
HalfTrack() byte
@ -16,14 +17,25 @@ type Disk interface {
Writeable() bool
}
const (
MODE_READ = 0
MODE_WRITE = 1
MODE_SHIFT = 0
MODE_LOAD = 2
)
type DiskCard struct {
rom [256]byte
cm CardManager
slot byte
slotbit byte
disks [2]Disk
active int
phases byte
rom [256]byte
cm CardManager
slot byte
slotbit byte
disks [2]Disk
active int
phases byte
mode byte
onOff bool
dataRegister byte
lastAccess int
}
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) {
if !dc.onOff {
return
}
phaseBit := byte(1 << phase)
if onOff {
dc.phases |= phaseBit
@ -98,15 +113,90 @@ func (dc *DiskCard) handleAccess(address byte) {
dc.handlePhase(phase, onOff)
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 {
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) {
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 {
@ -128,3 +218,11 @@ func (dc *DiskCard) Write256(address byte, value byte) {
func (dc *DiskCard) LoadDisk(d Disk, which int) {
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) {
// 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.
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.
func appendAddress(target []byte, t, s, v byte) []byte {
target = append(target, 0xD5, 0xAA, 0x96)
fmt.Println("s=", s)
target = append44(target, v)
target = append44(target, t)
target = append44(target, s)

View File

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

View File

@ -55,6 +55,11 @@ func (disk *Nybble) Read() byte {
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) {
track := disk.Tracks[disk.halfTrack/2]
disk.position = (disk.position + 1) % len(track)

View File

@ -155,6 +155,59 @@ Apple II notes
| $F3 | 243 | FLASH MASK |
| $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
|-------------+--------------------------------------|
@ -217,7 +270,7 @@ Apple II notes
- http://www.textfiles.com/apple/peeks.pokes.2
- http://www.applefritter.com/node/24236
- 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
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
(/ 51024 8)
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 (
"fmt"
"io/ioutil"
"log"
"path/filepath"
"strconv"
"time"
"github.com/zellyn/go6502/cpu"
"github.com/zellyn/goapple2/cards"
"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 {
mem [65536]byte
cpu cpu.Cpu
@ -26,12 +43,15 @@ type Apple2 struct {
card12kMask byte
card12kConflict bool "True if more than one card is handling the 12k ROM area"
card12kHandler byte
cardTickerMask byte
pcActions map[uint16][]PCAction
}
func NewApple2(p videoscan.Plotter, rom []byte, charRom [2048]byte) *Apple2 {
a2 := Apple2{
// 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)
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])
}
a2.cardMask |= slotbit
if card.WantTicker() {
a2.cardTickerMask |= slotbit
}
a2.cards[slot] = card
return nil
}
@ -75,20 +98,28 @@ func (a2 *Apple2) handleC00X(address uint16, value byte, write bool) byte {
}
switch address {
case 0xC050: // GRAPHICS
fmt.Printf("$%04X: GRAPHICS\n", a2.cpu.PC())
a2.scanner.SetGraphics(true)
case 0xC051: // TEXT
fmt.Printf("$%04X: NO GRAPHICS\n", a2.cpu.PC())
a2.scanner.SetGraphics(false)
case 0xC052: // NOMIX
fmt.Printf("$%04X: NOMIX\n", a2.cpu.PC())
a2.scanner.SetMix(false)
case 0xC053: // MIX
fmt.Printf("$%04X: MIX\n", a2.cpu.PC())
a2.scanner.SetMix(true)
case 0xC054: // PAGE 1
fmt.Printf("$%04X: PAGE1\n", a2.cpu.PC())
a2.scanner.SetPage(1)
case 0xC055: // PAGE 2
fmt.Printf("$%04X: PAGE2\n", a2.cpu.PC())
a2.scanner.SetPage(2)
case 0xC056: // LORES
fmt.Printf("$%04X: LORES\n", a2.cpu.PC())
a2.scanner.SetHires(false)
case 0xC057: // HIRES
fmt.Printf("$%04X: HIRES\n", a2.cpu.PC())
a2.scanner.SetHires(true)
}
}
@ -197,12 +228,33 @@ func (a2 *Apple2) Keypress(key byte) {
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 {
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()
}
func (a2 *Apple2) Tick() {
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() {
@ -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) {
return
lines := []string{
"10 GR",
"20 POKE -16302,0",
@ -272,6 +273,14 @@ func typeProgram(a2 *goapple2.Apple2) {
"80 NEXT",
"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 _, ch := range line {
a2.Keypress(byte(ch))
@ -315,10 +324,11 @@ func RunEmulator() {
log.Fatal(err)
}
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 {
log.Fatal(err)
}
disk1.SetHalfTrack(50)
diskCard.LoadDisk(disk1, 0)
steps := *steplimit
@ -332,7 +342,10 @@ func RunEmulator() {
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 {
err := a2.Step()

View File

@ -96,15 +96,19 @@ func (s *Scanner) inc() {
var last [192][40]uint16
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()
_, _, _ = m, row, column
var data uint16
switch {
case !s.graphics || (s.mix && s.lastFour):
data = s.textData(m, row, column)
data = s.textData(m, row)
case s.hires:
data = s.hiresData(m, row, column)
data = s.hiresData(m)
default: // lores
data = s.loresData(m, row, column)
}
@ -112,6 +116,9 @@ func (s *Scanner) Scan1() {
if !s.hbl && !s.vbl {
change := last[row][column] != (data | s.graphicsBit)
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{
Row: byte(row),
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)]
// Invert if flash
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
// 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
@ -249,11 +256,11 @@ var HIRES_DOUBLES = [128]uint16{
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 {
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 {

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)
}
}
}