Test boot process for all models
This commit is contained in:
parent
9d6393f078
commit
cde673a552
|
@ -0,0 +1,37 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ivanizag/izapple2/screen"
|
||||||
|
)
|
||||||
|
|
||||||
|
type apple2Tester struct {
|
||||||
|
a *Apple2
|
||||||
|
terminateCondition func(a *Apple2) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeApple2Tester(model string) *apple2Tester {
|
||||||
|
a := newApple2()
|
||||||
|
a.setup(0, true) // Full speed
|
||||||
|
initModel(a, model, defaultInternal, defaultInternal)
|
||||||
|
|
||||||
|
a.AddLanguageCard(0)
|
||||||
|
|
||||||
|
var at apple2Tester
|
||||||
|
at.a = a
|
||||||
|
a.addTracer(&at)
|
||||||
|
return &at
|
||||||
|
}
|
||||||
|
|
||||||
|
func (at *apple2Tester) inspect() {
|
||||||
|
if at.terminateCondition(at.a) {
|
||||||
|
at.a.SendCommand(CommandKill)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (at *apple2Tester) run() {
|
||||||
|
at.a.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (at *apple2Tester) getText() string {
|
||||||
|
return screen.RenderTextModeString(at.a, false, false, false, at.a.isApple2e)
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPlusBoots(t *testing.T) {
|
||||||
|
at := makeApple2Tester("2plus")
|
||||||
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
|
return a.cpu.GetCycles() > 200_000
|
||||||
|
}
|
||||||
|
at.run()
|
||||||
|
|
||||||
|
text := at.getText()
|
||||||
|
if !strings.Contains(text, "APPLE ][") {
|
||||||
|
t.Errorf("Expected 'APPLE ][', got '%s'", text)
|
||||||
|
}
|
||||||
|
if !strings.Contains(text, "\n]") {
|
||||||
|
t.Errorf("Expected ] prompt, got '%s'", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test2EBoots(t *testing.T) {
|
||||||
|
at := makeApple2Tester("2e")
|
||||||
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
|
return a.cpu.GetCycles() > 200_000
|
||||||
|
}
|
||||||
|
at.run()
|
||||||
|
|
||||||
|
text := at.getText()
|
||||||
|
if !strings.Contains(text, "Apple ][") {
|
||||||
|
t.Errorf("Expected 'Apple ][', got '%s'", text)
|
||||||
|
}
|
||||||
|
if !strings.Contains(text, "\n]") {
|
||||||
|
t.Errorf("Expected ] prompt, got '%s'", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test2EnhancedBoots(t *testing.T) {
|
||||||
|
at := makeApple2Tester("2enh")
|
||||||
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
|
return a.cpu.GetCycles() > 200_000
|
||||||
|
}
|
||||||
|
at.run()
|
||||||
|
|
||||||
|
text := at.getText()
|
||||||
|
if !strings.Contains(text, "Apple //e") {
|
||||||
|
t.Errorf("Expected 'Apple //e', got '%s'", text)
|
||||||
|
}
|
||||||
|
if !strings.Contains(text, "\n]") {
|
||||||
|
t.Errorf("Expected ] prompt, got '%s'", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBase64Boots(t *testing.T) {
|
||||||
|
at := makeApple2Tester("base64a")
|
||||||
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
|
return a.cpu.GetCycles() > 1_000_000
|
||||||
|
}
|
||||||
|
at.run()
|
||||||
|
|
||||||
|
text := at.getText()
|
||||||
|
if !strings.Contains(text, "BASE 64A") {
|
||||||
|
t.Errorf("Expected 'BASE 64A', got '%s'", text)
|
||||||
|
}
|
||||||
|
if !strings.Contains(text, "\n]") {
|
||||||
|
t.Errorf("Expected ] prompt, got '%s'", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlusDOS33Boots(t *testing.T) {
|
||||||
|
at := makeApple2Tester("2plus")
|
||||||
|
|
||||||
|
err := at.a.AddDisk2(6, "<internal>/dos33.dsk", "")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
|
return a.cpu.GetCycles() > 100_000_000
|
||||||
|
}
|
||||||
|
at.run()
|
||||||
|
|
||||||
|
text := at.getText()
|
||||||
|
if !strings.Contains(text, "DOS VERSION 3.3") {
|
||||||
|
t.Errorf("Expected 'APPLE ][', got '%s'", text)
|
||||||
|
}
|
||||||
|
if !strings.Contains(text, "\n]") {
|
||||||
|
t.Errorf("Expected ] prompt, got '%s'", text)
|
||||||
|
}
|
||||||
|
}
|
144
apple2main.go
144
apple2main.go
|
@ -189,86 +189,24 @@ func MainApple() *Apple2 {
|
||||||
a.addTracer(newTraceApplecorn(a, false))
|
a.addTracer(newTraceApplecorn(a, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
var charGenMap charColumnMap
|
initModel(a, *model, *romFile, *charRomFile)
|
||||||
initialCharGenPage := 0
|
a.cpu.SetTrace(*traceCPU)
|
||||||
|
|
||||||
|
// Disable incompatible cards
|
||||||
switch *model {
|
switch *model {
|
||||||
case "2plus":
|
case "2plus":
|
||||||
setApple2plus(a)
|
|
||||||
if *romFile == defaultInternal {
|
|
||||||
*romFile = "<internal>/Apple2_Plus.rom"
|
|
||||||
}
|
|
||||||
if *charRomFile == defaultInternal {
|
|
||||||
*charRomFile = "<internal>/Apple2rev7CharGen.rom"
|
|
||||||
}
|
|
||||||
charGenMap = charGenColumnsMap2Plus
|
|
||||||
*vidHDCardSlot = -1
|
*vidHDCardSlot = -1
|
||||||
|
|
||||||
case "2e":
|
case "2e":
|
||||||
setApple2e(a)
|
*videxCardSlot = -1
|
||||||
if *romFile == defaultInternal {
|
|
||||||
*romFile = "<internal>/Apple2e.rom"
|
|
||||||
}
|
|
||||||
if *charRomFile == defaultInternal {
|
|
||||||
*charRomFile = "<internal>/Apple IIe Video Unenhanced - 342-0133-A - 2732.bin"
|
|
||||||
}
|
|
||||||
a.isApple2e = true
|
|
||||||
charGenMap = charGenColumnsMap2e
|
|
||||||
|
|
||||||
case "2enh":
|
case "2enh":
|
||||||
setApple2eEnhanced(a)
|
*videxCardSlot = -1
|
||||||
if *romFile == defaultInternal {
|
|
||||||
*romFile = "<internal>/Apple2e_Enhanced.rom"
|
|
||||||
}
|
|
||||||
if *charRomFile == defaultInternal {
|
|
||||||
*charRomFile = "<internal>/Apple IIe Video Enhanced - 342-0265-A - 2732.bin"
|
|
||||||
}
|
|
||||||
a.isApple2e = true
|
|
||||||
charGenMap = charGenColumnsMap2e
|
|
||||||
|
|
||||||
case "base64a":
|
case "base64a":
|
||||||
setBase64a(a)
|
|
||||||
if *romFile == defaultInternal {
|
|
||||||
err := loadBase64aRom(a)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*romFile = ""
|
|
||||||
}
|
|
||||||
if *charRomFile == defaultInternal {
|
|
||||||
*charRomFile = "<internal>/BASE64A_ROM7_CharGen.BIN"
|
|
||||||
initialCharGenPage = 1
|
|
||||||
}
|
|
||||||
charGenMap = charGenColumnsMapBase64a
|
|
||||||
*vidHDCardSlot = -1
|
*vidHDCardSlot = -1
|
||||||
*videxCardSlot = -1 // The videx firmware crashes the BASE64A, probably by use of ANN0
|
*videxCardSlot = -1 // The videx firmware crashes the BASE64A, probably by use of ANN0
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("Model not supported")
|
panic("Model not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.isApple2e {
|
|
||||||
// Videx not used on Apple IIe
|
|
||||||
*videxCardSlot = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
a.cpu.SetTrace(*traceCPU)
|
|
||||||
|
|
||||||
// Load ROM if not loaded already
|
|
||||||
if *romFile != "" {
|
|
||||||
err := a.LoadRom(*romFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load character generator if it loaded already
|
|
||||||
cg, err := newCharacterGenerator(*charRomFile, charGenMap, a.isApple2e)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
cg.setPage(initialCharGenPage)
|
|
||||||
a.cg = cg
|
|
||||||
|
|
||||||
// Externsion cards
|
// Externsion cards
|
||||||
if *languageCardSlot >= 0 {
|
if *languageCardSlot >= 0 {
|
||||||
a.AddLanguageCard(*languageCardSlot)
|
a.AddLanguageCard(*languageCardSlot)
|
||||||
|
@ -362,3 +300,73 @@ func MainApple() *Apple2 {
|
||||||
|
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initModel(a *Apple2, model string, romFile string, charRomFile string) {
|
||||||
|
var charGenMap charColumnMap
|
||||||
|
initialCharGenPage := 0
|
||||||
|
switch model {
|
||||||
|
case "2plus":
|
||||||
|
setApple2plus(a)
|
||||||
|
if romFile == defaultInternal {
|
||||||
|
romFile = "<internal>/Apple2_Plus.rom"
|
||||||
|
}
|
||||||
|
if charRomFile == defaultInternal {
|
||||||
|
charRomFile = "<internal>/Apple2rev7CharGen.rom"
|
||||||
|
}
|
||||||
|
charGenMap = charGenColumnsMap2Plus
|
||||||
|
|
||||||
|
case "2e":
|
||||||
|
setApple2e(a)
|
||||||
|
if romFile == defaultInternal {
|
||||||
|
romFile = "<internal>/Apple2e.rom"
|
||||||
|
}
|
||||||
|
if charRomFile == defaultInternal {
|
||||||
|
charRomFile = "<internal>/Apple IIe Video Unenhanced - 342-0133-A - 2732.bin"
|
||||||
|
}
|
||||||
|
charGenMap = charGenColumnsMap2e
|
||||||
|
|
||||||
|
case "2enh":
|
||||||
|
setApple2eEnhanced(a)
|
||||||
|
if romFile == defaultInternal {
|
||||||
|
romFile = "<internal>/Apple2e_Enhanced.rom"
|
||||||
|
}
|
||||||
|
if charRomFile == defaultInternal {
|
||||||
|
charRomFile = "<internal>/Apple IIe Video Enhanced - 342-0265-A - 2732.bin"
|
||||||
|
}
|
||||||
|
charGenMap = charGenColumnsMap2e
|
||||||
|
|
||||||
|
case "base64a":
|
||||||
|
setBase64a(a)
|
||||||
|
if romFile == defaultInternal {
|
||||||
|
err := loadBase64aRom(a)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
romFile = ""
|
||||||
|
}
|
||||||
|
if charRomFile == defaultInternal {
|
||||||
|
charRomFile = "<internal>/BASE64A_ROM7_CharGen.BIN"
|
||||||
|
initialCharGenPage = 1
|
||||||
|
}
|
||||||
|
charGenMap = charGenColumnsMapBase64a
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("Model not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load ROM if not loaded already
|
||||||
|
if romFile != "" {
|
||||||
|
err := a.LoadRom(romFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load character generator if it loaded already
|
||||||
|
cg, err := newCharacterGenerator(charRomFile, charGenMap, a.isApple2e)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
cg.setPage(initialCharGenPage)
|
||||||
|
a.cg = cg
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ func RenderTextModeAnsi(vs VideoSource, is80Columns bool, isSecondPage bool, isA
|
||||||
line := ""
|
line := ""
|
||||||
for c := 0; c < columns; c++ {
|
for c := 0; c < columns; c++ {
|
||||||
char := text[l*columns+c]
|
char := text[l*columns+c]
|
||||||
line += textMemoryByteToString(char, isAltText, isApple2e)
|
line += textMemoryByteToString(char, isAltText, isApple2e, true)
|
||||||
}
|
}
|
||||||
content += fmt.Sprintf("# %v #\n", line)
|
content += fmt.Sprintf("# %v #\n", line)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ $e0-$ff Low Nor Low Nor Low Nor Low Nor
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func textMemoryByteToString(value uint8, isAltCharSet bool, isApple2e bool) string {
|
func textMemoryByteToString(value uint8, isAltCharSet bool, isApple2e bool, ansi bool) string {
|
||||||
// Normal, inverse or flash
|
// Normal, inverse or flash
|
||||||
topBits := value >> 6
|
topBits := value >> 6
|
||||||
isInverse := topBits == 0
|
isInverse := topBits == 0
|
||||||
|
@ -80,13 +80,13 @@ func textMemoryByteToString(value uint8, isAltCharSet bool, isApple2e bool) stri
|
||||||
value = '_'
|
value = '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFlash {
|
if ansi && isFlash {
|
||||||
if value == ' ' {
|
if value == ' ' {
|
||||||
// Flashing space in Apple is the full box. It can't be done with ANSI codes
|
// Flashing space in Apple is the full box. It can't be done with ANSI codes
|
||||||
value = '_'
|
value = '_'
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("\033[5m%v\033[0m", string(value))
|
return fmt.Sprintf("\033[5m%v\033[0m", string(value))
|
||||||
} else if isInverse {
|
} else if ansi && isInverse {
|
||||||
return fmt.Sprintf("\033[7m%v\033[0m", string(value))
|
return fmt.Sprintf("\033[7m%v\033[0m", string(value))
|
||||||
} else {
|
} else {
|
||||||
return string(value)
|
return string(value)
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package screen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RenderTextModeString returns the text mode contents ignoring reverse and flash
|
||||||
|
func RenderTextModeString(vs VideoSource, is80Columns bool, isSecondPage bool, isAltText bool, isApple2e bool) string {
|
||||||
|
|
||||||
|
var text []uint8
|
||||||
|
if is80Columns {
|
||||||
|
text = getText80FromMemory(vs, isSecondPage)
|
||||||
|
} else {
|
||||||
|
text = getTextFromMemory(vs, isSecondPage, false)
|
||||||
|
}
|
||||||
|
columns := len(text) / textLines
|
||||||
|
|
||||||
|
content := ""
|
||||||
|
for l := 0; l < textLines; l++ {
|
||||||
|
line := ""
|
||||||
|
for c := 0; c < columns; c++ {
|
||||||
|
char := text[l*columns+c]
|
||||||
|
line += textMemoryByteToString(char, isAltText, isApple2e, false)
|
||||||
|
}
|
||||||
|
content += fmt.Sprintf("%v\n", line)
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ func TestTextMemoryByteToString(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func charExpectation(t *testing.T, arg uint8, alt bool, expect string) {
|
func charExpectation(t *testing.T, arg uint8, alt bool, expect string) {
|
||||||
s := textMemoryByteToString(arg, alt, alt)
|
s := textMemoryByteToString(arg, alt, alt, true)
|
||||||
if s != expect {
|
if s != expect {
|
||||||
t.Errorf("For 0x%02x:%v, got %v, expected %v", arg, alt, s, expect)
|
t.Errorf("For 0x%02x:%v, got %v, expected %v", arg, alt, s, expect)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue