/
HowToUseTheStatusBar.html
5090 lines (4773 loc) · 223 KB
/
HowToUseTheStatusBar.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<html>
<head>
<style>
html {
background-color: #000207;
color: white;
font-family: sans-serif;
}
table, th, td {
border: 1px solid white;
border-collapse: collapse;
}
span.NoLineBreak {
white-space: nowrap;
}
abbr{cursor: help;}
</style>
</head>
<body onload="Calculate()">
<center><h1>How to use the status bar</h1></center>
<hr>
<p>So you want to edit the HUD to display custom info to the player. Obviously, this requires at least basic ASM knowledge. Fear not! Once you got the pattern down, you'll understand how it works!</p>
<p>You should know these before doing this:</p>
<ul>
<li><p>Ersanio's ASM tutorial<sup><a href="https://www.smwcentral.net/?p=section&a=details&id=15073">[1]</a><a href="https://www.smwcentral.net/?p=section&a=details&id=14268">[2]</a></sup>
on something simple as how to write a value to memory. You should also know about binary and hexidecimal, since many data explained here stored in memory are bitwise (mainly the tile properties).</p></li>
<li><p>Understand the mechanics of <a href="https://www.smwcentral.net/?p=section&a=details&id=19043">Asar</a>, such as defines and labels.</p></li>
</ul>
<p>If you want to easily copy codes (or any text displayed like a text editor) presented here, hold down CTRL and double-click them and CTRL+C.</p>
<hr>
<h2>Table of contents and other links</h2>
<h3>Javascript tools</h3>
<ul>
<li>
<a href="Readme_Files/TileDataAddr.html" id="QuickJSTools_TileDataAddr">S-PPU tile addressing tool</a>
</li>
<li>
<a href="Readme_Files/JS_YXPCCCTT.html" id="QuickJSTools_JS_YXPCCCTT">Quick Hex ↔ Binary converter for YXPCCCTT bit format (layer 3).</a>
</li>
<li>
<a href="Readme_Files/JS_YXPPCCCT.html" id="QuickJSTools_JS_YXPPCCCT">Quick Hex ↔ Binary converter for YXPPCCCT bit format (sprite OAM).</a>
</li>
<li>
<a href="Readme_Files/JS_FrameToTimer.html" id="QuickJSTools_JS_FrameToTimer">Convert 32-bit frame to and from timer format.</a>
</li>
<li>
<a href="Readme_Files/JS_32BitHexDecRAMUsage.html" id="32BitHexDec_BackTOC">32-bit Hexdec memory layout.</a>
</li>
<li>
<a href="Readme_Files/JS_StripeHeaderConverter.html" id="Stripe_BackTOC">Stripe header reader</a>
</li>
</ul>
<h3>Quick navigation</h3>
<ul>
<li><a href="#WarmUp" id="WarmUpBack">The warm-up: SMW's status bar.</a></li>
<li><a href="#StuffYouMayNeed" id="StuffYouMayNeedBack">Stuff you may need</li>
<li><a href="#TileFormat" id="TileFormatBack">Tile data format</a></li>
<li><a href="#SBPatches" id="SBPatchesBack">Other status bar patches</a></li>
<li><a href="#RoutineControl" id="RoutineControlBack">Adding routine-controlled display to the HUD.</a></li>
<ul>
<li><a href="#DisplayNumbers" id="DisplayNumbersBack">Displaying decimal numbers</a></li>
<ul>
<li><a href="#RemoveLeadingZeroes" id="RemoveLeadingZeroesBack">Remove leading zeroes (leading spaces)</a></li>
</ul>
<ul>
<li><a href="#AlignedDigits" id="AlignedDigitsBack">Remove Leading zeros, with left/right-aligned digits</a></li>
<ul>
<li><a href="#RightAligned" id="RightAlignedBack">Right aligned display</a></li>
</ul>
<li><a href="#IncrementDecrementAnimation" id="IncrementDecrementAnimationBack">Counting animation</a></li>
<li><a href="#Frames2Timer" id="Frames2TimerBack">Display a timer in Hours:Minutes:Seconds:Centiseconds format</a></li>
<li><a href="#Percentage" id="PercentageBack">Display a percentage</li>
</ul>
<li><a href="#RepeatedSymbol" id="RepeatedSymbolBack">Displaying repeated symbol n times.</a></li>
<li><a href="#GraphicalBar" id="GraphicalBarBack">Display a graphical bar</a></li>
</ul>
<li><a href="#OverworldBorder" id="OverworldBorderBack">Displaying stuff on the overworld border</a></li>
<li><a href="#OAMHud" id="OAMHudBack">Sprite-based HUD</a></li>
<ul>
<li><a href="#PixiSprite" id="PixiSpriteBack">Pixi sprites</a></li>
<li><a href="#HUDSprites" id="HUDSpritesBack">HUD-like sprites</a></li>
</ul>
<li><a href="#StripeHud" id="StripeHudBack">Stripe-based HUD</a></li>
<li><a href="#OtherNotes" id="OtherNotesBack">Other Notes</a></li>
<li><a href="#Credits" id="CreditsBack">Credits</a></li>
</ul>
<h3 id="StuffYouMayNeed"><a href="#StuffYouMayNeedBack">^</a>Stuff you may need</h3>
<p>These are resources that enhances the layer 3 HUD, usually if you need advanced stuff:</p>
<ul>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=19247">Super status bar</a></li>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=21665">Overworld border +</a></li>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=21562">Minimalist status bar(s)</a></li>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=21573">SMB3 status bar</a></li>
</ul>
<p>Sprite OAM for HUD-related stuff:</p>
<ul>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=26026">Pixi</a></li>
</ul>
<p>Patching tool:</p>
<ul>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=25953">Asar</a>, for inserting various patches and the sprite HUD patch included.</li>
</ul>
<p>Other</p>
<ul>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=19545">Shared Subroutines</a>, if you are running low on space in your hack. Currently, they work on its own without the shared subroutines. To migrate,
Here is the <a href="Readme_Files/GettingSharedSubToWork.html" id="SharedSubroutinesBackToTOC">template here you can copy</a>.</li>
<li><a href="https://www.smwcentral.net/?p=section&a=details&id=24816">No More Sprite Tile Limits</a>, unless you installed SA-1. Vanilla SMW had horrible sprite tile limits resulting tiles going missing when having
certain sprite combinations on screen.</li>
</ul>
<hr>
<p>I have provided several routines to make writing codes much user-friendly as possible. So lets begin.</p>
<h2 id="WarmUp"><a href="#WarmUpBack">^</a>The warm-up: SMW's status bar.</h2>
<p>Here, consider looking at this image:<br>
<img src="Readme_Files/StatusBarMap.png"><br>
RAM address <kbd>$7E0EF9-$7E0F2E</kbd> controls what tile to display. How do you modify it? Well you edit the
contents in the mentioned RAM address. There are 2 ways you can edit RAM addresses:
<ul>
<li>Most recommended: a debugger, specifically, <a href="https://www.smwcentral.net/?p=section&a=details&id=21616">Bsnes+</a>. This method applies temporary changes to your game for testing purposes,
it does not actually make “permanent” changes to your game. To make modifications, be at a level, and on the debugger, go to the menubar on the window, Tools → Debugger.
Now on the Debugger window: → Tools → Memory Editor, and enter on the search bar “<kbd>7E0EF9</kbd>” (or just “<kbd>0EF9</kbd>”, if using a SA-1 ROM, its
“<kbd>6EF9</kbd>”). It should now scroll to that address. From here, type in the hexidecimal number. At the time of making this, the memory editor lacks filtering features like entering a range. However, when you select a byte (a pair of hexidecimal digits), the info bar at the bottom shows what address you are on
to edit:<br>
<img src="Readme_Files/MemoryEditorAddress.png"></li>
<li>Create an ASM file to run under <a href="https://www.smwcentral.net/?p=section&a=details&id=19982">uberasm tool</a> as <kbd>level</kbd> under <kbd>main:</kbd> label, and just use <kbd>LDA #$xx : STA $xxxxxx</kbd>.</li>
</ul>
Now, for the RAM address, each byte is each 8x8 tile, meaning each time you go to the next address, its the next 8x8 tile number:<br><br>
<span id="SMWStatusBarTable"></span><br>
All of these bytes stored in RAM <kbd>$7E0EF9-$7E0F2E</kbd> are <kbd>TTTTTTTT</kbd>, which means you can only modify the tile number through RAM in-game. You may be wondering, if I wanted to edit the tiles here,
what value should I put? Well, <kbd>TTTTTTTT</kbd> is actually <i>what tile number to use</i>, from the tile data. When viewing the tile data stored in <kbd>GFX28</kbd> via <a href="https://www.smwcentral.net/?p=section&a=details&id=15671">YY-CHR</a>,
you can find what tile graphic to use by hovering your mouse over the 8x8 tile you want to use. After this, you should see on the bottom bar of the window showing what tile number that is:<br>
<img src="Readme_Files/TileNumber.png"><br>
Of course, you can view the tile “number” with bsnes+'s “Tile Viewer” (Debugger → S-PPU → Tile Viewer), it will only display its <i>tilemap address</i> (bottom bar of window).
I've made a conversion <a id="TileDataAddr" href="Readme_Files/TileDataAddr.html">cheatsheet here</a>.
Now you have the value you need. Just write that desired tile number to any tile number you want to write (either debugger or uberasm, if uberasm tool, you have to save, insert to your game, and run it to view the changes). For example, to write an
item box corner tile on the left of the top row, then it is tile number <kbd>$3A</kbd> to write at <kbd>$7E0EF9</kbd>, if using uberasm tool, its <kbd>LDA #$3A : STA $0EF9|!addr</kbd>.</p>
<p>Now hold up, when you play around with the tiles, many tiles will use the wrong palette, well, this is part of the tile properties (in binary, its <kbd>%YXPCCCTT</kbd>) that's hard-coded, that's right, there are actually 2 bytes of information
for each 8x8 tile with the tile numbers being in ROM and RAM and the properties all being hardcoded. When the level loads, the status bar routine will first transfer default tile number and properties from ROM addresses <kbd>$008C81-$008CFE</kbd>,
which contains all the tiles for the status bar, while the RAM itself only contain some of the tile numbers. To modify hardcoded tiles, I've already presented their addresses that directly correlates to the RAM addresses, for the top and bottom
lines of the item box:
<table>
<tr><th>Coordinate</th><th>Address (%TTTTTTTT, %YXPCCCTT)</th></tr>
<tr><th colspan="2">Top line of item box</th></tr>
<tr><td><kbd>(14,1)</kbd> (<kbd>($E,$1)</kbd>)</td><td><kbd>$008C81</kbd>, <kbd>$008C82</kbd></td></tr>
<tr><td><kbd>(15,1)</kbd> (<kbd>($F,$1)</kbd>)</td><td><kbd>$008C83</kbd>, <kbd>$008C84</kbd></td></tr>
<tr><td><kbd>(16,1)</kbd> (<kbd>($10,$1)</kbd>)</td><td><kbd>$008C85</kbd>, <kbd>$008C86</kbd></td></tr>
<tr><td><kbd>(17,1)</kbd> (<kbd>($11,$1)</kbd>)</td><td><kbd>$008C87</kbd>, <kbd>$008C88</kbd></td></tr>
<tr><th colspan="2">bottom line of item box</th></tr>
<tr><td><kbd>(14,4)</kbd> (<kbd>($E,$4)</kbd>)</td><td><kbd>$008CF7</kbd>, <kbd>$008CF8</kbd></td></tr>
<tr><td><kbd>(15,4)</kbd> (<kbd>($F,$4)</kbd>)</td><td><kbd>$008CF9</kbd>, <kbd>$008CFA</kbd></td></tr>
<tr><td><kbd>(16,4)</kbd> (<kbd>($10,$4)</kbd>)</td><td><kbd>$008CFB</kbd>, <kbd>$008CFC</kbd></td></tr>
<tr><td><kbd>(17,4)</kbd> (<kbd>($11,$4)</kbd>)</td><td><kbd>$008CFD</kbd>, <kbd>$008CFE</kbd></td></tr>
</table>
If you modify these hardcoded values through a debugger, the changes will only apply on the next level reload, since the hardcoded upload only occurs on level load.</p>
<p>You may be wondering, why did I include the tile coordinate in these tables? Well, is because of bsnes+'s ability to display the tile coordinates in the tilemap viewer (Debugger → S-PPU →
Tilemap Viewer, and select “3” on the BG.):<br>
<img src="Readme_Files/TileCoordinate.png"><br>
and then you can use that coordinate and CTRL+F here on this HTML file to locate what RAM/ROM address to modify, which is way easier than to trial-and-error modifying each address to find the desired location.</p>
<p>I've also provided a patch, <kbd>DefaultTiles.asm</kbd> which is intended to be an ASM patch version of the <a href="https://www.smwcentral.net/?p=section&a=details&id=4580">SMW status bar editor tool</a>.</p>
<hr>
<h2 id="TileFormat"><a href="#TileFormatBack">^</a>Tile data format</h2>
<p>As explained earlier about the palette issue, and that each 8x8 tile possess 2 bytes, I'll give you a summary of the entire tile data:
<ul>
<li>
<kbd>TTTTTTTT</kbd> is the tile number (previously explained already), refers to what tile to use from the tile stored in VRAM. Note that this alone is the tile number within a page.
</li>
<li>
<kbd>YXPCCCTT</kbd> is the tile properties:
<ul>
<li>
<kbd>TT</kbd> (ranges from 0-3): Page number. This is technically the upper bits of the tile number, which is why I use the same <kbd>T</kbd> instead
another letter, therefore 10 bits used for the tile number (referred to as “character” in the debugger and the SNES dev manuel),
but we refer the upper 2 bits as the page number, while the lower 8 bits as the tile number (within a page):<br>
<img src="Readme_Files/PageNumberTT.png">
</li>
<li>
<kbd>CCC</kbd> (ranges from 0-7): Palette number. This refers to what group-4 on the palette to use:<br>
<img src="Readme_Files/PaletteCCC_0_7.png">
</li>
<li>
<kbd>P</kbd> (0 or 1): Priority (1 is above everything or 0 is behind). Usually these are SET to prevent things (such as sprites,
layer 1 or layer 2 (including those if they have priority)) from overlapping the HUD.
For example, the bonus star symbol with and without priority against a cloud tile:<br>
<img src="Readme_Files/PriorityP.png">
</li>
<li>
<kbd>X</kbd> (0 or 1): X-flip. i.e: <img src="Readme_Files/HorizFlipX.png">
</li>
<li>
<kbd>Y</kbd> (0 or 1): Y-flip. i.e: <img src="Readme_Files/VertiFlipY.png">
</li>
</ul>
</li>
</ul>
I've made a <a href="Readme_Files/JS_YXPCCCTT.html" id="JS_YXPCCCTT">simple converter</a> since most debuggers only allow hex for editing and viewing values.
Now there are 2 formats on how a pair of bytes are being stored. SMW uses both, where default tiles are the “2-bytes together” format while RAM address <kbd>$7E0EF9-$7E0F2E</kbd>
are “TileNumber and Props in their own table” format but without the properties.
<table>
<tr>
<th>TileNumber and Props in their own table</th>
<th>2-bytes together</th>
</tr>
<tr>
<td><table><tr><td><pre>!TileNumbAddr: TTTTTTTT (tile 0)
!TileNumbAddr+1: TTTTTTTT (tile 1)
!TileNumbAddr+2: TTTTTTTT (tile 2)
!TileNumbAddr+3: TTTTTTTT (tile 3)
[...]</pre></td></tr></table>
On a separate table:
<table><tr><td><pre>!TilePropAddr: YXPCCCTT (tile 0)
!TilePropAddr+1: YXPCCCTT (tile 1)
!TilePropAddr+2: YXPCCCTT (tile 2)
!TilePropAddr+3: YXPCCCTT (tile 3)
[...]
;I refer this as "[TTTTTTTT, TTTTTTTT,...], [YXPCCCTT, YXPCCCTT,...]"</pre></td></tr></table><br>
Tile numbers and properties are stored in 2 separate tables, one for each.
Every time you advance the next byte, you are on the next 8x8 tile.
</td>
<td><table><tr><td><pre>!TileAddr: TTTTTTTT (tile 0)
!TileAddr+1: YXPCCCTT (tile 0)
!TileAddr+2: TTTTTTTT (tile 1)
!TileAddr+3: YXPCCCTT (tile 1)
!TileAddr+4: TTTTTTTT (tile 2)
!TileAddr+5: YXPCCCTT (tile 2)
!TileAddr+6: TTTTTTTT (tile 3)
!TileAddr+7: YXPCCCTT (tile 3)
[...]
;I call refer this as "[TTTTTTTT, YXPCCCTT, TTTTTTTT, YXPCCCTT...]"</pre></td></tr></table><br>
Every tile have 2 bytes placed one after another, so both tile numbers and properties are alternating
every byte. Therefore, to get to the next 8x8 tile, is a move of 2 bytes instead of one.
</td>
</tr>
</table>
</p>
<p>I've made a “friendly hex-edit” patch included in this package called <kbd>DefaultTiles.asm</kbd>, which is an alternative to
<a href="https://www.smwcentral.net/?p=section&a=details&id=4580">Smallhacker's status bar editor patch</a>.</p>
<hr>
<h2 id="SBPatches"><a href="#SBPatchesBack">^</a>Other status bar patches</h2>
<p>Because SMW's status bar is extremely limited, other users decided to make status bar patches:
<ul>
<li><p><a href="https://www.smwcentral.net/?p=section&a=details&id=19247">Super Status Bar</a>. This gives you a 32x5 8x8 tile area,
therefore 160 8x8 tiles to edit.<sup>1</sup></p></li>
<li><p><a href="https://www.smwcentral.net/?p=section&a=details&id=21665">Overworld Border +</a>. Not a “status bar”, but similar
in terms of editing the layer 3 8x8 tiles. This gives you <kbd>32*!TopLines</kbd> (for the top of the screen) and <kbd>32*!BottomLines</kbd> (for the bottom of the screen)
8x8 tile area, therefore, up to 224 8x8 tiles you can edit.<sup>1</sup> <kbd>!TopLines</kbd> is how many lines for the top of the screen, an integer 0-5.
<kbd>!BottomLines</kbd> is same as previously mentioned, but for the bottom of the screen being an integer 0-2.</p></li>
<li><p><a href="https://www.smwcentral.net/?p=profile&id=4842">Ladida</a>'s status bar patches<sup>2</sup></p></li>
<ul>
<li><p><a href="https://www.smwcentral.net/?p=section&a=details&id=21562">Minimalist Status Bars</a>. This shrinks your status bar to 1 or
2 lines of 8x8 tiles. Therefore, 32 or 64 tiles you can edit.</p></li>
<li><p><a href="https://www.smwcentral.net/?p=section&a=details&id=21573">SMB3 Status Bar</a>. True to the name, this replaces SMW's HUD with
SMB3's HUD on the bottom of the screen. This gives you a 32x4 8x8 tile area, therefore 128 8x8 tiles to edit</p></li>
</ul>
</ul>
<p>You may be wondering:
<ul>
<li><sup>1</sup>Uses the “2-bytes together” format, the <kbd>[TTTTTTTT, YXPCCCTT, TTTTTTTT, YXPCCCTT...]</kbd></li>
<li><sup>2</sup>Uses the “TileNumber and Props in their own table” format, the <kbd>[TTTTTTTT, TTTTTTTT,...], [YXPCCCTT, YXPCCCTT,...]</kbd></li>
</ul>
Therefore, many ASM patches that display on the status bar that I made are hybrid-support of these two formats. The define <kbd>!StatusbarFormat</kbd> is what
you need to edit to work with other status bar patches, set it to <kbd>$01</kbd> if “TileNumber and Props in their own table” otherwise set it to
<kbd>$02</kbd>. In case if you are wondering why there are duplicate routines (<abbr title="For “TileNumber and Props in their own table">a normal version</abbr> and <abbr title="For “2-bytes together”">a version with “Format2” appended to the end of
the subroutine label</abbr>) in the ASM files is because in case if you are designing a hack that have the status bar patch using the “TileNumber and Props in
their own table” format and the overworld border plus patch, which your hack is now using BOTH formats.</p>
<p>Thankfully, I made HTML Javascript files for all of these ASM resources to enable you to search what RAM address of a given tile.</p>
</p>
<hr>
<h2 id="RoutineControl"><a href="#RoutineControlBack">^</a>Adding routine-controlled display to the HUD.</h2>
<p>Now we are REALLY into using the status bar and ASM.</p>
<p>This method assumes you are using uberasm tool to test your code to write to the status bar. The first thing you want to do is have all the ASM files, except “<kbd>OAMBasedHUD.asm</kbd>”, in “<kbd>StatusBarRoutines</kbd>”
inserted into uberasm tool's “<kbd>library</kbd>” folder, and have the entire folder “<kbd>StatusBarRoutinesDefines</kbd>” be in the same directory as the uberasm tool exe program is at.</p>
<p>Feel free to look into the library ASM files, since I added descriptions and comments to give a better understanding how they work.</p>
<p>NOTE: most codes provided here do not modify the tile properties in any way, since very unlikely that many hacks would ever modify the tile properties. If you happen to have 2+ displays using the same tile(s) (such as
the score gets covered by boss's heath bar that only shows up during bosses) and are using different tile properties, don't forget to switch the YXPCCCTT data back else you had whatever last written to the YXPCCCTT data.
(even better is to make any of the counters using that same space to always write their own YXPCCCTT when their TTTTTTTT is written).</p>
<p>You also need to insert the graphic file from this provided <kbd>ExGFX</kbd> folder (<kbd>ExGFX80.bin</kbd>) because many of the stuff here uses a graphic not provided by SMW, such as the “/” symbol when displaying
2 numbers, and the “%” symbol representing a percentage. It should be inserted as “LG1” on the layer 3 GFX.</p>
<p>Before we start using the subroutines, we will demonstrate how to write tiles that change based on a given info <kbd>!RAMToMeasure</kbd>. It's very easy to write a tile that is different depending on
a condition using branches:
<table><tr><td><pre>!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00
!RAMToMeasure = $60 ;>[1 byte]
main:
LDA !RAMToMeasure
BEQ +
LDA #$00
BRA ++
+
LDA #$01
++
STA !StatusBarPosition
RTL</pre></td></tr></table><br>
and if you are tired of using branches, then an even simpler approach is to use tables:
<table><tr><td><pre>!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00
!RAMToMeasure = $60 ;>[1 byte]
main:
LDA !RAMToMeasure
TAX
LDA Table,x
STA !StatusBarPosition
RTL
Table:
db $xx ;>When !RAMToMeasure = $00
db $xx ;>When !RAMToMeasure = $01
db $xx ;>When !RAMToMeasure = $02</pre></td></tr></table></p>
<p>But what about other not-so-simple displays? Well, the following shows you how to do that.</p>
<h3 id="DisplayNumbers"><a href="#DisplayNumbersBack">^</a>Displaying decimal numbers</h3>
<p>To display a single digit, just store the value directly to the tile RAM. Digits are correctly displayed if the value is 0-9. Any value greater than 9 will display other symbol or graphics besides digits.</p>
<p>But how do you display multi-digits on the status bar? Well, you need to have a code that obtain decimal digits that wraps at every 10<sup>n</sup> unit.
(ones place reset every 10s, 10s digit resets every 100th value, and the 1000s digit resets every 10000s, and so on). The routines, <kbd>HexDec</kbd> converts a given number to
<a href="https://en.wikipedia.org/wiki/Binary-coded_decimal">binary-coded decimal (BCD, unpacked)</a> to write each digit tiles onto the status bar. Thankfully,
the status bar graphic tiles on GFX28 starts with digits 0-9 so you don't have to convert digit numbers to digit graphics.</p>
<p>Note that all of them treats the number as unsigned (negatives don't exists, you'll
have to “negate” the number by EOR $FF/$FFFF, then INC it (for 32-bit number do EOR $FFFF twice in 16-bit mode for both low and high words, increment the low word by 1, and if it overflows past $FFFF, carry
should increment the highword), before calling the routine, and also write the minus sign (tile $27) to represent a negative number. However, this really isn't that necessary, negative numbers are RARELY used in video games).</p>
<p>Another note is that leading zeroes do exist, if there are fewer digits than the maximum number of digits of a given subroutine.</p>
<p>Here is a cheatsheet of all the HexDec routines in <kbd>HexDec.asm</kbd>:
<table>
<tr>
<th>Subroutine</th><th>Maximum number of digits</th><th>Input</th><th>Output</th>
</tr>
<tr>
<td><kbd>EightBitHexDec</kbd></td>
<td><kbd>2</kbd> (or <kbd>3</kbd> when called again for the hundreds)</td>
<td>A (8-bit/1-byte)</td>
<td>
<table>
<tr>
<th>Output</th>
<th>Description</th>
</tr>
<tr>
<td><kbd>A register</kbd> (8-bit)</td>
<td>1s place</td>
</tr>
<tr>
<td><kbd>X index</kbd> (8-bit)</td>
<td>10s place, if the input <kbd>A register</kbd> is >99, will start using values beyond 9, which picks graphics besides numbers.</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><kbd>EightBitHexDec3Digits</kbd></td>
<td><kbd>3</kbd></td>
<td>A (8-bit/1-byte)</td>
<td>
<table>
<tr>
<th>Output</th>
<th>Description</th>
</tr>
<tr>
<td><kbd>A register</kbd> (8-bit)</td>
<td>1s place</td>
</tr>
<tr>
<td><kbd>X index</kbd> (8-bit)</td>
<td>10s place</td>
</tr>
<tr>
<td><kbd>Y index</kbd> (8-bit)</td>
<td>100s place</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><kbd>SixteenBitHexDecDivision</kbd></td>
<td><kbd>5</kbd></td>
<td><kbd>$00</kbd>-<kbd>$01</kbd> (16-bit/2-byte)</td>
<td>
<table>
<tr><th>Relative to RAM being defined</th><th>RAM used in normal hack</th><th>RAM used under SA-1</th><th>Place value</th></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+0</kbd></td><td><kbd>$02</kbd></td><td><kbd>$04</kbd></td><td>10,000s place</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+1</kbd></td><td><kbd>$03</kbd></td><td><kbd>$05</kbd></td><td>1,000s place</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+2</kbd></td><td><kbd>$04</kbd></td><td><kbd>$06</kbd></td><td>100s place</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+3</kbd></td><td><kbd>$05</kbd></td><td><kbd>$07</kbd></td><td>10s place</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+4</kbd></td><td><kbd>$06</kbd></td><td><kbd>$08</kbd></td><td>1s place</td></tr>
</table>
</td>
</tr>
<tr>
<td><kbd>ThirtyTwoBitHexDecDivision</kbd></td>
<td><kbd>1</kbd>-<kbd>10</kbd> digits, defined as <kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd></td>
<td><kbd>$00</kbd>-<kbd>$03</kbd> (32-bit/4-byte)</td>
<td>
<kbd>!Scratchram_32bitHexDecOutput</kbd> to <kbd>!Scratchram_32bitHexDecOutput+(!Setting_32bitHexDec_MaxNumberOfDigits-1)</kbd>: Contains value 0-9 on every byte,
in decreasing significant digits (last byte is always the 1s place). Formula to get what RAM of a given digit:<br><br><kbd>!Scratchram_32bitHexDecOutput+(!Setting_32bitHexDec_MaxNumberOfDigits-1)-(DigitIndex)</kbd><br><br>
Where <kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd> indicates the largest number of digits in your entire hack, up to 10.<br>
Where <kbd>DigitIndex</kbd>, ranging from 0 to <kbd>!Setting_32bitHexDec_MaxNumberOfDigits-1</kbd> represent which digit in positional notation:
<ul>
<li>DigitIndex = 0: 1s</li>
<li>DigitIndex = 1: 10s</li>
<li>DigitIndex = 2: 100s</li>
<li>DigitIndex = 3: 1,000s</li>
<li>DigitIndex = 4: 10,000s</li>
<li>...</li>
<li>DigitIndex = 9: 1,000,000,000s (maximum unsigned 32-bit integer is 4,294,967,295)</li>
</ul>
Due to the maximum number of digits giving out such a huge combinations, I provided <a href="Readme_Files/JS_32BitHexDecRAMUsage.html" id="32BitHexDec_Back">this JS tool</a> that you can adjust to find out its data layout.
</td>
</tr>
<tr>
<td><kbd>SupressLeadingZeros</kbd>, <kbd>SupressLeadingZerosPercentageLeaveLast2</kbd>, <kbd>SupressLeadingZerosPercentageLeaveLast3</kbd>, and <kbd>SupressLeadingZeros32Bit</kbd></td>
<td>
<ul>
<li>5 digits for <kbd>SupressLeadingZeros</kbd>, <kbd>SupressLeadingZerosPercentageLeaveLast2</kbd>, <kbd>SupressLeadingZerosPercentageLeaveLast3</kbd></li><br>
<li>N digits, with N equal to <kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd> for <kbd>SupressLeadingZeros32Bit</kbd></li>
</ul>
</td>
<td>
<table>
<tr>
<th>Input</th>
<th>Description</th>
</tr>
<tr>
<td>
<ul>
<li>For <kbd>SupressLeadingZeros</kbd>, <kbd>SupressLeadingZerosPercentageLeaveLast2</kbd>, and <kbd>SupressLeadingZerosPercentageLeaveLast3</kbd>:
<kbd>!Scratchram_16bitHexDecOutput</kbd> to <kbd>!Scratchram_16bitHexDecOutput+4</kbd> (5 bytes)
</li><br>
<li>For <kbd>SupressLeadingZeros32Bit</kbd>:
<kbd>!Scratchram_32bitHexDecOutput</kbd> to <kbd>!Scratchram_32bitHexDecOutput+(!Setting_32bitHexDec_MaxNumberOfDigits-1)</kbd> (<kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd> bytes)
</li>
</ul>
</td>
<td>A string of characters.</td>
</tr>
<tr>
<td><kbd>X index</kbd> (8-bit)</td>
<td>The starting location within the table to place the string in. X=$00 means the starting byte.</td>
</tr>
<tr>
<td><kbd>$09</kbd> (1 byte)</td>
<td>For only <kbd>SupressLeadingZerosPercentageLeaveLast2</kbd> and <kbd>SupressLeadingZerosPercentageLeaveLast3</kbd>, Character number for decimal point. By default status bar, it must be <kbd>#$24</kbd> to represent the decimal point, for sprite OAM, it must be <kbd>#$0D</kbd>.</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<th>Output</th>
<th>Description</th>
</tr>
<tr>
<td><kbd>!Scratchram_CharacterTileTable</kbd> to <kbd>!Scratchram_CharacterTileTable+(NumberOfChar-1)</kbd> (NumberOfChar bytes)</td>
<td>A string (without leading zeroes)</td>
</tr>
<tr>
<td><kbd>X index</kbd> (8-bit)</td>
<td>
The location to place string AFTER the numbers (increments every character written). 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.
</td>
</tr>
</table>
</td>
</tr>
</table>
</p>
<p>Lets start with something very simple with small number of digits.</p>
<p>Do note that all subroutines here will always have leading zeroes stored (either as in the registers for 2-3 digits or as an array of digits for 4+ digit displays) when there are fewer
digits than the maximum by default. If you wanted to remove leading zeroes, I will explain that in the next section.</p>
<p>For the overworld border (<abbr title="So far, there isn't any other patches that lets you edit the overworld border similarly to the status bar.">assuming you're using the Overworld Border+ patch</abbr>),
which is stored in <kbd>GFX2A</kbd>, after calling the HexDec routine, you must add each digit by #$22 (<a href="Readme_Files/OWB_Digits.png" target="_blank">digit graphics are stored as tile numbers $22-$2B</a>),
and make sure you are using page 1 and not 0 on the tile properties (example, to display the number “<span style="color: red">1</span><span style="color: blue">2</span>”, the values must be <kbd><span style="color: red">$01</span>+$22 = $23</kbd>
for the tens and <kbd><span style="color: blue">$02</span>+$22 = $24</kbd> for the ones). I'll later explain how to display digits on the overworld border later on this document.</p>
<p>Create a text file, name it <kbd>StatusBarTest.asm</kbd>. Edit that text file to include only this code:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[1 byte] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
LDA !RAMToMeasure
JSL HexDec_EightBitHexDec ;>Get digits, A = 1s, X = 10s
STA !StatusBarPosition+(1*!StatusbarFormat) ;>Write 1s place
TXA ;>if you are using status bar patches, STX $xxxxxx will not work.
STA !StatusBarPosition ;>Write 10s place
RTL
</pre></td></tr></table></p>
<p>Now save, and insert in either as gamemode under number 14 (level), or level. Then run the tool to apply the changes.</p>
<p>Test the game (using a debugger, bsnes+) by going to the level that you inserted this created ASM file (or any level if using gamemode 14), you should see the
digits displayed on the HUD. Again, palettes, X/Y flips and other weird shenanigans can happen if you use SMW's vanilla status bar without
editing the default values. Now depending on what RAM to use set by <kbd>!RAMToMeasure</kbd>, it could display <kbd>00</kbd> or <kbd>[<digits 0-9 or glitched tile here><digits 0-9>]</kbd>,
then on the debugger window, go to memory editor and search for the RAM you set for <kbd>!RAMToMeasure</kbd> (example: <kbd>$60</kbd>, <kbd>$3060</kbd> for SA-1), edit the value in that byte to be any values $00-$63. It should
display their decimal version of the hex number:
<table>
<tr><th>Hex bytes</th><th>Displayed</th></tr>
<tr><td>$09</td><td>09</td></tr>
<tr><td>$0A</td><td>10</td></tr>
<tr><td>$0B</td><td>11</td></tr>
<tr><td colspan="2">...</td></tr>
<tr><td>$61</td><td>97</td></tr>
<tr><td>$62</td><td>98</td></tr>
<tr><td>$63</td><td>99</td></tr>
<tr><td bgcolor="red">$64</td><td bgcolor="red">A0</td></tr>
<tr><td bgcolor="red">$65</td><td bgcolor="red">A1</td></tr>
<tr><td bgcolor="red">$66</td><td bgcolor="red">A2</td></tr>
<tr><td colspan="2" bgcolor="red">(10s place digit cycles through the alphabet characters every 10th value until...)</td></tr>
<tr><td bgcolor="red">$FF</td><td bgcolor="red">P5</td></tr>
</table>
What happens if you enter numbers greater than $63? Well, when the routine is used like that, it is designed to display up to 2 digits. If the number is greater than $64, the 10s place digit start showing graphics
other than digits. This is because of how the routine works, is that the 10s place digit wasn't wrapped (wrapped, as in digits reset to 0 when going past 9: 8, 9, 0, 1 , 2...), so it can display
“digits” greater than 9 (98, 99, A0, A1, A2...).</p>
<p>You can fix this problem and make it allow the full range of the 8-bit value by after writing the ones digit, TXA, then call the routine again, and after that, A is the 10s, and X is the 100s place:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[1 byte] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
LDA !RAMToMeasure
JSL HexDec_EightBitHexDec
STA !StatusBarPosition+(2*!StatusbarFormat) ;>Write 1s place
TXA ;>Call again to get the 10s and 100s.
JSL HexDec_EightBitHexDec
STA !StatusBarPosition+(1*!StatusbarFormat) ;>Write 10s place
TXA ;>STX $xxxxxx don't exists.
STA !StatusBarPosition ;> Write 100s place
RTL
</pre></td></tr></table><br>
How this works is that it uses <a href="https://en.wikipedia.org/wiki/Euclidean_division">euclidean division</a>. The subroutine <kbd>HexDec_EightBitHexDec</kbd>
subtract the input A by 10 repeatedly until A is less than 10, it also counts how many times it subtracted, and have that value in the X register. Because euclidean division
is essentially repeated subtraction, the output X is the quotient (rounded down) and A is the remainder. With the X register increasing every 10th value, this can represent the
10s place (since in decimal, 10s place increases at every 10th unit), and with the A register being in the 0-9 range, can represent the ones place (since real-world decimal will wrap when exceeding 9).
When handling the 100s digit, you basically divided the value by 10 again, which is <kbd>!RAMToMeasure/10/10</kbd>, which is the same as <kbd>!RAMToMeasure/(10^2)</kbd> which leads to <kbd>!RAMToMeasure/100</kbd>,
resulting A now being the 10s place wrapping every 100 and X increasing every 100. Because the maximum value stored in an 8-bit byte is 255, there is no way the hundreds place digit
will be able to glitch out at 1000. For more information how base conversion works, I found a wikipedia article on positional notation <a href="https://en.wikipedia.org/wiki/Positional_notation#Base_conversion">on converting radixes</a>.</p>
<p id="Left2RightHexDec">Do note that this method have a cost: The higher the number to convert to BCD, the more subtractions by 10<sup>1 → (NumberOfDigits-1)</sup> it would take to get all the
digits outputted, not really harsh since this is 8-bit, so up to 25 iterations for a 2-digit (plus up to 2 more for 10s and 100s by calling the HexDec routine again). However, if you
DO want speed/performance, do this instead. This works from left-to-right by repeatedly subtracting 100 until A is 0-99. The number of subtractions is now how many 100s for the 100s digit, after
that, with A being 0-99, we do the same but with 10s (how many 10s) instead of 100s to find how many 10s. After that, A is 0-9 which is already the ones place:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[1 byte] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
LDA !RAMToMeasure
JSL HexDec_EightBitHexDec3Digits ;>Obtain 3 digits in A (1s), X (10s) and Y (100s).
STA !StatusBarPosition+(2*!StatusbarFormat) ;>Write 1s place
TXA ;\Write 10s place
STA !StatusBarPosition+(1*!StatusbarFormat) ;/
TYA ;\Write 100s place.
STA !StatusBarPosition ;/
RTL
</pre></td></tr></table></p>
<p>What about if I wanted to convert 16-bit numbers (or if you have 3-5 digits and want to go above 255)? Well, good news for you, I've provided more than just 8-bit HexDec
converters. Introducing <kbd>SixteenBitHexDecDivision</kbd>. Here is the following code in question (up to 65535). Example, RAM $60 contains the value <kbd>$1234</kbd>
(in memory, its <kbd>$34, $12</kbd>), I write my 16-bit number to address <kbd>$00-$01</kbd>, then call the subroutine <kbd>SixteenBitHexDecDivision</kbd> after that,
the digits of 4660 are stored. Note that this is ordered in decreasing significant decimal digits (<abbr title="!Scratchram_16bitHexDecOutput+4">last</abbr> byte = 1s, second-last = 10s...):
<table>
<tr><th>Define relative address</th><th>RAM address (normal/Sa-1)</th><th>Output value</th></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+0</kbd></td><td><kbd>$02/$04</kbd></td><td><kbd>$00</kbd> (represents “0” for 10000s place)</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+1</kbd></td><td><kbd>$03/$05</kbd></td><td><kbd>$04</kbd> (represents “4” for 1000s place)</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+2</kbd></td><td><kbd>$04/$06</kbd></td><td><kbd>$06</kbd> (represents “6” for 100s place)</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+3</kbd></td><td><kbd>$05/$07</kbd></td><td><kbd>$06</kbd> (represents “6” for 10s place)</td></tr>
<tr><td><kbd>!Scratchram_16bitHexDecOutput+4</kbd></td><td><kbd>$06/$08</kbd></td><td><kbd>$00</kbd> (represents “0” 1s place)</td></tr>
</table><br>
As a side note, all “bigger number” (handles more bytes to handle larger values) hexdec subroutines can work with “smaller numbers” (less bytes) hexdec subroutines by masking out
the higher byte(s). For example: using a 16-bit hexdec, an 8-bit number #$FF, when supplied into the input, you then zero out the high byte (<kbd>STZ $01</kbd>) so that it treats the value as <kbd>#$00FF</kbd>.
Here is the code in question for you to test:</p>
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[2 bytes] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
!NumberOfDigitsDisplayed = 5 ;>How many digits, enter 1-5 (pointless if you enter less than 3).
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
REP #$20
LDA !RAMToMeasure ;\Submit the value into $00-$01, the input to convert the raw binary value into BCD, unpacked.
STA $00 ;/
SEP #$20
JSL HexDec_SixteenBitHexDecDivision
;Write to status bar
if !StatusbarFormat == $01
LDX.b #(!NumberOfDigitsDisplayed-1)
-
LDA.b !Scratchram_16bitHexDecOutput+$04-(!NumberOfDigitsDisplayed-1),x
STA !StatusBarPosition,x
DEX
BPL -
else
LDX.b #((!NumberOfDigitsDisplayed-1)*2)
LDY.b #(!NumberOfDigitsDisplayed-1)
-
LDA.w (!Scratchram_16bitHexDecOutput)+$04-(!NumberOfDigitsDisplayed-1)|!dp,y
STA !StatusBarPosition,x
DEY
DEX #2
BPL -
endif
RTL</pre></td></tr></table><br>
<p>Greater than 65535? (unlikely for other stuff besides a huge currency counter or custom score). This one is a 32-bit (4 bytes) HexDec, up to
4,294,967,295 (<kbd>$FFFFFFFF</kbd>) can be properly displayed. Remember, this is little endian, for example: a number
<abbr title="287454020 in decimal">$11223344</abbr> must be inputted as
<kbd>[$44, $33, $22, $11]</kbd> into <kbd>$00-$03</kbd> and should output as <kbd>[$02, $08, $07, $04, $05, $04, $00, $02, $00]</kbd> in
<kbd>!Scratchram_32bitHexDecOutput</kbd>.<br>
Here is an example using the previously mentioned number, with <kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd> set to <kbd>9</kbd>, using default <kbd>!Scratchram_32bitHexDecOutput</kbd>.
Again, the digits stored in each byte are ordered in decreasing significant decimal digits (<abbr title="!Scratchram_32bitHexDecOutput+(!Setting_32bitHexDec_MaxNumberOfDigits-1)">last byte</abbr> is always
the 1s place, regardless of <kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd>'s value)
<table>
<tr><th>Define relative address</th><th>Default RAM address (normal/Sa-1)</th><th>Output value</th></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+0</kbd></td><td><kbd>$7F844E/$404140</kbd></td><td><kbd>$02</kbd> (represents “2” for 100000000s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+1</kbd></td><td><kbd>$7F844F/$404141</kbd></td><td><kbd>$08</kbd> (represents “8” for 10000000s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+2</kbd></td><td><kbd>$7F8450/$404142</kbd></td><td><kbd>$07</kbd> (represents “7” for 1000000s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+3</kbd></td><td><kbd>$7F8451/$404143</kbd></td><td><kbd>$04</kbd> (represents “4” for 100000s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+4</kbd></td><td><kbd>$7F8452/$404144</kbd></td><td><kbd>$05</kbd> (represents “5” for 10000s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+5</kbd></td><td><kbd>$7F8453/$404145</kbd></td><td><kbd>$04</kbd> (represents “4” for 1000s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+6</kbd></td><td><kbd>$7F8454/$404146</kbd></td><td><kbd>$00</kbd> (represents “0” for 100s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+7</kbd></td><td><kbd>$7F8455/$404147</kbd></td><td><kbd>$02</kbd> (represents “2” for 10s)</td></tr>
<tr><td><kbd>!Scratchram_32bitHexDecOutput+8</kbd></td><td><kbd>$7F8456/$404148</kbd></td><td><kbd>$00</kbd> (represents “0” for 1s)</td></tr>
</table><br>
Code in question:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[4 bytes] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
;When handling the number of digits, be careful not to have !NumberOfDigitsDisplayed be greater than !Setting_32bitHexDec_MaxNumberOfDigits
;(the number digits stored in each byte in !Scratchram_32bitHexDecOutput), else garbage will appear.
!NumberOfDigitsDisplayed = 9 ;>How many digits, enter 1-10 (pointless if you enter less than 5).
incsrc "../StatusBarRoutinesDefines/Defines.asm"
;Don't change this.
if defined("sa1") == 0
if read1($00FFD5) == $23
!sa1 = 1
sa1rom
else
!sa1 = 0
endif
endif
main:
REP #$20
LDA !RAMToMeasure ;\Low word (example: $3344 -> [$44, $33] in $00, $01)
STA $00 ;/
LDA !RAMToMeasure+2 ;\High word (example: $1122 -> [$22, $11] in $02, $03)
STA $02 ;/
SEP #$20
JSL HexDec_ThirtyTwoBitHexDecDivision ;>Should Output example as: [$02, $08, $07, $04, $05, $04, $00, $02, $00] on !Scratchram_32bitHexDecOutput.
;Write to status bar:
if !StatusbarFormat == $01
LDX.b #(!NumberOfDigitsDisplayed-1)
-
LDA !Scratchram_32bitHexDecOutput+(!Setting_32bitHexDec_MaxNumberOfDigits-1)-(!NumberOfDigitsDisplayed-1),x
STA !StatusBarPosition,x
DEX
BPL -
else
LDX #((!NumberOfDigitsDisplayed-1)*2)
LDY #(!NumberOfDigitsDisplayed-1)
-
PHX
TYX ;>Sigh, LDA $xxxxxx,y does not exist.
LDA (!Scratchram_32bitHexDecOutput)+(!Setting_32bitHexDec_MaxNumberOfDigits-1)-(!NumberOfDigitsDisplayed-1),x
PLX
STA !StatusBarPosition,x
DEY
DEX #2
BPL -
endif
RTL</pre></td></tr></table><br>
Don't ask me to make an even larger number display, because not many games would ever display 10 digits at once, plus, it takes up space on the HUD.</p>
<p>Also note that <kbd>!NumberOfDigitsDisplayed</kbd> and <kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd> are different. <kbd>!NumberOfDigitsDisplayed</kbd> refers to only the low (rightmost digits) n digits being
displayed, while <kbd>!Setting_32bitHexDec_MaxNumberOfDigits</kbd> is the fixed number of digits (uses leading zeroes if fewer digits) stored in the digit table (you can set this to a
<abbr title="If you have multiple number displays and they are using the 32-bit HexDec each with different maximums (example: 99,999 and 999,999), then set !Setting_32bitHexDec_MaxNumberOfDigits to whatever number using this routine uses the most digits
(in this case, 6, not 5).">lower number to save space</abbr>).
Having both in case if you want to only display up to n digits less than the routine can output. For example, a counter to display 6 digits, 0-999999. You wouldn't want display
unused digits always showing 0 on the millions (1000000s) and beyond (so don't display “0000999999”, those first 4 digits are always 0 and therefore redundant).</p>
<p>Funnily enough, I wrote this tutorial before
<a href="https://www.youtube.com/watch?v=v3-a-zqKfgA">this video</a> came out on how to convert binary to decimal for display.</p>
<h4 id="RemoveLeadingZeroes"><a href="#RemoveLeadingZeroesBack">^</a>Removing leading zeroes (leading spaces)</h4>
<p>I've also provided routines that removes leading zeroes by simply replacing the first n digits with tile $FC in the digits table (I call these “leading spaces”). After calling the the JSL routines to convert them into decimal
digits, you then call <kbd>RemoveLeadingZeroes16Bit</kbd> or <kbd>RemoveLeadingZeroes32Bit</kbd> before writing to the status bar. Make sure you match the 16 and 32 bits between hexdec and removing leading zeroes routines. For the 8-bit versions,
a routine isn't necessary:
<ul>
<li>For 2-digit numbers, just replace the 10s place digit with tile #$FC (defined as <kbd>!StatusBarBlankTile</kbd>) if it is 0:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[1 byte] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
LDA !RAMToMeasure
JSL HexDec_EightBitHexDec
STA !StatusBarPosition+(1*!StatusbarFormat) ;>Write 1s place
TXA ;>if you are using status bar patches, STX $xxxxxx will not work.
BNE .NotLeadingZero
.LeadingZero
LDA #!StatusBarBlankTile ;>Blank tile
.NotLeadingZero
STA !StatusBarPosition ;>Write 10s place
RTL</pre></td></tr></table>
</li>
<li>For 3-digit numbers, use this code template:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[1 byte] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
.DisplayNumbers
LDA !RAMToMeasure
JSL HexDec_EightBitHexDec3Digits ;>Obtain 3 digits in A (1s), X (10s) and Y (100s).
STA !StatusBarPosition+(2*!StatusbarFormat) ;>Write 1s place (will always show)
..HandleHundreds
CPY #$00 ;\Is the 100s place nonzero? Yes, then leave that and the 10s showing up
BNE ...HundredsNonZero ;/No, then erase the 100s digit and see if 10s is also part of the leading 0s.
...NoHundreds
LDA #!StatusBarBlankTile ;\Erase the 100s digit
STA !StatusBarPosition ;/
BRA ..HandleTens ;>Check if the next digit is also part of the leading zero.
...HundredsNonZero
TYA
STA !StatusBarPosition ;>Write 100s place
BRA ..HandleTens_WriteTens ;>If at least 100, 10s shouldn't be omitted.
..HandleTens
CPX #$00 ;\Is the 10s place nonzero? Yes, then leave the 10s place showing up
BNE ...WriteTens ;/No, then erase the 10s.
...NoTens
LDX #!StatusBarBlankTile ;>If 0, replace 10s digit with blank.
...WriteTens
TXA ;\Write 10s place
STA !StatusBarPosition+(1*!StatusbarFormat) ;/
.Done
RTL</pre></td></tr></table></li>
</ul>
</p>
<h3 id="AlignedDigits"><a href="#AlignedDigitsBack">^</a>Remove Leading zeros, with left/right-aligned digits</h3>
<p>Perhaps, in actuality, leading zeros are replaced with leading spaces, giving a <i>right-aligned</i> look. If you are designing a HUD
with an icon (or a “label”) to the left of the number when the number have a small number of digits, you'll have a space in between the icon and the digits, which looks ugly, especially if the number
have a huge number of digits it can display up to. Or, if you wanted the number display to look more compact. For example, a 5-digit, 16-bit number:<br>
<img src="Readme_Files/LeadingSpaces.png"><br><br>
The first routine, <kbd>SupressLeadingZeros</kbd> works like this: Search all the digits in <kbd>!Scratchram_16bitHexDecOutput</kbd> from left-to-right, and copy all the non-leading zero digits
on and after the first (leftmost) digit and paste them into <kbd>!Scratchram_CharacterTileTable</kbd>. You've now have a “compressed string” table stored. After this routine, The X register now contains a number incremented for every character being written.
See picture below:<br>
<img src="Readme_Files/NoLeadingZeroesLeftAligned.png"><br>
When written to the status bar:<br>
<img src="Readme_Files/StatusBarStringTransfer.png">
</p>
<p>To use the routine, here is the code template:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[2 bytes] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
!NumberOfDigitsDisplayed = 5 ;>How many digits, enter 1-5 (pointless if you enter less than 3).
!StatusBarPositionProps = !FreeramFromAnotherPatch_StatusBarPropStart+$00 ;>Same as above but tile props (when enabled).
!TileProps = %00111000 ;>Properties of the digits (when enabled).
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
.NumberDisplayTest
;Clear the tiles. To prevent leftover "ghost" tiles that should've
;disappear when the number of digits decreases (so when "10" becomes "9",
;won't display "90"). Also setup tile properties when enabled.
LDX.b #(!NumberOfDigitsDisplayed-1)*!StatusbarFormat
-
LDA #!StatusBarBlankTile
STA !StatusBarPosition,x
if !StatusBar_UsingCustomProperties != 0
LDA.b #!TileProps
STA !StatusBarPositionProps,x
endif
DEX #!StatusbarFormat
BPL -
;Number to string.
;Process HexDec
REP #$20 ;\Convert a given number to decimal digits.
LDA !RAMToMeasure ;|
STA $00 ;|
SEP #$20 ;|
JSL HexDec_SixteenBitHexDecDivision ;/
;Remove leading zeroes and have it as a character table
LDX #$00 ;>Start at character position 0.
JSL HexDec_SupressLeadingZeros ;>Write the digits (without leading zeroes) starting at position 0.
;Prevent writing too much characters.
CPX.b #!NumberOfDigitsDisplayed+1 ;\Failsafe to avoid writing more characters than intended would write onto tiles
BCS ..TooMuchDigits ;/not being cleared from the previous code.
;Write to status bar
LDA.b #!StatusBarPosition : STA $00
LDA.b #!StatusBarPosition>>8 : STA $01
LDA.b #!StatusBarPosition>>16 : STA $02
if !StatusBar_UsingCustomProperties != 0
LDA.b #!StatusBarPositionProps : STA $03
LDA.b #!StatusBarPositionProps>>8 : STA $04
LDA.b #!StatusBarPositionProps>>16 : STA $05
LDA.b #!TileProps
STA $06
endif
if !StatusbarFormat == $01
JSL HexDec_WriteStringDigitsToHUD
else
JSL HexDec_WriteStringDigitsToHUDFormat2
endif
..TooMuchDigits
RTL</pre></td></tr></table>
32-bit? Here you go:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[4 bytes] RAM address you what to show
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
!NumberOfDigitsDisplayed = 9 ;>How many digits, enter 1-5 (pointless if you enter less than 3).
!StatusBarPositionProps = !FreeramFromAnotherPatch_StatusBarPropStart+$00 ;>Same as above but tile props (when enabled).
!TileProps = %00111000 ;>Properties of the digits (when enabled).
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
.NumberDisplayTest
;Clear the tiles. To prevent leftover "ghost" tiles that should've
;disappear when the number of digits decreases (so when "10" becomes "9",
;won't display "90"). Also setup tile properties when enabled.
LDX.b #(!NumberOfDigitsDisplayed-1)*!StatusbarFormat
-
LDA #!StatusBarBlankTile
STA !StatusBarPosition,x
if !StatusBar_UsingCustomProperties != 0
LDA.b #!TileProps
STA !StatusBarPositionProps,x
endif
DEX #!StatusbarFormat
BPL -
;Number to string.
;Process HexDec
REP #$20 ;\Convert a given number to decimal digits.
LDA !RAMToMeasure ;|
STA $00 ;|
LDA !RAMToMeasure+2 ;|
STA $02 ;|
SEP #$20 ;|
JSL HexDec_ThirtyTwoBitHexDecDivision ;/
;Remove leading zeroes and have it as a character table
LDX #$00 ;>Start at character position 0.
JSL HexDec_SupressLeadingZeros32Bit ;>Write the digits (without leading zeroes) starting at position 0.
;Prevent writing too much characters.
CPX.b #!NumberOfDigitsDisplayed+1 ;\Failsafe to avoid writing more characters than intended would write onto tiles
BCS ..TooMuchDigits ;/not being cleared from the previous code.
;Write to status bar
LDA.b #!StatusBarPosition : STA $00
LDA.b #!StatusBarPosition>>8 : STA $01
LDA.b #!StatusBarPosition>>16 : STA $02
if !StatusBar_UsingCustomProperties != 0
LDA.b #!StatusBarPositionProps : STA $03
LDA.b #!StatusBarPositionProps>>8 : STA $04
LDA.b #!StatusBarPositionProps>>16 : STA $05
LDA.b #!TileProps
STA $06
endif
if !StatusbarFormat == $01
JSL HexDec_WriteStringDigitsToHUD
else
JSL HexDec_WriteStringDigitsToHUDFormat2
endif
..TooMuchDigits
RTL</pre></td></tr></table><br>
But what if I wanted to display 2 numbers “X/Y” (for example: “260/300”)? Well, you just have the codes be (loosely) called twice under “<kbd>Number to string</kbd>” but like this instead:
<ol>
<li>
<p>The number of tiles to clear uses <kbd>LDX.b #(((!NumberOfDigitsDisplayed*2)+1)-1)*!StatusbarFormat</kbd>, 2 numbers, plus 1 (because of the “/” symbol), minus 1 (because indexing ranges from 0 to
<kbd>NumberOfItems-1</kbd>), its then multiplied by 1 or 2 to adjust the status bar data formatting.</p>
</li>
<li>
<p>You have codes <kbd>Process HexDec</kbd> and <kbd>Remove leading zeroes and have it as a character table</kbd> be the same for the first number (using previous example, the 260). We will not use the failsafe
of too many digits until after we write the second number.</p>
</li>
<li>
<p>After writing the first number, you then write a character that will be in between the 2 numbers (in this case, the “/” symbol), after this, you <kbd>INX</kbd> so you will place the second
number after it.</p>
</li>
<li>
<p>You do the same thing as the first number, but use <kbd>PHX</kbd>, then the code that uses HexDec, without <kbd>LDX #$00</kbd>, then <kbd>PLX</kbd>, because X needs to continue counting the characters, and that HexDec routine modifies
this to handle each of the 5 digits. After this, then you DO need that failsafe, using <kbd>CPX.b #(((!NumberOfDigitsDisplayed*2)+1)+1)</kbd>, 2 numbers, plus 1 for the “/” symbol, plus 1 AGAIN because BCS branches if
<kbd>X ≥ ComparedValue</kbd></p>
</li>
</ol>
Here is an example using 2 16-bit numbers:
<table><tr><td><pre>
!RAMToMeasure = $60 ;>[2 bytes] RAM address you what to show
!RAMToMeasure2 = $62 ;>[2 bytes] Same as above
!StatusBarPosition = !FreeramFromAnotherPatch_StatusBarTileStart+$00 ;>Where to write on the status bar
!StatusBarPositionProps = !FreeramFromAnotherPatch_StatusBarPropStart+$00 ;>Same as above but tile props (when enabled).
!NumberOfDigitsDisplayed = 5 ;>How many digits, enter 1-5 (pointless if you enter less than 3).
!TileProps = %00111000 ;>Properties of the digits (when enabled).
incsrc "../StatusBarRoutinesDefines/Defines.asm"
main:
.NumberDisplayTest
;Clear the tiles. To prevent leftover "ghost" tiles that should've
;disappear when the number of digits decreases (so when "10" becomes "9",
;won't display "90").
LDX.b #(((!NumberOfDigitsDisplayed*2)+1)-1)*!StatusbarFormat ;>2 NumberOfDigitsDisplayed due to 2 numbers displayed, plus 1 because of the "/" symbol.
-
LDA #!StatusBarBlankTile
STA !StatusBarPosition,x
if !StatusBar_UsingCustomProperties != 0
LDA.b #!TileProps
STA !StatusBarPositionProps,x
endif
DEX #!StatusbarFormat
BPL -
;First number
;Process HexDec
REP #$20 ;\Convert a given number to decimal digits.
LDA !RAMToMeasure ;|
STA $00 ;|
SEP #$20 ;|
JSL HexDec_SixteenBitHexDecDivision ;/
;Remove leading zeroes and have it as a character table
LDX #$00 ;>Start at character position 0.
JSL HexDec_SupressLeadingZeros ;>Write the digits (without leading zeroes) starting at position 0.
;"/" symbol
LDA #!StatusBarSlashCharacterTileNumb ;\Slash symbol.
STA !Scratchram_CharacterTileTable,x ;/
INX ;>Next character position.
;Second number
;Process HexDec
PHX ;>Push X because it gets modified by the HexDec routine.
REP #$20 ;\Convert a given number to decimal digits.
LDA !RAMToMeasure2 ;|
STA $00 ;|