mirror of
https://github.com/trudnai/Steve2.git
synced 2024-05-31 15:41:35 +00:00
Undocumented Instruction Implementations
This commit is contained in:
parent
6b43345ca2
commit
c7b202c9d0
|
@ -25,13 +25,10 @@
|
|||
#define __6502_INSTR_BRANCH_H__
|
||||
|
||||
INLINE void BRA( int8_t reladdr ) {
|
||||
m6502.PC += reladdr;
|
||||
#ifdef CLK_ABSOLUTE_PRECISE
|
||||
uint8_t pg = m6502.PC >> 8;
|
||||
m6502.clktime += m6502.PC >> 8 == pg ? 1 : 2;
|
||||
#else
|
||||
// m6502.clktime++;
|
||||
#endif
|
||||
m6502.PC += reladdr;
|
||||
m6502.clkfrm += m6502.PC >> 8 == pg ? 1 : 2;
|
||||
|
||||
#ifdef DEBUG
|
||||
if ( reladdr == -2 ) {
|
||||
dbgPrintf2("Infinite Loop at %04X!\n", m6502.PC);
|
||||
|
@ -208,4 +205,79 @@ INLINE void BVS( int8_t reladdr ) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
BBR BBS - Branch on Bit Reset or Set
|
||||
|
||||
BBR and BBS test the specified zero page location and branch if the specified bit is clear (BBR) or set (BBS).
|
||||
Note that as with TRB, the term reset in BBR is used to mean clear.
|
||||
|
||||
On the 6502 and 65C02, bit 7 is typically the most convenient bit to use for I/O and software flags because
|
||||
it can be tested by several instructions, such as BIT and LDA. BBR and BBS can test any of the 8 bits without
|
||||
affecting any flags or using any registers. Unlike other branch instructions, BBR and BBS always take the same
|
||||
number of cycles (five) whether the branch is taken or not. It is often useful to test bit 0, for example, to test
|
||||
whether a byte is even or odd. However, the usefulness of BBR and BBS is somewhat limited for a couple of reasons.
|
||||
First, there is only a single addressing mode for these instructions -- no indexing by X or Y, for instance.
|
||||
Second, they are restricted to zero page locations. For software flags this may be just fine, but it may not be very
|
||||
convenient (or cost effective) to add any additional address decoding hardware that may be necessary to
|
||||
map I/O locations to the zero page.
|
||||
|
||||
The addressing mode is a combination of zero page addressing and relative addressing -- really just a juxtaposition of the two.
|
||||
The bit to test is typically specified as part of the instruction name rather than the operand, i.e.
|
||||
|
||||
Flags affected: none
|
||||
|
||||
OP LEN CYC MODE FLAGS SYNTAX
|
||||
-- --- --- ---- ----- ------
|
||||
0F 3 5 zp,rel ........ BBR0 $12,LABEL
|
||||
1F 3 5 zp,rel ........ BBR1 $12,LABEL
|
||||
2F 3 5 zp,rel ........ BBR2 $12,LABEL
|
||||
3F 3 5 zp,rel ........ BBR3 $12,LABEL
|
||||
4F 3 5 zp,rel ........ BBR4 $12,LABEL
|
||||
5F 3 5 zp,rel ........ BBR5 $12,LABEL
|
||||
6F 3 5 zp,rel ........ BBR6 $12,LABEL
|
||||
7F 3 5 zp,rel ........ BBR7 $12,LABEL
|
||||
8F 3 5 zp,rel ........ BBS0 $12,LABEL
|
||||
9F 3 5 zp,rel ........ BBS1 $12,LABEL
|
||||
AF 3 5 zp,rel ........ BBS2 $12,LABEL
|
||||
BF 3 5 zp,rel ........ BBS3 $12,LABEL
|
||||
CF 3 5 zp,rel ........ BBS4 $12,LABEL
|
||||
DF 3 5 zp,rel ........ BBS5 $12,LABEL
|
||||
EF 3 5 zp,rel ........ BBS6 $12,LABEL
|
||||
FF 3 5 zp,rel ........ BBS7 $12,LABEL
|
||||
|
||||
**/
|
||||
#define BBR(n) INLINE void BBR##n( uint8_t src, int8_t reladdr ) { \
|
||||
dbgPrintf("BBR"#n" "); \
|
||||
disPrintf(disassembly.inst, "BBR"#n); \
|
||||
if ( ! (src & (1 << n) ) ) { \
|
||||
BRA( reladdr ); \
|
||||
} \
|
||||
}
|
||||
|
||||
BBR(0)
|
||||
BBR(1)
|
||||
BBR(2)
|
||||
BBR(3)
|
||||
BBR(4)
|
||||
BBR(5)
|
||||
BBR(6)
|
||||
BBR(7)
|
||||
|
||||
#define BBS(n) INLINE void BBS##n( uint8_t src, int8_t reladdr ) { \
|
||||
dbgPrintf("BBS"#n" "); \
|
||||
disPrintf(disassembly.inst, "BBS"#n); \
|
||||
if ( (src & (1 << n) ) ) { \
|
||||
BRA( reladdr ); \
|
||||
} \
|
||||
}
|
||||
|
||||
BBS(0)
|
||||
BBS(1)
|
||||
BBS(2)
|
||||
BBS(3)
|
||||
BBS(4)
|
||||
BBS(5)
|
||||
BBS(6)
|
||||
BBS(7)
|
||||
|
||||
#endif // __6502_INSTR_BRANCH_H__
|
||||
|
|
|
@ -45,6 +45,9 @@ INLINE void JMP( uint16_t addr ) {
|
|||
dbgPrintf("Infinite Loop at %04X!\n", m6502.PC);
|
||||
}
|
||||
#endif
|
||||
if (m6502.PC >> 8 != addr >> 8) {
|
||||
m6502.clkfrm += 1;
|
||||
}
|
||||
m6502.PC = addr;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,62 @@ INLINE void BIT( uint8_t src ) {
|
|||
set_flags_Z(m6502.A & src);
|
||||
}
|
||||
|
||||
/**
|
||||
TRB - Test and Reset Bits
|
||||
|
||||
TRB can be one the more confusing instructions for a couple of reasons.
|
||||
|
||||
First, the term reset is used to refer to the clearing of a bit, whereas the term clear had been used consistently before, such as CLC
|
||||
which stands for CLear Carry. Second, the effect on the Z flag is determined by a different function than the effect on memory.
|
||||
|
||||
TRB has the same effect on the Z flag that a BIT instruction does. Specifically, it is based on whether the result of a bitwise AND of the
|
||||
accumulator with the contents of the memory location specified in the operand is zero. Also, like BIT, the accumulator is not affected.
|
||||
|
||||
The accumulator determines which bits in the memory location specified in the operand are cleared and which are not affected.
|
||||
The bits in the accumulator that are ones are cleared (in memory), and the bits that are zeros (in the accumulator) are not affected (in memory).
|
||||
This is the same as saying that the resulting memory contents are the bitwise AND of the memory contents with the complement of the
|
||||
accumulator (i.e. the exclusive-or of the accululator with $FF).
|
||||
|
||||
OP LEN CYC MODE FLAGS SYNTAX
|
||||
-- --- --- ---- ----- ------
|
||||
14 2 5 zp ......Z. TRB $12
|
||||
1C 3 6 abs ......Z. TRB $3456
|
||||
|
||||
**/
|
||||
INLINE void TRB( uint16_t addr ) {
|
||||
dbgPrintf("TRB(%02X) ", src);
|
||||
disPrintf(disassembly.inst, "TRB");
|
||||
set_flags_Z( WRLOMEM[addr] & m6502.A );
|
||||
WRLOMEM[addr] &= ~m6502.A;
|
||||
}
|
||||
|
||||
/**
|
||||
TSB - Test and Set Bits
|
||||
|
||||
TSB, like TRB, can be confusing. For one, like TRB, the effect on the Z flag is determined by a different function than the effect on memory.
|
||||
|
||||
TSB, like TRB, has the same effect on the Z flag that a BIT instruction does. Specifically, it is based on whether the result of a bitwise AND
|
||||
of the accumulator with the contents of the memory location specified in the operand is zero. Also, like BIT (and TRB), the accumulator is not affected.
|
||||
|
||||
The accumulator determines which bits in the memory location specified in the operand are set and which are not affected. The bits in the
|
||||
accumulator that are ones are set to one (in memory), and the bits that are zeros (in the accumulator) are not affected (in memory).
|
||||
This is the same as saying that the resulting memory contents are the bitwise OR of the memory contents with the accumulator.
|
||||
|
||||
Flags affected: Z
|
||||
|
||||
OP LEN CYC MODE FLAGS SYNTAX
|
||||
-- --- --- ---- ----- ------
|
||||
04 2 5 zp ......Z. TSB $12
|
||||
0C 3 6 abs ......Z. TSB $3456
|
||||
|
||||
**/
|
||||
INLINE void TSB( uint16_t addr ) {
|
||||
dbgPrintf("TSB(%02X) ", src);
|
||||
disPrintf(disassembly.inst, "TSB");
|
||||
set_flags_Z( WRLOMEM[addr] & m6502.A );
|
||||
WRLOMEM[addr] |= m6502.A;
|
||||
}
|
||||
|
||||
/**
|
||||
CMP Compare Memory with Accumulator
|
||||
|
||||
|
|
|
@ -80,6 +80,23 @@ INLINE void INY() {
|
|||
dbgPrintf("%02X ", m6502.Y);
|
||||
}
|
||||
|
||||
/**
|
||||
INA Increment Accumulator by One
|
||||
|
||||
A + 1 -> A N Z C I D V
|
||||
+ + - - - -
|
||||
|
||||
addressing assembler opc bytes cyles
|
||||
--------------------------------------------
|
||||
implied INA C8 1 2
|
||||
**/
|
||||
INLINE void INA() {
|
||||
dbgPrintf("INA %02X -> ", m6502.A);
|
||||
disPrintf(disassembly.inst, "INA");
|
||||
set_flags_NZ( ++m6502.A );
|
||||
dbgPrintf("%02X ", m6502.A);
|
||||
}
|
||||
|
||||
/**
|
||||
DEC Decrement Memory by One
|
||||
|
||||
|
@ -135,5 +152,22 @@ INLINE void DEY() {
|
|||
dbgPrintf("%02X ", m6502.Y);
|
||||
}
|
||||
|
||||
/**
|
||||
DEA Decrement Accumulator by One
|
||||
|
||||
A - 1 -> A N Z C I D V
|
||||
+ + - - - -
|
||||
|
||||
addressing assembler opc bytes cyles
|
||||
--------------------------------------------
|
||||
implied DEC 88 1 2
|
||||
**/
|
||||
INLINE void DEA() {
|
||||
dbgPrintf("DEA %02X -> ", m6502.A);
|
||||
disPrintf(disassembly.inst, "DEA");
|
||||
set_flags_NZ( --m6502.A );
|
||||
dbgPrintf("%02X ", m6502.A);
|
||||
}
|
||||
|
||||
#endif // __6502_INSTR_INC_DEC_H__
|
||||
|
||||
|
|
|
@ -168,5 +168,24 @@ INLINE void STY( uint16_t addr ) {
|
|||
STR(addr, m6502.Y);
|
||||
}
|
||||
|
||||
/**
|
||||
STZ Store Zero (0) in Memory
|
||||
|
||||
0 -> M N Z C I D V
|
||||
- - - - - -
|
||||
|
||||
OP LEN CYC MODE FLAGS SYNTAX
|
||||
-- --- --- ---- ----- ------
|
||||
64 2 3 zp ........ STZ $12
|
||||
74 2 4 zp,X ........ STZ $12,X
|
||||
9C 3 4 abs ........ STZ $3456
|
||||
9E 3 5 abs,X ........ STZ $3456,X
|
||||
**/
|
||||
INLINE void STZ( uint16_t addr ) {
|
||||
dbgPrintf("STZ ");
|
||||
disPrintf(disassembly.inst, "STZ");
|
||||
STR(addr, 0);
|
||||
}
|
||||
|
||||
#endif // __6502_INSTR_LOAD_STORE_H__
|
||||
|
||||
|
|
|
@ -136,5 +136,75 @@ INLINE void SEI() {
|
|||
m6502.I = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
RMB SMB - Reset or Set Memory Bit
|
||||
|
||||
RMB and SMB clear (RMB) or set (SMB) the specified bit in the specified zero page location,
|
||||
and can be used in conjuction with the BBR and BBS instructions. Again, note that as with BBR and TRB,
|
||||
the term reset in RMB is used to mean clear.
|
||||
|
||||
The function of RMB and SMB is very similar to the function of TRB and TSB, except that RMB and SMB
|
||||
can clear or set only one zero page bit, whereas TRB and TSB can clear or set any number of bits. Also,
|
||||
only zero page addressing is available with RMB and SMB, whereas zero page and absolute addressing
|
||||
are available for both TRB and TSB. As a result, RMB and SMB do not offer much that isn't already available
|
||||
with TRB and TSB (which are available on 65C02s from all manufacturers). The main advantages are that
|
||||
RMB and SMB, unlike TRB and TSB, do not use the accumulator, leaving it available, and do not affect any flags.
|
||||
However, it is worth noting that it is rarely useful to preserve the value of the Z (zero) flag (the only flag affected by
|
||||
TRB and TSB), unlike other flags (such as the carry).
|
||||
|
||||
Like BBR and BBS, the bit to test is typically specified as part of the instruction name rather than the operand, i.e.
|
||||
|
||||
Flags affected: none
|
||||
|
||||
OP LEN CYC MODE FLAGS SYNTAX
|
||||
-- --- --- ---- ----- ------
|
||||
07 2 5 zp ........ RMB0 $12
|
||||
17 2 5 zp ........ RMB1 $12
|
||||
27 2 5 zp ........ RMB2 $12
|
||||
37 2 5 zp ........ RMB3 $12
|
||||
47 2 5 zp ........ RMB4 $12
|
||||
57 2 5 zp ........ RMB5 $12
|
||||
67 2 5 zp ........ RMB6 $12
|
||||
77 2 5 zp ........ RMB7 $12
|
||||
87 2 5 zp ........ SMB0 $12
|
||||
97 2 5 zp ........ SMB1 $12
|
||||
A7 2 5 zp ........ SMB2 $12
|
||||
B7 2 5 zp ........ SMB3 $12
|
||||
C7 2 5 zp ........ SMB4 $12
|
||||
D7 2 5 zp ........ SMB5 $12
|
||||
E7 2 5 zp ........ SMB6 $12
|
||||
F7 2 5 zp ........ SMB7 $12
|
||||
**/
|
||||
#define RMB(n) INLINE void RMB##n( uint8_t zpg ) { \
|
||||
dbgPrintf("RMB"#n" "); \
|
||||
disPrintf(disassembly.inst, "RMB"#n); \
|
||||
WRLOMEM[zpg] &= ~(1 << n); \
|
||||
}
|
||||
|
||||
RMB(0)
|
||||
RMB(1)
|
||||
RMB(2)
|
||||
RMB(3)
|
||||
RMB(4)
|
||||
RMB(5)
|
||||
RMB(6)
|
||||
RMB(7)
|
||||
|
||||
#define SMB(n) INLINE void SMB##n( uint8_t zpg ) { \
|
||||
dbgPrintf("SMB"#n" "); \
|
||||
disPrintf(disassembly.inst, "SMB"#n); \
|
||||
WRLOMEM[zpg] |= (1 << n); \
|
||||
}
|
||||
|
||||
SMB(0)
|
||||
SMB(1)
|
||||
SMB(2)
|
||||
SMB(3)
|
||||
SMB(4)
|
||||
SMB(5)
|
||||
SMB(6)
|
||||
SMB(7)
|
||||
|
||||
|
||||
#endif // __6502_INSTR_SET_CLR_H__
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
absolute,X ASL oper,X 1E 3 7
|
||||
**/
|
||||
INLINE void _ASL( uint16_t addr ) {
|
||||
m6502.C = WRLOMEM[addr] & 0x80;
|
||||
m6502.C = memread(addr) & 0x80;
|
||||
set_flags_NZ( WRLOMEM[addr] <<= 1 );
|
||||
}
|
||||
INLINE void ASL( uint16_t addr ) {
|
||||
|
|
|
@ -63,6 +63,38 @@ INLINE void PHA() {
|
|||
PUSH( m6502.A );
|
||||
}
|
||||
|
||||
/**
|
||||
PHX Push index X on Stack
|
||||
|
||||
push X N Z C I D V
|
||||
- - - - - -
|
||||
|
||||
addressing assembler opc bytes cyles
|
||||
--------------------------------------------
|
||||
implied PHX 48 1 3
|
||||
**/
|
||||
INLINE void PHX() {
|
||||
dbgPrintf("PHX %02X ", m6502.X);
|
||||
disPrintf(disassembly.inst, "PHX");
|
||||
PUSH( m6502.X );
|
||||
}
|
||||
|
||||
/**
|
||||
PHY Push index Y on Stack
|
||||
|
||||
push Y N Z C I D V
|
||||
- - - - - -
|
||||
|
||||
addressing assembler opc bytes cyles
|
||||
--------------------------------------------
|
||||
implied PHY 48 1 3
|
||||
**/
|
||||
INLINE void PHY() {
|
||||
dbgPrintf("PHY %02X ", m6502.Y);
|
||||
disPrintf(disassembly.inst, "PHY");
|
||||
PUSH( m6502.Y );
|
||||
}
|
||||
|
||||
/**
|
||||
PLA Pull Accumulator from Stack
|
||||
|
||||
|
@ -80,6 +112,40 @@ INLINE void PLA() {
|
|||
set_flags_NZ( m6502.A );
|
||||
}
|
||||
|
||||
/**
|
||||
PLX Pull index X from Stack
|
||||
|
||||
pull X N Z C I D V
|
||||
+ + - - - -
|
||||
|
||||
addressing assembler opc bytes cyles
|
||||
--------------------------------------------
|
||||
implied PLX 68 1 4
|
||||
**/
|
||||
INLINE void PLX() {
|
||||
m6502.X = POP();
|
||||
dbgPrintf("PLX %02X ", m6502.X);
|
||||
disPrintf(disassembly.inst, "PLX");
|
||||
set_flags_NZ( m6502.X );
|
||||
}
|
||||
|
||||
/**
|
||||
PLY Pull index Y from Stack
|
||||
|
||||
pull Y N Z C I D V
|
||||
+ + - - - -
|
||||
|
||||
addressing assembler opc bytes cyles
|
||||
--------------------------------------------
|
||||
implied PLY 68 1 4
|
||||
**/
|
||||
INLINE void PLY() {
|
||||
m6502.Y = POP();
|
||||
dbgPrintf("PLY %02X ", m6502.Y);
|
||||
disPrintf(disassembly.inst, "PLY");
|
||||
set_flags_NZ( m6502.Y );
|
||||
}
|
||||
|
||||
/**
|
||||
PHP Push Processor Status on Stack
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user