Beginnings of various virtual machine tests

* Tests run the virtual 65c02 machine through the debugger interface
    * The various display mode tests verify against the SHA1 of the framebuffer
    * Includes some Applesoft BASIC script tests (also loaded on disks/testvm1.dsk.gz)
This commit is contained in:
Aaron Culliney 2014-03-30 14:08:13 -07:00
parent 3005fcaa6e
commit c1e2e99c26
6 changed files with 637 additions and 2 deletions

View File

@ -83,14 +83,20 @@ LOG_DRIVER = testcpu ## hack TODO/FIXME ... should be wrapper shell script acce
A2_TEST_SOURCES = $(apple2ix_SOURCES)
A2_TEST_CFLAGS = -DTESTING=1 -Isrc/greatest
TESTS = testcpu
check_PROGRAMS = testcpu
TESTS = testcpu testvm
check_PROGRAMS = testcpu testvm
testcpu_SOURCES = src/test/testcpu.c $(A2_TEST_SOURCES) $(EXTRA_apple2ix_SOURCES)
testcpu_CFLAGS = $(apple2ix_CFLAGS) $(A2_TEST_CFLAGS)
testcpu_LDFLAGS = $(apple2ix_LDFLAGS)
testcpu_DEPENDENCIES = $(apple2ix_DEPENDENCIES)
testvm_SOURCES = src/test/testvm.c $(A2_TEST_SOURCES) $(VM_SRC) $(META_SRC)
# HACK FIXME TODO NOTE: why don't these CFLAGS here pass down to the .S and .c files in the subdirectories?
testvm_CFLAGS = $(apple2ix_CFLAGS) $(A2_TEST_CFLAGS) -UAUDIO_ENABLED -UVIDEO_X11 -UINTERFACE_CLASSIC
testvm_LDFLAGS = $(apple2ix_LDFLAGS)
testvm_DEPENDENCIES = @VM_O@ @META_O@
###############################################################################
# Misc

15
src/test/testhires.bas Normal file
View File

@ -0,0 +1,15 @@
10 REM
20 REM SINEWAVE HIRES TEST
30 REM
35 REM FOR EMULATOR : DEADC0DE.ORG/apple2
40 HGR
50 IF PEEK(7986)<>127 THEN POKE -16302,0 : REM SET NOMIXED
60 FOR X = 0 TO 279
70 HCOLOR = 3
80 Y = 80 + SIN(15*(X-K)/279) * 40
90 HPLOT X,Y
100 NEXT X
110 POKE 7987,255

67
src/test/testhires80.bas Normal file
View File

@ -0,0 +1,67 @@
10 REM
20 REM HIRES80 SINEWAVES
30 REM
40 REM BY JIM SATHER --- 6/21/84
50 REM
70 REM
80 REM FOR EMULATOR : DEADC0DE.ORG/apple2
90 REM
110 REM
120 REM INITIALIZE
130 REM
140 STR80 = -16383 : COL80 = -16371 : NOMIX = -16302 : MBD = -16300
150 AUX = -16299 : FRCTXT = -16290 : HCLEER = 768
170 HGR : POKE STR80,0 : POKE NOMIX,0 : POKE COL80,0
180 POKE FRCTXT,0 : HCOLOR=3
190 REM MOVE "LDA #$20, JMP $F3EA" TO HCLEER
200 POKE HCLEER,169 : POKE HCLEER+1,32 : POKE HCLEER+2,76
210 POKE HCLEER+3,234 : POKE HCLEER+4,243
220 POKE AUX,0 : CALL HCLEER : REM CLEAR AUX
230 HPLOT 140,0 TO 140,26 : HPLOT 140,167 TO 140,191 : REM DRAW VERT LINE
240 POKE MBD,0 : HPLOT 139,0 TO 139,26 : HPLOT 139,167 TO 139,191
260 REM
270 REM GENERATE 140 X 192 SINEWAVES
280 REM
290 FOR C = 1 TO 15 : CLR = C : GOSUB 600 : REM FOR COLOR = 1 TO 15; GET BINARY
300 FOR H0 = 0 TO 276 STEP 4 : REM LEFT SCREEN; 1 OF 4 POINTS
310 FOR HPART = 0 TO 2 : REM PLOT V FOR H, H+1.3, H+2.7 TO SMOOTH OUT
320 V=24.5 + C*9 - 32 * SIN((H0 + 1.333333 * HPART) * .0224399)
330 IF C(1) THEN H = H0 : GOSUB 500 : REM BLUE DOT
340 IF C(2) THEN H = H0+1 : GOSUB 500 : REM BLUE-GREEN DOT
350 IF C(3) THEN H = H0+2 : GOSUB 500 : REM BROWN DOT
360 IF C(0) THEN H = H0+3 : GOSUB 500 : REM MAGENTA DOT
370 NEXT HPART : NEXT H0
380 REM
390 REM GENERATE 560X192 SINEWAVES
400 REM
410 REM SIN(2*PI*H/280) ; RIGHT SIDE
420 FOR H0 = 280 TO 556 : V = 24.5 + C*9 - 32*SIN(H0 * .0224399)
430 PRT4% = (H0/4 - INT(H0/4)) * 4 + .5 : REM PRT4% = H0 MOD 4
440 IF PRT4%=0 AND C(1) OR PRT4%=1 AND C(2) OR PRT4%=2 AND C(3) OR PRT4%=3 AND C(0) THEN H = H0 : GOSUB 500 : REM PLOT POSITION+0
450 IF PRT4%=0 AND C(2) OR PRT4%=1 AND C(3) OR PRT4%=2 AND C(0) OR PRT4%=3 AND C(1) THEN H = H0+1 : GOSUB 500 : REM PLOT POSITION+0
460 IF PRT4%=0 AND C(3) OR PRT4%=1 AND C(0) OR PRT4%=2 AND C(1) OR PRT4%=3 AND C(2) THEN H = H0+2 : GOSUB 500 : REM PLOT POSITION+0
470 IF PRT4%=0 AND C(0) OR PRT4%=1 AND C(1) OR PRT4%=2 AND C(2) OR PRT4%=3 AND C(3) THEN H = H0+3 : GOSUB 500 : REM PLOT POSITION+0
480 NEXT H0 : NEXT C : END
500 REM
510 REM PLOT H,V (H=0-559, V=0-191)
520 REM
530 MOD14 = INT(H)/14 : MOD14% = MOD14
540 PART14% = 14 * (MOD14 - MOD14%) + .5
550 POKE AUX,0 : IF PART14% > 6 THEN POKE MBD,0 : PART14% = PART14% - 7
560 HPLOT MOD14% * 7 + PART14%,V
570 RETURN
590 REM
600 REM CONVERT CLR FROM DECIMAL TO HEX
610 REM
620 CLR% = CLR : FOR A = 0 TO 3
630 C(A) = 1 : CLR = CLR% / 2 : CLR% = CLR% /2
640 IF CLR=CLR% THEN C(A) = 0
650 NEXT : RETURN

22
src/test/testhires_2.bas Normal file
View File

@ -0,0 +1,22 @@
10 REM
20 REM HGR MOIRE
30 REM
35 REM FOR EMULATOR : DEADC0DE.ORG/apple2
40 HGR
45 IF PEEK(7986)<>127 THEN POKE -16302,0 : REM SET NOMIXED
50 FOR Y = 0 TO 190 STEP 2
60 HCOLOR = 4 : REM BLACK
70 HPLOT 0,191-Y TO 279,Y
80 HCOLOR = 7 : REM WHITE
90 HPLOT 0,190-Y TO 279,Y+1
100 NEXT Y
110 FOR X = 0 TO 278 STEP 3
120 HCOLOR = 4
130 HPLOT 279-X,0 TO X,191
140 HCOLOR = 7
150 HPLOT 278-X,0 TO X+1,191
160 NEXT X
170 POKE 7987,255

41
src/test/testlores80.bas Normal file
View File

@ -0,0 +1,41 @@
10 REM
20 REM LORES80 CONCENTRIC CIRCLES
30 REM
40 REM BY JIM SATHER --- 6/21/84
50 REM
70 REM
80 REM FOR EMULATOR : DEADC0DE.ORG/apple2
90 REM
110 REM
120 REM INITIALIZE
130 REM
140 STR80 = -16383 : COL80 = -16371 : NOMIX = -16302 : MBD = -16300
150 AUX = -16299 : FRCTXT = -16290 : LCLEER = -1998 : CLR = 48
170 GR : POKE STR80,0 : POKE NOMIX,0 : POKE COL80,0 : POKE FRCTXT,0
180 POKE AUX,0 : CALL LCLEER : POKE MBD,0 : CALL LCLEER : REM CLEAR SCREEN
200 REM
210 REM COLOR / CIRCLE COMPUTATIONS
220 REM
230 PTS = 208 : REM NUMBER OF ANGLES SAMPLED
240 FOR ANGLE = 0 TO PTS-1
250 A1 = 6.2831852 * ANGLE / PTS : SI = SIN(A1) : C0 = COS(A1)
260 FOR C = 0 TO 7 : COLOR = C : IF ANGLE > PTS/2 THEN COLOR = C+8
270 IF C = 0 THEN COLOR = 15
280 R = 24 - C*3 : V = 24 - SI * R : H = 40 + C0 * R * 1.36 : GOSUB 330
290 NEXT C : NEXT ANGLE
300 GOTO 300
320 REM
330 REM PLOT H,V (H = 0-79, V = 0-47)
340 REM
350 H2 = INT(H) / 2
360 IF H2 - INT(H2) THEN POKE MBD,0 : PLOT H2,V : GOTO 400
370 POKE AUX,0 : CSAV = PEEK(CLR) : C2 = CSAV / 2 : C2% = C2
380 IF C2 - C2% THEN C2% = C2% + 128
390 POKE CLR,C2% : PLOT H2,V : POKE CLR,CSAV
400 RETURN

484
src/test/testvm.c Normal file
View File

@ -0,0 +1,484 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#include "greatest.h"
#include "testcommon.h"
#ifdef HAVE_OPENSSL
#include <openssl/sha.h>
#else
#error "these tests require OpenSSL libraries"
#endif
#define TEST_FINISHED 0xff
#define MIXSWITCH_ADDR 0x1f32 // PEEK(7986)
#define WATCHPOINT_ADDR 0x1f33 // PEEK(7987)
static char *input_str = NULL; // ASCII
static unsigned int input_length = 0;
static unsigned int input_counter = 0;
static bool test_do_reboot = true;
extern unsigned char joy_button0;
static void testvm_setup(void *arg) {
apple_ii_64k[0][MIXSWITCH_ADDR] = 0x00;
apple_ii_64k[0][WATCHPOINT_ADDR] = 0x00;
input_counter = 0;
input_length = 0;
joy_button0 = 0xff; // OpenApple
if (test_do_reboot) {
cpu65_interrupt(ResetSig);
}
}
static void testvm_teardown(void *arg) {
if (input_str) {
free(input_str);
}
input_str = NULL;
}
static void testvm_breakpoint(void *arg) {
fprintf(GREATEST_STDOUT, "set breakpoint on testvm_breakpoint to check for problems...\n");
}
static void sha1_to_str(const uint8_t * const md, char *buf) {
int i=0;
for (int j=0; j<SHA_DIGEST_LENGTH; j++, i+=2) {
sprintf(buf+i, "%02X", md[j]);
}
sprintf(buf+i, "%c", '\0');
}
// ----------------------------------------------------------------------------
// test video functions and stubs
void video_sync(int ignored) {
if (input_counter >= input_length) {
return;
}
uint8_t ch = (uint8_t)input_str[input_counter];
if (ch == '\n') {
fprintf(stderr, "converting '\\n' to '\\r' in test input string...");
ch = '\r';
}
if ( (apple_ii_64k[0][0xC000] & 0x80) || (apple_ii_64k[1][0xC000] & 0x80) ) {
// last character typed not processed by emulator...
return;
}
apple_ii_64k[0][0xC000] = ch | 0x80;
apple_ii_64k[1][0xC000] = ch | 0x80;
++input_counter;
}
// ----------------------------------------------------------------------------
// Stub functions because I've reached diminishing returns with the build system ...
//
// NOTE: You'd think the commandline CFLAGS set specifically for this test program would pass down to the sources in
// subdirectories, but it apparently isn't. GNU buildsystem bug? Also see HACK FIXME TODO NOTE in Makefile.am
//
uint8_t c_MB_Read(uint16_t addr) {
return 0x0;
}
void c_MB_Write(uint16_t addr, uint8_t byte) {
}
uint8_t c_PhasorIO(uint16_t addr) {
return 0x0;
}
void SpkrToggle() {
}
void c_interface_print(int x, int y, const int cs, const char *s) {
}
// ----------------------------------------------------------------------------
// VM TESTS ...
#define DO_SHA(SHA_STR) \
uint8_t md[SHA_DIGEST_LENGTH]; \
char mdstr[(SHA_DIGEST_LENGTH*2)+1]; \
const uint8_t * const fb = video_current_framebuffer(); \
SHA1(fb, SCANWIDTH*SCANHEIGHT, md); \
sha1_to_str(md, mdstr);
#define BOOT_TO_DOS() \
if (test_do_reboot) { \
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED); \
c_debugger_go(); \
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) { \
DO_SHA(); \
if (strcmp(mdstr, "F8D6C781E0BB7B3DDBECD69B25E429D845506594") != 0) { \
FAILm("expected boot framebuffer not found"); \
} \
} else { \
FAILm("boot watchpoint not triggered"); \
} \
apple_ii_64k[0][WATCHPOINT_ADDR] = 0x00; \
}
TEST test_boot_disk() {
char *disk = "./disks/testvm1.dsk.gz";
if (c_new_diskette_6(0, disk, 0)) {
FAILm("Cannot insert test diskette!");
}
BOOT_TO_DOS();
PASS();
}
// ----------------------------------------------------------------------------
// TEXT
TEST test_40col_normal() {
BOOT_TO_DOS();
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("RUN TESTNORMALTEXT\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "51E5960115380C64351ED00A2ACAB0EB67970249") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_80col_normal() {
BOOT_TO_DOS();
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("PR#3\rRUN TESTNORMALTEXT\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "ED9CE59F41A51A5ABB1617383A411455677A78E3") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_40col_inverse() {
BOOT_TO_DOS();
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("RUN TESTINVERSETEXT\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "20957B960C3C0DE0ABEE0058A08C0DDA24AB31D8") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_80col_inverse() {
BOOT_TO_DOS();
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("PR#3\rRUN TESTINVERSETEXT\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "037798F4BCF740D0A7CBF7CDF5FC5D1B0B3C77A2") == 0) {
PASS();
}
}
FAILm("test fail");
}
// ----------------------------------------------------------------------------
// HIRES
//
// NOTE : These tests assume standard color mode (not b/w or interpolated)
//
TEST test_hires_with_80col() {
BOOT_TO_DOS();
SKIPm("hires with 80col appears to be b0rken");
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("PR#3\rRUN TESTHIRES_2\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "8EF89A5E0501191847E9907416309B33D4B48713") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_hires_with_40col() {
BOOT_TO_DOS();
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("RUN TESTHIRES_2\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "1A5DD96B7E3538C2C3625A37653E013E3998F825") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_hires_40colmix_normal() {
BOOT_TO_DOS();
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("POKE 7986,127\rRUN TESTHIRES\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "37F41F74EB23F8812498F732E6DA34A0EBC4D68A") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_hires_40colmix_inverse() {
BOOT_TO_DOS();
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("INVERSE\rPOKE 7986,127\rRUN TESTHIRES\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "253D1823F5DAC0300B46B3D49C04CD59CC70076F") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_hires_80colmix_normal() {
BOOT_TO_DOS();
SKIPm("hires with 80col appears to be b0rken");
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("PR#3\rLOAD TESTHIRES\rLIST\rLIST\rPOKE 7986,127\rRUN\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "032BD68899749265EB2934A76A35D7068642824B") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_hires_80colmix_inverse() {
BOOT_TO_DOS();
SKIPm("hires with 80col appears to be b0rken");
ASSERT(apple_ii_64k[0][WATCHPOINT_ADDR] != TEST_FINISHED);
input_str = strdup("PR#3\rLOAD TESTHIRES\rLIST\rLIST\rPOKE 7986,127\rINVERSE\rRUN\r");
input_length = strlen(input_str);
c_debugger_go();
if (apple_ii_64k[0][WATCHPOINT_ADDR] == TEST_FINISHED) {
DO_SHA();
if (strcmp(mdstr, "253D1823F5DAC0300B46B3D49C04CD59CC70076F") == 0) {
PASS();
}
}
FAILm("test fail");
}
TEST test_80col_lores() {
BOOT_TO_DOS();
// this graphic looks wrong compared to the manual ...
SKIPm("mode needs to be properly implemented");
}
TEST test_80col_hires() {
BOOT_TO_DOS();
// double-hires graphics are in-flux ...
SKIPm("mode needs to be properly implemented");
}
// ----------------------------------------------------------------------------
// Test Suite
extern void cpu_thread(void *dummyptr);
GREATEST_SUITE(test_suite_vm) {
srandom(time(NULL));
GREATEST_SET_SETUP_CB(testvm_setup, NULL);
GREATEST_SET_TEARDOWN_CB(testvm_teardown, NULL);
GREATEST_SET_BREAKPOINT_CB(testvm_breakpoint, NULL);
do_logging = false;// silence regular emulator logging
setenv("APPLE2IXCFG", "nosuchconfigfile", 1);
load_settings();
c_initialize_firsttime();
// kludgey set max CPU speed...
cpu_scale_factor = CPU_SCALE_FASTEST;
cpu_altscale_factor = CPU_SCALE_FASTEST;
g_bFullSpeed = true;
caps_lock = true;
// spin off cpu thread
pthread_create(&cpu_thread_id, NULL, (void *) &cpu_thread, (void *)NULL);
pthread_mutex_lock(&interface_mutex);
c_debugger_set_watchpoint(WATCHPOINT_ADDR);
c_debugger_set_timeout(5);
// TESTS --------------------------
RUN_TESTp(test_boot_disk);
// text modes
RUN_TESTp(test_40col_normal);
test_do_reboot = false;
RUN_TESTp(test_40col_normal);
test_do_reboot = true;
RUN_TESTp(test_40col_normal);
test_do_reboot = false;
RUN_TESTp(test_40col_normal);
test_do_reboot = true;
RUN_TESTp(test_80col_normal);
test_do_reboot = false;
RUN_TESTp(test_80col_normal);
test_do_reboot = true;
RUN_TESTp(test_40col_inverse);
test_do_reboot = false;
RUN_TESTp(test_40col_inverse);
test_do_reboot = true;
RUN_TESTp(test_80col_inverse);
test_do_reboot = false;
RUN_TESTp(test_80col_inverse);
test_do_reboot = true;
// lores
// RUN_TEST(test_lores);
//
// RUN_TEST(test_lores_40colmix_normal);
//
// RUN_TEST(test_lores_40colmix_inverse);
//
// RUN_TEST(test_lores_80colmix_normal);
//
// RUN_TEST(test_lores_80colmix_inverse);
// hires
// HACK FIXME TODO : there appears to be bugs with various 80col graphics modes ...
RUN_TESTp(test_hires_with_80col);
test_do_reboot = false;
RUN_TESTp(test_hires_with_80col);
test_do_reboot = true;
RUN_TEST(test_hires_with_40col);
test_do_reboot = false;
RUN_TEST(test_hires_with_40col);
test_do_reboot = true;
RUN_TEST(test_hires_40colmix_normal);
test_do_reboot = false;
RUN_TEST(test_hires_40colmix_normal);
test_do_reboot = true;
RUN_TEST(test_hires_40colmix_inverse);
test_do_reboot = false;
RUN_TEST(test_hires_40colmix_inverse);
test_do_reboot = true;
RUN_TESTp(test_hires_80colmix_normal);
test_do_reboot = false;
RUN_TESTp(test_hires_80colmix_normal);
test_do_reboot = true;
RUN_TESTp(test_hires_80colmix_inverse);
test_do_reboot = false;
RUN_TESTp(test_hires_80colmix_inverse);
test_do_reboot = true;
// double-lo/hi
RUN_TEST(test_80col_lores);
test_do_reboot = false;
RUN_TEST(test_80col_lores);
test_do_reboot = true;
RUN_TEST(test_80col_hires);
test_do_reboot = false;
RUN_TEST(test_80col_hires);
test_do_reboot = true;
// ...
c_eject_6(0);
pthread_mutex_unlock(&interface_mutex);
}
SUITE(test_suite_vm);
GREATEST_MAIN_DEFS();
int main(int argc, char **argv) {
GREATEST_MAIN_BEGIN();
RUN_SUITE(test_suite_vm);
GREATEST_MAIN_END();
}