1
0
mirror of https://github.com/zellyn/go6502.git synced 2024-06-24 15:29:40 +00:00

Most simple instructions done.

This commit is contained in:
Zellyn Hunter 2013-02-17 21:09:38 -08:00
parent 90369c5a23
commit 7d26907926
7 changed files with 6269 additions and 0 deletions

145
cpu/cpu.go Normal file
View File

@ -0,0 +1,145 @@
/*
6502 implementation.
TODO(zellyn): Provide configurable options
TODO(zellyn): Implement IRQ, NMI
*/
package cpu
import (
"fmt"
)
/* Bugs and Quirks.
See http://en.wikipedia.org/wiki/MOS_Technology_6502#Bugs_and_quirks
*/
const (
// TODO(zellyn) implement
// JMP xxFF reads xx00 instead of (xx+1)00
OPTION_BUG_JMP_FF = true
OPTION_BUG_INDEXED_ADDR_ACROSS_PAGE_INVALID_READ = true
OPTION_BUG_READ_MODIFY_WRITE_TWO_WRITES = true
OPTION_BUG_NVZ_INVALID_IN_BCD = true
OPTION_BUG_NO_CLD_ON_INTERRUPT = true
OPTION_BUG_INTERRUPTS_CLOBBER_BRK = true
)
type Cpu interface {
Reset()
Step() error
SetPC(uint16)
A() byte
X() byte
Y() byte
PC() uint16
P() byte // [NV-BDIZC]
SP() byte
// TODO(zellyn): Add signaling of interrupts
}
type Memory interface {
Read(uint16) byte
Write(uint16, byte)
}
type Ticker interface {
Tick()
}
const (
STACK_BASE = 0x100
IRQ_VECTOR = 0xFFFE
NMI_VECTOR = 0xFFFA
RESET_VECTOR = 0xFFFC
)
const (
FLAG_C = 1 << iota
FLAG_Z
FLAG_I
FLAG_D
FLAG_B
FLAG_UNUSED
FLAG_V
FLAG_N
)
type registers struct {
A byte
X byte
Y byte
PC uint16
P byte // [NV-BDIZC]
SP byte
}
type cpu struct {
m Memory
t Ticker
r registers
}
func NewCPU(memory Memory, ticker Ticker) Cpu {
c := cpu{m: memory, t: ticker}
c.r.P |= FLAG_UNUSED // Set unused flag to 1
return &c
}
func (c *cpu) A() byte {
return c.r.A
}
func (c *cpu) X() byte {
return c.r.X
}
func (c *cpu) Y() byte {
return c.r.Y
}
func (c *cpu) PC() uint16 {
return c.r.PC
}
func (c *cpu) P() byte {
return c.r.P
}
func (c *cpu) SP() byte {
return c.r.SP
}
func (c *cpu) readWord(address uint16) uint16 {
return uint16(c.m.Read(address)) + (uint16(c.m.Read(address+1)) << 8)
}
func (c *cpu) Reset() {
c.r.SP = 0
c.r.PC = c.readWord(RESET_VECTOR)
c.r.P |= FLAG_I // Turn interrupts off
if !OPTION_BUG_NO_CLD_ON_INTERRUPT {
c.r.P &^= FLAG_D
}
}
func (c *cpu) Step() error {
i := c.m.Read(c.r.PC)
c.r.PC++
c.t.Tick()
if f, ok := opcodes[i]; ok {
f(c)
return nil
}
return fmt.Errorf("Unknown opcode at location $%04X: $%02X", c.r.PC, i)
}
func (c *cpu) SetPC(address uint16) {
c.r.PC = address
}
func (c *cpu) SetNZ(value byte) {
c.r.P = (c.r.P &^ FLAG_N) | (value & FLAG_N)
if value == 0 {
c.r.P |= FLAG_Z
} else {
c.r.P &^= FLAG_Z
}
}

366
cpu/opcodes.go Normal file
View File

@ -0,0 +1,366 @@
package cpu
import (
_ "fmt"
)
func samePage(a1 uint16, a2 uint16) bool {
return a1^a2&0xFF00 == 0
}
// Simple, one-off instructions ----------------------------------------------
func clearFlag(flag byte) func(*cpu) {
return func(c *cpu) {
c.r.P &^= flag
c.t.Tick()
}
}
func setFlag(flag byte) func(*cpu) {
return func(c *cpu) {
c.r.P |= flag
c.t.Tick()
}
}
func dex(c *cpu) {
c.r.X--
c.SetNZ(c.r.X)
c.t.Tick()
}
func dey(c *cpu) {
c.r.Y--
c.SetNZ(c.r.Y)
c.t.Tick()
}
func inx(c *cpu) {
c.r.X++
c.SetNZ(c.r.X)
c.t.Tick()
}
func iny(c *cpu) {
c.r.Y++
c.SetNZ(c.r.Y)
c.t.Tick()
}
func pha(c *cpu) {
c.t.Tick()
c.m.Write(0x100+uint16(c.r.SP), c.r.A)
c.r.SP--
c.t.Tick()
}
func php(c *cpu) {
c.t.Tick()
c.m.Write(0x100+uint16(c.r.SP), c.r.P)
c.r.SP--
c.t.Tick()
}
func pla(c *cpu) {
c.t.Tick()
c.r.SP++
c.t.Tick()
c.r.A = c.m.Read(0x100 + uint16(c.r.SP))
c.t.Tick()
}
func plp(c *cpu) {
c.t.Tick()
c.r.SP++
c.t.Tick()
c.r.P = c.m.Read(0x100 + uint16(c.r.SP))
c.t.Tick()
}
func nop(c *cpu) {
c.t.Tick()
}
func tax(c *cpu) {
c.r.X = c.r.A
c.SetNZ(c.r.X)
c.t.Tick()
}
func tay(c *cpu) {
c.r.Y = c.r.A
c.SetNZ(c.r.Y)
c.t.Tick()
}
func tsx(c *cpu) {
c.r.X = c.r.SP
c.SetNZ(c.r.X)
c.t.Tick()
}
func txa(c *cpu) {
c.r.A = c.r.X
c.SetNZ(c.r.A)
c.t.Tick()
}
func txs(c *cpu) {
c.r.SP = c.r.X
c.SetNZ(c.r.SP)
c.t.Tick()
}
func tya(c *cpu) {
c.r.A = c.r.Y
c.SetNZ(c.r.A)
c.t.Tick()
}
func jmpAbsolute(c *cpu) {
// T1
c.r.PC++
addr := uint16(c.m.Read(c.r.PC))
c.t.Tick()
// T2
c.r.PC++
addr |= (uint16(c.m.Read(c.r.PC)) << 8)
c.r.PC = addr
c.t.Tick()
}
func jmpIndirect(c *cpu) {
// T1
c.r.PC++
iAddr := uint16(c.m.Read(c.r.PC))
c.t.Tick()
// T2
c.r.PC++
iAddr |= (uint16(c.m.Read(c.r.PC)) << 8)
c.t.Tick()
// T3
addr := uint16(c.m.Read(iAddr))
c.t.Tick()
// T4
if (iAddr&0xff == 0xff) && OPTION_BUG_JMP_FF {
addr |= (uint16(c.m.Read(iAddr&0xff00)) << 8)
} else {
addr |= (uint16(c.m.Read(iAddr+1)) << 8)
}
c.r.PC = addr
c.t.Tick()
}
func jsr(c *cpu) {
// T1
c.r.PC++
addr := uint16(c.m.Read(c.r.PC)) // We actually push PC(next) - 1
c.t.Tick()
// T2
c.r.PC++
c.t.Tick()
// T3
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC>>8))
c.r.SP--
c.t.Tick()
// T4
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC&0xff))
c.r.SP--
c.t.Tick()
// T5
addr |= (uint16(c.m.Read(c.r.PC)) << 8)
c.r.PC = addr
c.t.Tick()
}
func rts(c *cpu) {
// T1
c.t.Tick()
// T2
c.r.SP++
c.t.Tick()
// T3
addr := uint16(c.m.Read(0x100 + uint16(c.r.SP)))
c.r.SP++
c.t.Tick()
// T4
addr |= (uint16(c.m.Read(0x100+uint16(c.r.SP))) << 8)
c.t.Tick()
// T5
c.r.PC = addr + 1 // Since we pushed PC(next) - 1
c.t.Tick()
}
func rti(c *cpu) {
// T1
c.t.Tick()
// T2
c.r.SP++
c.t.Tick()
// T3
c.r.P = c.m.Read(0x100 + uint16(c.r.SP))
c.r.SP++
// T4
addr := uint16(c.m.Read(0x100 + uint16(c.r.SP)))
c.r.SP++
c.t.Tick()
// T5
addr |= (uint16(c.m.Read(0x100+uint16(c.r.SP))) << 8)
c.r.PC = addr
c.t.Tick()
}
// Note that BRK skips the next instruction:
// http://en.wikipedia.org/wiki/Interrupts_in_65xx_processors#Using_BRK_and_COP
func brk(c *cpu) {
// T1
c.r.PC++
c.r.SP--
c.t.Tick()
// T2
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC>>8))
c.r.SP--
c.t.Tick()
// T3
c.m.Write(0x100+uint16(c.r.SP), byte(c.r.PC&0xff))
c.r.SP--
c.t.Tick()
// T4
c.m.Write(0x100+uint16(c.r.SP), c.r.P|FLAG_B) // Set B flag
c.r.P |= FLAG_I // Disable interrupts
c.t.Tick()
// T5
addr := uint16(c.m.Read(IRQ_VECTOR))
c.t.Tick()
// T6
addr |= (uint16(c.m.Read(IRQ_VECTOR+1)) << 8)
c.r.PC = addr
c.t.Tick()
}
func branch(mask, value byte) func(*cpu) {
return func(c *cpu) {
offset := c.m.Read(c.r.PC)
c.r.PC++
c.t.Tick()
oldPC := c.r.PC
if c.r.P&mask == value {
c.t.Tick()
c.r.PC = c.r.PC + uint16(offset)
if offset >= 128 {
c.r.PC = c.r.PC - 256
}
if !samePage(c.r.PC, oldPC) {
c.t.Tick()
}
}
}
}
func immediate(f func(*cpu, byte)) func(*cpu) {
return func(c *cpu) {
// T1
value := c.m.Read(c.r.PC)
c.r.PC++
f(c, value)
c.t.Tick()
}
}
func lda(c *cpu, value byte) {
c.r.A = value
c.SetNZ(value)
}
func ldx(c *cpu, value byte) {
c.r.X = value
c.SetNZ(value)
}
func ldy(c *cpu, value byte) {
c.r.Y = value
c.SetNZ(value)
}
func ora(c *cpu, value byte) {
c.r.A |= value
c.SetNZ(c.r.A)
}
func and(c *cpu, value byte) {
c.r.A &= value
c.SetNZ(c.r.A)
}
func eor(c *cpu, value byte) {
c.r.A ^= value
c.SetNZ(c.r.A)
}
func cmp(c *cpu, value byte) {
v := c.r.A - value
c.SetNZ(v)
}
func cpx(c *cpu, value byte) {
v := c.r.X - value
c.SetNZ(v)
}
func cpy(c *cpu, value byte) {
v := c.r.Y - value
c.SetNZ(v)
}
var opcodes = map[byte]func(*cpu){
0x18: clearFlag(FLAG_C), // CLC
0xD8: clearFlag(FLAG_D), // CLD
0x58: clearFlag(FLAG_I), // CLI
0xB8: clearFlag(FLAG_V), // CLV
0x38: setFlag(FLAG_C), // SEC
0xF8: setFlag(FLAG_D), // SED
0x78: setFlag(FLAG_I), // SEI
0xEA: nop,
0xAA: tax,
0xA8: tay,
0xBA: tsx,
0x8A: txa,
0x9A: txs,
0x98: tya,
0xCA: dex,
0x88: dey,
0xE8: inx,
0xC8: iny,
0x48: pha,
0x08: php,
0x68: pla,
0x28: plp,
0x4C: jmpAbsolute,
0x6C: jmpIndirect,
0x20: jsr,
0x60: rts,
0x40: rti,
0x00: brk,
0x90: branch(FLAG_C, 0), // BCC
0xB0: branch(FLAG_C, FLAG_C), // BCS
0xF0: branch(FLAG_Z, FLAG_Z), // BEQ
0x30: branch(FLAG_N, FLAG_N), // BMI
0xD0: branch(FLAG_Z, 0), // BNE
0x10: branch(FLAG_N, 0), // BPL
0x50: branch(FLAG_V, 0), // BVC
0x70: branch(FLAG_V, FLAG_V), // BVS
0x09: immediate(ora),
0x29: immediate(and),
0x49: immediate(eor),
// 0x69: immediate(adc),
0xC0: immediate(cpy),
0xC9: immediate(cmp),
0xA0: immediate(ldy),
0xA2: immediate(ldx),
0xA9: immediate(lda),
0xE0: immediate(cpx),
// 0xE9: immediate(sbc),
}

123
docs/6502.org Normal file
View File

@ -0,0 +1,123 @@
* References
- Detailed per-instruction timing, etc: http://www.masswerk.at/6502/6502_instruction_set.html
- aaabbbcc-type breakdown: http://www.llx.com/~nparker/a2/opcodes.html
- 6502 Hardware manual (KIM-1): http://users.telenet.be/kim1-6502/6502/hwman.html#AA
- Stack explanation (annoying writing): http://homepage.ntlworld.com/cyborgsystems/CS_Main/6502/6502.htm#STACK
- For doublechecking cycle counts: http://www.obelisk.demon.co.uk/6502/reference.html
* Reset sequences
- http://www.pagetable.com/?p=410
** Reset/interrupt vectors
|---------+-------------|
| Address | Description |
|---------+-------------|
| $FFFE/F | IRQ |
| $FFFA/B | NMI |
| $FFFC/D | Reset |
|---------+-------------|
* Misc references
- http://axis.llx.com/~nparker/a2/opcodes.html
* Timing
aaabbbcc
** cc = 01
| aaa | opcode | flags |
|-----+--------+-------|
| 000 | ORA | NZ |
| 001 | AND | NZ |
| 010 | EOR | NZ |
| 011 | ADC | NZCV |
| 100 | STA | |
| 101 | LDA | NZ |
| 110 | CMP | NZC |
| 111 | SBC | NZCV |
| bbb | addressing mode |
|-----+-----------------|
| 000 | (zero page,X) |
| 001 | zero page |
| 010 | #immediate |
| 011 | absolute |
| 100 | (zero page),Y |
| 101 | zero page,X |
| 110 | absolute,Y |
| 111 | absolute,X |
| Addressing Mode | ORA | | AND | | EOR | | ADC | | STA | | LDA | | CMP | | SBC | |
|-----------------+-----+------+-----+------+-----+------+-----+------+-----+-----+-----+------+-----+------+-----+------|
| (zp,X) | 01 | 2/6 | 21 | 2/6 | 41 | 2/6 | 61 | 2/6 | 81 | 2/6 | A1 | 2/6 | C1 | 2/6 | E1 | 2/6 |
| zp | 05 | 2/3 | 25 | 2/3 | 45 | 2/3 | 65 | 2/3 | 85 | 2/3 | A5 | 2/3 | C5 | 2/3 | E5 | 2/3 |
| # | 09 | 2/2 | 29 | 2/2 | 49 | 2/2 | 69 | 2/2 | | | A9 | 2/2 | C9 | 2/2 | E9 | 2/2 |
| abs | 0D | 3/4 | 2D | 3/4 | 4D | 3/4 | 6D | 3/4 | 8D | 3/4 | AD | 3/4 | CD | 3/4 | ED | 3/4 |
| (zp),Y | 11 | 2/5* | 31 | 2/5* | 51 | 2/5* | 71 | 2/5* | 91 | 2/6 | B1 | 2/5* | D1 | 2/5* | F1 | 2/5* |
| zp,X | 15 | 2/4 | 35 | 2/4 | 55 | 2/4 | 75 | 2/4 | 95 | 2/4 | B5 | 2/4 | D5 | 2/4 | F5 | 2/4 |
| abs,Y | 19 | 3/4* | 39 | 3/4* | 59 | 3/4* | 79 | 3/4* | 99 | 3/5 | B9 | 3/4* | D9 | 3/4* | F9 | 3/4* |
| abs,X | 1D | 3/4* | 3D | 3/4* | 5D | 3/4* | 7D | 3/4* | 9D | 3/5 | BD | 3/4* | DD | 3/4* | FD | 3/4* |
(*) - add 1 to cycles if page boundary is crossed
** cc = 10
| aaa | opcode | flags |
|-----+--------+-------|
| 000 | ASL | NZC |
| 001 | ROL | NZC |
| 010 | LSR | NZC |
| 011 | ROR | NZC |
| 100 | STX | |
| 101 | LDX | NZ |
| 110 | DEC | NZ |
| 111 | INC | NZ |
| bbb | addressing mode | |
|-----+-----------------+-----------------------|
| 000 | #immediate | |
| 001 | zero page | |
| 010 | accumulator | |
| 011 | absolute | |
| 101 | zero page,X | STX,LDX: zero page, Y |
| 111 | absolute,X | LDX: absolute,Y |
| | ASL | | ROL | | LSR | | ROR | | STX | | LDX | | DEC | | INC | |
|-------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+-----+-----+-----|
| # | | | | | | | | | | | A2 | 2/2 | | | | |
| zp | 06 | 2/5 | 26 | 2/5 | 46 | 2/5 | 66 | 2/5 | 86 | 2/3 | A6 | 2/3 | C6 | 2/5 | E6 | 2/5 |
| A | 0A | 1/2 | 2A | 1/2 | 4A | 1/2 | 6A | 1/2 | | | | | | | | |
| abs | 0E | 3/6 | 2E | 3/6 | 4E | 3/6 | 6E | 3/6 | 8E | 3/4 | AE | 3/4 | CE | 3/3 | EE | 3/6 |
| zp,X/zp,Y | 16 | 2/6 | 36 | 2/6 | 56 | 2/6 | 76 | 2/6 | 96 | 2/4 | B6 | 2/4 | D6 | 2/6 | F6 | 2/6 |
| abs,X/abs,Y | 1E | 3/7 | 3E | 3/7 | 5E | 3/7 | 7E | 3/7 | ?? | | BE | 3/4* | DE | 3/7 | FE | 3/7 |
(*) - add 1 to cycles if page boundary is crossed
** cc = 00
| aaa | opcode | flags |
|-----+-----------+-------|
| 001 | BIT | NZV |
| 010 | JMP | |
| 011 | JMP (abs) | |
| 100 | STY | |
| 101 | LDY | |
| 110 | CPY | NZC |
| 111 | CPX | NZC |
| bbb | addressing mode |
|-----+-----------------|
| 000 | #immediate |
| 001 | zero page |
| 011 | absolute |
| 101 | zero page,X |
| 111 | absolute,X |
| | BIT | | JMP | | JMP() | | STY | | LDY | | CPY | | CPX | |
|-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+-----+-----+-----+-----|
| # | | | | | | | | | A0 | 2/2 | C0 | 2/2 | E0 | 2/2 |
| zp | 24 | 2/3 | | | | | 84 | 2/3 | A4 | 2/3 | C4 | 2/3 | E4 | 2/3 |
| abs | 2C | 3/4 | 4C | 3/3 | 6C | 3/5 | 8C | 3/4 | AC | 3/4 | CC | 3/4 | EC | 3/4 |
| zp,X | | | | | | | 94 | 2/4 | B4 | 2/4 | | | | |
| abs,X | | | | | | | | | BC | 3/4* | | | | |

276
docs/apple2.org Normal file
View File

@ -0,0 +1,276 @@
Apple II notes
** Memory Map
|---------+-------------------------------------------|
| Page | Use |
|---------+-------------------------------------------|
| $00 | Page Zero |
| $01 | System stack |
| $02 | Input buffer |
| $03 | Monitor vector locations |
| $04-$07 | Text/Lo-Res Primary |
| $08-$0B | Text/Lo-Res Secondary |
| $0C-$1F | Free RAM |
| $20-$3F | Hires Primary |
| $40-$5F | Hires Secondary |
| $60-$BF | Free RAM |
| $C0 | I/O and softswitches |
| $C1-$C7 | ROM for seven peripheral cards |
| $C8-$CF | switchable ROM for peripheral cards |
| $D0-$D7 | empty ROM socket #1 / Programmer's Aid #1 |
| $D8-$DF | empty ROM socket #2 |
| $E0-$F7 | Integer BASIC ROM |
| $F8-$FF | Monitor ROM |
|---------+-------------------------------------------|
* Integer Basic Map
|-------------+---------------------------------|
| Address | Use |
|-------------+---------------------------------|
| $E000-$F424 | BASIC |
| $F425-$F4FB | Floating point math package 1/2 |
| $F4FC-$F4FF | ??? (4 bytes) |
| $F500-$F63C | Miniassembler |
| $F63D-$F65D | Floating point math package 2/2 |
| $F65E-$F665 | ??? (8 bytes) |
| $F666-$F668 | Miniassembler entry point |
| $F669-$F688 | ??? (32 bytes) |
| $F689-$F7FC | SWEET 16 interpreter |
| $F800-$FFFF | Monitor |
|-------------+---------------------------------|
* I/O and softswitches
|---------+--------+------------------------------------------------------------------|
| Address | Dec | Use |
|---------+--------+------------------------------------------------------------------|
| $C00X | -16384 | Read keyboard: High bit = key pressed |
| $C01X | -16368 | Set to 0 to clear keyboard strobe |
| $C01A | -16358 | Read: High bit 1 = TEXT switch "ON" |
| $C01B | -16357 | Read: High bit 1 = MIXED switch "ON" |
| $C01C | -16356 | Read: High bit 1 = PAGE2 switch "ON" |
| $C01D | -16355 | Read: High bit 1 = HIRES switch "ON" |
| $C02X | | Casette output toggle |
| $C03X | -16336 | Speaker toggle |
| $C04X | | Output strobe to Game I/O connector |
| $C050 | -16304 | Set graphics mode |
| $C051 | -16303 | Set text mode |
| $C052 | -16302 | Set bottom 4 lines graphics |
| $C053 | -16301 | Set bottom 4 lines text |
| $C054 | -16300 | Display primary page |
| $C055 | -16299 | Display secondary page |
| $C056 | -16298 | Set HIRES graphics mode |
| $C057 | -16297 | Set color graphics mode |
| $CØ58 | -l6296 | Clear Game I/O ANØ output |
| $CØ59 | -l6295 | Set Game I/O ANØ output |
| $CØ5A | -l6294 | Clear Game I/O ANl output |
| $CØ5B | -l6293 | Set Game I/O ANl output |
| $CØ5C | -l6292 | Clear Game I/O AN2 output |
| $CØ5D | -l629l | Set Game I/O AN2 output |
| $CØ5E | -l629Ø | Clear Game I/O AN3 output |
| $CØ5F | -l6289 | Set Game I/O AN3 output |
| $C060/8 | | Tape in (bit 7) |
| $C061/9 | -16287 | "SW1": PDL(0) switch: High bit 1 = "on" |
| $C062/A | -16286 | "SW2": PDL(1) switch: High bit 1 = "on" |
| $C063/B | -16285 | "SW3": PDL(2) switch: High bit 1 = "on" |
| $C064/C | | Paddle 0 timer output: state of timer output in high bit |
| $C065/D | | Paddle 1 timer output: state of timer output in high bit |
| $C066/E | | Paddle 2 timer output: state of timer output in high bit |
| $C067/F | | Paddle 3 timer output: state of timer output in high bit |
| $C064 | | Paddle0 |
| $C07X | | Trigger paddle timers during \Zero_2 |
| $C08X | | Device select 0: Pin 41 on Peripheral Connector low during 0_2 |
| $C09X | | Device select 1: Pin 41 on Peripheral Connector low during 0_2 |
| $C0AX | | Device select 2: Pin 41 on Peripheral Connector low during 0_2 |
| $C0BX | | Device select 3: Pin 41 on Peripheral Connector low during 0_2 |
| $C0CX | | Device select 4: Pin 41 on Peripheral Connector low during 0_2 |
| $C0DX | | Device select 5: Pin 41 on Peripheral Connector low during 0_2 |
| $C0EX | | Device select 6: Pin 41 on Peripheral Connector low during 0_2 |
| $C0FX | | Device select 7: Pin 41 on Peripheral Connector low during 0_2 |
| $C10X | | Device select (8): Pin 41 on Peripheral Connector low during 0_2 |
| $C11X | | Device select (9): Pin 41 on Peripheral Connector low during 0_2 |
| $C12X | | Device select (A): Pin 41 on Peripheral Connector low during 0_2 |
| $C13X | | Device select (B): Pin 41 on Peripheral Connector low during 0_2 |
| $C14X | | Device select (C): Pin 41 on Peripheral Connector low during 0_2 |
| $C15X | | Device select (D): Pin 41 on Peripheral Connector low during 0_2 |
| $C16X | | Device select (E): Pin 41 on Peripheral Connector low during 0_2 |
| $C17X | | Device select (F): Pin 41 on Peripheral Connector low during 0_2 |
| $C1XX | | I/O Select 1: Pin 1 on Peripheral Connector low during 0_2 |
| $C2XX | | I/O Select 2: Pin 1 on Peripheral Connector low during 0_2 |
| $C3XX | | I/O Select 3: Pin 1 on Peripheral Connector low during 0_2 |
| $C4XX | | I/O Select 4: Pin 1 on Peripheral Connector low during 0_2 |
| $C5XX | | I/O Select 5: Pin 1 on Peripheral Connector low during 0_2 |
| $C6XX | | I/O Select 6: Pin 1 on Peripheral Connector low during 0_2 |
| $C7XX | | I/O Select 7: Pin 1 on Peripheral Connector low during 0_2 |
| $C8XX | | I/O Select (8): Pin 1 on Peripheral Connector low during 0_2 |
| $C9XX | | I/O Select (9): Pin 1 on Peripheral Connector low during 0_2 |
| $CAXX | | I/O Select (A): Pin 1 on Peripheral Connector low during 0_2 |
| $CBXX | | I/O Select (B): Pin 1 on Peripheral Connector low during 0_2 |
| $CCXX | | I/O Select (C): Pin 1 on Peripheral Connector low during 0_2 |
| $CDXX | | I/O Select (D): Pin 1 on Peripheral Connector low during 0_2 |
| $CEXX | | I/O Select (E): Pin 1 on Peripheral Connector low during 0_2 |
| $CFXX | | I/O Select (F): Pin 1 on Peripheral Connector low during 0_2 |
| $CFFF | | switchable peripheral ROM - find out more |
| | | |
| | | |
* Page 00
|---------+---------+----------------------------------------------------------------|
| Address | Dec | Description |
|---------+---------+----------------------------------------------------------------|
| $00-$1F | | Register area for "sweet 16" |
| $18 | | (DOS) first track of data |
| $19 | | (DOS) first sector of data |
| $1A | | (DOS) number of sectors to load |
| $1B | | (DOS) HIGH BYTE of buffer (LO is always 0) |
| $1A/B | | Shape pointer used by DRAW and XDRAW |
| $1C | | Last color used (HCOLOR converted to its color byte) |
| $20 | 32 | Left edge of window (0-39) |
| $21 | 33 | Width of the window (0 to 40 - Left) |
| $22 | 34 | Top edge of window (0-23) |
| $23 | 35 | Bottom of window (0-23) Bottom > Top |
| $24 | 36 | HTAB: 0-39 |
| $25 | 37 | VTAB: 0-23 |
| $2B | 43 | Boot SLOT * 16 |
| $2C | 44 | Lo-res line end-point |
| $30 | 48 | Lores color * 17 |
| $32 | 50 | Inverse($3F), Flash($7F), Normal($FF), Invisible($80) |
| $33 | | Prompt-char |
| $4A/B | 74-75 | LOMEM address (INT) |
| $4C/D | 76-77 | HIMEM address (INT) |
| $4E-$4F | 78-79 | 16-bit number, randomized with each key entry |
| | 214 | Poke 255 to auto-run program |
| | 212 | Error code flag in decimal |
| | 216 | High bit set if error detected: zero to clear |
| | 222 | Error code |
| | 224-226 | HIRES GR X&Y coordinates |
| $E4 | 228 | Color being used {0=0:42=1:85=2:127=3:128=4:170=5:213=6:255=7} |
| $E6 | 230 | HIRES PLOTTING PAGE (32=1/64=2/96=3) |
| $E7 | 231 | SCALE of shape |
| $EA | 234 | COLLISION COUNTER for shapes |
| | 241 | 256-SPEED |
| $F3 | 243 | FLASH MASK |
| $F9 | 249 | ROT |
* Page 03
|-------------+--------------------------------------|
| Address | Description |
|-------------+--------------------------------------|
| $0320/1 | Low-endian HIRES X coordinate: 0-279 |
| $0322 | HIRES Y coordinate: 0-159 |
| $0324/5 | Start address of SHAPE TABLE |
| $032C | COLOR for HIRES |
| $03F8 | CTRL-Y in Monitor with JSR here |
| $03FB | NMI |
| $03FE-$03FF | IRQ sent to this address |
| | |
* Basic locations
|---------+------+--------------------------------|
| Address | Dec | Description |
|---------+------+--------------------------------|
| | 2049 | Set to 1 for "list protection" |
* DOS routines
|---------+-------+-------------|
| Address | Dec | Description |
|---------+-------+-------------|
| | 42350 | Catalog |
* Monitor routines
|---------+-------+-------+------------------------------------------------------------|
| Address | Dec | Dec | Description |
|---------+-------+-------+------------------------------------------------------------|
| | $F3D4 | -3116 | HGR2 |
| | $F3DE | -3106 | HGR |
| | $F3F2 | -3086 | CLEAR HIRES SCREEN |
| | $F3F6 | -3082 | CLEAR HIRES SCREEN to HCOLOR |
| | $FB2F | -1233 | TEXT |
| | $FB40 | -1216 | GR |
| | $FB60 | -1184 | PRINT "APPLE ][" |
| | $FBE4 | -1052 | RING BELL |
| | $FBF4 | -1036 | MOVE CURSOR RIGHT |
| | $FC10 | -1008 | MOVE CURSOR LEFT |
| | $FC1A | -998 | MOVE CURSOR UP 1 LINE |
| | $FC58 | -936 | HOME |
| | $FD0C | -756 | WAIT FOR KEYPRESS |
| | $FD67 | -665 | GET a LINE of input with PROMPT, LINE FEED, and wait. |
| | $FD6A | -662 | GET a LINE of input with PROMPT, NO LINE FEED, and wait. |
| | $FD6F | -657 | GET a LINE of input with NO PROMPT or LINE FEED, and wait. |
| | $FE80 | -384 | INVERSE |
| | $FE84 | -380 | NORMAL |
| | $FECA | -310 | WRITE to tape |
| | $FEFD | -259 | READ from tape |
| | | | |
** References
- http://apple2history.org/history/ah03/
- http://www.textfiles.com/apple/peekpk.txt
- http://www.textfiles.com/apple/peekpoke.app
- http://www.textfiles.com/apple/peeks.pokes.2
- http://www.applefritter.com/node/24236
* Language card
Install in slot 0.
Replaces RAM chip in E3 with a cable to the card.
Contains "autostart ROM"
Adds Esc-ijkm movement
Ctrl-S stop/start program listings
** Autostart ROM details
$3F0/1 Break vector: $59,$FA
$3F2/3 Reset vector: $03, $E0 (for non-disk systems after power-up reset)
$3F4 Powered up mask: $45
** Memory details
|-------------+-------------------------|
| Address | Description |
|-------------+-------------------------|
| $D000-$DFFF | 4K bank-switched memory |
| $E000-$F7FF | RAM |
| $F800-$FFFF | Language card ROM |
|-------------+-------------------------|
* Control codes
Bit 2 is ignored.
Bit 3 chooses which RAM banks is switched in to $D000-$DFFF.
When RAM is deselected (and write-enabled), it can be written but not read.
When RAM is deselected, Language Card ROM is mapped to $F800-$FFFF.
Power-on RESET initializes ROM to read mode, and RAM to write mode,
and selects the second 4K bank for $D000-$DFFF.
|----------------+---------------+---------------------------------------------------|
| Second 4K Bank | First 4K Bank | Description |
|----------------+---------------+---------------------------------------------------|
| $C080 | $C088 | Select RAM read. Write-protect RAM |
| $C081 | $C089 | Deselect RAM reaad (enable ROM) |
| | | Two or more successive reads = write-enable RAM |
| $C082 | $C08A | Deselect RAM read (enable ROM). Write-protect RAM |
| $C083 | $C08B | Select RAM read. |
| | | Two or more successive reads = write-enable RAM |
|----------------+---------------+---------------------------------------------------|
* Revisions
** II
- Integer Basic
- Revision 0
- Only 4 hires colors: black, white, violet, green
- Revision 1
- 6 hires colors
- Color killer
- power-on-reset
- auto-start
- 24k memory map problem (?!)
- keyboard strobe flip flop connected to reset
** II+
- Applesoft
- Usually with 16k "Language Card"
** IIe
- 80-column card

File diff suppressed because it is too large Load Diff

Binary file not shown.

52
harness/harness.go Normal file
View File

@ -0,0 +1,52 @@
package main
import (
"fmt"
"io/ioutil"
"github.com/zellyn/go6502/cpu"
)
type K64 [65536]byte
func (m *K64) Read(address uint16) byte {
return m[address]
}
func (m *K64) Write(address uint16, value byte) {
m[address] = value
}
type CycleCount uint64
func (c *CycleCount) Tick() {
*c += 1
}
func main() {
fmt.Println("Hello, world.")
bytes, err := ioutil.ReadFile("6502_functional_test.bin")
if err != nil {
panic("Cannot read file")
}
var m K64
var cc CycleCount
OFFSET := 0xa
copy(m[OFFSET:len(bytes)+OFFSET], bytes)
c := cpu.NewCPU(&m, &cc)
c.Reset()
c.SetPC(0x1000)
for {
oldPC := c.PC()
err := c.Step()
if err != nil {
fmt.Println(err)
break
}
if c.PC() == oldPC {
fmt.Printf("Stuck at 0x%X: 0x%X\n", oldPC, m[oldPC])
break
}
}
fmt.Println("Goodbye, world.")
}