1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-12-21 08:30:55 +00:00

Add TSB (Test and Set Bits) instruction

This commit also moves the TRB code from loadstor to bits, which is
where it should have been all along.
This commit is contained in:
Peter Evans 2018-02-22 13:39:48 -06:00
parent a4c3d1c4ef
commit 90892c32e4
9 changed files with 97 additions and 51 deletions

View File

@ -126,6 +126,7 @@ enum instruction {
TAX, // Transfer Accumulator to X TAX, // Transfer Accumulator to X
TAY, // Transfer Accumulator to Y TAY, // Transfer Accumulator to Y
TRB, // Test and Reset Bits TRB, // Test and Reset Bits
TSB, // Test and Set Bits
TSX, // Transfer Stack register to X TSX, // Transfer Stack register to X
TXA, // Transfer X to Accumulator TXA, // Transfer X to Accumulator
TXS, // Transfer X to Stack register TXS, // Transfer X to Stack register

View File

@ -235,6 +235,7 @@ DECL_INST(stz);
DECL_INST(tax); DECL_INST(tax);
DECL_INST(tay); DECL_INST(tay);
DECL_INST(trb); DECL_INST(trb);
DECL_INST(tsb);
DECL_INST(tsx); DECL_INST(tsx);
DECL_INST(txa); DECL_INST(txa);
DECL_INST(txs); DECL_INST(txs);

View File

@ -19,7 +19,7 @@
*/ */
static int addr_modes[] = { static int addr_modes[] = {
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F // 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 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 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 REL, IDY, ZPG, NOA, ZPX, ZPX, ZPX, NOA, IMP, ABY, ACC, NOA, ABX, ABX, ABX, NOA, // 3x

View File

@ -203,3 +203,53 @@ DEFINE_INST(ror)
cpu->A = result & 0xff; 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);
}

View File

@ -27,7 +27,7 @@
*/ */
static int instructions[] = { static int instructions[] = {
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F // 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 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 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 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(tax),
INST_HANDLER(tay), INST_HANDLER(tay),
INST_HANDLER(trb), INST_HANDLER(trb),
INST_HANDLER(tsb),
INST_HANDLER(tsx), INST_HANDLER(tsx),
INST_HANDLER(txa), INST_HANDLER(txa),
INST_HANDLER(txs), INST_HANDLER(txs),
@ -131,7 +132,7 @@ static mos6502_instruction_handler instruction_handlers[] = {
*/ */
static int cycles[] = { static int cycles[] = {
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F // 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 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 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 2, 5, 5, 0, 4, 4, 6, 0, 2, 4, 2, 0, 4, 4, 7, 0, // 3x

View File

@ -102,6 +102,7 @@ static char *instruction_strings[] = {
"TAX", "TAX",
"TAY", "TAY",
"TRB", "TRB",
"TSB",
"TSX", "TSX",
"TXA", "TXA",
"TXS", "TXS",

View File

@ -156,37 +156,6 @@ DEFINE_INST(tay)
cpu->Y = cpu->A; 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. * Transfer the stack pointer (S register) to X.
*/ */

View File

@ -127,3 +127,43 @@ Test(mos6502_bits, ror)
mos6502_handle_ror(cpu, 0); mos6502_handle_ror(cpu, 0);
cr_assert_eq(mos6502_get(cpu, 123), 128); 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);
}

View File

@ -140,23 +140,6 @@ Test(mos6502_loadstor, tay)
cr_assert_eq(cpu->Y, 111); 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) Test(mos6502_loadstor, tsx)
{ {
cpu->S = 111; cpu->S = 111;