From c0e01bce21b06bf9e62d9f2bf436ad93914512e8 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Fri, 25 Dec 2020 21:00:58 +0100 Subject: more testing --- LINKS | 2 + emu/6502.c | 2 +- emu/tests/test_cpu_6502.c | 463 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 465 insertions(+), 2 deletions(-) diff --git a/LINKS b/LINKS index ed38050..f84bea6 100644 --- a/LINKS +++ b/LINKS @@ -52,6 +52,7 @@ https://www.instructables.com/DIY-Paper-TapePunch http://wilsonminesco.com/BenchCPU/ (VIAs for all kind of external stuff) https://github.com/mist64/msbasic https://www.pagetable.com/?p=46 +http://www.baltissen.org/newhtm/ttl6502.htm documentation on old 8-bit logic: http://www.zimmers.net/anonftp/pub/cbm/documents/chipdata/index.html @@ -137,6 +138,7 @@ https://sta.c64.org/cbm64mem.html https://www.pagetable.com/?p=410 https://github.com/Klaus2m5/6502_65C02_functional_tests http://nparker.llx.com/a2/opcodes.html +https://blog.scottlogic.com/2020/08/26/codegen-6502-webassembly.html testing: http://www.ucunit.org/_related__sites.html diff --git a/emu/6502.c b/emu/6502.c index ff178f9..cab72b3 100644 --- a/emu/6502.c +++ b/emu/6502.c @@ -372,7 +372,7 @@ void cpu_6502_step( cpu_6502_t *cpu ) case SBC_IMM: operand8 = cpu_6502_read_byte( cpu, cpu->PC ); cpu->PC++; - tmp = cpu->A - operand8 - ( cpu_6502_is_carry( cpu ) ? 0 : 1 ); + tmp = cpu->A - operand8 - ( cpu_6502_is_carry( cpu ) ? 1 : 0 ); update_negative_and_sign( cpu, tmp ); if( tmp & 0xFF00 ) { cpu->PS |= PS_C; diff --git a/emu/tests/test_cpu_6502.c b/emu/tests/test_cpu_6502.c index b926c1a..86d1115 100644 --- a/emu/tests/test_cpu_6502.c +++ b/emu/tests/test_cpu_6502.c @@ -562,6 +562,273 @@ START_TEST( test_cpu_6502_inc_flags_zero ) } END_TEST +// INX + +START_TEST( test_cpu_6502_inx_flags_positive ) +{ + INIT_CPU_TEST + + CODE_SET1( 0xc8 ); // INX + + cpu.Y = 0x34; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, 0x35 ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_inx_flags_negative ) +{ + INIT_CPU_TEST + + CODE_SET1( 0xc8 ); // INX + + cpu.Y = 0xf6; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, 0xf7 ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_inx_flags_zero ) +{ + INIT_CPU_TEST + + CODE_SET1( 0xc8 ); // INX + + cpu.Y = 0xff; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, 0x00 ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +// DEX + +START_TEST( test_cpu_6502_dex_flags_positive ) +{ + INIT_CPU_TEST + + CODE_SET1( 0xca ); // DEX + + cpu.X = 0x56; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, 0x55 ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_dex_flags_negative ) +{ + INIT_CPU_TEST + + CODE_SET1( 0xca ); // DEX + + cpu.X = 0x93; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, 0x92 ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_dex_flags_zero ) +{ + INIT_CPU_TEST + + CODE_SET1( 0xca ); // DEX + + cpu.X = 0x01; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, 0x00 ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +// DEY + +START_TEST( test_cpu_6502_dey_flags_positive ) +{ + INIT_CPU_TEST + + CODE_SET1( 0x88 ); // DEY + + cpu.Y = 0x56; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, 0x55 ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_dey_flags_negative ) +{ + INIT_CPU_TEST + + CODE_SET1( 0x88 ); // DEY + + cpu.Y = 0x93; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, 0x92 ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_dey_flags_zero ) +{ + INIT_CPU_TEST + + CODE_SET1( 0x88 ); // DEY + + cpu.Y = 0x01; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, 0x00 ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + // ROL START_TEST( test_cpu_6502_rol_flags_accumulator_carry ) @@ -595,6 +862,98 @@ START_TEST( test_cpu_6502_rol_flags_accumulator_carry ) } END_TEST +// CPX + +START_TEST( test_cpu_6502_cpx_flags_equals ) +{ + INIT_CPU_TEST + + CODE_SET2( 0xe0, 0x10 ); // CPX #$10 + + cpu.X = 0x10; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_C | PS_N | PS_Z ), before_cpu.PS & ~( PS_C | PS_N | PS_Z ) ); + ck_assert( cpu_6502_is_carry( &cpu ) ); + ck_assert( cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_cpx_flags_less ) +{ + INIT_CPU_TEST + + CODE_SET2( 0xe0, 0x10 ); // CPX #$10 + + cpu.X = 0x01; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_C | PS_N | PS_Z ), before_cpu.PS & ~( PS_C | PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_carry( &cpu ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_cpx_flags_greater ) +{ + INIT_CPU_TEST + + CODE_SET2( 0xe0, 0x10 ); // CPX #$10 + + cpu.X = 0x20; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_C | PS_N | PS_Z ), before_cpu.PS & ~( PS_C | PS_N | PS_Z ) ); + ck_assert( cpu_6502_is_carry( &cpu ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + // BCC START_TEST( test_cpu_6502_bcc_carry_clear ) @@ -651,6 +1010,62 @@ START_TEST( test_cpu_6502_bcc_carry_set ) } END_TEST +// BNE + +START_TEST( test_cpu_6502_bne_zero_clear ) +{ + INIT_CPU_TEST + + CODE_SET2( 0xD0, 0x12 ); // BNE $12 + + cpu.PS &= ~PS_Z; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC + 2 + 0x12 ); // zero is clear, jump taken + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS, before_cpu.PS ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_bne_zero_set ) +{ + INIT_CPU_TEST + + CODE_SET2( 0xD0, 0x12 ); // BNE $12 + + cpu.PS |= PS_Z; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); // zero is set, execute next opcode, don't jump + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS, before_cpu.PS ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + // JMP START_TEST( test_cpu_6502_jmp_abs ) @@ -701,7 +1116,7 @@ START_TEST( test_cpu_6502_jsr_abs ) ck_assert_int_eq( cpu.A, before_cpu.A ); ck_assert_int_eq( cpu.X, before_cpu.X ); ck_assert_int_eq( cpu.Y, before_cpu.Y ); - ck_assert_mem_eq( &ram.cell[0x0], &before_ram.cell[0x0], 0x1FF - 2 ); + ck_assert_mem_eq( &ram.cell[0x0], &before_ram.cell[0x0], 0x1fd ); ck_assert_int_eq( ram.cell[0x01fe], 0x02 ); // next PC after return on stack ck_assert_int_eq( ram.cell[0x01ff], 0xf8 ); @@ -709,6 +1124,37 @@ START_TEST( test_cpu_6502_jsr_abs ) } END_TEST +// RTS + +START_TEST( test_cpu_6502_rts ) +{ + INIT_CPU_TEST + + CODE_SET1( 0x60 ); // RTS + + cpu.SP = 0xfd; + ram.cell[0x01fe] = 0x02; + ram.cell[0x01ff] = 0xf8; + + COPY_CPU_TEST + + cpu_6502_run( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, 0xf802 + 1 ); // PC restored to the value on the stack + ck_assert_int_eq( cpu.SP, 0xff ); // stack empty + ck_assert_int_eq( cpu.PS, before_cpu.PS ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + + DEINIT_CPU_TEST +} +END_TEST + static void create_cpu_6502_testcase( Suite *suite, const char *name, const TTest *test_func ) { TCase *tc; @@ -741,11 +1187,26 @@ static Suite *make_cpu_6502_testsuite( void ) create_cpu_6502_testcase( suite, "INC (flags, positive)", test_cpu_6502_inc_flags_positive ); create_cpu_6502_testcase( suite, "INC (flags, negative)", test_cpu_6502_inc_flags_negative ); create_cpu_6502_testcase( suite, "INC (flags, zero)", test_cpu_6502_inc_flags_zero ); + create_cpu_6502_testcase( suite, "INX (flags, positive)", test_cpu_6502_inx_flags_positive ); + create_cpu_6502_testcase( suite, "INX (flags, negative)", test_cpu_6502_inx_flags_negative ); + create_cpu_6502_testcase( suite, "INX (flags, zero)", test_cpu_6502_inx_flags_zero ); + create_cpu_6502_testcase( suite, "DEX (flags, positive)", test_cpu_6502_dex_flags_positive ); + create_cpu_6502_testcase( suite, "DEX (flags, negative)", test_cpu_6502_dex_flags_negative ); + create_cpu_6502_testcase( suite, "DEX (flags, zero)", test_cpu_6502_dex_flags_zero ); + create_cpu_6502_testcase( suite, "DEY (flags, positive)", test_cpu_6502_dey_flags_positive ); + create_cpu_6502_testcase( suite, "DEY (flags, negative)", test_cpu_6502_dey_flags_negative ); + create_cpu_6502_testcase( suite, "DEY (flags, zero)", test_cpu_6502_dey_flags_zero ); create_cpu_6502_testcase( suite, "ROL (flags, carry)", test_cpu_6502_rol_flags_accumulator_carry ); + create_cpu_6502_testcase( suite, "CPX (flags, equals)", test_cpu_6502_cpx_flags_equals ); + create_cpu_6502_testcase( suite, "CPX (flags, less)", test_cpu_6502_cpx_flags_less ); + create_cpu_6502_testcase( suite, "CPX (flags, greater)", test_cpu_6502_cpx_flags_greater ); create_cpu_6502_testcase( suite, "BCC (branch taken, carry clear)", test_cpu_6502_bcc_carry_clear ); create_cpu_6502_testcase( suite, "BCC (branch not taken, carry set)", test_cpu_6502_bcc_carry_set ); + create_cpu_6502_testcase( suite, "BNE (branch not taken, zero clear)", test_cpu_6502_bne_zero_clear ); + create_cpu_6502_testcase( suite, "BNE (branch taken, zero set)", test_cpu_6502_bne_zero_set ); create_cpu_6502_testcase( suite, "JMP (absolute)", test_cpu_6502_jmp_abs ); create_cpu_6502_testcase( suite, "JSR (absolute)", test_cpu_6502_jsr_abs ); + create_cpu_6502_testcase( suite, "RTS", test_cpu_6502_rts ); return suite; } -- cgit v1.2.3-54-g00ecf