Something we should probably do is map out the possible "valid" respawn locations from the ROM look-up tables. As you can see in my previous post, each level has a table of "valid" respawn locations that it compares and sets to $17E upon death and restart. All ROM addresses will be >$8000.
Each level you can see where the indirect pointer is indicating ROM space by looking at the death restart routine. There should be an decremental string of values in ROM from $xx (end of level) to $xx (start of level). See the post about "valid" locations.
(Now... I'm at work, so I'm just postulating at the moment and can't verify right now.)
For example, let's say level 3 has a string of values is as follows: (end) 2E, 17, 12, 0A, 07, 04, and (start) 00. The "checkpoint validator" routine increments the ROM lookup value for the table and compares (CMP) until the value is greater than or equal to $17E...
So... If you know your value of $17E gets buffered to $24, when you die it will spawn you at checkpoint $17.
Are you able to rip the spawn location values per level on your own? This should be a valuable tool for your research. To save time with speedruns you should know which value to buffer to and suicide for optimal warping.
I would be able to make a list of spawn locations by mere trial and error but using lua script to set 017E and then dying to see where it takes me and that new value written to 017E would be a spawn point. Since the manipulation of 017E has only occurred where I mentioned the one and only death abuse for a good spawn point would be value 26 in level 4, since this would save a net of 10 seconds or so.
While I compile the spawn point values, the one thing that is most concern for me right now and has the best practical value is to find out why the extra increments occur beyond screen scrolling and reaching scroll barriers. IF we knew why this one location seems special we may be able to apply it to other areas in the game and potentially find another warp location where the data of all level spawn points would be very useful.
3, 7, 11, 14
4, 9, 14, 18
5, 8, 15, 23, 26
5, 10, 15, 20, 26, 33
Just to teach you to get into good habits, start thinking with hexadecimal... When you wrap your mind around thinking only in hexadecimal, it's easier to start thinking in bitwise operations. ...And then all videogames and speedrunning hacking.
Hex is 0-F (0-15). There are 8 bits in a byte, they are ordered as: 76543210 (yes this order). Think of those as "powers" of binary. Binary means two values. 2 to the power of 0 is 1. 2 to the power of 1 is 2... etc. In hex the representation (right to left) goes as: $01, $02, $04, $08, $10, $20, $40, $80. Notice the pattern? Then the bits values are just added together.
Bitwise values go from most valuable to least valuable. NES/Famicom does this with 16-bit (2 byte) addresses as well. This is called "little endian" (ends on the little value -- reading left to right.) So if you are tracing some code in the debugger at address $8000, there may be an assembly opcode addressing it in data as 0080. Now that you know binary, you would know that this is also represented as 00000000 10000000.
Another neat trick is when a videogame thinks about negative values. Values $01-$7F (1 through 127) are considered to be positive. Values $FF-$80 (-1 through -128) are considered to be negative. This is called "two's compliment." Videogames use this all the time!
Hex is easier.
Basically, there are many possible ways to represent negative numbers using only bits. Two's complement has a special property that the curcuits for adding and subtracting are the same for signed numbers and unsigned numbers, which makes things easier, so most computers use it.
If we to consider a hypothetical 4 bit two's complement system, which covers -8 to 7, the numbers would be represented like this:
0000: $0: 0
0001: $1: 1
0010: $2: 2
0011: $3: 3
0100: $4: 4
0101: $5: 5
0110: $6: 6
0111: $7: 7
1000: $8: -8
1001: $9: -7
1010: $A: -6
1011: $B: -5
1100: $C: -4
1101: $D: -3
1110: $D: -2
1111: $F: -1
Notice that at the half-way point, the numbers wrap from the highest possible (7) to the lowest possible (-8).
To prove that addition is the same, let's try adding 5 and -1, which should give us 4.
Here are the bit representations. You can add them just like in decimal, but remember if the number is 2 or 3, you need to carry the 1 to the next place.
Code: Select all
0101 + 1111 ---------- (1)0100
If we extend this to 8 bit numbers, as on the 6502, we get a similar table of values:
0000 0000: $00: 0
0000 0001: $01: 1
0111 1111: $7F: 127
1000 0000: $80: -128
1000 0001: $81: -127
1111 1110: $FE: -2
1111 1111: $FF: -1
Each group of 4 bits can be represented as a hex digit.
- Imagine a 4-digit odometer at 0000. If you roll it backwards one step you'll get 9999. Sometimes it's useful to say this 9999 represents -1. For example, on a 4-digit adding machine, if you enter 0007 + 9999 you'll get 0006 because it only keeps 4 digits. The 9999 is effectively working like a -1. You can keep going to represent other negative numbers: 9998 represents -2, 9997 represents -3, and so on.
The same concept works in binary: If you have a binary odometer at 0000 and roll it backwards one step you'll get 1111. Sometimes it's useful to say this 1111 represents -1.
Why is it called two's complement? Here's my hypothesis.
- The Wikipedia article Method of compelments says:
The nines' complement of a number is formed by replacing each digit with nine minus that digit. [...] The nines' complement plus one is known as the tens' complement.I don't know if there's a better-sounding reason why it's called ten's complement other than it's the "nine's complement plus one". I tried searching online a little but didn't find anything.
In binary the concepts are called one's complement (replacing each digit with one minus that digit) and two's complement (the one's complement plus one). Unless someone knows of a better-sounding reason, maybe it's just called two's complement because it's the "one's complement plus one."
Link to Increment - http://wiki.donkeykonggenius.com/Silius_Analysis
Link to Non-Increment - http://wiki.donkeykonggenius.com/~donke ... oldid=2435
Code: Select all
$DF14:20 B5 DF JSR $DFB5 A:60 X:DD Y:00 S:F9 P:nvUbdIzC $DFB5:AD 7B 01 LDA $017B = #$00 A:60 X:DD Y:00 S:F7 P:nvUbdIzC $DFB8:D0 30 BNE $DFEA A:00 X:DD Y:00 S:F7 P:nvUbdIZC $DFBA:A5 30 LDA $0030 = #$60 A:00 X:DD Y:00 S:F7 P:nvUbdIZC $DFBC:4A LSR A:60 X:DD Y:00 S:F7 P:nvUbdIzC $DFBD:4A LSR A:30 X:DD Y:00 S:F7 P:nvUbdIzc $DFBE:4A LSR A:18 X:DD Y:00 S:F7 P:nvUbdIzc $DFBF:4A LSR A:0C X:DD Y:00 S:F7 P:nvUbdIzc $DFC0:85 00 STA $0000 = #$60 A:06 X:DD Y:00 S:F7 P:nvUbdIzc $DFC2:A5 32 LDA $0032 = #$20 A:06 X:DD Y:00 S:F7 P:nvUbdIzc $DFC4:29 F0 AND #$F0 A:20 X:DD Y:00 S:F7 P:nvUbdIzc $DFC6:05 00 ORA $0000 = #$06 A:20 X:DD Y:00 S:F7 P:nvUbdIzc $DFC8:85 00 STA $0000 = #$06 A:26 X:DD Y:00 S:F7 P:nvUbdIzc $DFCA:29 01 AND #$01 A:26 X:DD Y:00 S:F7 P:nvUbdIzc $DFCC:AA TAX A:00 X:DD Y:00 S:F7 P:nvUbdIZc $DFCD:46 00 LSR $0000 = #$26 A:00 X:00 Y:00 S:F7 P:nvUbdIZc $DFCF:A4 00 LDY $0000 = #$13 A:00 X:00 Y:00 S:F7 P:nvUbdIzc $DFD1:B1 54 LDA ($54),Y @ $806A = #$21 A:00 X:00 Y:13 S:F7 P:nvUbdIzc $DFD3:3D 6B E0 AND $E06B,X @ $E06B = #$F0 A:21 X:00 Y:13 S:F7 P:nvUbdIzc $DFD6:E0 01 CPX #$01 A:20 X:00 Y:13 S:F7 P:nvUbdIzc $DFD8:F0 04 BEQ $DFDE A:20 X:00 Y:13 S:F7 P:NvUbdIzc $DFDA:4A LSR A:20 X:00 Y:13 S:F7 P:NvUbdIzc $DFDB:4A LSR A:10 X:00 Y:13 S:F7 P:nvUbdIzc $DFDC:4A LSR A:08 X:00 Y:13 S:F7 P:nvUbdIzc $DFDD:4A LSR A:04 X:00 Y:13 S:F7 P:nvUbdIzc $DFDE:29 0F AND #$0F A:02 X:00 Y:13 S:F7 P:nvUbdIzc $DFE0:C9 0F CMP #$0F A:02 X:00 Y:13 S:F7 P:nvUbdIzc $DFE2:F0 07 BEQ $DFEB A:02 X:00 Y:13 S:F7 P:NvUbdIzc $DFE4:8D 77 01 STA $0177 = #$02 A:02 X:00 Y:13 S:F7 P:NvUbdIzc $DFE7:EE 7E 01 INC $017E = #$08 A:02 X:00 Y:13 S:F7 P:NvUbdIzc
The value of $0030 makes the difference here. It gets loaded to the A register then bitwise AND is executed between that and 0xF0. The result is compared to the value of $0002 (which is likely some temp variable), and depending on which is bigger, the game either jumps to the subroutine than increments your number, of continues normal execution. The glitch succeeds if $0030 is 0x60, but not if it's 0x5F.
Code: Select all
$DF0C:A5 30 LDA $0030 = #$60 A:50 $DF0E:29 F0 AND #$F0 A:60 $DF10:C5 02 CMP $0002 = #$50 A:60 $DF12:F0 03 BEQ $DF17 A:60 $DF14:20 B5 DF JSR $DFB5 A:60
Code: Select all
$DF0C:A5 30 LDA $0030 = #$5F A:50 $DF0E:29 F0 AND #$F0 A:5F $DF10:C5 02 CMP $0002 = #$50 A:50 $DF12:F0 03 BEQ $DF17 A:50 $DF17:A5 30 LDA $0030 = #$5F A:50
And I can tell you that the answer is yes to both! I found another 017E memory address manipulation point in Level 3, under the same conditions as Level 4. And then I found that if I jumped straight up and did a one frame input to the left that my speed will maintain 1 every frame until I land. This made it so that we can effectually "convert" whatever micro frames (x position low byte) we have left to address 017E, the address that tracks your progress through the stage. Enjoy!