11 March 2016 - Forum Rules

Main Menu

NES ASM pointer question

Started by Pikachumanson, December 02, 2012, 03:07:55 AM

Previous topic - Next topic


Ok I have finally gotten my DTE routine to compile... YESSSS!!!! But now I have a question on what to with it since I have to tweak it a little to fit the game I am working on.
I have found the first line of text in Outlanders for the nes and the line of code I am looking at is this:

C965:D5 CA     CMP $CA,X @ $01C9 = #$00CMP $CA,X @ $01C9 = #$00

That is the first two letters of the intro
and over here is the next two letters:

C967:F5 C6     SBC $C6,X @ $01C5 = #$35

Since, the NES is little endian I am assuming that $01C9 and $01C5 are actually C901 and C501 in the hex editor right?
Would those be pointers and if so which one do I have my LDY statement load first?

0001C975 where this located in the hex editor
I believe it's in Rom Bank 0001C000
Here is the actual routine(s) I want to use.
I worked it from that Shin Megami dte txt that Ghideon Zhi(or was Gil Galad?) uploaded.

.org $8000

DTE_Routine: NOP
  LDY $0658        ;<-----This value has to be changed but I am not sure how I should do it
  LDA ($BE),Y      ;This is the second.
  CMP #$30         ;DTE Range compare 1
  BCC End          ;If A is < #$30, skip DTE (Branch if carry clear)
  CLC              ;Clear the carry flag - we get here because
  CMP #$80         ;DTE Range Compare 2
  BCS End          ;If A is >=#$80, skip DTE (Branch if carry set)
                   ;If we get past the upper and lower bound range compares, we have DTE!
RunCheck: NOP
  STX $07FE        ;Store X temporarily to THE DEPTHS OF UNUSED RAM
  SBC #$30
  LDX $07FF        ;Load X with a test byte from THE DEPTHS OF UNUSED RAM
  CPX #$9D         ;Compare it with test=true (#$9D in this case)
  BEQ SecondRun    ;If test=true, we're printing the second character
  BNE FirstRun     ;If test=false, we're printing the FIRST character.
FirstRun:  NOP     ;For outputting the first character...
  LDX #$9D         ;Load X with Test=true
  DEC $0658        ;Decrement load index (which gets transferred into Y)
  ASL A            ;Double A to get DTE Pair Number
  JMP Base_Routine ;Jump to the shared bits of the routine (4C2780)
SecondRun: NOP
  ASL A            ;Double A for DTE Pair Number
  CLC              ;Clear the carry cuz we're gonna add
  ADC #$01         ;Increment the accumulator
                   ;A is now the index location of the second byte in the DTE Pair.
  LDX #$00         ;Load X with Test=FALSE
Base_Routine: NOP
  STX $07FF        ;Store test byte to THE DEPTHS OF UNUSED RAM
  TAX              ;Transer A to X for the DTE table lookup
  LDA Create_DTE_Table,X  ;Load A with where ever your DTE table happens to be,X
  LDX $07FE       ;Restore X from before routine
End: NOP
  JMP $AF29        ;Jump back to the routine
Create_DTE_Table: NOP
.DSW 80            ;Create an 80 ($50) byte empty DTE table to be later filled in
                   ;This gets put -immediately- after the routine.2389


Wow I don't know which simulator you're using but apprently it's a very bad one.
If the indexing of a zero page indexed opcodes overflows from zero page it looks back in zero page and you can never adress page 1 that way.

In this case if X = FF :
cmp $ca,X

will compare with $c9, NOT with $1c9.

cmp $00ca,X
WILL compare with $1ca


 Thank you. Can you explain how to find number comparisons like that?


This is what my DTE code usually looks like when I code one.

0F:C1B6:AD FF 06  LDA $06FF = #$00   ;
0F:C1B9:D0 1A     BNE $C1D5    ; If 2nd_Pass = 0 Then
0F:C1BB:B1 81     LDA ($81),Y @ $8478 = #$8D orig  ; A = vbl_82|vbl_81[Y++]
0F:C1BD:C8        INY    orig  ;
0F:C1BE:C9 80     CMP #$80     ;
0F:C1C0:B0 01     BCS $C1C3     ;
0F:C1C2:60        RTS      ;
0F:C1C3:C9 E0     CMP #$E0     ;
0F:C1C5:B0 FB     BCS $C1C2     ; If A >= 80 And A < E0 Then
0F:C1C7:EE FF 06  INC $06FF = #$00     ; 2nd_Pass = 1
0F:C1CA:38        SEC       ;
0F:C1CB:E9 80     SBC #$80      ;
0F:C1CD:0A        ASL       ;
0F:C1CE:AA        TAX       ;
0F:C1CF:BD 40 BD  LDA $BD40,X @ $BD40 = #$5E    ; A = tbl_bd40[(A - 80) * 2]
0F:C1D2:A2 00     LDX #$00      ; X = 0
0F:C1D4:60        RTS     Else
0F:C1DD:CE FF 06  DEC $06FF = #$00    ; 2nd_Pass = 0
                  DEY      ;
0F:C1E2:B1 81     LDA ($81),Y @ $8478 = #$8D   ; A = vbl_82|vbl_81[--Y]
0F:C1E4:C8        INY      ;
0F:C1E5:38        SEC      ; Y++
0F:C1E6:E9 80     SBC #$80     ;
0F:C1E8:0A        ASL      ;
0F:C1E9:AA        TAX      ;
0F:C1EA:BD 41 BD  LDA $BD41,X @ $BD41 = #$01   ; A = tbl_bd40[(A - 80) * 2 + 1]
0F:C1FA:60        RTS     Return


What game is it for of you don't mind me asking? Right now, I am looking over the debugger to see how my values should correspond with the code. It's confusing because while I know where my text starts, I am not sure how what values go where in the code. From what I've seen CMP #$80 seems like the norm in DTE so I know I won't have to change that. And I see you have CMP #$E0 where as I was using CMP #$30. Does that matter? Basically what I am confused about is what does the first line LDA $06FF or in case LDY $0658  supposed to signify? Am I loading A or Y to an offset or an empty line of code or something?


I don't remember and it's not really important as I've stripped the code down and removed the game specific hacks and have what is essentially a "base" routine. Most games will require tweaks of some sort so the code works properly.

In my case, I am seeing if $06FF is anything other than 0 and if so I am branching to the second pass routine. $06FF is an unused byte in RAM that I chose because I need to store a variable there. You will see $06FF a few places in the code; once to initialize it and another time to reset it once the DTE process is finished.

You should look at Kingmike's DTE doc.


Quote from: Bregalad on December 02, 2012, 12:05:58 PM
Wow I don't know which simulator you're using but apprently it's a very bad one.
Looks like FCUEX's debugger display, which can be... quirky. Doesn't mean that the actual emu doesn't wrap around, though.

But I'm confused. How do you mean that this is the first four letters of the intro? Are we looking at data or code?
C965:D5 CA     CMP $CA,X @ $01C9 = #$00
C967:F5 C6     SBC $C6,X @ $01C5 = #$35

While it's possible, this doesn't really look like code to me. It looks like data. If that's the case, $01C9 and $01C5 (or $C9 and $C5) don't come into play at all since those addresses come from adding completely unrelated numbers to each other. But if I'm severely misunderstanding the situation, just ignore me.

On another note: you really don't need those NOPs after every label. I know somebody suggested that, but that's not what fixed your problem.

Also, when the debugger does show an address in the disassembler, you don't need to reverse the bytes. Whenever you see anything like $ABCD, that's the actual address or value. You only need to swap the byte order when converting between actual adresses/values and raw binary (raw binary meaning hex editor or program code that works with individual bytes).


Thank you Pennywise and Bregalad! Ok, I got my code the way I want it... now one question if anyone doesn't mind. How do I patch the bin to the rom? How you guys go about it? I know some of you code your own ips patcher into the assembler but I was wondering if there was some other way to do it.


You might just want to try inserting the code by hand. If this helps you, I'll try putting general steps in coding a DTE routine.

Step 1: Locate the text read routine. This is done by setting a read breakpoint for a text offset in the PRG-RAM ($8000-$FFFF).
Step 2: Locate free space in the ROM to put your code. The ideal place is in the fixed bank of the ROM and it is common for games to have a bunch of unused FF's at the end of the ROM. However, some games are packed so tightly, there is no extra space to put your code. Such a situation more or less rules out DTE for something else. If the mapper supports additional expansion, PRG-ROM expansion is the way to go.
Step 3: JSR from the game's text read routine to the free where the code will be.
Step 4: Locate a free byte in RAM for what is essentially a DTE second pass check byte. It determines whether or not to branch to the second part of the routine that loads the second and final character for the "compression". Your free byte can be anywhere in RAM that appears unused. A general rule of thumb is to avoid zero page ($00-FF) due it's dynamic nature and pick somewhere in the end of RAM ($0600-07FF).
Step 5: Add the original read routine to the game followed by the DTE check. Your are checking to see if A is between a range of values ie #$80-E0 and if so we are branching to the DTE table load routine aka the first pass.

Is this information of use to you or is it redundant to you? I have a hard gauging what you know etc.


It's redundant unfortunately. It's ok to lay on the moonspeak and advanced techniques on me. If I don't know something I will research it and then ask questions.I tried doing what you said before on fceuxd but it doesn't accept branching operations in it's mini assembler. This is pretty much what's holding me back. Although Snarfblam was gracious enough to let me see his asm program that creates ips patches so I'm hoping that works out. But I'm interested in other techniques applying the code to the rom. Should I just disassemble the whole rom and then use notepad to add my routines in? If so then I guess I will just have to rewrite the the whole code from the original rom to make sure it compiles right and even the how would I go about turning a bin file into a rom? Just change the extension?


I don't disassemble entire ROM's. It's really not worth the time and effort especially for translations. Through debugging and logging the code, I isolate the code I want to hack and go from there. For years, all I did reverse-engineer the code in real-time and then hack it to my needs.  I pretty much did everything by hand, the old-fashioned way and it never gave me any problems. These days for any new or modified routines, I just create an ASM file and run it through ASM6 to produce a binary file which I then insert into the ROM with Windhex.