-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdraw.asm
953 lines (753 loc) · 16.1 KB
/
draw.asm
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
; no arguments, fills both nametables with 24 (blank blue character). bulk drawing only! wayyy too much for vblank!
clearScreen:
initPalette:
lda $2002
lda #$3F
sta $2006
lda #$10
sta $2006
ldx #$00
SpritePaletteLoop: ; sprites first
lda spritePalette, x
sta $2007
inx
cpx #$10
bne SpritePaletteLoop
lda $2002
lda #$3F
sta $2006
lda #$00
sta $2006
ldx #$00
BGPaletteLoop: ; then the background palettes
lda dawnPalette, x
sta $2007
inx
cpx #$10
bne BGPaletteLoop
initNametables:
lda $2002
lda #$20
sta $2006
lda #$00
sta $2006
ldx #$00
BGLoop:
lda #$24 ; blank blue tile
sta $2007
sta $2007
sta $2007
sta $2007
inx
cpx #$f0
bne BGLoop
loadAttr:
lda $2002
lda #$23
sta $2006
lda #$C0
sta $2006
ldx #$00
attrLoop:
lda #$00 ; all the first palette
sta $2007
inx
cpx #$40
bne attrLoop
lda $2002
lda #$24
sta $2006
lda #$00
sta $2006
ldx #$00
SecondBGLoop:
lda #$24 ; blank blue tile
sta $2007
sta $2007
sta $2007
sta $2007
inx
cpx #$f0
bne SecondBGLoop
loadSecondAttr:
lda $2002
lda #$27
sta $2006
lda #$C0
sta $2006
ldx #$00
secondAttrLoop:
lda #$00 ; all the first palette
sta $2007
inx
cpx #$40
bne secondAttrLoop
rts
changeSeasonPalette:
;lda #%00000110 ; background and sprites disabled
;lda $2001
lda $2002
lda #$3F
sta $2006
lda #$00
sta $2006
lda season
asl a
asl a
asl a
asl a
tay
ldx #$00
seasonPaletteLoop:
lda dawnPalette, y
sta $2007
inx
iny
cpx #$10
bne seasonPaletteLoop
;lda #%00011110 ; background and sprites enabled
;lda $2001
rts
; tile type in param1, X and Y position of tile in param2 and param3
; attr in param8? maybe it should be optional? (it was added on later don't judge xD)
; you should only call this during vblank or while bulk drawing with ppu off
drawTile:
tya ; This is Y clobber prevention so that you can keep using Y to do other things if you like.
pha
jsr setAttributes
lda #$20 ; initial address of 2000 and nametable 0, the only nametable of this game >:)
sta tilePPUAddress
lda param3 ; y*2
asl a
lsr a ; shifted right 3 times for just the high 2 bits
lsr a
lsr a
ora tilePPUAddress
sta tilePPUAddress
lda param3 ; y*2 again
asl a
asl a ; this time shifted left 5 times for the low 3 bits on top
asl a
asl a
asl a
asl a
sta tilePPUAddress+1
lda param2 ; x*2
asl a
ora tilePPUAddress+1
sta tilePPUAddress+1
drawTileTop:
lda $2002
lda tilePPUAddress
sta $2006
lda tilePPUAddress + 1
sta $2006
lda param1
asl a
asl a
tay
lda MetaTiles, y
sta $2007
iny
lda MetaTiles, y
sta $2007
iny
clc ; go down a row
lda tilePPUAddress + 1
adc #$20
sta tilePPUAddress + 1
bcc drawTileBottom
inc tilePPUAddress
drawTileBottom:
lda $2002
lda tilePPUAddress
sta $2006
lda tilePPUAddress + 1
sta $2006
lda MetaTiles, y
sta $2007
iny
lda MetaTiles, y
sta $2007
iny
drawTileDone:
pla ; This is the second and concluding portion of the Y clobber prevention.
tay
rts
; param2 and param3 should still hold the X and Y values passed in from DrawTile
; y would be clobbered but since this is only called inside a function that pushes y to the stack, its safe
setAttributes:
lda param2
lsr a ; x divided by 0x02
sta param8
lda #$00
sta tilePPUAddress
lda param3
lsr a ; y divided by 0x02
clc
asl a ; then multiplied by 0x08
asl a
asl a
sta tilePPUAddress + 1
clc ; x and y are added together
lda param8
adc tilePPUAddress + 1
sta tilePPUAddress + 1
sta param8 ; param8 has the low byte of the ppu address index before it gets added to $23c0
; so that it can be used to index the attribute buffer in RAM
bcc setAttributeAddDone
inc tilePPUAddress
setAttributeAddDone:
clc ; the sum of x and y are added to the value $23c0 which is the start of the attribute table
lda tilePPUAddress + 1
adc #$c0
sta tilePPUAddress + 1
lda tilePPUAddress
adc #$23
sta tilePPUAddress
lda $2002
lda tilePPUAddress
sta $2006
lda tilePPUAddress + 1
sta $2006
loadAttributeByte:
; param1 still has the tile value which will soon be drawn
; param2 and param3 still have the x and y values when they were passed in!
; this code is based on a description given by rainwarrior here on this thread: https://forums.nesdev.com/viewtopic.php?t=13950
; however the janky implementation is entirely my fault ;)
attributeClearMaskLoad: ;first bitmask for bits that stay the same
lda param2
and #$01
cmp #$01
beq attrClrXOdd
jmp attrClrXEven
attrClrXOdd:
lda param3
and #$01
cmp #$01
beq attrClrXOddYOdd
attrClrXOddYEven:
lda #%00001100
sta param9
jmp attrClearMaskLoadDone
attrClrXOddYOdd:
lda #%11000000
sta param9
jmp attrClearMaskLoadDone
attrClrXEven:
lda param3
and #$01
cmp #$01
beq attrClrXEvenYOdd
attrClrXEvenYEven:
lda #%00000011
sta param9
jmp attrClearMaskLoadDone
attrClrXEvenYOdd:
lda #%00110000
sta param9
; param8 has the index to access the target value from attributesBuffer
; param9 has the bitmask to be used on the target value
attrClearMaskLoadDone:
lda param7 ; param7 temporarily used
pha
lda param9
eor #%11111111 ; oops I have to invert it again, maybe will remove this and fix later
sta param9 ; this bitmask is used for the bits that are staying the same
ldy param8
lda attributesBuffer, y ; target value loaded
and param9 ; bitmasked target value to be put in param7
sta param7
lda param9
eor #%11111111 ; inverted bitmask for bits that are changing
sta param9
ldy param1
lda MetaTileAttributes, y ; new value loaded
and param9 ; bitmasked target value
ora param7 ; or together new value and target value
ldy param8
sta attributesBuffer, y ; back to its original position in the attributes buffer, and into the ppu
sta $2007
pla
sta param7 ; restore param7
rts
; no arguments, draws the entire map (bulk drawing only! this is far too much to fit in vblank!)
drawMap:
ldx #$00
mapByteLoop:
lda MapData, x
cmp #TILE_GRASS
beq mapByteLoopDrawGrass
cmp #TILE_WATER
beq mapByteLoopDrawWater
jmp drawMapTileRegular
mapByteLoopDrawGrass:
jsr HandleGrassDraw
sta <param1
jmp drawMapTransform
mapByteLoopDrawWater:
jsr HandleWaterDraw
sta <param1
jmp drawMapTransform
drawMapTileRegular:
lda MapData, x
sta currentMapByte
sta <param1
drawMapTransform:
txa ; x coordinate (modulo 16)
and #%00001111
sta param2
txa ; y coordinate (divided by 16 and floored)
lsr a
lsr a
lsr a
lsr a
clc
adc #MAP_DRAW_Y ; 3 metatiles added on to the position of the tile because hotbar
sta param3
jsr drawTile
inx
cpx #$c0
bne mapByteLoop
drawMapDone:
rts
; this is simply buffering metatiles and can be called whenever!
; param4/5: x and y position
; param6/7: width and height
; param2 temporarily used as the sum of the x+plus width
; param3 temporarily used as the sum of the y+plus height
drawTextBox:
ldy param5
drawTextBoxYLoop:
ldx param4
drawTextBoxXLoop:
jsr loadTextboxTileToA
sta <param1 ; param1 holds the tiletype for now, which was previously loaded into A
stx <param2 ; param2 holds the x position for a little while, since X is occupied
sty <param3
;sta param1 ; these lines uncommented would produce a bulk drawing result.
;stx param2 ; maybe in the future there can be an additional parameter to switch between
;sty param3 ; buffered textbox drawing and bulk textbox drawing. that would be pretty cool.
;jsr drawTile
txa ; x is temporarily used for indexing the tile buffer
pha
jsr placeTileInBuffer
pla ; the old x returns
tax
lda <param4 ; param2 = (x + width)
clc
adc <param6
sta <param2
inx
cpx <param2
bne drawTextBoxXLoop
lda <param5 ; param3 = (y + height)
clc
adc <param7
sta <param3
iny
cpy <param3
bne drawTextBoxYLoop
rts
loadTextboxTileToA:
lda <param4 ; param2 = (x + width)
clc
adc <param6
sta <param2
lda <param5 ; param3 = (y + height)
clc
adc <param7
sta <param3
checkX:
cpx <param4
bne checkXSummed
lda #$08
cpy <param5
beq textBoxTileLoaded
lda #$0e
dec <param3
cpy <param3
beq textBoxTileLoaded
lda #$0b
jmp textBoxTileLoaded
checkXSummed:
dec <param2 ; its evaluating (width+x)-1
lda <param2
cpx <param2
bne checkXOther
lda #$0a
cpy <param5
beq textBoxTileLoaded
lda #$10
dec <param3
cpy <param3
beq textBoxTileLoaded
lda #$0d
jmp textBoxTileLoaded
checkXOther:
lda #$09
cpy <param5
beq textBoxTileLoaded
lda #$0f
dec <param3
cpy <param3
beq textBoxTileLoaded
lda #$0c
textBoxTileLoaded:
rts
; drawString works like this: you set stringPtr and strPPUAddress
; before you call this subroutine. As long as you do that, you're good to go!
; Oh, and make sure all your strings end in $ff, or else you get corrupto!!
; Also, newline character is $fe.
; todo make it buffered also lol it used to clobber everything, I didn't know how to use ram hardly when I wrote this
drawString:
txa
pha
tya
pha
ldy #$00
setStringAddr:
ldx strPPUAddress
stx $2006
ldx strPPUAddress + 1
stx $2006
drawStringLoop:
lda [stringPtr], y
cmp #$ff
beq drawStringDone
cmp #$fe
beq newLine
writeChar:
sta $2007
iny
jmp drawStringLoop
; newLine adds 40 to the initial nametable address where text starts rendering,
; moving it down 2 tiles = 16 pixels
newLine:
clc
lda strPPUAddress + 1
adc #$40
sta strPPUAddress + 1
bcc newLineDone
inc strPPUAddress
newLineDone:
iny
jmp setStringAddr
drawStringDone:
pla
tay
pla
tax
rts
; drawMapChunk behaves just like drawTextBox! it can be called whenever, it's buffered!
; param4/5: x and y position
; param6/7: width and height
; param2 temporarily used as the sum of the x+plus width
; param3 temporarily used as the sum of the y+plus height
drawMapChunk:
ldy <param5
drawMapChunkYLoop:
ldx <param4
drawMapChunkXLoop:
stx <param2
sty <param3
txa ; the following subroutines clobber x
pha
; this first portion of the map chunk rendering attempts to bypass the typical tile drawing by
; iterating through each players units and seeing if their coordinates match up with the X/Y iterators.
; If so, it loads those metatiles to the buffer and skips the tile rendering.
ldx #$00
cpx <p1UnitCount
bne scanP1Pieces
jmp scanP2Pieces
scanP1Pieces:
scanP1PiecesLoop:
lda p1PiecesX, x
cmp <param2
bne scanP1PiecesLoopTail
lda p1PiecesY, x
clc
adc #MAP_DRAW_Y
cmp <param3
bne scanP1PiecesLoopTail
lda p1PiecesType, x ; if both the X and Y coordinates match, then add the unit to the buffer
clc
adc #P1_UNITS_START_OFFSET
sta <param1
; param2 and param3 are already set up and good to go
jsr placeTileInBuffer
pla ; before leaving the player unit iteration loop, we have to restore the original x and y registers!
tax
jmp drawMapChunkXLoopTail
scanP1PiecesLoopTail:
inx
cpx p1UnitCount
bne scanP1PiecesLoop
; Now checking PLAYER 2 UNITS:
scanP2Pieces:
ldx #$00 ; here the x register is used to iterate through all of player 2's units
cpx <p2UnitCount
bne scanP2PiecesLoop
jmp scanP2PiecesDone
scanP2PiecesLoop:
lda p2PiecesX, x
cmp <param2
bne scanP2PiecesLoopTail
lda p2PiecesY, x
clc
adc #MAP_DRAW_Y
cmp <param3
bne scanP2PiecesLoopTail
lda p2PiecesType, x ; if both the X and Y coordinates match, then add the unit to the buffer
clc
adc #P2_UNITS_START_OFFSET
sta <param1
; param2 and param3 are already set up and good to go
jsr placeTileInBuffer
pla ; before leaving the player unit iteration loop, we have to restore the original x and y registers!
tax
jmp drawMapChunkXLoopTail
scanP2PiecesLoopTail:
inx
cpx <p2UnitCount
bne scanP2PiecesLoop
scanP2PiecesDone:
pla
tax
drawTileNoUnit:
; map tile has to get to A so here goes...
tya
sec
sbc #MAP_DRAW_Y
asl a
asl a
asl a
asl a
stx <param2
clc
adc <param2 ; mapdata index = (y*16)+x
sta <param2 ; param2 stores the mapdata index temporarily
txa ; frees x to be used for indexing the mapdata
pha
ldx <param2
lda MapData, x ; now A has the tile value, which is now given to param1
; grass is handled uniquely when drawing tiles
; water is also now!
cmp #TILE_GRASS
beq GrassDraw
cmp #TILE_WATER
beq WaterDraw
jmp NormalTileDraw
GrassDraw:
jsr HandleGrassDraw
jmp NormalTileDraw
WaterDraw:
jsr HandleWaterDraw
NormalTileDraw:
sta <param1
pla ; x is back in business
tax
stx <param2 ; param2 holds the x position and param3 holds the y position
sty <param3
txa ; (x is clobbered by the following subroutine)
pha
; param1 2 and 3 get passed into here
jsr placeTileInBuffer
pla ; the old x returns
tax
drawMapChunkXLoopTail: ; here the temp variables in param2&3 are generated to determine whether the rectangle has been fully achieved.
lda <param4 ; param2 = (x + width)
clc
adc <param6
sta <param2
inx
cpx <param2
beq drawMapRowDone
jmp drawMapChunkXLoop
drawMapRowDone:
lda <param5 ; param3 = (y + height)
clc
adc <param7
sta <param3
iny
cpy <param3
beq drawMapChunkDone
jmp drawMapChunkYLoop
drawMapChunkDone:
rts
; input: iterator in X (either X position or combined Y*16+X, either work)
; output: tile value in A
HandleGrassDraw:
; if a grass tile, lets check to see if its on the edge of the border
txa
and #$0f
cmp #$07
beq drawBorderLeft
cmp #$08
beq drawBorderRight
lda #$02
rts
drawBorderLeft:
lda #$15
rts
drawBorderRight
lda #$16
rts
; input: absolute index to mapdata in X
; output: tile value in A
HandleWaterDraw:
lda #$00
sta <param8 ;param8 temporarily stores index into metatile data "which tile to draw"
txa
stx <param9 ;param9 temporarily contains index to MapData "where is the tile in question?"
LandTileRightCheck: ; right tile = +01
clc
adc #$01
tax
lda MapData, x
cmp #TILE_WATER
beq LandTileUpCheck
inc param8
LandTileUpCheck: ; up tile = +02
lda <param9
sec
sbc #$10
tax
lda MapData, x
cmp #TILE_WATER
beq LandTileLeftCheck
lda <param8
clc
adc #$02
sta <param8
LandTileLeftCheck:
lda <param9
sec
sbc #$01
tax
lda MapData, x
cmp #TILE_WATER
beq LandTileDownCheck
lda <param8
clc
adc #$04
sta <param8
LandTileDownCheck:
lda <param9
clc
adc #$10
tax
lda MapData, x
cmp #TILE_WATER
beq LandTileCheckDone
lda <param8
clc
adc #$08
sta <param8
LandTileCheckDone:
lda <param9
tax ; clobber protection is done
lda <param8
clc
adc #$17
rts
; You know how it goes, param1 type, param2 Xposition, param3 Yposition
; clobbers X
placeTileInBuffer:
lda tileBufferLength ; bufferlength * 4 is the start position of the new item in the buffer to place
asl a
asl a
tax
lda <param1
sta tileBuffer, x ; tiletype stored in param1 for now
inx
lda <param2
sta tileBuffer, x ; x value stored in param2 for now
inx
lda <param3
sta tileBuffer, x ; y value directly stored in Y
inc tileBufferLength
rts
updateHotbar:
lda #$01
sta param4
lda #$01
sta param5
lda #$0e
sta param6
lda #$02
sta param7
jsr drawTextBox
StringTest:
lda #$20
sta strPPUAddress
lda #$43
sta strPPUAddress + 1
lda #LOW(text_EngineTitle)
sta stringPtr
lda #HIGH(text_EngineTitle)
sta stringPtr+1
updateStringBuffer:
ldx #$00
clearStringBufferLoop:
lda #$28 ; period character
sta stringBuffer, x
inx
cpx #$20
bne clearStringBufferLoop
lda turn
cmp #$00
beq loadPointerPlayer1Name
jmp loadPointerPlayer2Name
loadPointerPlayer1Name:
lda #LOW(text_Player1Turn+2)
sta param1
lda #HIGH(text_Player1Turn+2)
sta param1+1
jmp bufferPlayerName
loadPointerPlayer2Name:
lda #LOW(text_Player2Turn+2)
sta param1
lda #HIGH(text_Player2Turn+2)
sta param1+1
bufferPlayerName:
ldy #$00
bufferPlayerNameLoop:
lda [param1], y
sta stringBuffer, y
iny
cpy #$08
bne bufferPlayerNameLoop
bufferGoldText:
ldy turn
lda p1Gold, y
bcdGoldDisplay:
sta Hex0
jsr HexToDecimal.8
lda DecTens
sta stringBuffer+11
lda DecOnes
sta stringBuffer+12
lda #$10
sta stringBuffer+13 ; "g"
bcdFarmCount:
lda p1FarmCount, y
sta Hex0
jsr HexToDecimal.8
lda #$42 ; farm symbol
sta stringBuffer+17
lda DecTens
sta stringBuffer+18
lda DecOnes
sta stringBuffer+19
bcdUnitCount:
lda #$2f
sta stringBuffer+22
ldy turn
lda p1UnitCount,y
sta stringBuffer+23
lda #$27
sta stringBuffer+24
lda #$08
sta stringBuffer+25
rts