diff --git a/Makefile.am b/Makefile.am index 1ca76b55..372f93f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/src/test/testhires.bas b/src/test/testhires.bas new file mode 100644 index 00000000..615bae45 --- /dev/null +++ b/src/test/testhires.bas @@ -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 + diff --git a/src/test/testhires80.bas b/src/test/testhires80.bas new file mode 100644 index 00000000..4f13f031 --- /dev/null +++ b/src/test/testhires80.bas @@ -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 + diff --git a/src/test/testhires_2.bas b/src/test/testhires_2.bas new file mode 100644 index 00000000..de3e4d88 --- /dev/null +++ b/src/test/testhires_2.bas @@ -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 + diff --git a/src/test/testlores80.bas b/src/test/testlores80.bas new file mode 100644 index 00000000..75e2d6f6 --- /dev/null +++ b/src/test/testlores80.bas @@ -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 + diff --git a/src/test/testvm.c b/src/test/testvm.c new file mode 100644 index 00000000..40836a59 --- /dev/null +++ b/src/test/testvm.c @@ -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 +#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= 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(); +} +