-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.asm
1783 lines (1696 loc) · 40.7 KB
/
main.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
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
#include "kernel.inc"
#include "corelib.inc"
.db "KEXC"
.db KEXC_ENTRY_POINT
.dw start
.db KEXC_STACK_SIZE
.dw 20
.db KEXC_KERNEL_VER
.db 0, 6
.db KEXC_NAME
.dw name
.db KEXC_DESCRIPTION
.dw description
.db KEXC_HEADER_END
name:
.db "ZTetris",0
description:
.db "The Best TI-83+ Tetris",0
corelibPath:
.db "/lib/core", 0
; 2 byte variables are stored little-endian (i.e. reverse order of registers)
cBitOfs .equ 0 ;* WORD
cBit .equ 2 ;* 4 BYTE
cXY .equ 6 ;* WORD
cB .equ 8 ;* 24 BYTE
cRot .equ 32 ;* BYTE
flags .equ 33 ;* BYTE (0 - Quit, 1 - Update)
newXY .equ 34 ;* WORD
newRot .equ 36 ;* BYTE
counter .equ 37 ;* BYTE
next .equ 38 ;* BYTE
linesflag .equ 39 ;* BYTE
score .equ 40 ;* WORD
scoreU .equ 42 ;* BYTE
lines .equ 43 ;* BYTE
level .equ 44 ;* BYTE
string .equ 45 ;* 6 BYTE
place .equ 51 ;* BYTE
cNBitOfs .equ 52 ;* WORD
players .equ 54 ;* BYTE
lcounter .equ 55 ;* WORD
declines .equ 57 ;* BYTE
stlevel .equ 58 ;* BYTE
lastbar .equ 59 ;* BYTE
hsflag .equ 60 ;* BYTE
scrflag .equ 61 ;* BYTE
gap .equ 62 ;* BYTE
sbyte .equ 63 ;* BYTE
sthigh .equ 64 ;* BYTE
high .equ 65 ;* BYTE
linkcnt .equ 66 ;* BYTE
lastKey .equ 67 ;* BYTE
lastKCnt .equ 68 ;* BYTE
board .equ 69 ;* 40 BYTE
;108 Bytes total
APD_BUF .equ 109 ;* 768 BYTE
;876 Bytes total
memSize .equ 877
start:
; KnightOS:
; TIOS stores system flags in IY+offset
; we don't need this so we can reclaim IY for the screen buffer
pcall(getLcdLock)
pcall(getKeypadLock)
kld(de,corelibPath)
pcall(loadLibrary)
pcall(allocScreenBuffer)
ret nz ; Exit if insufficient memory
ld bc, memSize
pcall(malloc)
ret nz ; Exit if insufficient memory
; Read high scores from file
kld(de, hiscorePath)
pcall(fileExists)
kjp(nz, ReProgStart)
pcall(openFileRead)
push ix
kld(ix, Hiscore)
ld bc, 48
pcall(streamReadBuffer)
pop ix
pcall(closeStream)
; KnightOS TODO:
; Add way to save/load game from file
jr ReProgStart
; KnightOS TODO:
; Put Menu and Game code in seperate files
; Invert selected level
drawLevelCursor: ; Puts digit A on the correct place
push af ; Used when choosing ProgStart level
push bc
ld l, 15
ld b, a
inc b
cp 5
jr c, FirstRow
ld l, 23
sub 5
ld b, a
inc b
FirstRow:
add a, 5
djnz FirstRow
add a, 10
ld e, a
ld bc, 7 << 8 | 5
pcall(rectXOR)
pop bc
pop af
ret
; KnightOS TODO:
; This function is only used once and should probably be spliced straight into the code
drawLevelGrid:
ld de, 16 << 8 | 16
xor a
ld b, 5
levelNumsLoop1: ; Print first row of numbers
pcall(drawDecA)
inc a
inc d
inc d
djnz levelNumsLoop1
ld de, 16 << 8 | 24
ld b, 5
levelNumsLoop2: ; Print second row of numbers
pcall(drawDecA)
inc a
inc d
inc d
djnz levelNumsLoop2
ld c, 15
ld l, 15
ld a, 14
ld b, 5
levelNumsLoop3: ; Draw vertical lines
pcall(drawVLine)
add a, 6
djnz levelNumsLoop3
pcall(drawVLine)
ld de, 14 << 8 | 14
ld hl, 44 << 8 | 14
ld a, e
ld b, 3
levelNumsLoop4: ; Draw horizontal lines
pcall(drawLine)
add a, 8
ld e, a
ld l, a
djnz levelNumsLoop4
ret
drawHeightNum:
ld bc, 7 << 8 | 5
ld e, 58
ld l, 21
pcall(rectAND) ; Clear area
ld de, 59 << 8 | 22
pcall(drawDecA) ; And show the High
ret
ReProgStart:
kcall(ShowFrame) ; Show title
ld de, 6 << 8 | 44 ; X = 6, Y = 50
ld b, 16 ; Center second line
kld(hl,Coder)
pcall(drawStr)
kld(hl,PlChoose) ; "Choose player mode"
ld de, 12 << 8 | 18 ; X = 12 Y = 18
pcall(drawStr)
kld(hl,PlChoose1) ; "1 player"
ld de, 32 << 8 | 24
pcall(drawStr)
kld(hl,PlChoose2) ; "2 player"
ld de, 32 << 8 | 30
pcall(drawStr)
xor a
ld (ix+stlevel),a ; When ProgStarting a new game, stLevel and stHigh
ld (ix+sthigh),a ; will be reset
inc a
ld (ix+players),a ; Default option, 1 player
ChoosePlayers:
ld a,(ix+players)
ld d, 26 ; X = 26
push af
add a,3
push af ; Multiply by 6 (1 row of text)
sla a
ld e, a
pop af
sla a
sla a
add a, e
ld e,a
ld a, 62 ; >
pcall(drawChar) ; Put the small arrow
pop af
ld d, 26
sub 6
neg
push af
sla a
ld e, a
pop af
sla a
sla a
add a, e
ld e, a
ld a, 62
pcall(drawCharAND) ; And remove it from the other position
WKCP:
pcall(fastCopy)
pcall(flushKeys)
corelib(appWaitKey)
push ix \ pop hl
push de
ld de, players
add hl, de
pop de
cp kMode
kjp(z,Quit)
cp kEnter
jr z,LevelChoose
cp k2nd
jr z,LevelChoose
cp kUp
jr z,ChangePlayers
cp kDown
jr z,ChangePlayers
jr WKCP
ChangePlayers:
ld a,(hl)
xor 3 ; This will turn 1 -> 2 and 2 -> 1
ld (hl),a
jr ChoosePlayers
LevelChoose:
kcall(ShowFrame) ; I know I used these 2x, but that's to stop the on screen trash.
ld a,(ix+stlevel)
ld (ix+level),a ; The cursor will ProgStart at the last played level
ld a,(ix+sthigh)
ld (ix+high),a ; And with the high
NewDigit:
ld de, 14 << 8 | 8 ; "Level:"
kld(hl,levelTxt)
pcall(drawStr)
kcall(drawLevelGrid)
ld de, 57 << 8 | 14
kld(hl,HighTxt) ; "Height"
pcall(drawStr)
ld c, 7 ; Draw box for height number
ld a, 57
ld l, 21
pcall(drawVLine)
add a, 6
pcall(drawVLine)
ld de, 57 << 8 | 20
ld hl, 63 << 8 | 20
pcall(drawLine)
ld e, 28
ld l, 28
pcall(drawLine)
ld a, (ix+level)
kcall(drawLevelCursor) ; Invert the ProgStarting level digit
ld a,(ix+high)
kcall(drawHeightNum)
ld a,(ix+players)
dec a ; Check if the hiscore should be shown
jr nz,Show2PlayOpt ; or two player options
ld de, 10 << 8 | 32
kld(hl,hiScoresTxt)
pcall(drawStr)
kld(hl,Hiscore)
ld a, 38
ld b, 3
NewPos:
ld d, 10
ld e, a
add a,6
push af
push bc
pcall(drawStr) ; Show name
ld a,0x46
ld bc, 14
add hl, bc
ld b,5 ; Show 5 digits for high scores
push hl
kcall(LD_HL_MHL) ; Get that persons score
ld d, 66
kcall(DM_HL_DECI3) ; And show it
pop hl
inc hl
inc hl ; HL -> next hiscore table entry
pop bc
pop af
djnz NewPos
jr ZWaitKey
Show2PlayOpt:
ld de, 14 << 8 | 38
kld(hl,SLTxt)
pcall(drawStr) ; "Send 2-4 lines"
ld de, 14 << 8 | 44
kld(hl,InfoText3)
pcall(drawStr) ; "Lines "
ld bc, 13 << 8 | 5
ld e, 13
ld l, 37
pcall(rectXOR) ; Invert S and L
xor a
ld (ix+declines),a ; Clear the flags to the two option above
ld (ix+scrflag),a
ZWaitKey:
ld a,(ix+players)
dec a ; If two players, the two players options
jr z,ZGetKey ; should be shown as well
ld bc, 6 << 8 | 43
ld e, 38
ld l, 44
pcall(rectAND)
kld(hl,ScrambleTxt)
ld a,(ix+scrflag)
or a
jr z,ShowScrFlag
ld de,12
add hl,de
ShowScrFlag:
ld de, 38 << 8 | 44
pcall(drawStr) ; Show "scrambled" or "unscrambled"
ZGetKey:
pcall(fastCopy)
pcall(flushKeys)
corelib(appWaitKey)
or a
jr z,ZGetKey
cp kMode
kjp(z,Quit)
cp kEnter
kjp(z,ProgStartGame)
cp k2nd
kjp(z,ProgStartGame)
cp kMinus
jr z,DecHigh
cp kPlus
jr z,IncHigh
cp kRParen ; Right Parentheses
kjp(z,ChangeScrFlag)
cp kLn
jr nz,CheckLevChg
ld a,(ix+players)
dec a
jr z,ZGetKey
ld a,(ix+declines)
xor 1 ; Change the declines flag (1-3 or 2-4)
ld (ix+declines),a
add a,a
add a,a
ld bc, 6 << 8 | 12
ld e, 34
ld l, 38
pcall(rectAND)
kld(hl,NLTxt)
ld d,0
ld e,a
add hl,de
ld de, 34 << 8 | 38
pcall(drawStr) ; Update it on the screen
jr ZGetKey
CheckLevChg:
dec a
jr z,ChangeRow
dec a
jr z,LevLeft
dec a
jr z,LevRight
dec a
kjp(nz,ZGetKey)
jr ChangeRow
DecHigh:
ld a,(ix+high)
or a
jr z,ZGetKey ; Don't decrease if high is 0
dec a
ld (ix+high),a
kcall(drawHeightNum)
kjp(ZWaitKey)
IncHigh:
ld a,(ix+high)
cp 5
kjp(z,ZGetKey) ; Don't increase if high is 5
inc a
ld (ix+high),a
kcall(drawHeightNum)
kjp(ZWaitKey)
ChangeRow:
ld a,(ix+level)
add a,5 ; Changing row is like adding with 5, mod 10
ChkLevEdges:
daa ; Modulo 10 (sort of)
and 0x0F
SetLevel:
ld b,a
ld a,(ix+level)
kcall(drawLevelCursor) ; Remove the inverted digit
ld a,b
ld (ix+level),a ; And set the new level
kcall(drawLevelCursor)
kjp(ZWaitKey)
LevLeft:
ld a,(ix+level)
dec a
jr ChkLevEdges
LevRight:
ld a,(ix+level)
inc a
jr ChkLevEdges
ChangeScrFlag:
ld a, 1
xor (ix+scrflag)
ld (ix+scrflag), a
kjp(ZWaitKey)
ProgStartGame:
ld a,(ix+level)
ld (ix+stlevel),a ; Copy the selected level and high so they will
ld a,(ix+high) ; be default next time
ld (ix+sthigh),a
ld a,(ix+players)
dec a
jr z,NoWait
xor a
ld (ix+lastbar),a
kcall(ShowFrame)
ld de, 34 << 8 | 26
kld(hl,WaitTxt)
pcall(drawStr) ; "* WAITING *"
pcall(fastCopy)
kcall(ReceiveByte)
or a
jr nz,NoWait ; If byte gotten, the other calc was waiting
ld a,1
ld (ix+hsflag),a ; This will allow the user to cancel with EXIT
ld a,0xAA
kcall(SendByte) ; Else wait until the other calc responds
NoWait:
xor a
ld (ix+hsflag),a
ld (ix+sbyte),a ; This is a linkbuffer byte
pcall(clearBuffer)
kcall(RandP) ; Randomize the first piece
ld hl,0xFFFF
ld (ix+board+3),h
ld (ix+board+2),l
push ix \ pop hl
push de
ld de, board+4
add hl, de
pop de
ld b,18
InitRow: ; Setting up the border aroudn the well
ld (hl),0b00000111
inc hl
ld (hl),0b11100000
inc hl
djnz InitRow
kcall(ShowLayout)
ld (ix+linkcnt),10 ; This counter decrease every frame. When 0,
ld a,(ix+players) ; check link port. If too often check, it slows down
dec a
ld a,0 ; Can't use 'xor a' here! It would affect the Z flag
kcall(nz,ShowBar) ; Show the bar at height 0
ResetVars:
xor a
ld (ix+flags),a ; Clear a lot of vars
ld (ix+cBit),a
ld (ix+scoreU), a ; Clears lines as well
ld (ix+lines), a
ld (ix+score+1), a
ld (ix+score), a
ld (ix + lastKey), a
ld (ix + lastKCnt), 5
kcall(NewB) ; Prepares a new piece
pcall(fastCopy)
ld a,(ix+high)
or a
jr z,MainLoop
ld b,(ix+scrflag) ; If starting with trash lines, they should
push bc ; always be scrambled even though the option
push hl ; unscrambled is set
ld (ix+scrflag),1 ; So temporary set it to scrambled lines
add a,a ; No trash lines = high*2
kcall(AddLines) ; Create trash lines
pop hl
pop bc
ld (ix+scrflag),b ; And reset the scrflag
pcall(fastCopy)
MainLoop: ; The main loop
kld(hl,LevelCnts)
ld a,(ix+level)
ld d,0
ld e,a
add hl,de
ld a,(hl) ; A = the delay time of the current level
ld (ix+counter),a
DelayLoop:
; Timing measurements for this loop (T-States):
; 6 Mhz: 15 Mhz:
; MirageOS: Ion: KnightOS (ZTv1.1.1): KnightOS (ZTv1.1.2):
; 166655 144840 86509 412569
; 166737 144777 86509 412562
; 166602 143577 85704 412562
; 166667 144782 86509 413367
; 166667 143577 86509 412569
; 166667 144777 86509 413367
res 1,(ix+flags) ; Clear the update flag
; KnightOS TODO:
; Switch to interrupt based timing once its implemented in KnightOS
; This is what the MirageOS version uses
corelib(appGetKey) ; TIOS: 2596 T-States
; KnightOS: 2199 T-States
; KnightOS TODO:
; corelib suspends the thread when switching to the castle or threadlist
; but it might be fun to show off KnightOS's multitasking abilities by not doing that
; KnightOS TODO:
; Redraw immediately after a context switch
; TIOS's getCSC filters repeats, but KnightOS's getKey does not
; The following remembers the last key pressed
; and only repeats it after 5 loops.
; lastKey is reset if the key is released
; so that you can move faster than the natural repeat
cp (ix + lastKey)
jr nz, notSameKey
dec (ix + lastKCnt)
jr z, repeatKey
xor a
jr doKey
notSameKey:
ld (ix + lastKey), a ; Save last key
repeatKey:
ld b, 5 ; Reset key repeat counter
ld (ix + lastKCnt), b
doKey:
cp kClear
kjp(z,AbortGame)
cp kMode
kjp(z,Pause)
cp k2nd
kjp(z,Rotate)
cp kAlpha
kjp(z,RotateBack)
cp kDel
kjp(z,TeacherKey)
cp kXTThetaN
kjp(z,Drop)
dec a
jr z,MoveDown
dec a
jr z,MoveLeft
dec a
jr z,MoveRight
dec a
jr z,Rotate
Wait:
bit 0,(ix+flags) ; Check if the player became gameover this frame
kjp(nz,GameOver)
bit 1,(ix+flags) ; Check if anything happened (movements)
kcall(nz,Update) ; If so, update that
;ld bc,3200 ; TIOS: 84910, 86115 T-States
; KnightOS: 84010
; KnightOS TODO:
; Change delay depending on clock speed of CPU
;ld bc, 6258 ; 6 Mhz Calcs
ld bc, 15645 ; 15 Mhz Calcs
; This gives correct fall rate on the TI-84+SE
dwait:
dec bc
ld a,b
or c
jr nz,dwait
dec (ix+linkcnt)
kcall(z,GetLinkInfo) ; If the link counter reaches zero, check link port
dec (ix+counter) ; Decrease the counter
jr nz,DelayLoop ; If not zero, check for keys again
jr FallDown
MoveDown:
inc (ix+scoreU) ; When DOWN is pressed, increase the score
FallDown:
kcall(GetLinkInfo) ; Before moving down, always check linkport
dec (ix+newXY) ; Decrease the y coordinate
kcall(Update) ; Check if possible
kjp(z,MainLoop) ; If so, repeat mainloop
BotReached:
push ix \ pop hl ; Else the bottom is reached
ld de, cB
add hl, de
ld d, (ix+cXY+1)
ld e, (ix+cXY)
ld b,4
StoreB: ; Store the piece in the well
push hl
kcall(LD_HL_MHL)
add hl,de
kcall(PutCoord)
pop hl
inc hl
inc hl
djnz StoreB
ld a,(ix+players)
dec a
kcall(nz,CheckBar) ; If two player, check your highest line
kcall(NewB) ; Last, randomize a new piece
pcall(fastCopy)
kjp(MainLoop)
MoveRight:
inc (ix+newXY+1) ; Increase X coordinate
jr SetUpdateFlag ; Set update flag
MoveLeft:
dec (ix+newXY+1) ; Decrease left coordinate
SetUpdateFlag:
set 1,(ix+flags) ; Set update flag
jr Wait
Rotate:
inc (ix+newRot)
jr SetUpdateFlag
RotateBack:
dec (ix+newRot)
jr SetUpdateFlag
Drop: ; When dropping, increase score with the
inc (ix+scoreU) ; number of fallen steps
dec (ix+newXY) ; Decrease Y coordinate
kcall(Update) ; Update it on screen
jr z,Drop ; If OK, move down again
kcall(GetLinkInfo) ; Get link info
jr BotReached ; Bottom reached, store piece.
TeacherKey:
ld a,(ix+players)
dec a
kjp(nz,Wait) ; If two players, teacher key not allowed
corelib(launchCastle)
Pause:
ld a,(ix+players)
dec a
kjp(nz,Wait) ; Pause not allowed in two player game
kld(hl,PauseTxt)
kld(de, pauseOptions)
xor a
ld b, a
corelib(showMessage)
; KnightOS TODO:
; Put calculator to sleep after a while
cp 1
kjp(z, Quit)
Unpause::
; Redraw game grid
pcall(clearBuffer)
kcall(ShowLayout)
kcall(ShowInfo)
kcall(ShowWell)
kcall(ShowCurB)
push ix \ pop hl ; Show next piece
ld de, cB+16
add hl, de
ld de, 0x1403
kcall(ShowB)
pcall(fastCopy)
pcall(flushKeys)
kjp(Wait)
ShowBar: ; Show the bar
ld (ix+lastbar),a
push iy \ pop hl
ld de, (63 * 12) + 11
add hl, de
ld de,-12
ld b,64
add a,a
add a,a
SB_Rep:
ld c,a
ld a,0x0F
and (hl)
ld (hl),a
ld a,c
or a
jr z,ClearBar
ld a,0x60
or (hl)
ld (hl),a
dec c
ClearBar:
ld a,c
add hl,de
djnz SB_Rep
ret
AddLines: ; Add A lines, scrambled or unscrambled
ld b,a
push bc
ld c,b
ld b,0
sla c
push ix \ pop hl
ld de, board+34
add hl, de
ld d, h
ld e, l
sbc hl,bc
ld a,32
sub c
ld b,0
ld c,a
lddr
pop bc
ld a,b
push af
ld a,10
kcall(PRandom)
ld (ix+gap),a
push ix \ pop hl
ld de, board+4
add hl, de
AddTrashRow:
push bc
ld de,0xFFFF
push hl
ld b,5
Holes:
push bc
ld hl,0xFFFB
ld a,(ix+scrflag)
or a
jr nz,RandGap
ld a,(ix+gap)
jr PutGap
RandGap:
ld a,10
kcall(PRandom)
PutGap:
ld b,a
inc b
RotWord:
.db 0xCB,0x35 ; SLL L - an undocumented Z80 instruction
rl h
djnz RotWord
pop bc
ld a,d
and h
ld d,a
ld a,e
and l
ld e,a
djnz Holes
pop hl
ld (hl),e
inc hl
ld (hl),d
inc hl
pop bc
djnz AddTrashRow
pop af
add a, (ix+newXY)
cp 0x10
jr c,TopNotReached
ld a,0x10
TopNotReached:
ld (ix+newXY), a
kcall(Update)
push af
kcall(ShowWell)
kcall(ShowCurB)
pop af
kjp(nz,GameOver)
ret
Update: ; Update the piece on the screen.
kcall(TestNewB) ; Return NZ if not possible move
jr nz,Sync
kcall(EraseCurB)
ld h, (ix+newXY+1)
ld l, (ix+newXY)
ld (ix+cXY+1), h
ld (ix+cXY), l
ld a,(ix+newRot)
ld (ix+cRot),a
ld b,0
kcall(Uncrunch)
kcall(ShowCurB)
xor a
Sync:
push af
ld h, (ix+cXY+1)
ld l, (ix+cXY)
ld (ix+newXY+1), h
ld (ix+newXY), l
ld a,(ix+cRot)
ld (ix+newRot),a
pcall(fastCopy)
pop af
ret
GetLinkInfo: ; Fins out what happens to the opponent
ld (ix+linkcnt),10 ; Reset the link counter
ld a,(ix+players)
dec a
ret z ; If one player, leave this routine
kcall(ReceiveByte) ; Get a byte
or a
jr z,CheckSByte ; If no byte received, check if a byte should be sent
ld b,a
and 0x0F
ld c,a
ld a,b
srl a
srl a
srl a
srl a
cp 0x0F
jr z,PenaltyRows
cp 0x0C
kjp(z,YouWinP)
cp 0x0D
jr z,UpdateBar
cp 0x0E
ret nz
ld c,16
UpdateBar:
ld a,c
kjp(ShowBar)
PenaltyRows:
ld a,c
inc a
kjp(AddLines)
CheckSByte:
ld a,(ix+sbyte) ; Check if byte in send buffer
or a
kcall(nz,SendByte) ; If so, send it
ret
AbortGame:
ld a,(ix+players)
dec a
kjp(z,CheckHiscore) ; If one player abort, check hiscore table
GameOver:
pcall(flushKeys)
ld a,(ix+players)
dec a
jr z, CheckHiscore ; If a two player game, send a byte telling
ld a,0xC0 ; that you lost
ld b,3
SendWinByte:
push bc
kcall(SendByte)
kcall(ReceiveByte) ; This is for clearing up stuff (if both sent
ld a,(ix+sbyte) ; at the same time)
or a
pop bc
jr z, CheckHiscore
djnz SendWinByte
CheckHiscore:
ld a,(ix+players)
dec a
kjp(nz,gameOverMessage) ; No hiscore when two players
kld(hl,Hiscore+14) ; HL -> hiscore
ld d, (ix+score+1) ; DE = your score
ld e, (ix+score)
ld b,3
CheckP:
push hl
kcall(LD_HL_MHL)
pcall(cpHLDE)
pop hl
jr c,ScoreGreater
push de
ld de,16
add hl,de ; HL -> next score in hiscore table
pop de
djnz CheckP
kjp(gameOverMessage)
ScoreGreater:
ld a,4
sub b
ld (ix+place),a
cp 3 ; If last place, no moves in hiscore table
jr z,MoveDone
kld(hl,Hiscore+19)
MoveAgain: ; Else move the other people down
ld d,h
ld e,l
ld bc,16
add hl,bc
ex de,hl
ld bc,13
ldir
kld(hl,Hiscore+3)
dec a
jr z,MoveAgain
MoveDone:
ld a,(ix+place) ; Find out where to enter your name
kld(hl,Hiscore+3)
ld de,16
dec a
jr z,EnterName
add hl,de
dec a
jr z,EnterName
add hl,de
EnterName:
push hl
ld b,10
RepClear:
ld (hl),32 ; Clear the previous name
inc hl
djnz RepClear
inc hl
ld d, (ix+score+1)
ld e, (ix+score)
ld (hl),e ; Put your score in the table
inc hl
ld (hl),d
pop hl
; Ask for name
push ix
push hl \ pop ix
kld(hl,EnterTxt)
ld bc, 10
xor a
ld (ix), a
corelib(promptString)
pop ix
; Store high scores to file
kld(de, hiscoreDir)
pcall(directoryExists)
pcall(nz, createDirectory)
kld(de, hiscorePath)
pcall(fileExists)
pcall(z, deleteFile)
pcall(openFileWrite)
push ix
kld(ix, Hiscore)
ld bc, 48
pcall(streamWriteBuffer)
pop ix
pcall(closeStream)
kjp(LevelChoose)
gameOverMessage:
kld(hl, GameOverText)
kld(de, gameOverOptions)
xor a
ld b, a
corelib(showMessage)
cp 1
kjp(z, Quit)
kjp(LevelChoose)
YouWinP:
pop hl
YouWin:
kld(hl, WinTxt)
kld(de, gameOverOptions)
xor a
ld b, a
corelib(showMessage)
cp 1
kjp(z, Quit)
kjp(LevelChoose)
CheckBar: ; Find out how it goes for ya
xor a
push ix \ pop hl
push de
ld de, board+4
add hl, de