-
Notifications
You must be signed in to change notification settings - Fork 0
/
MINER.MAC
969 lines (922 loc) · 28.2 KB
/
MINER.MAC
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
.TITLE MINER
.ASECT
MAXFLD = 18.*14. ; Максимальный размер поля, с учётом двух пустых колонок слева/справа
SCICON = 100022 ; Адрес на экране для смайлика
SCINDL = 100124 ; Адрес на экране для левого индикатора
SCINDR = 100156 ; Адрес на экране для правого индикатора
SCBACK = 103603 ; Адрес на экране с которого начинается фон
.MACRO PUSH RR
MOV RR,-(SP)
.ENDM
.MACRO POP RR
MOV (SP)+,RR
.ENDM
.=001000
START::
CLR @#177560
; Определяем верхнюю границу памяти, ставим стек
MOV #177776, R0
EMT 354 ; .SETTOP
MOV R0, SP ; Ставим стек по SETTOP
; Ставим прерывание таймера
MOV #200, @#000102
MOV #TMEINT, @#000100
MENU:
CALL STPALG
MOV #GAMESC, R1 ; Строка для подготовки игрового экрана
CALL PRINT
CALL PAUSE
; Вывод лого
CALL DRLOGO
; Вывод текста меню
MOV #MENUSC, R1
CALL PRINT
MNLOOP:
; Ждём нажатия и крутим случайные числа
1$: CALL GETKEY
BNE 2$
CALL RANDOM
BR 1$
2$:
; Анализ клавиши
CMP #130, R0 ; 'X'
BEQ FINISH
MOV #MITEMS, R5
MNLOO1:
MOV (R5)+, R1
BEQ MNLOOP
CMPB R0, R1
BEQ STGAME
ADD #8., R5
BR MNLOO1
FINISH:
MOV #EXITSC, R1 ; Строка очистки экрана перед выходом
CALL PRINT
CALL PAUSE
CALL STPALD
EMT 350 ; .EXIT
; клавиша, ширина, высота, кол-во мин, адрес начала поля на экране
MITEMS:
.WORD 060, 9., 9., 3., <SCBACK+<80.*36.>+8.>
.WORD 061, 9., 9., 10., <SCBACK+<80.*36.>+8.>
.WORD 062, 13., 10., 20., <SCBACK+<80.*28.>+4>
.WORD 063, 16., 14., 43., <SCBACK+<80.*4>+1>
.WORD 0
GAMESC: ; Строка подготовки игрового экрана
.BYTE 033,246,62 ; Формат экрана 40x24
.BYTE 033,240,67 ; Цвет символа
.BYTE 033,241,60 ; Цвет знакоместа 0
.BYTE 033,242,60 ; Цвет фона 0
.BYTE 14 ; Clear screen
.BYTE 033,131,67,40 ; Курсор в позицию
.BYTE 0
EXITSC: ; Строка очистки экрана перед выходом
.BYTE 033,246,061 ; Формат экрана 80x24
.BYTE 033,240,67 ; Цвет символа
.BYTE 033,241,61 ; Цвет знакоместа 1
.BYTE 033,242,61 ; Цвет фона 1
.BYTE 14 ; Очистить экран
.BYTE 0
MENUSC: ; Текст меню
.BYTE 033,240,67 ; Цвет символа
.BYTE 033,131,52,54 ; Курсор в позицию
.ASCII /0. Лошара/
.BYTE 033,131,53,54 ; Курсор в позицию
.ASCII /1. Новичок/
.BYTE 033,131,54,54 ; Курсор в позицию
.ASCII /2. Любитель/
.BYTE 033,131,55,54 ; Курсор в позицию
.ASCII /3. Профессионал/
.BYTE 033,131,57,54 ; Курсор в позицию
.ASCII /X. Выход/
.BYTE 033,240,62 ; Цвет символа
.BYTE 033,131,65,52 ; Курсор в позицию
.ASCII /БК версия 2012 VINXRU/
.BYTE 033,131,66,50 ; Курсор в позицию
.ASCII /УКНЦ версия 2023 nzeemin/
.BYTE 033,240,61 ; Цвет символа
.BYTE 033,131,67,54 ; Курсор в позицию
.INCLUDE /VERSIO.MAC/
.BYTE 0
.EVEN
; Начало игры
; R5 указывает на строку таблицы MITEMS после клавиши
STGAME:
; Параметры игрового поля
MOV #GMEWID, R3
MOV (R5)+, (R3)+ ; GMEWID
MOV (R5)+, (R3)+ ; GMEHEI
MOV (R5)+, (R3)+ ; BOMBCN
MOV (R5)+, (R3) ; FSCADR
CLR BOMBPT ; Сколько мин установлено на поле
; Подготовка игрового экрана
MOV #GAMESC, R1 ; Строка для подготовки игрового экрана
CALL PRINT
CALL PAUSE
CALL DRBLOCKS ; Заполнение фона
; Устанавливаем курсор в центр поля
MOV GMEWID, R0
ASR R0
MOV R0, CURSRX
MOV GMEHEI, R0
ASR R0
MOV R0, CURSRY
; Очистка игрового поля
MOV #<MAXFLD+36.>, R4 ; Счётчик
MOV #FIELD0, R0
10$: MOVB #200, (R0)+ ; Заполнение байта FIELD
SOB R4, 10$
;NOTE: Бомбы расставляем при первом открытии ячейки
; Вывод смайлика
MOV #SPGOOD, R2
MOV #SCICON, R3
CALL SPR24
; Рисование игрового поля
CALL DRFIELD
; Обнуляем время
CLR TIME
CLR TIMETK
INC TIMECH
; Игровой цикл
GMLOOP:
; Проверяем условия окончания игры
CALL CHKFLD
MFPS R0
PUSH R0
; Обновить левый индикатор
MOV BOMBMK, R1 ; Осталось бомб, обновляется в CHKFLD
MOV #SCINDL, R3
CALL DRIND3
POP R0
MTPS R0
BNE 5$ ; Игра окончена?
JMP GMEWIN
; Ожидание нажатия клавиши, с обновлением времени
5$: TST TIMECH
BEQ 8$
CLR TIMECH
; Обновляем правый индикатор
MOV TIME, R1
MOV #SCINDR, R3
CALL DRIND3
; Проверяем нажата ли клавиша
8$: CALL GETKEY
BEQ 5$ ; нет нажатия => ждём
; Проверяем на клавишу выхода
CMP #130, R0 ; 'X'
BNE 10$
JMP MENU ; Возврат в меню
; Нарисовать ячейку под курсором - без курсора
10$: PUSH R0
CALL DRCURC ; Рисуем ячейку под курсором
POP R0
; Анализ нажатой клавиши
CMP #15504, R0 ; Клавиша влево?
BEQ GOLEFT
CMP #15503, R0 ; Клавиша вправо?
BEQ GORIGHT
CMP #15501, R0 ; Клавиша вверх?
BEQ GOUP
CMP #15502, R0 ; Клавиша вниз?
BEQ GODOWN
CMP #040, R0 ; Клавиша пробел?
BEQ GOSPC
; Любая другая клавиша - поставить/снять флаг/вопрос
CALL CUR2FLD ; R1 = адрес в FIELD
MOVB (R1), R0
BPL GMCUR ; Ячейка уже открыта? => продолжаем игру
MOV R0, R2
BIC #177637, R2 ; Оставляем биты 140 - признаки флажка и вопроса
BEQ 20$ ; нет флагов
BIT #100, R2
BEQ 22$
; стоит флажок - меняем на вопрос
BIC #140, R0
BIS #040, R0
BR 25$
; нет флагов - меняем на флажок
20$: BIS #100, R0
BR 25$
; знак вопроса - меняем на пусто
22$: BIC #140, R0
25$: MOVB R0, (R1) ; записываем изменение флагов
CALL DRCURC ; Рисуем ячейку под курсором
; Нарисовать курсор и продолжить игровой цикл
GMCUR: CALL DRCURS ; Нарисовать курсор
BR GMLOOP
; Обработка клавиш-стрелок
GOLEFT:
MOV CURSRX, R0
BEQ GMCUR
DEC CURSRX
BR GMCUR ; Нарисовать курсор и продолжить игру
GORIGHT:
MOV CURSRX, R0
INC R0
CMP GMEWID, R0
BLOS GMCUR
INC CURSRX
BR GMCUR ; Нарисовать курсор и продолжить игру
GOUP:
MOV CURSRY, R0
BEQ GMCUR
DEC CURSRY
BR GMCUR ; Нарисовать курсор и продолжить игру
GODOWN:
MOV CURSRY, R0
INC R0
CMP GMEHEI, R0
BLOS GMCUR
INC CURSRY
BR GMCUR ; Нарисовать курсор и продолжить игру
; Клавиша пробел - открыть ячейку
GOSPC:
TST BOMBPT ; Бомбы уже расставлены?
BNE 5$ ; да => пропускаем
CALL PUTBOMBS ; Расставляем бомбы
5$: CALL CUR2FLD ; R1 = адрес в FIELD
MOVB (R1), R0
BPL GMCUR ; Ячейка уже открыта? => продолжаем игру
BIC #177740, R0 ; Стираем признак закрытости и флаги
MOVB R0, (R1)
PUSH R0
CALL DRCURC ; Рисуем ячейку под курсором
CALL DRCURS ; Нарисовать курсор
POP R0
BNE 10$ ; ячейка пуста?
; Ячейка пуста - раскрываем область вокруг неё
MOV CURSRX, R0
MOV CURSRY, R1
CALL OPENAREA
BR GMCUR ; Нарисовать курсор и продолжить игру
10$: BIT #20, R0 ; Это мина?
BEQ GMCUR ; нет => Нарисовать курсор и продолжить игру
JMP GMEOVR ; Это мина, игра окончена
; Текущая ячейка пуста и уже открыта, раскрываем свободную область вокруг неё, рекурсия
; R0 = X, R1 = Y
OPENAREA:
MOV R1, R3 ; сохраняем строку
CALL XY2FLD ; R1 = адрес в FIELD
MOV R1, R2
MOV R3, R1 ; R0 = X, R1 = Y, R2 = адрес в FIELD
; влево
TST R0 ; колонка 0?
BEQ 15$ ; пропускаем проверку слева
DEC R0
DEC R2
BITB #200, (R2) ; открыта?
BEQ 10$
BITB #20, (R2) ; можно открыть?
BNE 10$
CALL OPENCELL
10$:
; влево-вверх
TST R1 ; строка 0?
BEQ 12$ ; пропускаем проверку влево-вверх
SUB #18., R2
BITB #200, (R2) ; открыта?
BEQ 11$
BITB #20, (R2) ; можно открыть?
BNE 11$
DEC R1
CALL OPENCELL
INC R1
11$: ADD #18., R2
12$:
; влево-вниз
INC R1
CMP GMEHEI, R1 ; последняя строка?
BEQ 14$
ADD #18., R2
BITB #200, (R2) ; открыта?
BEQ 13$
BITB #20, (R2) ; можно открыть?
BNE 13$
CALL OPENCELL
13$: SUB #18., R2
14$: DEC R1
; восстанавливаем после влево
INC R0
INC R2
15$:
; вверх
TST R1 ; строка 0?
BEQ 25$ ; пропускаем проверку вверх
SUB #18., R2
BITB #200, (R2) ; открыта?
BEQ 24$
BITB #20, (R2) ; можно открыть?
BNE 24$
DEC R1
CALL OPENCELL
INC R1
24$: ADD #18., R2
25$:
; вправо
INC R2
INC R0
CMP GMEWID, R0 ; последняя колонка?
BEQ 38$
BITB #200, (R2) ; открыта?
BEQ 28$
BITB #20, (R2) ; можно открыть?
BNE 28$
CALL OPENCELL
28$:
; вправо-вверх
TST R1 ; строка 0?
BEQ 30$ ; пропускаем проверку вверх
SUB #18., R2
BITB #200, (R2) ; открыта?
BEQ 29$
BITB #20, (R2) ; можно открыть?
BNE 29$
DEC R1
CALL OPENCELL
INC R1
29$: ADD #18., R2
30$:
; вправо-вниз
INC R1
CMP GMEHEI, R1 ; последняя строка?
BEQ 37$
ADD #18., R2
BITB #200, (R2) ; открыта?
BEQ 36$
BITB #20, (R2) ; можно открыть?
BNE 36$
CALL OPENCELL
36$: SUB #18., R2
37$: DEC R1
; Восстанавливаем после вправо
38$: DEC R0
DEC R2
; вниз
INC R1
CMP GMEHEI, R1 ; последняя строка?
BEQ 45$
ADD #18., R2
BITB #200, (R2) ; открыта?
BEQ 44$
BITB #20, (R2) ; можно открыть?
BNE 44$
CALL OPENCELL
44$: SUB #18., R2
45$: DEC R1
RETURN
; Открыть пустую ячейку, с рекурсивным открытием дальше
; R0 = X, R1 = Y, R2 = адрес в FIELD
OPENCELL:
BICB #340, (R2) ; Открываем ячейку, снимаем флаги
PUSH R2
PUSH R0
PUSH R1
; Перерисовать ячейку
CALL CELL2SCR ; R1 = адрес на экране
MOV R1, R3
MOVB (R2), R0
PUSH R0 ; сохраняем содержимое ячейки
CALL DRCELL
POP R0 ; содержимое ячейки пусто?
BNE 5$ ; нет => не продолжаем рекурсию
; Ячейка пуста - продолжаем рекурсивное раскрытие
POP R1 ; Y
POP R0 ; X
PUSH R0
PUSH R1
CALL OPENAREA ; Продолжить рекурсию по соседним клеткам
5$: POP R1 ; Y
POP R0 ; X
POP R2 ; адрес в FIELD
RETURN
; Расставляем мины
PUTBOMBS:
MOV BOMBCN, R5 ; Счётчик цикла - сколько мин поставить
1$: PUSH R5
2$: CALL RANDOM ; R3 = случайное число
BIC #100000, R3 ; убираем знак
MOV R3, R1
CLR R0
DIV GMEWID, R0 ; R0 = частное, R1 = остаток = X
PUSH R1 ; сохраняем X
CALL RANDOM ; R3 = случайное число
BIC #100000, R3 ; убираем знак
MOV R3, R1
CLR R0
DIV GMEHEI, R0 ; R0 = частное, R1 = остаток = Y
POP R0 ; теперь R0 = X, R1 = Y
CMP CURSRX, R0
BNE 5$
CMP CURSRY, R1 ; совпало с курсором?
BEQ 2$ ; да => ищем другое место
5$: CALL XY2FLD
BITB #20, (R1) ; Мина уже стоит?
BNE 2$ ; да => ищем другое место
MOVB #237, (R1) ; ставим мину
;MOVB #037, (R1);DEBUG
INC BOMBPT ; количество мин на поле
POP R5
SOB R5, 1$
; Мины поставлены, теперь вычисляем количество мин вокруг каждой ячейки
MOV #FIELD, R2
CLR R4 ; Номер строки
11$:
PUSH R4
PUSH R2
CLR R3 ; Номер столбца
12$:
MOVB (R2), R0 ; Содержимое клетки
BIT #20, R0 ; это мина?
BNE 15$ ; да => пропускаем всё
; Для ячейки (R2) считаем количество мин
CALL BOMBCALC
15$: INC R2
; Завершение циклов по строке и по строкам
INC R3
CMP GMEWID, R3
BNE 12$ ; Продолжаем цикл по строке
POP R2
ADD #18., R2 ; к следующей строке FIELD
POP R4
INC R4
CMP GMEHEI, R4
BNE 11$ ; Продолжаем цикл по строкам
RETURN
; Подсчёт мин вокруг пусто ячейки; R2 = адрес в FIELD
; R3 = колонка, R4 = строка
; Записывает результат в (R2)
BOMBCALC:
PUSH R2 ; сохраняем адрес в FIELD
CLR R0 ; Счётчик мин вокруг
; Столбец слева
DEC R2
BITB #20, (R2) ; мина слева?
BEQ 13$
INC R0
13$:
; влево-вверх
SUB #18., R2
BITB #20, (R2) ; мина слева-вверху?
BEQ 14$
INC R0
14$: ADD #18., R2
; влево-вниз
ADD #18., R2
BITB #20, (R2) ; мина слева-вверху?
BEQ 15$
INC R0
; Вверх
15$: MOV (SP), R2 ; восстанавливаем адрес в FIELD
SUB #18., R2 ; на строку выше
BITB #20, (R2) ; мина сверху?
BEQ 16$
INC R0
16$: MOV (SP), R2 ; восстанавливаем адрес в FIELD
INC R3 ; на столбец вправо
; Столбец справа
INC R2
BITB #20, (R2) ; мина справа?
BEQ 17$
INC R0
; вправо-вверх
17$: SUB #18., R2
BITB #20, (R2) ; мина справа-вверху?
BEQ 18$
INC R0
18$: ADD #18., R2
; вправо-вниз
ADD #18., R2
BITB #20, (R2) ; мина справа-внизу?
BEQ 20$
INC R0
20$: SUB #18., R2
MOV (SP), R2 ; восстанавливаем адрес в FIELD
DEC R3 ; восстанавливаем столбец
; Вниз
ADD #18., R2
BITB #20, (R2) ; мина внизу?
BEQ 26$
INC R0
26$:
; Мины вокруг подсчитаны, сохраняем их количество
POP R2
BISB R0, (R2) ; Записываем число мин вокруг ячейки
;MOVB R0, (R2);DEBUG открытое число
RETURN
; Проверка поля на завершение; должна вызываться только когда мины расставлены
; Результат: Z=1 - игра окончена
CHKFLD:
CLR NUMCLO ; Очищаем счётчик закрытых ячеек
CLR NUMFLG ; Очищаем счётчик флажков
MOV #FIELD, R2
CLR R4 ; Номер строки
11$:
PUSH R4
PUSH R2
CLR R3 ; Номер столбца
12$:
MOVB (R2)+, R0 ; Содержимое клетки
BPL 20$ ; Ячейка раскрыта?
; Ячейка не раскрыта
INC NUMCLO
BITB #100, R0 ; это флажок?
BEQ 20$ ; нет => переход
; Это флажок
INC NUMFLG
; Завершение циклов по строке и по строкам
20$: INC R3
CMP GMEWID, R3
BNE 12$ ; Продолжаем цикл по строке
POP R2
ADD #18., R2 ; к следующей строке FIELD
POP R4
INC R4
CMP GMEHEI, R4
BNE 11$ ; Продолжаем цикл по строкам
; Вычисляем сколько осталось мин
MOV BOMBCN, R0
SUB NUMFLG, R0 ; количество мин - количество флажков
BPL 30$ ; меньше нуля?
CLR R0 ; да, показываем ноль
30$: MOV R0, BOMBMK ; Сохраняем число для левого индикатора
; Проверяем условие завершения игры:
MOV BOMBCN, R0
CMP NUMCLO, R0 ; количество закрытых ячеек == количество мин ?
BEQ 40$
CLZ
40$: RETURN
NUMCLO: .WORD 0
NUMFLG: .WORD 0
; Игра окончена, игрок выиграл
GMEWIN:
MOV #SPWIN, R2
; Нарисовать иконку, показать все мины, вернуться в меню
BR GMEOV1
; Игра окончена, игрок проиграл
GMEOVR:
MOV #SPBAD, R2
GMEOV1: MOV #SCICON, R3
CALL SPR24
; Раскрыть все мины
MOV #FIELD, R2
MOV #MAXFLD, R4
10$: MOVB (R2), R0
BIT #20, R0
BEQ 12$
BICB #340, R0
MOVB R0, (R2)
12$: INC R2
SOB R4, 10$
; Отрисовать поле
CALL DRFIELD
; Ждём клавиши и выходим в меню
CALL WTKEY
JMP MENU
; Перевести R0 = X, R1 = Y в адрес в FIELD
; результат в R1
XY2FLD:
MUL #18., R1
ADD R0, R1
ADD #FIELD, R1
RETURN
; Перевести координаты курсора в адрес в FIELD
; результат в R1
CUR2FLD:
MOV CURSRY, R1
MUL #18., R1
ADD CURSRX, R1
ADD #FIELD, R1
RETURN
; Перевести координаты курсора в адрес на экране
CUR2SCR:
MOV CURSRX, R0
MOV CURSRY, R1
; Перевести R0=X и R1=Y в адрес на экране
; результат в R1
CELL2SCR:
MUL #<80.*16.>, R1
ASL R0
ADD R0, R1
ADD FSCADR, R1
RETURN
; Нарисовать ячейку под курсором
DRCURC:
CALL CUR2SCR ; R1 = адрес на экране
MOV R1, R3
CALL CUR2FLD ; R1 = адрес в FIELD
MOVB (R1), R0
;JMP DRCELL ; Нарисовать ячейку
; Нарисовать ячейку игрового поля
; R0 = содержимое ячейки, R3 = адрес на экране
DRCELL:
TSTB R0
BPL 5$
; Ячейка закрыта
BIT #100, R0
BEQ 1$
; Флажок
MOV #SPFLAG, R2
JMP SPR16
1$: BIT #040, R0
BEQ 2$
; Знак вопроса
MOV #SPQSGN, R2
JMP SPR16
; Просто закрытая ячейка
2$: MOV #SPUNKN, R2
JMP SPR16
; Ячейка открыта
5$: BIT #20, R0
BEQ 6$
MOV #SPMINE, R2 ; Мина
JMP SPR16
; Число мин вокруг 0..8
6$: MOV R0, R2
ASL R2
ASL R2
ASL R2
ASL R2
ASL R2
ASL R2
ADD #SP0, R2
JMP SPR16
; Нарисовать игровое поле
DRFIELD:
MOV FSCADR, R3 ; Адрес начала игрового поля на экране
MOV #FIELD, R2
MOV GMEHEI, R4 ; Счётчик строк
1$:
PUSH R4
PUSH R3
MOV GMEWID, R4 ; Счётчик столбцов
PUSH R2 ; адрес в FIELD
2$:
PUSH R4
PUSH R3
MOVB (R2)+, R0 ; Содержимое клетки
PUSH R2
CALL DRCELL ; Рисуем клетку
POP R2
POP R3
INC R3
INC R3
POP R4
SOB R4, 2$
; Завершение цикла по строкам
POP R2 ; адрес в FIELD
ADD #18., R2 ; К следующей строке FIELD
POP R3
ADD #<80.*16.>, R3 ; К следующей строке экрана
POP R4
SOB R4, 1$
; Нарисовать курсор
CALL DRCURS
RETURN
; Нарисовать фон
DRBLOCKS:
MOV #<SCBACK>, R3
MOV #17., R0 ; Счётчик столбцов
1$: PUSH R0
PUSH R3
MOV #14., R4 ; Счётчик строк
2$: MOV #SPBLOCK, R2
CALL SPR16
SOB R4, 2$
POP R3
INC R3
INC R3
POP R0
SOB R0, 1$
RETURN
; Вывести битмап с названием игры
DRLOGO:
MOV #SPLOGO, R2
MOV #36., R5
MOV #103612, R3
1$:
MOV #16., R4
2$:
MOV R3, @#176640
MOV (R2)+, @#176642
INC R3
SOB R4, 2$
ADD #<80.-16.>, R3 ; next line
SOB R5, 1$
RETURN
; Вывод 3-значного числа цифрами 16 x 21
; R1 = число, R3 = адрес на экране
DRIND3:
; Первая цифра
CLR R0 ; [R0:R1] делим на 100.
DIV #100., R0 ; R0 = частное, R1 = остаток
PUSH R1 ; сохраняем остаток 0..99.
PUSH R3 ; сохраняем адрес на экране
CALL DRIND1
POP R3
POP R1 ; восстанавливаем остаток
; Вторая цифра
INC R3
INC R3
CLR R0 ; [R0:R1] делим на 10.
DIV #10., R0 ; R0 = частное, R1 = остаток
PUSH R1 ; сохраняем остаток 0..9.
PUSH R3 ; сохраняем адрес на экране
CALL DRIND1
POP R3
POP R0 ; восстанавливаем остаток
; Третья цифра
INC R3
INC R3
; BR DRIND1
; Вывод цифры 0..9 спрайтом 16 x 21
; R0 = цифра 0..9, R3 = адрес на экране
DRIND1:
MOV R0, R1
MUL #<4*21.>, R1
MOV R1, R2
ADD #SPN0, R2
MOV #21., R5
BR SPR16N
; Вывод спрайта 16 x 16 пикселей
; R2 = адрес спрайта, R3 = адрес на экране
; Портит R5, R2, R3
SPR16:
MOV #16., R5 ; Количество строк
; Вывод спрайта 16 x N пикселей; R5 = N
SPR16N:
1$: MOV R3, @#176640
MOV (R2)+, @#176642 ; Первое слово
INC R3
MOV R3, @#176640
MOV (R2)+, @#176642 ; Второе слово
ADD #<80.-1>, R3 ; next line
SOB R5, 1$
RETURN
; Нарисовать спрайт курсора
DRCURS:
CALL CUR2SCR
MOV R1, R3
MOV #SPCURS, R2
;JMP SPR16M
; Вывод спрайта 16 x 16 пикселей с маской
; R2 = адрес спрайта, R3 = адрес на экране
; Портит R5, R2, R3, R0
SPR16M:
MOV #16., R5 ; Количество строк
1$:
MOV R3, @#176640
MOV @#176642, R0
BIC (R2)+, R0 ; Первое слово, маска
BIS (R2)+, R0 ; Первое слово, цвета
MOV R0, @#176642
INC R3
MOV R3, @#176640
MOV @#176642, R0
BIC (R2)+, R0 ; Второе слово, маска
BIS (R2)+, R0 ; Второе слово, цвета
MOV R0, @#176642
ADD #<80.-1>, R3 ; next line
SOB R5, 1$
RETURN
; Вывод спрайта 24 x 24 пикселей
; R2 = адрес спрайта, R3 = адрес на экране
SPR24:
MOV #24., R5 ; Количество строк
1$:
MOV R3, @#176640
MOV (R2)+, @#176642 ; Первое слово
INC R3
MOV R3, @#176640
MOV (R2)+, @#176642 ; Второе слово
INC R3
MOV R3, @#176640
MOV (R2)+, @#176642 ; Третье слово
ADD #<80.-2>, R3 ; next line
SOB R5, 1$
RETURN
; Подпрограмма: Печать строки: R1 = адрес строки, строка завершается 0; портит R0
; После завершения R1 указывает на байт следующий за 0
PRINT:
10$: MOVB (R1)+, R0 ; Конец строки?
BEQ RETN ; да => выходим
20$: TSTB @#177564 ; Источник канала 0 готов?
BPL 20$ ; нет => ждём
MOV R0, @#177566 ; передаём символ в канал 0
BR 10$
; Подпрограмма: пауза после очистки экрана чтобы ПП закончил работу
PAUSE: ; Pause to let PPU finish the previous commands
MOV #177777, R5
1$: NOP
SOB R5, 1$
RETN: RETURN
; Подпрограмма: Ожидание символа с клавиатуры: R0 = полученный символ
WTKEY: TSTB @#177560
BPL WTKEY
CLR R0
MOVB @#177562, R0 ; символ в R0
CMPB R0, #33
BNE RETN
ESCKEY: TSTB @#177560
BPL ESCKEY
MOVB @#177562, R0 ; символ в R0
BIS #15400,R0 ; #33 в верхний байт
RETURN
; Подпрограмма: Получение символа с клавиатуры: флаг Z=0 = есть символ, R0 = полученный символ
GETKEY: TSTB @#177560 ; есть символ?
BPL 10$ ; нет символа => выходим
MOVB @#177562, R0 ; символ в R0
CMPB R0, #33 ; Esc ?
BEQ ESCKEY
RETURN
10$: CLR R0 ; возвращаем пустой код клавиши
RETURN
; Палитра для установки в памяти ПП
PALETG: .WORD ^B1111101000100000 ;
.WORD ^B1111110101001001 ;
PALETD: .WORD 135230, 177334 ; Палитра УКНЦ по умолчанию
; Массив параметров для обмена с ПП по каналу 2
PPBLCK: .BYTE 0 ; В этом байте будет содержаться код ошибки или 0
PPBCMD: .BYTE 20 ; Команда (01-ВЫДЕЛИТЬ ПАМЯТЬ, 02-ОСВОБОДИТЬ ПАМЯТЬ 10-ЧТЕНИЕ, 20-ЗАПИСЬ, 30-ПУСК)
.WORD 32 ; Устройство - периферийный процессор
PPBAPP: .WORD 002470 ; Адрес ОЗУ ПП - два слова палитры
PPBACP: .WORD PALETG ; Адрес ОЗУ ЦП - адрес новой палитры
.WORD 2 ; Длина блока в словах
PPBADR: .WORD PPBLCK ; Слово всегда содержит начальный адрес массива параметров
.WORD 401 ; Стоповый элемент (используется при передаче)
; Установить игровую палитру
STPALG:
MOV #PALETG, @#PPBACP
BR PPSEND
; Вернуть стандартную палитру
STPALD:
MOV #PALETD, @#PPBACP
;BR PPSEND
; Подпрограмма передачи по каналу К2 массива параметров в ПП
PPSEND: MOV #PPBADR, R0
MOV #5, R1
MTPS #200
BR 1$
2$: MOVB (R0)+, @#176676
1$: TSTB @#176674
BPL 1$
SOB R1, 2$
MTPS #0
RETURN
; Routine: Get pseudo-random number, result in R3
; (copied from uknc-highwayencounter code)
RANDOM:
MOV RANDSD, R2 ; A270 LD DE,($5C76) ; Read RND SEED variable
MOV R2, R3 ; A274 LD H,E
SWAB R3
BIC #377, R3
ADD #375, R3 ; A275 LD L,$FD
MOV R2, R0 ; A277 LD A,D
SWAB R0
BIC #177400, R0 ; A278 OR A
SUB R2, R3 ; A279 SBC HL,DE
SBCB R0 ; A27B SBC A,$00
SUB R2, R3 ; A27D SBC HL,DE
SBCB R0 ; A27F SBC A,$00
; A281 LD E,A
BIC #177400, R0 ; A282 LD D,$00
SUB R0, R3 ; A284 SBC HL,DE
BHIS LA289 ; A286 JR NC,$A289
INC R3 ; A288 INC HL
LA289: MOV R3, RANDSD ; A289 LD ($5C76),HL ; Write RND SEED variable
RETURN ; A28C RET
RANDSD: .WORD 123456 ; Pseudo-random seed
; Прерывание таймера ЦП, вызывается 50 раз в секунду
TMEINT:
INC TIMETK
CMP TIMETK, #50. ; Накопилось >= 50. тиков?
BLT 20$ ; нет => выходим
SUB #50., TIMETK
CMP TIME, #999. ; максимальное время?
BEQ 20$ ; да => выходим
INC TIME ; Увеличиваем время на секунду
INC TIMECH ; флаг что время изменилось
20$: RTI
.INCLUDE /TILES.MAC/
; Параметры игры, задаются при выборе уровня
; порядок полей соответствует таблице MITEMS
GMEWID: .WORD 0 ; Ширина игрового поля в клетках
GMEHEI: .WORD 0 ; Высота игрового поля в клетках
BOMBCN: .WORD 0 ; Количество мин на этом уровне игры
FSCADR: .WORD 103600 ; АДрес начала игрового поля
; Переменные
BOMBPT: .WORD 0 ; Сколько мин сейчас на игровом поле
CURSRX: .WORD 0 ; Позиция X курсора
CURSRY: .WORD 0 ; Позиция Y курсора
BOMBMK: .WORD 0 ; Сколько осталось мин, число для индикатора
TIMETK: .WORD 0 ; Накопитель для тиков таймера
TIME: .WORD 0 ; Время от начала игры, 000..999.
TIMECH: .WORD 0 ; Флаг того, что значение TIME изменилось
STACK:: .BLKW 20 ; Временный стек, используется при запуске
ENDSTK = .
; Игровое поле; со всех сторон обрамлено свободным местом минимум в 1 ячейку
FIELD0 = ENDSTK ; .BLKB 18. ; Пустое пространство перед полем
FIELD = FIELD0 + 18. ; .BLKB MAXFLD ; Основная часть игрового поля
FIELD1 = FIELD + MAXFLD ; .BLKB 18. ; Пустое пространство после поля
;
ENDALL = FIELD1 + 18.
.END START