From cde673a55277423ca3fb93815f9728e2d6ed35e1 Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Mon, 21 Feb 2022 23:05:53 +0100 Subject: [PATCH] Test boot process for all models --- apple2Tester.go | 37 +++++++++++ apple2_test.go | 92 ++++++++++++++++++++++++++ apple2main.go | 144 ++++++++++++++++++++++------------------- screen/textToAnsi.go | 8 +-- screen/textToString.go | 28 ++++++++ screen/text_test.go | 2 +- 6 files changed, 238 insertions(+), 73 deletions(-) create mode 100644 apple2Tester.go create mode 100644 apple2_test.go create mode 100644 screen/textToString.go diff --git a/apple2Tester.go b/apple2Tester.go new file mode 100644 index 0000000..f92ceb9 --- /dev/null +++ b/apple2Tester.go @@ -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) +} diff --git a/apple2_test.go b/apple2_test.go new file mode 100644 index 0000000..a7d95e3 --- /dev/null +++ b/apple2_test.go @@ -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, "/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) + } +} diff --git a/apple2main.go b/apple2main.go index b508ba4..3d3e708 100644 --- a/apple2main.go +++ b/apple2main.go @@ -189,86 +189,24 @@ func MainApple() *Apple2 { a.addTracer(newTraceApplecorn(a, false)) } - var charGenMap charColumnMap - initialCharGenPage := 0 + initModel(a, *model, *romFile, *charRomFile) + a.cpu.SetTrace(*traceCPU) + + // Disable incompatible cards switch *model { case "2plus": - setApple2plus(a) - if *romFile == defaultInternal { - *romFile = "/Apple2_Plus.rom" - } - if *charRomFile == defaultInternal { - *charRomFile = "/Apple2rev7CharGen.rom" - } - charGenMap = charGenColumnsMap2Plus *vidHDCardSlot = -1 - case "2e": - setApple2e(a) - if *romFile == defaultInternal { - *romFile = "/Apple2e.rom" - } - if *charRomFile == defaultInternal { - *charRomFile = "/Apple IIe Video Unenhanced - 342-0133-A - 2732.bin" - } - a.isApple2e = true - charGenMap = charGenColumnsMap2e - + *videxCardSlot = -1 case "2enh": - setApple2eEnhanced(a) - if *romFile == defaultInternal { - *romFile = "/Apple2e_Enhanced.rom" - } - if *charRomFile == defaultInternal { - *charRomFile = "/Apple IIe Video Enhanced - 342-0265-A - 2732.bin" - } - a.isApple2e = true - charGenMap = charGenColumnsMap2e - + *videxCardSlot = -1 case "base64a": - setBase64a(a) - if *romFile == defaultInternal { - err := loadBase64aRom(a) - if err != nil { - panic(err) - } - *romFile = "" - } - if *charRomFile == defaultInternal { - *charRomFile = "/BASE64A_ROM7_CharGen.BIN" - initialCharGenPage = 1 - } - charGenMap = charGenColumnsMapBase64a *vidHDCardSlot = -1 *videxCardSlot = -1 // The videx firmware crashes the BASE64A, probably by use of ANN0 - default: 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 if *languageCardSlot >= 0 { a.AddLanguageCard(*languageCardSlot) @@ -362,3 +300,73 @@ func MainApple() *Apple2 { 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 = "/Apple2_Plus.rom" + } + if charRomFile == defaultInternal { + charRomFile = "/Apple2rev7CharGen.rom" + } + charGenMap = charGenColumnsMap2Plus + + case "2e": + setApple2e(a) + if romFile == defaultInternal { + romFile = "/Apple2e.rom" + } + if charRomFile == defaultInternal { + charRomFile = "/Apple IIe Video Unenhanced - 342-0133-A - 2732.bin" + } + charGenMap = charGenColumnsMap2e + + case "2enh": + setApple2eEnhanced(a) + if romFile == defaultInternal { + romFile = "/Apple2e_Enhanced.rom" + } + if charRomFile == defaultInternal { + charRomFile = "/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 = "/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 +} diff --git a/screen/textToAnsi.go b/screen/textToAnsi.go index d7541a9..397fbee 100644 --- a/screen/textToAnsi.go +++ b/screen/textToAnsi.go @@ -26,7 +26,7 @@ func RenderTextModeAnsi(vs VideoSource, is80Columns bool, isSecondPage bool, isA line := "" for c := 0; c < columns; c++ { char := text[l*columns+c] - line += textMemoryByteToString(char, isAltText, isApple2e) + line += textMemoryByteToString(char, isAltText, isApple2e, true) } 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 topBits := value >> 6 isInverse := topBits == 0 @@ -80,13 +80,13 @@ func textMemoryByteToString(value uint8, isAltCharSet bool, isApple2e bool) stri value = '_' } - if isFlash { + if ansi && isFlash { if value == ' ' { // Flashing space in Apple is the full box. It can't be done with ANSI codes 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)) } else { return string(value) diff --git a/screen/textToString.go b/screen/textToString.go new file mode 100644 index 0000000..d3e39d5 --- /dev/null +++ b/screen/textToString.go @@ -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 +} diff --git a/screen/text_test.go b/screen/text_test.go index 1fd2009..8ac4be7 100644 --- a/screen/text_test.go +++ b/screen/text_test.go @@ -26,7 +26,7 @@ func TestTextMemoryByteToString(t *testing.T) { } 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 { t.Errorf("For 0x%02x:%v, got %v, expected %v", arg, alt, s, expect) }