1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-10-14 09:23:54 +00:00
erc-c/src/mos6502.bits.c
2018-01-05 14:18:39 -06:00

174 lines
3.6 KiB
C

/*
* mos6502.bits.c
*
* The code here is used to implement instructions which operate
* specifically on bits of values.
*/
#include "mos6502.h"
#include "mos6502.enums.h"
/*
* The and instruction will assign the bitwise-and of the accumulator
* and a given operand.
*/
DEFINE_INST(and)
{
cpu->A &= oper;
mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->A);
}
/*
* This is the "arithmetic" shift left instruction.
*
* Here we will shift the contents of the given operand left by one bit.
* If the operand was the accumulator, then we'll store it back there;
* if not, we will store it in the last effective address in memory.
*
* Note that we use the carry bit to help us figure out what the "last
* bit" is, and whether we should now set the carry bit as a result of
* our operation.
*/
DEFINE_INST(asl)
{
cpu->P &= ~MOS_CARRY;
if (oper & 0x80) {
cpu->P |= MOS_CARRY;
}
oper <<= 1;
if (cpu->last_addr) {
vm_segment_set(cpu->memory, cpu->last_addr, oper);
} else {
cpu->A = oper;
}
mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_CARRY | MOS_ZERO, oper);
}
/*
* The bit instruction will test a given operand for certain
* characteristics, and assign the negative, overflow, and/or carry bits
* in the status register as a result.
*/
DEFINE_INST(bit)
{
cpu->P &= ~MOS_NEGATIVE;
if (oper & MOS_NEGATIVE) {
cpu->P |= MOS_NEGATIVE;
}
cpu->P &= ~MOS_OVERFLOW;
if (oper & MOS_OVERFLOW) {
cpu->P |= MOS_OVERFLOW;
}
if (oper & cpu->A) {
cpu->P &= ~MOS_ZERO;
} else {
cpu->P |= MOS_ZERO;
}
}
/*
* Compute the bitwise-exclusive-or between the accumulator and operand,
* and store the result in A.
*/
DEFINE_INST(eor)
{
cpu->A ^= oper;
mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->A);
}
/*
* This is pretty similar in spirit to the ASL instruction, except we
* shift right rather than left.
*
* Note that the letters in the instruction stand for "logical" shift
* right.
*/
DEFINE_INST(lsr)
{
cpu->P &= ~MOS_CARRY;
if (oper & 0x01) {
cpu->P |= MOS_CARRY;
}
oper >>= 1;
if (cpu->last_addr) {
vm_segment_set(cpu->memory, cpu->last_addr, oper);
} else {
cpu->A = oper;
}
// MOS_NEGATIVE is intentionally not included here.
mos6502_modify_status(cpu, MOS_CARRY | MOS_ZERO, oper);
}
/*
* Compute the bitwise-or of the accumulator and operand, and store the
* result in the A register.
*/
DEFINE_INST(ora)
{
cpu->A |= oper;
mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->A);
}
/*
* This instruction is interesting; it's a _rotation_ left, which means
* that what was in the 8th bit will move to the 1st bit, and everything
* else moves down one place.
*/
DEFINE_INST(rol)
{
MOS_CARRY_BIT();
if (oper & 0x80) {
carry = 1;
}
oper <<= 1;
if (carry) {
oper |= 0x01;
}
if (cpu->last_addr) {
vm_segment_set(cpu->memory, cpu->last_addr, oper);
} else {
cpu->A = oper;
}
mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_CARRY | MOS_ZERO, oper);
}
/*
* Here it's a rotation to the right, just like the ROL instruction. All
* bits are maintained.
*/
DEFINE_INST(ror)
{
MOS_CARRY_BIT();
if (oper & 0x01) {
carry = 1;
}
oper >>= 1;
if (carry) {
oper |= 0x80;
}
if (cpu->last_addr) {
vm_segment_set(cpu->memory, cpu->last_addr, oper);
} else {
cpu->A = oper;
}
mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_CARRY | MOS_ZERO, oper);
}