mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-06-10 06:29:30 +00:00
Fixed text mode. Some softswitches implemented. Boots Apple 2+ to prompt.
This commit is contained in:
parent
1a6e9e006a
commit
9f783e53d8
|
@ -6,6 +6,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFunctional(t *testing.T) {
|
func TestFunctional(t *testing.T) {
|
||||||
|
|
||||||
|
t.SkipNow()
|
||||||
|
|
||||||
var s state
|
var s state
|
||||||
// Test suite from https://github.com/Klaus2m5/6502_65C02_functional_tests
|
// Test suite from https://github.com/Klaus2m5/6502_65C02_functional_tests
|
||||||
s.memory.loadBinary("tests/6502_functional_test.bin")
|
s.memory.loadBinary("tests/6502_functional_test.bin")
|
||||||
|
|
|
@ -544,7 +544,8 @@ func executeInstruction(s *state, log bool) {
|
||||||
}
|
}
|
||||||
opcode.action(s, line, opcode)
|
opcode.action(s, line, opcode)
|
||||||
if log {
|
if log {
|
||||||
fmt.Printf("%v, %x\n", s.registers, line)
|
value, address, _ := resolve(s, line, opcode)
|
||||||
|
fmt.Printf("%v, [%04x:%02x], [%02x]\n", s.registers, address, value, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,7 +573,7 @@ func lineString(s *state, line []uint8, opcode opcode) string {
|
||||||
case modeAbsoluteX:
|
case modeAbsoluteX:
|
||||||
t += fmt.Sprintf(" $%04x,X", getWordInLine(line))
|
t += fmt.Sprintf(" $%04x,X", getWordInLine(line))
|
||||||
case modeAbsoluteY:
|
case modeAbsoluteY:
|
||||||
t += fmt.Sprintf(" $%04x,X", getWordInLine(line))
|
t += fmt.Sprintf(" $%04x,Y", getWordInLine(line))
|
||||||
case modeIndirect:
|
case modeIndirect:
|
||||||
t += fmt.Sprintf(" ($%04x)", getWordInLine(line))
|
t += fmt.Sprintf(" ($%04x)", getWordInLine(line))
|
||||||
case modeIndexedIndirectX:
|
case modeIndexedIndirectX:
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
func TestLoad(t *testing.T) {
|
func TestLoad(t *testing.T) {
|
||||||
var s state
|
var s state
|
||||||
s.memory.initWithRam()
|
s.memory.initWithRAM()
|
||||||
|
|
||||||
executeLine(&s, []uint8{0xA9, 0x42})
|
executeLine(&s, []uint8{0xA9, 0x42})
|
||||||
if s.registers.getA() != 0x42 {
|
if s.registers.getA() != 0x42 {
|
||||||
|
@ -89,7 +89,7 @@ func TestLoad(t *testing.T) {
|
||||||
|
|
||||||
func TestStore(t *testing.T) {
|
func TestStore(t *testing.T) {
|
||||||
var s state
|
var s state
|
||||||
s.memory.initWithRam()
|
s.memory.initWithRAM()
|
||||||
s.registers.setA(0x10)
|
s.registers.setA(0x10)
|
||||||
s.registers.setX(0x40)
|
s.registers.setX(0x40)
|
||||||
s.registers.setY(0x80)
|
s.registers.setY(0x80)
|
||||||
|
@ -401,7 +401,7 @@ func TestCompare(t *testing.T) {
|
||||||
}
|
}
|
||||||
func TestBit(t *testing.T) {
|
func TestBit(t *testing.T) {
|
||||||
var s state
|
var s state
|
||||||
s.memory.initWithRam()
|
s.memory.initWithRAM()
|
||||||
|
|
||||||
s.registers.setA(0x0F)
|
s.registers.setA(0x0F)
|
||||||
s.memory.poke(0x0040, 0xF0)
|
s.memory.poke(0x0040, 0xF0)
|
||||||
|
@ -450,7 +450,7 @@ func TestBranch(t *testing.T) {
|
||||||
|
|
||||||
func TestStack(t *testing.T) {
|
func TestStack(t *testing.T) {
|
||||||
var s state
|
var s state
|
||||||
s.memory.initWithRam()
|
s.memory.initWithRAM()
|
||||||
|
|
||||||
s.registers.setSP(0xF0)
|
s.registers.setSP(0xF0)
|
||||||
s.registers.setA(0xA0)
|
s.registers.setA(0xA0)
|
||||||
|
|
96
ioC0Page.go
Normal file
96
ioC0Page.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type ioC0Page struct {
|
||||||
|
ioFlags uint64
|
||||||
|
data [1]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://www.kreativekorp.com/miscpages/a2info/iomemory.shtml
|
||||||
|
// See https://stason.org/TULARC/pc/apple2/programmer/004-I-d-like-to-do-some-serious-Apple-II-programming-Whe.html
|
||||||
|
|
||||||
|
const (
|
||||||
|
ioFlagNone uint8 = 0
|
||||||
|
ioFlagGraphics uint8 = 3
|
||||||
|
ioFlagMixed uint8 = 8
|
||||||
|
ioFlagSecondPage uint8 = 1
|
||||||
|
ioFlagHiRes uint8 = 2
|
||||||
|
ioFlagAnnunciator0 uint8 = 4
|
||||||
|
ioFlagAnnunciator1 uint8 = 5
|
||||||
|
ioFlagAnnunciator2 uint8 = 6
|
||||||
|
ioFlagAnnunciator3 uint8 = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ioDataKeyboard uint8 = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
type softSwitch struct {
|
||||||
|
ioFlag uint8
|
||||||
|
value bool
|
||||||
|
onWriteOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var softSwitches = [256]softSwitch{
|
||||||
|
0x50: softSwitch{ioFlagGraphics, false, false},
|
||||||
|
0x51: softSwitch{ioFlagGraphics, true, false},
|
||||||
|
0x52: softSwitch{ioFlagMixed, false, false},
|
||||||
|
0x53: softSwitch{ioFlagMixed, true, false},
|
||||||
|
0x54: softSwitch{ioFlagSecondPage, false, false},
|
||||||
|
0x55: softSwitch{ioFlagSecondPage, true, false},
|
||||||
|
0x56: softSwitch{ioFlagHiRes, false, false},
|
||||||
|
0x57: softSwitch{ioFlagHiRes, true, false},
|
||||||
|
0x58: softSwitch{ioFlagAnnunciator0, false, false},
|
||||||
|
0x59: softSwitch{ioFlagAnnunciator0, true, false},
|
||||||
|
0x5a: softSwitch{ioFlagAnnunciator1, false, false},
|
||||||
|
0x5b: softSwitch{ioFlagAnnunciator1, true, false},
|
||||||
|
0x5c: softSwitch{ioFlagAnnunciator2, false, false},
|
||||||
|
0x5d: softSwitch{ioFlagAnnunciator2, true, false},
|
||||||
|
0x5e: softSwitch{ioFlagAnnunciator3, false, false},
|
||||||
|
0x5f: softSwitch{ioFlagAnnunciator3, true, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ioC0Page) peek(address uint8) uint8 {
|
||||||
|
//fmt.Printf("Peek on $C0%02x ", address)
|
||||||
|
return p.access(address, false, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ioC0Page) poke(address uint8, value uint8) {
|
||||||
|
//fmt.Printf("Poke on $C0%02x with %02x ", address, value)
|
||||||
|
p.access(address, true, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ioC0Page) getData() *[256]uint8 {
|
||||||
|
var blankPage [256]uint8
|
||||||
|
return &blankPage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ioC0Page) access(address uint8, isWrite bool, value uint8) uint8 {
|
||||||
|
|
||||||
|
ss := softSwitches[address]
|
||||||
|
if ss.ioFlag != ioFlagNone {
|
||||||
|
if !isWrite || !!ss.onWriteOnly {
|
||||||
|
if ss.value {
|
||||||
|
p.ioFlags |= 1 << ss.ioFlag
|
||||||
|
} else {
|
||||||
|
p.ioFlags &^= 1 << ss.ioFlag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch address {
|
||||||
|
case 0x00: // keyboard
|
||||||
|
// TODO: keyboard suppport
|
||||||
|
return 0 //128 + 'A'
|
||||||
|
case 0x10: // strobe
|
||||||
|
result := p.data[ioDataKeyboard]
|
||||||
|
p.data[ioDataKeyboard] &^= 1 << 7
|
||||||
|
return result
|
||||||
|
case 0x30: // spkr
|
||||||
|
// TODO: Support sound
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown softswitch 0xC0%02x", address))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
17
main.go
17
main.go
|
@ -3,12 +3,25 @@ package main
|
||||||
func main() {
|
func main() {
|
||||||
var s state
|
var s state
|
||||||
var t textPages
|
var t textPages
|
||||||
|
var io ioC0Page
|
||||||
|
|
||||||
|
/*
|
||||||
|
for c := uint8(0); c < 40; c++ {
|
||||||
|
for l := uint8(0); l < 24; l++ {
|
||||||
|
t.write(c, l, '0'+(c+l)%10)
|
||||||
|
t.dump()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//s.memory.initWithRomAndText("../roms/APPLE2.ROM", &t)
|
||||||
|
s.memory.initWithRomAndText("../roms/apple.rom", &t)
|
||||||
|
//s.memory.initWithRomAndText("../roms/apple2o.rom", &t)
|
||||||
|
s.memory.data[0xc0] = &io
|
||||||
|
|
||||||
s.memory.initWithRomAndText("../roms/APPLE2.ROM", &t)
|
|
||||||
startAddress := s.memory.getWord(0xfffc)
|
startAddress := s.memory.getWord(0xfffc)
|
||||||
s.registers.setPC(startAddress)
|
s.registers.setPC(startAddress)
|
||||||
for true {
|
for true {
|
||||||
log := true
|
log := false
|
||||||
pc := s.registers.getPC()
|
pc := s.registers.getPC()
|
||||||
executeInstruction(&s, log)
|
executeInstruction(&s, log)
|
||||||
if pc == s.registers.getPC() {
|
if pc == s.registers.getPC() {
|
||||||
|
|
14
memory.go
14
memory.go
|
@ -69,7 +69,7 @@ func (m *memory) getZeroPageWord(address uint8) uint16 {
|
||||||
return uint16(m.peek(uint16(address))) + 0x100*uint16(m.peek(uint16(address+1)))
|
return uint16(m.peek(uint16(address))) + 0x100*uint16(m.peek(uint16(address+1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) initWithRam() {
|
func (m *memory) initWithRAM() {
|
||||||
var ramPages [256]ramPage
|
var ramPages [256]ramPage
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
m.data[i] = &ramPages[i]
|
m.data[i] = &ramPages[i]
|
||||||
|
@ -99,17 +99,15 @@ func (m *memory) initWithRomAndText(filename string, textPages *textPages) {
|
||||||
}
|
}
|
||||||
|
|
||||||
size := stats.Size()
|
size := stats.Size()
|
||||||
if size != 20480 {
|
|
||||||
panic("Invalid ROM file size. It must be 20480 bytes")
|
|
||||||
}
|
|
||||||
bytes := make([]byte, size)
|
bytes := make([]byte, size)
|
||||||
|
|
||||||
buf := bufio.NewReader(f)
|
buf := bufio.NewReader(f)
|
||||||
buf.Read(bytes)
|
buf.Read(bytes)
|
||||||
|
|
||||||
m.initWithRam()
|
m.initWithRAM()
|
||||||
|
romStart := uint16(0xFFFF - size + 1)
|
||||||
for i, v := range bytes {
|
for i, v := range bytes {
|
||||||
m.poke(uint16(i)+0xB000, uint8(v))
|
m.poke(uint16(i)+romStart, uint8(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
var i uint8
|
var i uint8
|
||||||
|
@ -118,7 +116,7 @@ func (m *memory) initWithRomAndText(filename string, textPages *textPages) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := 0; j < 4; j++ {
|
for j := 0; j < 4; j++ {
|
||||||
m.data[4+i] = &textPages.pages[i]
|
m.data[4+j] = &(textPages.pages[j])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +139,7 @@ func (m *memory) loadBinary(filename string) {
|
||||||
buf := bufio.NewReader(f)
|
buf := bufio.NewReader(f)
|
||||||
buf.Read(bytes)
|
buf.Read(bytes)
|
||||||
|
|
||||||
m.initWithRam()
|
m.initWithRAM()
|
||||||
for i, v := range bytes {
|
for i, v := range bytes {
|
||||||
m.poke(uint16(i), uint8(v))
|
m.poke(uint16(i), uint8(v))
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (p *textPage) getData() *[256]uint8 {
|
||||||
func textMemoryByteToString(value uint8) string {
|
func textMemoryByteToString(value uint8) string {
|
||||||
value = value & 0x7F
|
value = value & 0x7F
|
||||||
if value < ' ' {
|
if value < ' ' {
|
||||||
return " "
|
return "@"
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(value)
|
return string(value)
|
||||||
|
@ -51,11 +51,11 @@ func (tp *textPages) dump() {
|
||||||
for _, p := range tp.pages {
|
for _, p := range tp.pages {
|
||||||
// The two half pages
|
// The two half pages
|
||||||
for _, h = range []uint8{0, 128} {
|
for _, h = range []uint8{0, 128} {
|
||||||
line := "|"
|
line := ""
|
||||||
for j = i + h; j < i+h+40; j++ {
|
for j = i + h; j < i+h+40; j++ {
|
||||||
line += textMemoryByteToString(p.peek(j))
|
line += textMemoryByteToString(p.peek(j))
|
||||||
}
|
}
|
||||||
fmt.Println(line + "|")
|
fmt.Printf("| %v |\n", line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ func (tp *textPages) dumpIfDirty() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *textPages) charAddress(column uint8, line uint8) (page uint8, address uint8) {
|
func (tp *textPages) charAddress(column uint8, line uint8) (page uint8, address uint8) {
|
||||||
page = (line / 3) % 4
|
page = (line % 8) / 2
|
||||||
address = column + (line/8)*40 + (line%2)*128
|
address = column + (line/8)*40 + (line%2)*128
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
23
textPages_test.go
Normal file
23
textPages_test.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCharAddress(t *testing.T) {
|
||||||
|
var tp textPages
|
||||||
|
|
||||||
|
mappings := [][]uint8{
|
||||||
|
// column, line, page, address
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{0, 1, 0, 0x80},
|
||||||
|
{0, 2, 1, 0x00},
|
||||||
|
{0, 23, 3, 0xD0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range mappings {
|
||||||
|
page, address := tp.charAddress(v[0], v[1])
|
||||||
|
if page != v[2] || address != v[3] {
|
||||||
|
t.Errorf("Error on charAddress for (%v, %v) (%v:%02x) <> (%v:%02x)",
|
||||||
|
v[0], v[1], v[2], v[3], page, address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user