Here is the explanation how this works when you use JSL > RTS:
Code
PHK ;\This will push the 24bit address location
PEA.w .jslrtsreturn1 ;/after the JML (below) into the stack*
PEA $yyyy1 ;>This modifies the RTS in the pointed routine (below) to jump to an RTL in same bank.*
;^This RTL then pulls the stack (which is the 24bit address) to jump to a location after the JML
JML $xxxxxx ;>The desired routine that ends with RTS
.jslrtsreturn
*Means that when the PC (program counter) "returns" (weather RTS or RTL), it pulls the stack + 1. I've learned it from Ersanio's ASM tutorial (not the SMW version).
Originally posted by Ersanio
About JSR and JSL, and RTS and RTL:
Imagine that there is a JSR opcode located at $8000, and there is an LDA at $8003, and RTS at $B000. A JSR instruction is 3 bytes long (JSR $xxxx). So we have this:
Code
$8000 JSR $B000
$8003 LDA #$01
…
$B000 RTS
Now, what JSR does is, pushing the next instruction’s location  $0001 onto the stack. This means, that the value $8002 is pushed on the stack. Then JSR jumps to the specified address. What RTS does, is pulling that $8002 from the stack and adding $0001 to it, and storing it into the program counter register, causing the program to jump back to the instruction after that JSR.
JSL and RTL work the same way, except these push and pull 24bit addresses. JSL pushes the long address  1 onto the stack and jumps to the specified address, and RTL pulls the long address +1 onto the stack and jump to it.
 Give thanks to RPG hacker for working on Asar.
Count how many bits are set in a specified range of address:
Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Counts how many bits are set in a specified address range.
;
;Input:
; $00$01 = the starting address to count bits, low endian.
; ($AABB > $BB $AA)
;
; $02$03 = the ending address to count bits, same format
; as above.
;
; $04 = the bank byte of the address. The range of bytes to
; check must be in the same bank. if not, try calling this
; subroutine twice or more but with
; "JSL ContinueCountBits" instead.
;
;Output:
;*$05$06 = the number of bits being set.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CountSetBitsAtStart:
STZ $05 ;\Reset counter
STZ $06 ;/
ContinueCountBits:
PHB ;>Just in case
LDA $04 ;\Adjust bank.
PHA ;
PLB ;/
.LoopBytes
LDY #$07 ;>Check each bit in a byte
LDA ($00) ;>Load address stored in $00, A should have a value inside the pointed value
..LoopThroughBits
LSR ;>Shift bit right
BCC ...NextBit ;>If bit moved into carry is clear, don't count.
REP #$20 ;\Increment bit counter
INC $05 ;
SEP #$20 ;/
...NextBit
DEY ;>Next bit
BPL ..LoopThroughBits ;>Keep until full byte has been checked.
.NextByte
REP #$20 ;>16bit A
INC $00 ;>Next address to read
LDA $00 ;>This is so that the branches below works.
CMP $02 ;>The last byte
SEP #$20 ;>8bit A
BEQ .LoopBytes ;\If not past the final byte, loop
BCC .LoopBytes ;/
PLB ;>Restore bank
RTL
This is useful for hacks that have things stored in as bits and you want to count how many. For example, counting how many star coins collected total without having a separate RAM that increments each time you collect (since how levels remember that each star coins being collected and was stored as bits).
Here is how to use:
Code
;Example, checks how many set bits from $7E0060 to $7E0063
REP #$20
LDA #$0060 ;>Start Address
STA $00
LDA #$0063 ;>End address
STA $02
SEP #$20
LDA #$7E ;>Bank address
STA $04
JSL CountSetBits
RTL
Be careful not to have a possibility of having more than 65535 bits set, as the counter is 16bit. Here is an example on how to call.
 Give thanks to RPG hacker for working on Asar.
Honestly think you'd have an easier time passing the length instead of the end address. Like:
(untested code written in 5 minutes, don't kill me if it's actually nonsense.)
Code
;$00 = 3 byte pointer
;A = length  1
;X = 16 bit output counter
Count:
PHP
REP #$30 ; all 16 bit.
TAY ; length in Y
SEP #$20 ; A back to 8 bit
LDX #$0000 ; reset counter
PHB
LDA $02
PHA : PLB
.loop
LDA ($0000),y ; load value
BEQ + ; skip altogether if already empty
 LSR ; shift LSB into carry
BCC ++ ; skip increment if carry clear
INX ; increment
++ CMP #$00 ; \ end once A is 0
BNE  ; /
+ DEY ; more to go?
BPL .loop
PLB
PLP
RTL
Alternatively you could make Y the input for the length and not bother with the TAY thing at all.
Plus, I'm not too sure if TAY doesn't transfer all 16 bits regardless of process flag, so you could potentially skip the REP #$30 : SEP #$20 with just a REP #$10
 Anime statistic on MyAnimeList:
400 animes completed ✓
6000 episodes completed ✓
100 Days completed ✓
... what even am I doing with my life?
When are we need that code?
I'm created many sprites, blocks, and patches so far, but I didn't need that.
Stupid solution (untested)
Code
; Input:
; $00 = 24bit pointer to start address.
; A = number of bytes to count (16bit)
;
; Output:
; $03$04 = number of bits being set
Count: PHX
PHY
PHP
REP #$31
TAY
STZ $03
 LDA [$00],y
AND.w #$00FF
TAX
LDA.l .BitTable,x
AND.w #$00FF
ADC $03
STA $03
DEY
BPL 
PLP
PLY
PLX
RTL
.BitTable
db $00,$01,$01,$02,$01,$02,$02,$03,$01,$02,$02,$03,$02,$03,$03,$04
db $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05
db $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05
db $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06
db $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05
db $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06
db $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06
db $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07
db $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05
db $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06
db $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06
db $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07
db $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06
db $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07
db $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07
db $04,$05,$05,$06,$05,$06,$06,$07,$05,$06,$06,$07,$06,$07,$07,$08
Simple increment/decrement counter effect. Useful if you want to show to the player that something has decreased or increased.
Code
!RAM_Record = $xxxx ;>The value shown that slowly increases/decreases
!RAM_RealVal = $yyyy ;>The real value that isn't shown, but shown "slowly follows" this value.
LDA !RAM_Record
CMP !RAM_RealVal
BEQ .Done
BMI .Increment ;>Feel free to change to BCC if unsigned.
.Decrement
LDA $13 ;>Frame counter
AND #$03 ;>choose any value that is base2 minus 1 (modulo)
BNE .Done ;>If any remainder bits set, don't change.
LDA !RAM_Record
DEC
STA !RAM_Record
BRA .Done
.Increment
LDA $13 ;>Frame counter
AND #$03 ;>choose any value that is base2 minus 1 (modulo)
BNE ..Done ;>If any remainder bits set, don't change.
LDA !RAM_Record
INC
STA !RAM_Record
.Done
[...]
16bit version:
Code
!RAM_Record = $xxxx ;>The value shown that slowly increases/decreases
!RAM_RealVal = $yyyy ;>The real value that isn't shown, but shown "slowly follows" this value.
REP #$20
LDA !RAM_Record
CMP !RAM_RealVal
SEP #$20
BEQ .Done
BMI .Increment ;>Feel free to change to BCC if unsigned.
.Decrement
LDA $13 ;>Frame counter
AND #$03 ;>choose any value that is base2 minus 1 (modulo)
BNE .Done ;>If any remainder bits set, don't change.
REP #$20
LDA !RAM_Record
DEC
STA !RAM_Record
SEP #$20
BRA .Done
.Increment
LDA $13 ;>Frame counter
AND #$03 ;>choose any value that is base2 minus 1 (modulo)
BNE ..Done ;>If any remainder bits set, don't change.
REP #$20
LDA !RAM_Record
INC
STA !RAM_Record
SEP #$20
.Done
[...]
 Give thanks to RPG hacker for working on Asar.
Those are just some parameters for the Merseene Twister's algorithm. N is the degree of recurrence, which can be calculated as desired Mersenne prime (1279) divided by the number of bits in each RNG value (8), rounded up; this value plus 1 also indicates how many values need to be in the SeedData table. M is somewhat freely chosen, just with M < N. Lastly, the MATRIX value is a series of bitwise matrix coefficents, which has a formula that I can't really explain briefly (Wikipedia kinda does). Professional framebyframe time wizard. YouTube  Twitter  SMW Glitch List  SMW Randomizer
This is interesting. But don't the result will come messed up if you don't remove the sign bit in $2251 bit 15 and $2253 bit 15?... How you did the "to unsigned" logic there?
That routine reminds me of... I never managed to make a 32 bit by 16 bit divider (or even 24 bit by 16 bit) using 16 bit by 16 bit divider. But that probably is not possible unfortunately.
This is interesting. But don't the result will come messed up if you don't remove the sign bit in $2251 bit 15 and $2253 bit 15?... How you did the "to unsigned" logic there?
That routine reminds me of... I never managed to make a 32 bit by 16 bit divider (or even 24 bit by 16 bit) using 16 bit by 16 bit divider. But that probably is not possible unfortunately.
this code is implemented by my experience, but I can't prove mathematically why it works fine. I'm silly.
verification of calculate:
Code
1) 0x004C * 0x9A63
Signed : 0xFFE1 D564
Unsigned : 0x002D D564
Signed to Unsigned : 0xFFE1 D564 + 0x004C 0000 > 0x002D D564
2) 0x8765 * 0x4321
Signed : 0xE05F E305
Unsigned : 0x2380 E305
Signed to Unsigned : 0xE05F E305 + 0x4321 0000 > 0x2380 E305
3) 0x8000 * 0xABCD
Signed : 0x2A19 8000
Unsigned : 0x55E6 8000
Signed to Unsigned : 0x2A19 8000 + 0x8000 0000 + 0xABCD 0000 > 55E6 8000
Using an inlined reciprocal square root routine, I've created a pretty accurate aiming routine. Link
In order to use it, set $00 to be the 16bit value (shooter_x  target_x), set $02 to be the 16bit value (shooter_y  target_y), and set A to be the 8bit projectile speed. It will return the projectile's X speed in $00 and the projectile's Y speed in $02. Note that distances over $0100 pixels are not allowed.
Explanation: Suppose one fired the projectile with X speed dx, and Y speed dy. Then its speed would be sqrt(dx^{2}+dy^{2}). Thus, we can adjust its speed by multiplying by speed / sqrt(dx^{2}+dy^{2}). This routine calculates the reciprocal 1 / sqrt(dx^{2}+dy^{2}), multiplies by speed, then multiplies by either dx or dy.
erik edit: fixed link because dropbox
added the sa1 compatible variant (requires a !SA1 detection if you're using spritetool)
Aligned number display (the “X” symbol was meant to be the “/” graphic symbol, but SMW does not have that):
This code displays numbers left or rightaligned with leading zeroes suppressed (no leading zeroes nor spaces). This is useful if you don't want your hack to have a hint on the maximum number of digits the number can have. Very useful for compact display since the “characters” are positioned to the left or right as possible.
Notes:
Prior to writing the numbers to the status bar/overworld border (Overworld Border plus patch), you must have clear the tiles that would be overwritten by the digits so that if they got changed (“10” becomes a “9”), the tile that “disappeared” doesn't end up staying there and be duplicated (“10” becomes “90”, where the “0” tile wasn't cleared)
;SA1 detection (don't touch)
if defined("sa1") == 0
!dp = $0000
!addr = $0000
!sa1 = 0
!gsu = 0
if read1($00FFD6) == $15
sfxrom
!dp = $6000
!addr = !dp
!gsu = 1
elseif read1($00FFD5) == $23
sa1rom
!dp = $3000
!addr = $6000
!sa1 = 1
endif
endif
;RAM locations
if !sa1 == 0
!Scratchram_CharacterTileTable = $7F844A
else
!Scratchram_CharacterTileTable = $400198
endif
;^[X bytes] A table containing strings of "characters"
; (more specifically digits). The number of bytes used
; is how many characters you would write.
; For example:
; If you want to display a 5digit 16bit number 65535,
; that will be 5 bytes.
; If you want to display [10000/10000], that will be
; 11 bytes (there are 5 digits on each 10000, plus 1
; because "/"; 5 + 5 + 1 = 11)
!StatusbarFormat = $02
;^Number of grouped bytes per 8x8 tile:
; $01 = Minimalist/SMB3 [TTTTTTTT, TTTTTTTT]...[YXPCCCTT, YXPCCCTT]
; $02 = Super status bar/Overworld border plus [TTTTTTTT YXPCCCTT, TTTTTTTT YXPCCCTT]...
!StatusBar_UsingCustomProperties = 0
;^Set this to 0 if you are using the vanilla SMW status bar or any status bar patches
; that doesn't enable editing the tile properties, otherwise set this to 1 (you may
; have to edit "!Default_GraphicalBarProperties" in order for it to work though.).
; This define is needed to prevent writing what it assumes tile properties into invalid
; RAM addresses.
!BlankTile = $FC
;^Tile number for where there is no characters to be written for each 8x8 space.
!HexDecDigitTable = $02
if !sa1 != 0
!HexDecDigitTable = $04
endif
Routines:
Code
incsrc "../DisplayStringDefines/Defines.asm"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;16bit hex to 4 (or 5)digit decimal subroutine
;Input:
;$00$01 = the value you want to display
;Output:
;!HexDecDigitTable to !HexDecDigitTable+4 = a digit 09 per byte table (used for
; 1digit per 8x8 tile):
; +$00 = ten thousands
; +$01 = thousands
; +$02 = hundreds
; +$03 = tens
; +$04 = ones
;
;!HexDecDigitTable is address $02 for normal ROM and $04 for SA1.
;
;Note: Because SA1's multiplication/division registers are signed,
;values over 32,767 ($7FFF) will glitch when you patch SA1 on your
;game. Therefore, I added a Sa1 detection to use an unsigned division
;as the SNES registers become inaccessible on SA1 mode.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ConvertToDigits:
if !sa1 == 0
PHX
PHY
LDX #$04 ;>5 bytes to write 5 digits.
.Loop
REP #$20 ;\Dividend (in 16bit)
LDA $00 ;
STA $4204 ;
SEP #$20 ;/
LDA.b #10 ;\base 10 Divisor
STA $4206 ;/
JSR .Wait ;>wait
REP #$20 ;\quotient so that next loop would output
LDA $4214 ;the next digit properly, so basically the value
STA $00 ;in question gets divided by 10 repeatedly. [Value/(10^x)]
SEP #$20 ;/
LDA $4216 ;>Remainder (mod 10 to stay within 09 per digit)
STA $02,x ;>Store tile
DEX
BPL .Loop
PLY
PLX
RTL
.Wait
JSR ..Done ;>Waste cycles until the calculation is done
..Done
RTS
else
PHX
PHY
LDX #$04
.Loop
REP #$20 ;>16bit XY
LDA.w #10 ;>Base 10
STA $02 ;>Divisor (10)
SEP #$20 ;>8bit XY
JSL MathDiv ;>divide
LDA $02 ;>Remainder (mod 10 to stay within 09 per digit)
STA.b !HexDecDigitTable,x ;>Store tile
DEX
BPL .Loop
PLY
PLX
RTL
endif
if !sa1 != 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; unsigned 16bit / 16bit Division
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Arguments
; $00$01 : Dividend
; $02$03 : Divisor
; Return values
; $00$01 : Quotient
; $02$03 : Remainder
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MathDiv: REP #$20
ASL $00
LDY #$0F
LDA.w #$0000
 ROL A
CMP $02
BCC +
SBC $02
+ ROL $00
DEY
BPL 
STA $02
SEP #$20
RTL
endif
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Suppress Leading zeros via leftaligned positioning
;
;This routines takes a 16bit unsigned integer (works up to 5 digits),
;suppress leading zeros and moves the digits so that the first nonzero
;digit number is located where X is indexed to. Example: the number 00123
;with X = $00:
;
; [0] [0] [1] [2] [3]
;
; Each bracketed item is a byte storing a digit. The X above means the X
; index position.
; After this routine is done, they are placed in an address defined
; as "!Scratchram_CharacterTileTable" like this:
;
; X
; [1] [2] [3] [*] [*]...
;
; [*] Means garbage and/or unused data. X index is now set to $03, shown
; above.
;
;Usage:
; Input:
; !HexDecDigitTable to !HexDecDigitTable+4 = a 5digit 09 per byte (used for
; 1digit per 8x8 tile, using my 4/5 hexdec routine; ordered from high to low digits)
; X = the location within the table to place the string in.
; Output:
; !Scratchram_CharacterTileTable = A table containing a string of numbers with
; unnecessary spaces and zeroes stripped out.
; X = the location to place string AFTER the numbers. Also use for
; indicating the last digit (or any tile) number for how many tiles to
; be written to the status bar, overworld border, etc.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SupressLeadingZeros:
LDY #$00 ;>Start looking at the leftmost (highest) digit
LDA #$00 ;\When the value is 0, display it as single digit as zero
STA !Scratchram_CharacterTileTable,x ;/(gets overwritten should nonzero input exist)
.Loop
LDA.w !HexDecDigitTable!dp,Y ;\If there is a leading zero, move to the next digit to check without moving the position to
BEQ ..NextDigit ;/place the tile in the table
..FoundDigit
LDA.w !HexDecDigitTable!dp,Y ;\Place digit
STA !Scratchram_CharacterTileTable,x ;/
INX ;>Next string position in table
INY ;\Next digit
CPY #$05 ;
BCC ..FoundDigit ;/
RTL
..NextDigit
INY ;>1 digit to the right
CPY #$05 ;\Loop until no digits left (minimum is 1 digit)
BCC .Loop ;/
INX ;>Next item in table
RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Convert leftaligned to rightaligned.
;
;Use this routine after calling SupressLeadingZeros and before calling
;WriteStringDigitsToHUD. Note: Be aware that the math of handling the address
;does NOT account to changing the bank byte (address $XX****), so be aware of
;having status bar tables that crosses bank borders ($7EFFFF, then $7F0000,
;as an madeup example, but its unlikely though).
;
;Input:
; $00$02 = 24bit address location to write to status bar tile number.
; If tile properties are editable:
; $03$05 = Same as $00$02 but tile properties.
; $06 = the tile properties.
; X = The number of characters to write, ("123" would have X = 3)
;Output:
; $00$02 and $03$05 are subtracted by [(NumberOfCharacters1)*!StatusbarFormat]
; so that the last character is always at a fixed location and as the number
; of characters increase, the string would extend leftwards. Therefore,
; $00$02 and $03$05 before calling this routine contains the ending address
; which the last character will be written.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ConvertToRightAligned:
TXA
DEC
TAY ;>Transfer status bar leftmost position to Y
BRA +
ConvertToRightAlignedFormat2:
TXA
DEC
ASL
TAY ;>Transfer status bar leftmost position to Y
+
REP #$21 ;\(NumberOfTiles1)...
AND #$00FF ;
EOR #$FFFF ;
INC A ;/
ADC $00 ;>...+LastTilePos (we are doing LastTilePos  (NumberOfTiles1))
STA $00 ;>Store difference in $00$01
SEP #$20 ;\Handle bank byte
; LDA $02 ;
; SBC #$00 ;
; STA $02 ;/
if !StatusBar_UsingCustomProperties != 0
TYA
DEC
ASL
REP #$21 ;\(NumberOfTiles1)
AND #$00FF ;
EOR #$FFFF ;
INC A ;/
ADC $03 ;>+LastTilePos (we are doing LastTilePos  (NumberOfTiles1))
STA $03 ;>Store difference in $00$01
SEP #$20 ;\Handle bank byte
; LDA $05 ;
; SBC #$00 ;
; STA $05 ;/
endif
RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Write aligned digits to Status bar/OWB+
;
;Input:
; $00$02 = 24bit address location to write to status bar tile number.
; If tile properties are editable:
; $03$05 = Same as $00$02 but tile properties.
; $06 = the tile properties.
; X = The number of characters to write, ("123" would have X = 3)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
WriteStringDigitsToHUD:
DEX
TXY
.Loop
LDA !Scratchram_CharacterTileTable,x
STA [$00],y
if !StatusBar_UsingCustomProperties != 0
LDA $06
STA [$03],y
endif
DEX
DEY
BPL .Loop
RTL
WriteStringDigitsToHUDFormat2:
DEX
TXA ;\SSB and OWB+ uses a byte pair format.
ASL ;
TAY ;/
.Loop
LDA !Scratchram_CharacterTileTable,x
STA [$00],y
if !StatusBar_UsingCustomProperties != 0
LDA $06
STA [$03],y
endif
DEX
DEY #2
BPL .Loop
RTL
Example:
Code
incsrc "../DisplayStringDefines/Defines.asm"
!MaxChar = 5
;^Max number of characters to write, also how many tiles to clear
; so that no leftover tiles appear when it should disappear.
; i.e "65535/65535" is 11 characters.
!AlignMode = 1
;0 = leftaligned
;1 = rightaligned
!Freeram_FirstNumb = $60
!Freeram_SecondNumb = $62
;^[2 bytes] the numbers to be displayed.
if !AlignMode == 0
if !sa1 == 0
!StatusBarPos = $7FA000
else
!StatusBarPos = $404000
endif
else
if !sa1 == 0
!StatusBarPos = $7FA03E
else
!StatusBarPos = $40403E
endif
endif
;^Status bar position to write. When leftaligned, this represents the first tile address,
; if rightaligned, this is the last tile position (will occupy tiles this and BEFORE it).
main:
LDA $15 ;\These are to increase or decrease number via controller
BIT.b #%00001000 ;For testing numbers when they move to another tile when
BNE .Up ;the number of digits changes.
BIT.b #%00000100 ;UP/DOWN = FirstNumb
BNE .Down ;Left/RIGHT = SecondNumb
BRA +
.Up
REP #$20
LDA !Freeram_FirstNumb
INC A
STA !Freeram_FirstNumb
SEP #$20
BRA +
.Down
REP #$20
LDA !Freeram_FirstNumb
DEC A
STA !Freeram_FirstNumb
SEP #$20
+
LDA $15
BIT.b #%00000001
BNE .Left
BIT.b #%00000010
BNE .Right
BRA +
.Left
REP #$20
LDA !Freeram_SecondNumb
INC A
STA !Freeram_SecondNumb
SEP #$20
BRA +
.Right
REP #$20
LDA !Freeram_SecondNumb
DEC A
STA !Freeram_SecondNumb
SEP #$20
+
.StatusBarRemoveFrozenTiles
LDA #$FC ;\Clear out tiles so if the digit
LDX.b #(!MaxChar1)*!StatusbarFormat ;were to disappear, does not leave

if !AlignMode == 0 ;duplicated digits or frozen tiles.
STA !StatusBarPos,x ;
else ;
STA !StatusBarPos((!MaxChar1)*!StatusbarFormat),x ;
endif ;
DEX #2 ;
BPL  ;/
.WriteStatusBar
LDA !Freeram_FirstNumb ;\First number HEX>DEC
STA $00 ;
LDA !Freeram_FirstNumb+1 ;
STA $01 ;
JSL Routines_ConvertToDigits ;/
LDX #$00 ;>Initialize string position
JSL Routines_SupressLeadingZeros ;>Place only the necessary digits in the string table
LDA #$26 ;\The number separator (its "X" by default, acting as a "/")
STA !Scratchram_CharacterTileTable,x ;/
INX ;>Increment X to write next number preceding it
LDA !Freeram_SecondNumb ;\Do the same thing as above, this time without initalizing X
STA $00 ;so that it places the second number after the "/".
LDA !Freeram_SecondNumb+1 ;
STA $01 ;
JSL Routines_ConvertToDigits ;
JSL Routines_SupressLeadingZeros ;/
CPX.b #!MaxChar+1 ;\If there are more characters than the max, skip status bar write.
BCS ..TooMuch ;/
LDA.b #!StatusBarPos : STA $00 ;\Set address to write at a given status bar position.
LDA.b #!StatusBarPos>>8 : STA $01 ;
LDA.b #!StatusBarPos>>16 : STA $02 ;/
if !AlignMode != 0
if !StatusbarFormat == $01 ;\These offset the write position based on how many
JSL Routines_ConvertToRightAligned ;characters so that it is rightaligned.
else
JSL Routines_ConvertToRightAlignedFormat2 ;
endif ;/
endif
if !StatusbarFormat == $01 ;\Write to status bar
JSL Routines_WriteStringDigitsToHUD ;
else
JSL Routines_WriteStringDigitsToHUDFormat2 ;
endif ;/
..TooMuch
RTL
Note that this is tested using uberasm tool, so they call via JSL FileNameWithoutExtension_Label
 Give thanks to RPG hacker for working on Asar.
My 32bit frame counter to “hours:minutes:seconds.centiseconds” (centiseconds is actually a frame counter from 0 to 59; converted to 0 to 99). Give credit to Akaginite for the division routine.
Code
;Ram Addresses
!Scratchram_Frames2TimeOutput = $7F844E
;^[4 bytes], the output in HH:MM:SS.CC format:
;+$00 = hour
;+$01 = minutes
;+$02 = seconds
;+$03 = centiseconds (display 0099)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Convert frames to timer, By GreenHammerBro
; This routine converts frames into: HH:MM:SS.CC
;
;Unlike my previous stopwatch timer (rejected) and imamelia's,
;the 3 byte of RAM now stores ONE value; the 24bit frame
;counter, rather than the 3 units of time in each byte.
;
;Input:
;$00 to $03: the frame value.
;Output:
;!Scratchram_Frames2TimeOutput (4 bytes): time in real world
; units, mentioned on the above define.
;Overwritten:
;$00 to $05 was used by division routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Frames2TimerMain:
LDX #$03
.Loop
REP #$20 ;\divide by 60
LDA.w #60 ;
STA $04 ;
SEP #$20 ;/
JSL MathDiv32_16 ;>$00 = quotient (increases every 60 units), $04 = remainder (counter loops 0059)
CPX #$01 ;\allow hours to go above 59
BEQ .UnCappedHours ;/
LDA $04 ;\store looped value (frames, seconds and minutes loop 0059)
STA !Scratchram_Frames2TimeOutput,x ;/
..Next
DEX
BRA .Loop
.UnCappedHours
LDA $00 ;\write hours
STA !Scratchram_Frames2TimeOutput ;/
.ConvertFramesToCentiseconds
;simply put [Frames*100/60], highest [Frames*100 should be 5900]
if !sa1 == 0
LDA !Scratchram_Frames2TimeOutput+3 ;\Frames*100
STA $4202 ;
LDA.b #100 ;
STA $4203 ;/
JSR WaitCalculation ;>Wait 12 cycles in total (8 is minimum needed)
REP #$20
LDA $4216 ;>load product
STA $4204 ;>Product in dividend
SEP #$20
LDA.b #60 ;\product divide by 60 (divisor)
STA $4206 ;/
JSR WaitCalculation ;>Wait 12 cycles (16 is minimum needed)
NOP #2 ;>wait 4 cycles (16 cycles total)
LDX $4214 ;>quotient
LDA $4216 ;\if remainder is less than half the divisor, round down
else
STZ $2250 ;\>multiply mode
LDA !Scratchram_Frames2TimeOutput+3 ;Frames*100
STA $2251 ;
STZ $2252 ;
LDA.b #100 ;
STA $2253 ;
STZ $2254 ;/>this should start the calculation
NOP ;\Wait 5 cycles
BRA $00 ;/
REP #$20
LDA $2306 ;\backup the value in case of setting $2250 to divide
STA $00 ;/causes $2306 to lose its product
LDX #$01 ;\divide mode
STX $2250 ;/
LDA $00 ;\product divide by...
STA $2251 ;/
SEP #$20
LDA.b #60 ;\60
STA $2253 ;/
STZ $2254 ;>this triggers the calculation to run
NOP ;\Wait 5 cycles
BRA $00 ;/
LDX $2306 ;>Quotient
LDA $2308
endif
CMP.b #30
BCC ..NoRound
..Round
INX ;>round up quotient
..NoRound
TXA
STA !Scratchram_Frames2TimeOutput+3
RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Unsigned 32bit / 16bit Division
; By Akaginite (ID:8691), fixed the overflow
; bitshift by GreenHammerBro (ID:18802)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Arguments
; $00$03 : Dividend
; $04$05 : Divisor
; Return values
; $00$03 : Quotient
; $04$05 : Remainder
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MathDiv32_16: REP #$20
ASL $00
ROL $02
LDY #$1F
LDA.w #$0000
 ROL A
BCS +
CMP $04
BCC ++
+ SBC $04
SEC
++ ROL $00
ROL $02
DEY
BPL 
STA $04
SEP #$20
RTL
if !sa1 == 0
WaitCalculation: ;>The register to perform multiplication and division takes 8/16 cycles to complete.
RTS
endif
If you input a value #$0004F1A0 (324000 decimal frames: 1 hour and 30 minutes), it outputs:
Code
!Scratchram_Frames2TimeOutput is 1, the hour
!Scratchram_Frames2TimeOutput+1 is 30 (#$1E in hex) the minutes
!Scratchram_Frames2TimeOutput+2 is 00, this is the seconds
!Scratchram_Frames2TimeOutput+3 is 00, this is the approximate centiseconds.
Not to be confused with the old timer generators found on the sprite section (actually rejected due to smwc updating sections when UberasmTool hit the scene), this one is different:
The value representing the amount of time is a single value stored in 32bit (low endian; like the SNES would do) indicating a frame counter. The other codes that tries to replicate the real world timer stores 3 units of time in each byte: minutes:seconds:frames, where each value are handled individually rather than one, so if a value were to go past a certain threshold, say if frames reaches 60, it increases the seconds byte, and of course if it was a countdown, (frames goes down past 00), it would decrease the seconds byte (remember that the game runs at 60FPS).
Using my method, it makes it easier if you are manipulating time using math operators, such as adding a minute to the countdown, you simply use this code:
Code
REP #$20
LDA !Freeram_TimeFrames
CLC
ADC.w #3600 ;>3600 frames = 1 minute (60frames/seconds times 60)
STA !Freeram_TimeFrames
LDA !Freeram_TimeFrames+2
ADC #$0000
STA !Freeram_TimeFrames+2
SEP #$20
Because this is an input/output based subroutine, you can have multiple different timers use this without any issues, other timers that had its value stores it units separately uses the freeram directly and you had to rewrite the code with different RAM to store the time amount
To have a timer the increments/decrements, use this code:
Code
!Freeram_Time = $60
;^[4 bytes] frame counter
REP #$20
LDA !Freeram_Time
CLC ;\not to use INC because it doesn't set carry
ADC #$0001 ;/when the value overflows a 16bit boundary (#$FFFF)
STA !Freeram_Time
LDA !Freeram_Time+2 ;\high word
ADC #$0000 ;
STA !Freeram_Time+2 ;/
SEP #$20
For decrements (countdown), simply replace the CLC and 2 ADCs (not the number next to the opcode) with SEC and 2 SBCs.
Code to compare the time with another value (greater than/equal to and less than):
Code
!CompareTimerHour = 0
!CompareTimerMinute = 1
!CompareTimerSeconds = 0
!CompareFramesLowWord = (!CompareTimerHour*216000)+(!CompareTimerMinute*3600)+(!CompareTimerSeconds*60)
!CompareFramesHighWord = (!CompareTimerHour*216000)+(!CompareTimerMinute*3600)+(!CompareTimerSeconds*60)>>16
;^Placed like this because table stretched if the whole formula was applied in the code
REP #$20
LDA !Freeram_Timer ;\CMP sets the carry should A >= compare value (CMP is like SBC)
CMP.w #!CompareFramesLowWord ;/following SBC would subtract an additional 1 if carry is clear (borrowing)
LDA !Freeram_Timer+2 ;\SBC would set the carry should A subtract by subtrahend
SBC #!CompareFramesHighWord ;/if subtrahend was smaller than A (clear carry if underflow occurs)
SEP #$20
;Carry (BCC/BCS) = set should timer is above or equal to a value.
Compare if equal or not (best for executing events for 1 frame):
2/24/2018 Edit: improved the division loop routine in the frames > Hours:Minutes:Seconds:Centiseconds, it no longer holds a duplicate code, and the format stores the units from hours to frames instead the other way around, thus making loops to write to the status bar/HUD more easier to work with.
 Give thanks to RPG hacker for working on Asar.
32bit unsigned hexdec, great for numbers greater than 65535:
Code
!NumOfDigits = 6
;^Number of digits to be stored. Up to 10 because maximum
; 32bit unsigned integer is 4,294,967,295.
!Scratchram_32bitHexDecOutput = $7F844E
;^[bytes_used = !NumOfDigits] The output
; formatted each byte is each digit 09.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;32bit hexdec converter
;input:
;$00$03 = the 32bit number in question.
;output:
;!Scratchram_32bitHexDecOutput to
; (!Scratchram_32bitHexDecOutput+!NumOfDigits)1:
; Contains value 09 per byte; starting at the last
; byte would be the ones place, before that is the
; tens, and so on (binary coded decimal, big endian
; digits).
;Overwritten
;$04 to $05: because remainder of the division.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Convert32bitIntegerToDecDigits:
LDX.b #!NumOfDigits1
.Loop
LDA.b #10 ;\divide by 10 (the radix)
STA $04 ;
STZ $05 ;/
JSL MathDiv32_16 ;>divide.
LDA $04 ;\write remainder digit (obviously shouldn't exceed 255)
STA !Scratchram_32bitHexDecOutput,x ;/
..Next
DEX ;\loop until all digits are written
BPL .Loop ;/
RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Unsigned 32bit / 16bit Division
; By Akaginite (ID:8691), fixed the overflow
; bitshift by GreenHammerBro (ID:18802)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Arguments
; $00$03 : Dividend
; $04$05 : Divisor
; Return values
; $00$03 : Quotient
; $04$05 : Remainder
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MathDiv32_16: REP #$20
ASL $00
ROL $02
LDY #$1F
LDA.w #$0000
 ROL A
BCS +
CMP $04
BCC ++
+ SBC $04
SEC
++ ROL $00
ROL $02
DEY
BPL 
STA $04
SEP #$20
RTL
Remove leading zeroes (same as my 45 hexdec, but changeable with how many digits you are going to use):
Code
RemoveLeadingZeroes:
LDX #$00
.Loop
LDA !Scratchram_32bitHexDecOutput,x ;\if current digit nonzero, don't omit trailing zeros
BNE .NonZero ;/
LDA #$FC ;\blank tile to replace leading zero
STA !Scratchram_32bitHexDecOutput,x ;/
INX ;>next digit
CPX.b #!NumOfDigits1 ;>last digit to check (tens place). So that it can display a single 0.
BCC .Loop ;>if not done yet, continue looping.
.NonZero
RTL
 Give thanks to RPG hacker for working on Asar.
I've noticed that Akaginite's 32bit/16bit division routine have a bug on it where the bitshift overflow can happen and outputs the wrong values. This happens when the value in A (up to 16bits can be processed at a time) gets ASL A'ed when A holds a value $8000+, which ends up with bit 15 transferred to the carry without being checked (discarded), then compares this broken value to $04 (which can assume that this large value is less than $04) and incorrectly flags this value as less than and screws up the result. I've added a carry check and SEC (because it's expected that after the SBC operation, carry is ALWAYS set) and this bug is fixed.
This bug can easily happen if you were to have the (unsigned) 32bit integer #$80000000 or more (in binary, that is a 1 on the leftmost digit and 31 zeroes after or more), as the glitch triggers right away on the first loop on ASL A.
Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Unsigned 32bit / 16bit Division
; By Akaginite (ID:8691), fixed the overflow
; bitshift by GreenHammerBro (ID:18802)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Arguments
; $00$03 : Dividend
; $04$05 : Divisor
; Return values
; $00$03 : Quotient
; $04$05 : Remainder
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MathDiv32_16: REP #$20
ASL $00
ROL $02
LDY #$1F
LDA.w #$0000
 ROL A
BCS +
CMP $04
BCC ++
+ SBC $04
SEC
++ ROL $00
ROL $02
DEY
BPL 
STA $04
SEP #$20
RTL
 Give thanks to RPG hacker for working on Asar.
Follow Us On