diff --git a/include/mos6502.enums.h b/include/mos6502.enums.h index 8b2761f..07f9c06 100644 --- a/include/mos6502.enums.h +++ b/include/mos6502.enums.h @@ -126,6 +126,7 @@ enum instruction { TAX, // Transfer Accumulator to X TAY, // Transfer Accumulator to Y TRB, // Test and Reset Bits + TSB, // Test and Set Bits TSX, // Transfer Stack register to X TXA, // Transfer X to Accumulator TXS, // Transfer X to Stack register diff --git a/include/mos6502.h b/include/mos6502.h index 33bdd4c..c4a79c8 100644 --- a/include/mos6502.h +++ b/include/mos6502.h @@ -235,6 +235,7 @@ DECL_INST(stz); DECL_INST(tax); DECL_INST(tay); DECL_INST(trb); +DECL_INST(tsb); DECL_INST(tsx); DECL_INST(txa); DECL_INST(txs); diff --git a/src/mos6502.addr.c b/src/mos6502.addr.c index fa0a5dd..54a9572 100644 --- a/src/mos6502.addr.c +++ b/src/mos6502.addr.c @@ -19,7 +19,7 @@ */ static int addr_modes[] = { // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - IMP, IDX, NOA, NOA, NOA, ZPG, ZPG, NOA, IMP, IMM, ACC, NOA, NOA, ABS, ABS, NOA, // 0x + IMP, IDX, NOA, NOA, ZPG, ZPG, ZPG, NOA, IMP, IMM, ACC, NOA, ABS, ABS, ABS, NOA, // 0x REL, IDY, ZPG, NOA, ZPG, ZPX, ZPX, NOA, IMP, ABY, ACC, NOA, ABS, ABX, ABX, NOA, // 1x ABS, IDX, NOA, NOA, ZPG, ZPG, ZPG, NOA, IMP, IMM, ACC, NOA, ABS, ABS, ABS, NOA, // 2x REL, IDY, ZPG, NOA, ZPX, ZPX, ZPX, NOA, IMP, ABY, ACC, NOA, ABX, ABX, ABX, NOA, // 3x diff --git a/src/mos6502.bits.c b/src/mos6502.bits.c index b4be238..c94d9a7 100644 --- a/src/mos6502.bits.c +++ b/src/mos6502.bits.c @@ -203,3 +203,53 @@ DEFINE_INST(ror) cpu->A = result & 0xff; } } + +/* + * This is a really funky instruction. And not in the good, dancy kinda + * way. + * + * First, it does a BIT-style test to see if A & oper are zero; if so, + * it sets the Z flag. + * + * Second, it clears all bits in eff_addr where A's corresponding bits + * are set to 1. It ignores all bits in eff_addr where A's bits are + * zero. + * + * E.g.: + * + * A: 01011001 (accumulator) + * M: 11111111 (value in memory) + * R: 10100110 (result) + * + * And, as following that, the Z flag should be zero because A&M is a + * non-zero result. + */ +DEFINE_INST(trb) +{ + cpu->P &= ~MOS_ZERO; + if (!(cpu->A & oper)) { + cpu->P |= MOS_ZERO; + } + + mos6502_set(cpu, cpu->eff_addr, + (cpu->A ^ 0xff) & oper); +} + +/* + * Test to see if (A & oper) are zero and set Z flag if so. + * Additionally, set the bits in the byte at a given effective address + * (M) to 1 where the A register's bits are also 1. (Bits that are 0 in + * A are unchanged in M.) + */ +DEFINE_INST(tsb) +{ + cpu->P &= ~MOS_ZERO; + if (!(cpu->A & oper)) { + cpu->P |= MOS_ZERO; + } + + // The behavior described in the docblock here can be accomplished + // simply by OR'ing the accumulator and the operand, and storing + // back into memory at eff_addr. + mos6502_set(cpu, cpu->eff_addr, cpu->A | oper); +} diff --git a/src/mos6502.c b/src/mos6502.c index b59e921..8e7c7fb 100644 --- a/src/mos6502.c +++ b/src/mos6502.c @@ -27,7 +27,7 @@ */ static int instructions[] = { // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - BRK, ORA, BAD, BAD, BAD, ORA, ASL, BAD, PHP, ORA, ASL, BAD, BAD, ORA, ASL, BAD, // 0x + BRK, ORA, BAD, BAD, TSB, ORA, ASL, BAD, PHP, ORA, ASL, BAD, TSB, ORA, ASL, BAD, // 0x BPL, ORA, ORA, BAD, TRB, ORA, ASL, BAD, CLC, ORA, INC, BAD, TRB, ORA, ASL, BAD, // 1x JSR, AND, BAD, BAD, BIT, AND, ROL, BAD, PLP, AND, ROL, BAD, BIT, AND, ROL, BAD, // 2x BMI, AND, AND, BAD, BIT, AND, ROL, BAD, SEC, AND, DEC, BAD, BIT, AND, ROL, BAD, // 3x @@ -118,6 +118,7 @@ static mos6502_instruction_handler instruction_handlers[] = { INST_HANDLER(tax), INST_HANDLER(tay), INST_HANDLER(trb), + INST_HANDLER(tsb), INST_HANDLER(tsx), INST_HANDLER(txa), INST_HANDLER(txs), @@ -131,7 +132,7 @@ static mos6502_instruction_handler instruction_handlers[] = { */ static int cycles[] = { // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x + 7, 6, 0, 0, 5, 3, 5, 0, 3, 2, 2, 0, 6, 4, 6, 0, // 0x 2, 5, 5, 0, 5, 4, 6, 0, 2, 4, 2, 0, 6, 4, 7, 0, // 1x 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 2x 2, 5, 5, 0, 4, 4, 6, 0, 2, 4, 2, 0, 4, 4, 7, 0, // 3x diff --git a/src/mos6502.dis.c b/src/mos6502.dis.c index ab34638..0e6b637 100644 --- a/src/mos6502.dis.c +++ b/src/mos6502.dis.c @@ -102,6 +102,7 @@ static char *instruction_strings[] = { "TAX", "TAY", "TRB", + "TSB", "TSX", "TXA", "TXS", diff --git a/src/mos6502.loadstor.c b/src/mos6502.loadstor.c index 25bdb8b..bef57c7 100644 --- a/src/mos6502.loadstor.c +++ b/src/mos6502.loadstor.c @@ -156,37 +156,6 @@ DEFINE_INST(tay) cpu->Y = cpu->A; } -/* - * This is a really funky instruction. And not in the good, dancy kinda - * way. - * - * First, it does a BIT-style test to see if A & oper are zero; if so, - * it sets the Z flag. - * - * Second, it clears all bits in eff_addr where A's corresponding bits - * are set to 1. It ignores all bits in eff_addr where A's bits are - * zero. - * - * E.g.: - * - * A: 01011001 (accumulator) - * M: 11111111 (value in memory) - * R: 10100110 (result) - * - * And, as following that, the Z flag should be zero because A&M is a - * non-zero result. - */ -DEFINE_INST(trb) -{ - cpu->P &= ~MOS_ZERO; - if (!(cpu->A & oper)) { - cpu->P |= MOS_ZERO; - } - - mos6502_set(cpu, cpu->eff_addr, - (cpu->A ^ 0xff) & oper); -} - /* * Transfer the stack pointer (S register) to X. */ diff --git a/tests/mos6502.bits.c b/tests/mos6502.bits.c index 93213b8..7885c27 100644 --- a/tests/mos6502.bits.c +++ b/tests/mos6502.bits.c @@ -127,3 +127,43 @@ Test(mos6502_bits, ror) mos6502_handle_ror(cpu, 0); cr_assert_eq(mos6502_get(cpu, 123), 128); } + +Test(mos6502_bits, trb) +{ + cpu->A = 6; + mos6502_handle_trb(cpu, 3); + cr_assert_eq(cpu->P & MOS_ZERO, 0); + cpu->A = 9; + mos6502_handle_trb(cpu, 2); + cr_assert_eq(cpu->P & MOS_ZERO, MOS_ZERO); + + cpu->eff_addr = 111; + mos6502_set(cpu, cpu->eff_addr, 123); + mos6502_handle_trb(cpu, 123); + + cr_assert_eq(mos6502_get(cpu, cpu->eff_addr), + (cpu->A ^ 0xff) & 123); +} + +Test(mos6502_bits, tsb) +{ + // I borrowed this code from the trb test; TSB and TRB do exactly + // the same thing in regards to the zero bit. + cpu->A = 6; + mos6502_handle_trb(cpu, 3); + cr_assert_eq(cpu->P & MOS_ZERO, 0); + cpu->A = 9; + mos6502_handle_trb(cpu, 2); + cr_assert_eq(cpu->P & MOS_ZERO, MOS_ZERO); + + // This is similar to the segment in the trb test that focuses on + // the resetting (clearing) of bits, but modified to account for the + // differing result that tsb would provide--namely that the 1s in A + // will be set in the location in memory, rather than cleared. + cpu->eff_addr = 111; + mos6502_set(cpu, cpu->eff_addr, 123); + mos6502_handle_tsb(cpu, 123); + + cr_assert_eq(mos6502_get(cpu, cpu->eff_addr), cpu->A | 123); +} + diff --git a/tests/mos6502.loadstor.c b/tests/mos6502.loadstor.c index 3108f06..d1eb292 100644 --- a/tests/mos6502.loadstor.c +++ b/tests/mos6502.loadstor.c @@ -140,23 +140,6 @@ Test(mos6502_loadstor, tay) cr_assert_eq(cpu->Y, 111); } -Test(mos6502_loadstor, trb) -{ - cpu->A = 6; - mos6502_handle_trb(cpu, 3); - cr_assert_eq(cpu->P & MOS_ZERO, 0); - cpu->A = 9; - mos6502_handle_trb(cpu, 2); - cr_assert_eq(cpu->P & MOS_ZERO, MOS_ZERO); - - cpu->eff_addr = 111; - mos6502_set(cpu, cpu->eff_addr, 123); - mos6502_handle_trb(cpu, 123); - - cr_assert_eq(mos6502_get(cpu, cpu->eff_addr), - (cpu->A ^ 0xff) & 123); -} - Test(mos6502_loadstor, tsx) { cpu->S = 111;