News:

11 March 2016 - Forum Rules

Main Menu

Snes 65816 assembly - How do TRB and TSB opcodes work?

Started by PostMortem, November 11, 2020, 10:26:50 PM

Previous topic - Next topic

PostMortem

Hi everyone:

I've been analyzing MMX3 password logic and so far I have found a few interesting things that I will share once I'm done, but since I'm not an assembly guy I'm having a hard time trying to understand the following two opcodes:

QuoteTRB: Tests the bit similarly to "AND", and clears it, while affecting the flags.
TSB: Tests the bit similarly to "AND", and sets it, while affecting the flags.

flags: ------Z-

Source: https://wiki.superfamicom.org/65816-reference


As far as definitions go these are pretty succint and there isn't a lot more out there either. This next one is a little bit ::) more complete, but I still can't grasp the essence of it :banghead::

Quote
TRB

M ← M & (~A)

z ← Set if logical AND of memory and Accumulator is zero

Quote
TSB

M ← M | A

z ← Set if logical AND of memory and Accumulator is zero

Source: https://undisbeliever.net/snesdev/65816-opcodes.html


Can somebody explain to me with a practical example what is going on at a bit level when these opcodes happen?

As a random example:


lda #e0
tsb $#0f
???


or


lda #f0
trb $#01
???



Thanks in advance.


phonymike

I use 65816 Programming Primer for instruction details (well I have a plain text version of it.)

QuoteTRB Test and Reset Memory Bits
------------------------------

TRB performs a logical AND of the accumulator's compliment and the
effective address - data is then rewritten back to the specified address.
This clears each memory bit that has a corresponding bit set in the
accumulator, leaving all other memory bits unchanged.

To put it another way - TRB flips or inverts the accumulator value and then
AND's that value with memory operand and stores the result back to the
effective address.

QuoteTSB Test and Set Memory Bits
----------------------------

TSB logically OR's the accumulator and the data at the effective address.
This effectively sets a bit at the memory location for each bit set in the
accumulator.

QuoteFlags Affected: ------z-
                       z Set if memory value AND'ed with accumulator value is zero.

This is the only part that got me. If the value at the memory location (before the instruction begins) AND'ed with the accumulator value is zero, then Z is set.





;xkas v0.06
lorom

Start:
phk ; Put current bank on stack
plb ; make it current programming bank
clc ; Clear Carry flag
xce ; Native 16 bit mode  (no 6502 Emulation!)


;=======================================================
; TRB
;=======================================================

lda #$00
sta $0000
lda #$00
trb $0000

;result: $0000 = $00, zero flag is set

lda #$00
sta $0000
lda #$FF
trb $0000

;result: $0000 = $00, zero flag is set

lda #$FF
sta $0000
lda #$00
trb $0000

;result: $0000 = $FF, zero flag is set

lda #$FF
sta $0000
lda #$FF
trb $0000

;result: $0000 = $00, zero flag is cleared

;=======================================================
; TSB
;=======================================================

lda #$00
sta $0000
lda #$00
tsb $0000

;result: $0000 = $00, zero flag is set

lda #$00
sta $0000
lda #$FF
tsb $0000

;result: $0000 = $FF, zero flag is set

lda #$FF
sta $0000
lda #$00
tsb $0000

;result: $0000 = $FF, zero flag is set

lda #$FF
sta $0000
lda #$FF
tsb $0000

;result: $0000 = $FF, zero flag is cleared


- jmp -

;=====================
;CPU Exception Vectors
;=====================
org $FFE0
;Native $00:FFE0
dw $00, $00 ;4 bytes unused
dw Start ;2 bytes COP
dw Start ;2 bytes BRK
dw Start ;2 bytes ABORT
dw Start ;2 bytes NMI
dw $00 ;2 bytes unused (would be RESET, but the SNES always boots in Emulation mode)
dw Start ;2 bytes IRQ
;Emulation $00:FFF0
dw $00, $00 ;4 bytes unused
dw Start ;2 bytes COP
dw $00 ;2 bytes unused (would be BRK, but BRK and IRQ share the same vector in Emulation mode)
dw Start ;2 bytes ABORT
dw Start ;2 bytes NMI
dw Start ;2 bytes RESET
dw Start ;2 bytes IRQ/BRK

;==========================
;Create new internal header
;==========================
org $FFC0
db "Mike's test asm      "
;  "---------------------" 21 characters

db $30 ;LoROM and fastrom
db $02 ;ROM/RAM/BATT
db $0A ;ROM size 8 mbit
db $03 ;SRAM size 8KB
db $00 ;Country: Japan
db $01 ;Nintendo
db $01 ;version 1.1
dw $D609 ;checksum compliment
dw $29f6 ;good checksum ;)

PostMortem

Quote from: phonymike on November 12, 2020, 02:57:00 AM
;xkas v0.06
lorom

Start:
phk ; Put current bank on stack
plb ; make it current programming bank
clc ; Clear Carry flag
xce ; Native 16 bit mode  (no 6502 Emulation!)


;=======================================================
; TRB
;=======================================================

lda #$00
sta $0000
lda #$00
trb $0000

;result: $0000 = $00, zero flag is set

lda #$00
sta $0000
lda #$FF
trb $0000

;result: $0000 = $00, zero flag is set

lda #$FF
sta $0000
lda #$00
trb $0000

;result: $0000 = $FF, zero flag is set

lda #$FF
sta $0000
lda #$FF
trb $0000

;result: $0000 = $00, zero flag is cleared

;=======================================================
; TSB
;=======================================================

lda #$00
sta $0000
lda #$00
tsb $0000

;result: $0000 = $00, zero flag is set

lda #$00
sta $0000
lda #$FF
tsb $0000

;result: $0000 = $FF, zero flag is set

lda #$FF
sta $0000
lda #$00
tsb $0000

;result: $0000 = $FF, zero flag is set

lda #$FF
sta $0000
lda #$FF
tsb $0000

;result: $0000 = $FF, zero flag is cleared


- jmp -

;=====================
;CPU Exception Vectors
;=====================
org $FFE0
;Native $00:FFE0
dw $00, $00 ;4 bytes unused
dw Start ;2 bytes COP
dw Start ;2 bytes BRK
dw Start ;2 bytes ABORT
dw Start ;2 bytes NMI
dw $00 ;2 bytes unused (would be RESET, but the SNES always boots in Emulation mode)
dw Start ;2 bytes IRQ
;Emulation $00:FFF0
dw $00, $00 ;4 bytes unused
dw Start ;2 bytes COP
dw $00 ;2 bytes unused (would be BRK, but BRK and IRQ share the same vector in Emulation mode)
dw Start ;2 bytes ABORT
dw Start ;2 bytes NMI
dw Start ;2 bytes RESET
dw Start ;2 bytes IRQ/BRK

;==========================
;Create new internal header
;==========================
org $FFC0
db "Mike's test asm      "
;  "---------------------" 21 characters

db $30 ;LoROM and fastrom
db $02 ;ROM/RAM/BATT
db $0A ;ROM size 8 mbit
db $03 ;SRAM size 8KB
db $00 ;Country: Japan
db $01 ;Nintendo
db $01 ;version 1.1
dw $D609 ;checksum compliment
dw $29f6 ;good checksum ;)


Thank you phonymike  :thumbsup: :thumbsup:, that's exactly what I'm looking for. Incidentally I just found a couple helpful definitions & examples in the 65C02 specification, but I'm not sure if the 65316 has the same behavior, so I guess I will be testing this:

QuoteTRB - Test and Reset Bits

Flags affected: Z

OP LEN CYC MODE FLAGS    SYNTAX
-- --- --- ---- -----    ------
14 2   5   zp   ......Z. TRB $12
1C 3   6   abs  ......Z. TRB $3456

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). Thus, the operation of TRB operand is similar to

BIT operand
PHP
PHA
EOR #$FF
AND operand
STA operand
PLA
PLP
except TRB doesn't affect N or V and doesn't need (or use) any stack space. In the sequence above, the BIT instruction determines the Z flag result, and the EOR-AND-STA sequence determines the memory result.
Here are a couple of examples to help illustrate the operation of TRB. (Location $00 is assumed to be RAM in these examples.)

After,

LDA #$A6
STA $00
LDA #$33
TRB $00

the Z flag is 0 ($A6 AND $33 = $22), memory location $00 contains $84 ($A6 AND ($33 XOR $FF) = $84), and the accumulator is unaffected by the TRB instruction (and thus contains $33 from the LDA instruction).
After,

LDA #$A6
STA $00
LDA #$41
TRB $00

the Z flag is 1 ($A6 AND $41 = $00), memory location $00 contains $A6 ($A6 AND ($41 XOR $FF) = $A6), and again, the accumulator is unaffected by the TRB instruction (and thus contains $41 from the LDA instruction).
Incidentally, when the Z flag is 1 (after a TRB instruction), the memory location will have the same value it did before the TRB instruction. The reason this is true is that for the Z flag to be 1, the bits in the accumulator that are ones must be zeros in the memory location. TRB clears those bits (thus they remain the same) and doesn't affect the other bits.

TSB - Test and Set Bits

Flags affected: Z

OP LEN CYC MODE FLAGS    SYNTAX
-- --- --- ---- -----    ------
04 2   5   zp   ......Z. TSB $12
0C 3   6   abs  ......Z. TSB $3456

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. Thus, the operation of TSB operand is similar to

BIT operand
PHP
PHA
ORA operand
STA operand
PLA
PLP
except TSB doesn't affect N or V and doesn't need (or use) any stack space. In the sequence above, the BIT instruction determines the Z flag result, and the ORA-STA sequence determines the memory result.
Here are a couple of examples to help illustrate the operation of TSB. (Once again, location $00 is assumed to be RAM in these examples.)

After,

LDA #$A6
STA $00
LDA #$33
TSB $00

the Z flag is 0 ($A6 AND $33 = $22), memory location $00 contains $B7 ($A6 OR $33 = $B7), and the accumulator is unaffected by the TSB instruction (and thus contains $33 from the LDA instruction).
After,

LDA #$A6
STA $00
LDA #$41
TSB $00

the Z flag is 1 ($A6 AND $41 = $00), memory location $00 contains $E7 ($A6 OR $41 = $E7), and again, the accumulator is unaffected by the TSB instruction (and thus contains $41 from the LDA instruction).

Source: http://www.6502.org/tutorials/65c02opcodes.html

They seem to be really specific albeit obscure opcodes which have a really limited usecase. Time to study this badboy  :laugh: