MEMO

Last Update: 2010/11/01 22:33 +0900

65816 opecodes

PH? s

S (stack pointer) register always points new address you can write by PH? opecodes.

JSL al

Return address points next CPU instruction.

LDA d,s

Just "d" + "s".

CPU Registers

Name Size Short for ...
A 16 or 8 Accum
DB 8 Data bank
X 16 or 8 X index
Y 16 or 8 Y index
D 16 Direct
S 16 Stack
P 8 Processor status
PB 8 Program bank
PC 16 Program counter
bullet"The Status Flags" section is useful.
SNES Assembly Tutorial (Written By MarcTheMER)
 http://snescentral.edgeemu.com/Development/Tutorials/01asm.htm
bulletSize of "A" register is affected by "bit 5: Memory/Accumlator size" of processor status.
bulletSize of "X" and "Y" registers is affected by "bit 4: X/Y Register size" of processor status.

Jump/Call opecodes

Op Name Op. size  
4C JMP a 3  
6C JMP(a) 3  
7C JMP(a,x) 3  
5C JMP al 4  
80 BRA r 2  
82 BRL rl 3  
90 BCC r 2  
B0 BCS r 2  
F0 BEQ r 2  
D0 BNE r 2  
30 BMI r 2  
10 BPL r 2  
70 BVS r 2  
50 BVC r 2  
20 JSR a 3  
FC JSR(a,x) 3  
60 RTS s 1  
22 JSL al 4  
6B RTL s 1  
40 RTI s 1  
bulletSee also "Table 8. Opcode Matrix" (opcodes.txt)
"65816 docs" at http://www.zophar.net/tech/65816.html
bulletSize of jump/call opecodes is NOT affected by processor status register.

MVN/MVP opecode

Spec

MVN: 54 yy xx
MVP: 44 yy xx

[xx:x] → [yy:y]
x+1 → x
y+1 → y
...

Sample (*1)

$00/98BF 54 3F 7E    MVN 3F 7E               A:0003 X:2000 Y:8000 D:0000 DB:7E S:1FCA P:envmxdIzcHC:1008 VC:070 00 FL:65503
$00/98BF 54 3F 7E    MVN 3F 7E               A:0002 X:2001 Y:8001 D:0000 DB:3F S:1FCA P:envmxdIzcHC:1060 VC:070 00 FL:65503
$00/98BF 54 3F 7E    MVN 3F 7E               A:0001 X:2002 Y:8002 D:0000 DB:3F S:1FCA P:envmxdIzcHC:1112 VC:070 00 FL:65503
$00/98BF 54 3F 7E    MVN 3F 7E               A:0000 X:2003 Y:8003 D:0000 DB:3F S:1FCA P:envmxdIzcHC:1164 VC:070 00 FL:65503
$00/98C2 86 10       STX $10    [$00:0010]   A:FFFF X:2004 Y:8004 D:0000 DB:3F S:1FCA P:envmxdIzcHC:1216 VC:070 00 FL:65503
A → 0003
[7E:2000] → [3F:8000], X=2001, Y=8001, A → 0002
[7E:2001] → [3F:8001], X=2002, Y=8002, A → 0001
[7E:2002] → [3F:8002], X=2003, Y=8003, A → 0000
[7E:2003] → [3F:8003], X=2004, Y=8004, A → FFFF

Convert bitmap into bit-plane (*1)

Conversion from 7E:2000 ~ 7E:49FF

$00/98B1 8F 00 80 3F STA $3F8000[$3F:8000]   A:0001 X:4A00 Y:0004 D:0000 DB:7E S:1FCA P:envMxdIzcHC:0100 VC:074 00 FL:65503
$00/98B5 C2 20       REP #$20                A:0001 X:4A00 Y:0004 D:0000 DB:7E S:1FCA P:envMxdIzcHC:0140 VC:074 00 FL:65503
$00/98B7 A6 10       LDX $10    [$00:0010]   A:0001 X:4A00 Y:0004 D:0000 DB:7E S:1FCA P:envmxdIzcHC:0162 VC:074 00 FL:65503
$00/98B9 A0 00 80    LDY #$8000              A:0001 X:2000 Y:0004 D:0000 DB:7E S:1FCA P:envmxdIzcHC:0194 VC:074 00 FL:65503
$00/98BC A9 03 00    LDA #$0003              A:0001 X:2000 Y:8000 D:0000 DB:7E S:1FCA P:eNvmxdIzcHC:0218 VC:074 00 FL:65503
$00/98BF 54 3F 7E    MVN 3F 7E               A:0003 X:2000 Y:8000 D:0000 DB:7E S:1FCA P:envmxdIzcHC:0242 VC:074 00 FL:65503
$00/98BF 54 3F 7E    MVN 3F 7E               A:0002 X:2001 Y:8001 D:0000 DB:3F S:1FCA P:envmxdIzcHC:0294 VC:074 00 FL:65503
$00/98BF 54 3F 7E    MVN 3F 7E               A:0001 X:2002 Y:8002 D:0000 DB:3F S:1FCA P:envmxdIzcHC:0346 VC:074 00 FL:65503
$00/98BF 54 3F 7E    MVN 3F 7E               A:0000 X:2003 Y:8003 D:0000 DB:3F S:1FCA P:envmxdIzcHC:0398 VC:074 00 FL:65503
$00/990E 69 50 00    ADC #$0050              A:49AC X:49AC Y:801C D:0000 DB:3F S:1FCA P:envmxdIzcHC:0432 VC:153 00 FL:65503
$00/9911 AA          TAX                     A:49FC X:49AC Y:801C D:0000 DB:3F S:1FCA P:envmxdIzcHC:0456 VC:153 00 FL:65503
$00/9912 A9 03 00    LDA #$0003              A:49FC X:49FC Y:801C D:0000 DB:3F S:1FCA P:envmxdIzcHC:0470 VC:153 00 FL:65503
$00/9915 54 3F 7E    MVN 3F 7E               A:0003 X:49FC Y:801C D:0000 DB:3F S:1FCA P:envmxdIzcHC:0494 VC:153 00 FL:65503
$00/9915 54 3F 7E    MVN 3F 7E               A:0002 X:49FD Y:801D D:0000 DB:3F S:1FCA P:envmxdIzcHC:0546 VC:153 00 FL:65503
$00/9915 54 3F 7E    MVN 3F 7E               A:0001 X:49FE Y:801E D:0000 DB:3F S:1FCA P:envmxdIzcHC:0598 VC:153 00 FL:65503
$00/9915 54 3F 7E    MVN 3F 7E               A:0000 X:49FF Y:801F D:0000 DB:3F S:1FCA P:envmxdIzcHC:0650 VC:153 00 FL:65503

x register points the range from 2000 to 49FF.

High level procedure entrance and exit

Usually high level procedure entrance/exit can be used to realize a procedure function of C language. Here is an example found in Dungeon Master SNES ver.

Entrance of procedure _03_a2de:

|03/A2DE|        30|0B             |PHD         … PHD Push Direct Register on Stack
|03/A2DF|        30|3B             |TSC         … TSC* Transfer Stack Pointer Register to Accumulator 
|03/A2E0|        30|38             |SEC         … SEC Set Carry Flag
|03/A2E1|        30|E9 F3 00       |SBC #$00F3  … SBC Subtract Memory from Accumulator with Borrow
|03/A2E4|        30|5B             |TCD         … TCD* Transfer Accumulator to Direct Register 
|03/A2E5|        30|69 EC 00       |ADC #$00EC  … ADC Add Memory to Accumulator with Carry
|03/A2E8|        30|1B             |TCS         … TCS* Transfer Accumulator to Stack Pointer Register 

Entrance run example:

$03/A2DE 0B          PHD                     A:C5A2 X:0009 Y:0004 D:1ED1 DB:7E S:1FA5 P:eNvmxdIzCHC:0310 VC:036 00 FL:00
$03/A2DF 3B          TSC                     A:C5A2 X:0009 Y:0004 D:1ED1 DB:7E S:1FA3 P:eNvmxdIzCHC:0348 VC:036 00 FL:00
$03/A2E0 38          SEC                     A:1FA3 X:0009 Y:0004 D:1ED1 DB:7E S:1FA3 P:envmxdIzCHC:0370 VC:036 00 FL:00
$03/A2E1 E9 F3 00    SBC #$00F3              A:1FA3 X:0009 Y:0004 D:1ED1 DB:7E S:1FA3 P:envmxdIzCHC:0392 VC:036 00 FL:00
$03/A2E4 5B          TCD                     A:1EB0 X:0009 Y:0004 D:1ED1 DB:7E S:1FA3 P:envmxdIzCHC:0424 VC:036 00 FL:00
$03/A2E5 69 EC 00    ADC #$00EC              A:1EB0 X:0009 Y:0004 D:1EB0 DB:7E S:1FA3 P:envmxdIzCHC:0446 VC:036 00 FL:00
$03/A2E8 1B          TCS                     A:1F9D X:0009 Y:0004 D:1EB0 DB:7E S:1FA3 P:envmxdIzcHC:0478 VC:036 00 FL:00
$03/A2E9 A0 04 00    LDY #$0004              A:1F9D X:0009 Y:0004 D:1EB0 DB:7E S:1F9D P:envmxdIzcHC:0500 VC:036 00 FL:00

Exit of procedure _03_a2de:

|03/A330|        30|3B             |TSC         … TSC* Transfer Stack Pointer Register to Accumulator  
|03/A331|        30|18             |CLC         … CLC Clear Carry Flag
|03/A332|        30|69 06 00       |ADC #$0006  … ADC Add Memory to Accumulator with Carry
|03/A335|        30|1B             |TCS         … TCS* Transfer Accumulator to Stack Pointer Register
|03/A336|        30|2B             |PLD         … PLD Pull Direct Register from Stack
|03/A337|        30|6B             |RTL         … RTL Return from Subroutine Long 

Exit run example:

$03/A330 3B          TSC                     A:1F82 X:002F Y:1F82 D:1EB0 DB:7E S:1F9D P:envmxdIzcHC:1194 VC:203 00 FL:00
$03/A331 18          CLC                     A:1F9D X:002F Y:1F82 D:1EB0 DB:7E S:1F9D P:envmxdIzcHC:1216 VC:203 00 FL:00
$03/A332 69 06 00    ADC #$0006              A:1F9D X:002F Y:1F82 D:1EB0 DB:7E S:1F9D P:envmxdIzcHC:1238 VC:203 00 FL:00
$03/A335 1B          TCS                     A:1FA3 X:002F Y:1F82 D:1EB0 DB:7E S:1F9D P:envmxdIzcHC:1270 VC:203 00 FL:00
$03/A336 2B          PLD                     A:1FA3 X:002F Y:1F82 D:1EB0 DB:7E S:1FA3 P:envmxdIzcHC:1292 VC:203 00 FL:00
$03/A337 6B          RTL                     A:1FA3 X:002F Y:1F82 D:1ED1 DB:7E S:1FA5 P:envmxdIzcHC:1336 VC:203 00 FL:00

S:1F9D means stack register is equivalent inside procedure between entrance and exit. Of course stack register may be changed while inside procedure. It just means stack register is get back to S:1F9D at exit point ($03/A330) finally.

In this case, a target of explanation procedure is named _03_a2de to identify it. the name _03_a2de brings the start address of the procedure.

In C language representation, here is one example. The procedure content is omitted. This procedure brings 4 parameters. Each one occupies 2 byte. Totally 8 byte occupies. Usually procedure's parameters are brought through stack memory.

void _03_a2de(WORD aa, WORD bb, WORD c, WORD dd) {
  ...
}

Here is a procedure bit to call _03_a2de. In this code, it invokes 4 push instructions, then call the procedure, then invoke 4 pop instructions (release the parameters without fetching it). The caller seems to have responsibility to manage the allocation and release of parameters.
Each push and pop instruction seems to access the memory in word unit (2 bytes once).

|03/C533|         6|48             |PHA          … PHA Push Accumulator on Stack
|03/C534|         6|F4 DA CC       |PEA $CCDA    … PEA Push Effective Absolute Address on Stack (or Push Immediate Data on Stack)
|03/C537|         6|AE 5C C2       |LDX $C25C    … LDX Load Index X with Memory
|03/C53A|         6|AD 5A C2       |LDA $C25A    … LDA Load Accumulator with Memory
|03/C53D|         6|DA             |PHX          … PHX Push Index X on Stack
|03/C53E|         6|48             |PHA          … PHA Push Accumulator on Stack
|03/C53F|         6|22 DE A2 03    |JSL $03A2DE  … JSL** Jump Subroutine Long
|       |          |               |L802.3:
|03/C543|         6|7A             |PLY          … PLY Pull Index Y form Stack
|03/C544|         6|7A             |PLY          … PLY Pull Index Y form Stack 
|03/C545|         6|7A             |PLY          … PLY Pull Index Y form Stack 
|03/C546|         6|7A             |PLY          … PLY Pull Index Y form Stack 

Call run sample:

$03/C533 48          PHA                     A:0003 X:0000 Y:0004 D:1ED1 DB:7E S:1FB0 P:envmxdIzCHC:1356 VC:035 00 FL:65535
$03/C534 F4 DA CC    PEA $CCDA  [$7E:CCDA]   A:0003 X:0000 Y:0004 D:1ED1 DB:7E S:1FAE P:envmxdIzCHC:0026 VC:036 00 FL:65535
$03/C537 AE 5C C2    LDX $C25C  [$7E:C25C]   A:0003 X:0000 Y:0004 D:1ED1 DB:7E S:1FAC P:envmxdIzCHC:0074 VC:036 00 FL:65535
$03/C53A AD 5A C2    LDA $C25A  [$7E:C25A]   A:0003 X:0009 Y:0004 D:1ED1 DB:7E S:1FAC P:envmxdIzCHC:0122 VC:036 00 FL:65535
$03/C53D DA          PHX                     A:C5A2 X:0009 Y:0004 D:1ED1 DB:7E S:1FAC P:eNvmxdIzCHC:0170 VC:036 00 FL:65535
$03/C53E 48          PHA                     A:C5A2 X:0009 Y:0004 D:1ED1 DB:7E S:1FAA P:eNvmxdIzCHC:0208 VC:036 00 FL:65535
$03/C53F 22 DE A2 03 JSL $03A2DE[$03:A2DE]   A:C5A2 X:0009 Y:0004 D:1ED1 DB:7E S:1FA8 P:eNvmxdIzCHC:0246 VC:036 00 FL:65535
$03/C543 7A          PLY                     A:1FA3 X:002F Y:1F82 D:1ED1 DB:7E S:1FA8 P:envmxdIzcHC:0020 VC:204 00 FL:65535
$03/C544 7A          PLY                     A:1FA3 X:002F Y:C5A2 D:1ED1 DB:7E S:1FAA P:eNvmxdIzcHC:0064 VC:204 00 FL:65535
$03/C545 7A          PLY                     A:1FA3 X:002F Y:0009 D:1ED1 DB:7E S:1FAC P:envmxdIzcHC:0108 VC:204 00 FL:65535
$03/C546 7A          PLY                     A:1FA3 X:002F Y:CCDA D:1ED1 DB:7E S:1FAE P:eNvmxdIzcHC:0152 VC:204 00 FL:65535
$03/C547 F4 0C 00    PEA $000C  [$7E:000C]   A:1FA3 X:002F Y:0003 D:1ED1 DB:7E S:1FB0 P:envmxdIzcHC:0196 VC:204 00 FL:65535

Memory dump (*4). The stack memory instructions read/write bank 00. The specific area of bank 00 is shadow of bank 7E. See *3

The parameters are stored in following status.

# address value
4 $00/1FAF $0003
3 $00/1FAC $CCDA
2 $00/1FAA $0009
1 $00/1FA9 $C5A2

It points the return address $03/C542. it'll be incremented to the actual address $03/C543 in the process of RTL instruction.

...

Wallset address

I'm in the way to develop ObjDMv1. The problem is which RECT structure in 558 structure is used for rendering D3L,D3R,D3,D2L,D2R,D2,D1L,D1R,D1,D0L or D0R wall.
In current research result, they say it is hard coded such as:

|03/B560|        26|A9 03 00       |LDA #$0003
|03/B563|        26|48             |PHA 
|03/B564|        26|F4 B2 CC       |PEA $CCB2
|03/B567|        26|AE 54 C2       |LDX $C254
|03/B56A|        26|AD 52 C2       |LDA $C252
|03/B56D|        26|DA             |PHX 
|03/B56E|        26|48             |PHA 
|03/B56F|        26|22 84 A2 03    |JSL $03A284          -> L906.1-1
|       |          |               |L799.3:
|03/B573|        26|7A             |PLY 
|03/B574|        26|7A             |PLY 
|03/B575|        26|7A             |PLY 
|03/B576|        26|7A             |PLY 

The $03/CCB2 is a memory address which points the D3L RECT in 558 structure.

Known patterns:

Wall pos. 558 rect addr Hex x1, x2, y1, y2, cx/2, cy, srcx, srcy
D3L 03:CCB2 00 3E 19 4B 30 33 18 00   0, 62, 25, 75, 48, 51, 24,  0
D3R 03:CCBA 68 A6 19 4B 30 33 00 00 104,166, 25, 75, 48, 51,  0,  0
D3 03:CCAA 38 70 19 4B 30 33 10 00  56,112, 25, 75, 48, 51, 16,  0
D2L 03:CCCA 00 38 14 5A 36 47 30 00   0, 56, 20, 90, 54, 71, 48,  0
D2R 03:CCD2 70 A6 14 5A 36 47 00 00 112,166, 20, 90, 54, 71,  0,  0
D2 03:CCC2 2C 7C 14 5A 36 47 0C 00  44,124, 20, 90, 54, 71, 12,  0
D1L 03:CCE2 00 2F 09 77 60 6F 90 00   0, 47,  9,119, 96,111,144,  0
D1R 03:CCEA 78 A7 09 77 60 6F 00 00 120,167,  9,119, 96,111,  0,  0
D1 03:CCDA 18 8F 09 77 60 6F 24 00  24,143,  9,119, 96,111, 36,  0
D0L 03:CCFA 00 17 00 87 0C 88 00 00   0, 23,  0,135, 12,136,  0,  0
D0R 03:CD02 90 A7 00 87 0C 88 00 00 144,167,  0,135, 12,136,  0,  0

Each RECT has 8 bytes. (x0, x1, y0, y1, width/2, height, xoffset, yoffset).

...

Ornate sourceX

D2R:

0485D2(0000,007F,2000,007E,895B,0003,0000,0000,0018,0054,000A) R:03AA21 ->
- 558rect 03:895B
DSP2 c05 <x=0088 y=0025 cx=0010 cy=0025><dst=7E:2C68 src=0A:0000>


D2:

0485D2(0000,007F,2000,007E,8955,0003,0000,0000,0018,0054,000A) R:03AA21 ->
- 558rect 03:8955
DSP2 c05 <x=003C y=0025 cx=0017 cy=0025><dst=7E:2C42 src=0A:0000>


D2L:

0485D2(0000,007F,2000,007E,894F,0003,000F,0000,0018,0054,000A) R:03AA21 ->
- 558rect 03:894F
00 20 25 49 18 25

000F would be the source X offset of the vi altar. In below screenshot, left part of vi alter hides.

00 20 25 49 18 25 is a rect6 structure. It will have same format which ADGE shows at Wall Decoration Graphic Info Editor.

Then where is 000F from?

Probably wall group may affect.

000F is taken at 7th parameter. Track the write access for $E2.

|03/AA09| 8|F4 00 00    |PEA $0000 .8
|03/AA0C| 8|D4 E2       |PEI ($E2) .7
|03/AA0E| 8|D4 F0       |PEI ($F0) .6
|03/AA10| 8|D4 EE       |PEI ($EE) .5
|03/AA12| 8|AE 17 87    |LDX $8717
|03/AA15| 8|AD 15 87    |LDA $8715
|03/AA18| 8|DA          |PHX .4
|03/AA19| 8|48          |PHA .3
|03/AA1A| 8|D4 EC       |PEI ($EC) .2
|03/AA1C| 8|D4 EA       |PEI ($EA) .1
|03/AA1E| 8|22 D2 85 04 |JSL $0485D2

I found interesting code below.

|03/A8D1|         8|A5 FF          |LDA $FF
|03/A8D3|         8|C9 07 00       |CMP #$0007
|03/A8D6|         8|D0 21          |BNE $A8F9            -> L1215.1-2
|       |          |               |L1215.1:
|03/A8D8|         4|A7 EE          |LDA [$EE]
|03/A8DA|         4|29 FF 00       |AND #$00FF
|03/A8DD|         4|48             |PHA 
|03/A8DE|         4|A0 01 00       |LDY #$0001
|03/A8E1|         4|B7 EE          |LDA [$EE],y
|03/A8E3|         4|29 FF 00       |AND #$00FF
|03/A8E6|         4|FA             |PLX 
|03/A8E7|         4|86 F6          |STX $F6
|03/A8E9|         4|38             |SEC 
|03/A8EA|         4|E5 F6          |SBC $F6
|03/A8EC|         4|48             |PHA 
|03/A8ED|         4|A5 E2          |LDA $E2
|03/A8EF|         4|FA             |PLX 
|03/A8F0|         4|86 F6          |STX $F6
|03/A8F2|         4|38             |SEC 
|03/A8F3|         4|E5 F6          |SBC $F6
|03/A8F5|         4|85 E2          |STA $E2
|03/A8F7|         4|80 02          |BRA $A8FB            -> L1216.1-1
|       |          |               |L1215.2:
|03/A8F9|         4|64 E2          |STZ $E2
|       |          |               |L1216.1:

#$0007 would be wall ornate group number. 7 means "D2 Left Wall", if it's right. I tried to read the meaning of above code by rewriting in pseudo C language.

if ($FF == 0x0007) {
  $F6 = ($EE[0] & 0x00FF);
  $F6 = ($EE[1] & 0x00FF) -$F6 -1;
  $E2 = $E2 -$F6 -1;
}
else {
  $E2 = 0;
}

It makes me happy if it can be read like following code.

srcX = Width/2;

...

if (g == 7) {
  cx = X1;
  cx = X2 -cx -1;
  srcX = srcX -cx -1;
}
else {
  srcX = 0;
}

Appendix

*1

bulletPosted source code is disassembled from DM SNES JP 1.0.
Captured/Disassembled by Geiger's Snes9x Debugger.

*2

bulletThe mnemonic and help description in the code bit are brought from mnemonic.txt in "65816 docs" distributed by zophar.net.
http://www.zophar.net/tech/65816.html

*3

bulletSuper NES Programming/SNES memory map
http://en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map

*4

bulletMemory dump and test run result are presented by Geiger's Snes9x Debugger (v1.43.ep9r8).