-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtictac.asm
1898 lines (1860 loc) · 81.6 KB
/
tictac.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
;===========================================================|
; Basic ASCII Tic-Tac-Toe game, by Matthew Rease. |
; Created 9/22/2019, updated on file modification date. |
; My first program in a new language is almost always a |
; tictactoe game... |
;===========================================================|
.286
INCLUDE basic.mac ; Load in basic macro library
;-------------------|
; BEGIN STACK |
;-------------------|
DemoStack SEGMENT STACK
TheStack DB 32 DUP ('(C) Matthew R. ') ; Reserves 512 bytes of memory for Stack, containing copyright string repeated 32 times
DemoStack ENDS
;-------------------|
; END STACK |
;-------------------|
;-------------------|
; BEGIN DATA |
;-------------------|
MyData SEGMENT PUBLIC
EXTRN CRLF:BYTE
moveText DB "Your move, ",'$'
copyright DB "(c) 2020 - Matthew Rease",'$'
thanks DB "Thanks for playing!",'$'
congrats DB " Wins the game!",'$'
notfound DB " file wasn't found",'$'
helpText DB "Help:",0Dh,0Ah,
" r - restart game",0Dh,0Ah,
" q / esc - quit game",0Dh,0Ah,
" [7 - 9] - row 1, column 1-3",0Dh,0Ah,
" [4 - 6] - row 2, column 1-3",0Dh,0Ah,
" [1 - 3] - row 3, column 1-3",0Dh,0Ah,
"Press any key to continue...",'$'
videoMode DB "Please select video mode:",'$'
videos DB " 1 - Text only (default)",'$', ; add 19h to get next line
" 2 - Tandy/PC Jr. ",'$',
" 3 - CGA ",'$',
" 4 - EGA ",'$'
gameVars DW 0 ; lsb is the current player (0 = x, 1 = y), bits 1-15 are board layout (5 bits per row)
board DB " ³ ³ ÄÄÄÅÄÄÄÅÄÄÄ" ; building blocks of the game board
pieces DB "XO",0 ; possible pieces to place on board
cursor DW 0101h
vidmodes DB 3,9,4,0Dh ; video mode 3 (80x25 text), 9 is PCjr/Tandy 1000 320x200 16 color, 4 is CGA 320x200 4 color, D is EGA 320x200 16 color
; byte 0 = current video mode
; byte 1 = system video mode
settings DB 2 DUP (0)
; PCjr/Tandy graphics data
; sprite 0 = X - 0h
; sprite 1 = O - 80h
; sprite 2-5 = 'YOUR MOVE ' - 100h, 180h, 200h, 280h
; sprite 6 = fancy x - 300h
; sprite 7 = fancy o - 380h
; sprite 8-12 = "WINNER:" - 400h, 480h, 500h, 580h, 600h
; sprite 13 = vertical bar - 680h
; sprite 14 = horizontal bar - 700h
; sprite 15 = horizontal/vertical cross bar - 780h
; sprite 16 = trophy - 800h
; sprite 17 = top cursor arrow - 880h
; sprite 18 = left cursor arrow - 900h
; sprite 19 = right cursor arrow - 980h
; sprite 20 = bottom cursor arrow - A00h
; sprite 21 = quit - A80h
; sprite 22 = card "logo" - B00h
; sprite 23 = 7 - B80h
; sprite 24 = 8 - C00h
; sprite 25 = 9 - C80h
; sprite 26 = 4 - D00h
; sprite 27 = 5 - D80h
; sprite 28 = 6 - E00h
; sprite 29 = 1 - E80h
; sprite 30 = 2 - F00h
; sprite 31 = 3 - F80h
jrdata DB "JR.BIN",0,'$' ; location of binary data for PCjr/Tandy graphics
; CGA graphics data
; sprite 0 = X - 0h
; sprite 1 = O - 40h
; sprite 2-5 = 'YOUR MOVE ' - 80h, C0h, 100h, 140h
; sprite 6 = fancy x - 180h
; sprite 7 = fancy o - 1C0h
; sprite 8-12 = "WINNER:" - 200h, 240h, 280h, 2C0h, 300h
; sprite 13 = vertical bar - 340h
; sprite 14 = horizontal bar - 380h
; sprite 15 = horizontal/vertical cross bar - 3C0h
; sprite 16 = trophy - 400h
; sprite 17 = top cursor arrow - 440h
; sprite 18 = left cursor arrow - 480h
; sprite 19 = right cursor arrow - 4C0h
; sprite 20 = bottom cursor arrow - 500h
; sprite 21 = quit - 540h
; sprite 22 = card "logo" - 580h
; sprite 23 = 7 - 5C0h
; sprite 24 = 8 - 600h
; sprite 25 = 9 - 640h
; sprite 26 = 4 - 680h
; sprite 27 = 5 - 6C0h
; sprite 28 = 6 - 700h
; sprite 29 = 1 - 740h
; sprite 30 = 2 - 780h
; sprite 31 = 3 - 7C0h
cgadata DB "CGA.BIN",0,'$' ; location of binary data for CGA graphics
; EGA graphics data
; sprite offsets identical to PCjr/Tandy 1000 graphics data
egadata DB "EGA.BIN",0,'$' ; location of binary data for EGA graphics
grpieces DB 1080h DUP (0) ; reserve 2688 bytes for the graphics data
jrlast equ 1000h
cgalast equ 0800h
pattern equ 88h ; takes the binary form of 10001000, which, if shifted right,
; will let me draw board chunk 0,0,0,1,0,0,0,1,0,0,0 while only using 1 byte for the pattern
vidcount equ 4 ; there are currently 2 video options
MyData ENDS
;-------------------|
; END DATA |
;-------------------|
;-------------------|
; BEGIN CODE |
;-------------------|
MyCode SEGMENT PUBLIC
assume CS:MyCode,DS:MyData
EXTRN Write:PROC
EXTRN WriteLn:PROC
EXTRN WriteText:PROC
EXTRN WriteTextLn:PROC
;---------------------|
; Main Procedure |
;---------------------|
main PROC
;
; Start of Program
;
start:: mov AX,MyData ; Moves Data segment address to AX register
mov DS,AX ; Allowing us to move that address to the intended data segment register, DS
mov AX,0B800h ; most modes use this address for the video memory
mov ES,AX
;
; show user possible video modes
;
lea DX,videoMode ; get address of video mode string
call WriteLn ; write to screen
xor AX,AX ; count 0
ShowVideo: push AX ; backup count
lea DX,videos ; get video modes
mov CL,19h ; 19h = 25
mul CL ; multiply AX by 25
add DX,AX ; use as offset
call WriteLn ; print
pop AX ; restore count
inc AX ; add 1
cmp AX,vidcount ; test if we're done
jne ShowVideo ; if not, keep going
;
; get video mode that the user wants
;
AskVideo: mov AH,7 ; DOS function 1, get char without echo
int 21h ; Call DOS
cmp AL,1Bh ; check if escape was pressed
je ExitGame ; if so, return to DOS
sub AL,31h ; subtract 30h, (turn ascii number into actual number and subtract one)
cmp AL,vidcount ; make sure the user typed a valid number
jae AskVideo ; if not, wait for new input
mov [settings],AL ; set video mode
;
; get current video mode, and store in memory
;
mov AH,0Fh ; BIOS video function F, get current video mode
int 10h ; call BIOS video
mov [settings+1],AL ; place in memory
;
; set requested video mode
;
xor AH,AH ; Video function 0, set mode
mov BL,[settings] ; get video mode
xor BH,BH ; 0 out BH just in case
mov AL,[vidmodes+BX] ; get requested video mode
push BX ; backup video mode
int 10h ; call BIOS video
pop AX ; restore video mode
;
; Initialize and Load graphics (if a graphics mode was selected)
;
cmp AL,1 ; test PCjr/Tandy 1000 video
je JrInit
cmp AL,2 ; test CGA video
je CGAInit
cmp AL,3 ; test EGA video
je EGAInit
jmp InitGame
;
; PCjr/Tandy 1000 graphics initialization
;
;
; Video Gate Array Settings
;
JrInit: mov DX,03D8h ; Mode Select
mov AL,0Bh ; high-res clock, 320x200 graphics, enable video signal
out DX,AL
mov DX,03B8h
mov AL,88h
out DX,AL
;
; Load Graphics from File
;
mov AH,3Dh ; DOS function 3D, open existing file
xor AL,AL ; place 0 in AL
lea DX,jrdata ; get filename
int 21h ; call DOS, if successful, file handle will be stored in AX
jc NoFile ; if carry flag set, error
mov BX,AX ; place file handle in BX
mov AH,3Fh ; DOS function 3F, read from file or device
mov CX,0D80h ; load 22 sprites of 80h bytes each
lea DX,grpieces ; set address to load data
int 21h ; call DOS
jmp InitGame
;
; CGA graphics initialization
;
CGAInit: mov DX,3D8h ; CGA Mode control register
mov AL,0Ah ; bit 1 = graphics mode
out DX,AL
mov DX,3D9h ; CGA Color control register
mov AL,20h ; bit 5 = palette ('snow'), bit 4 = high intensity mode (which I don't want)
out DX,AL
;
; Load Graphics from File
;
mov AH,3Dh ; DOS function 3D, open existing file
xor AL,AL ; place 0 in AL
lea DX,cgadata ; get filename
int 21h ; call DOS, if successful, file handle will be stored in AX
jc NoFile ; if carry flag set, error
mov BX,AX ; place file handle in BX
mov AH,3Fh ; DOS function 3F, read from file or device
mov CX,0800h ; read 32 sprites of 40h bytes each
lea DX,grpieces ; get memory address where graphics are to be stored
int 21h ; call DOS (now CGA graphics are in RAM)
jmp InitGame
;
; EGA graphics initialization
;
EGAInit: mov BX,0A000h
mov ES,BX
mov DX,3C0h ; EGA Mode Control register
mov AL,1 ; bit 0 = graphics mode
out DX,AL
;
; Load Graphics from File
;
mov AH,3Dh ; DOS function 3D, open existing file
xor AL,AL ; place 0 in AL
lea DX,egadata ; get filename
int 21h ; call DOS, if successful, file handle will be stored in AX
jc NoFile ; if carry flag set, error
mov BX,AX ; place file handle in BX
mov AH,3Fh ; DOS function 3F, read from file or device
mov CX,0C80h ; load 22 sprites of 80h bytes each
lea DX,grpieces ; set address to load data
int 21h ; call DOS
jmp InitGame
;
; Unable to load graphics file
;
NoFile: push DX ; backup filename
xor AH,AH ; BIOS video function 0, set video mode
mov AL,[settings+1] ; restore original video mode
int 10h ; call BIOS video
pop DX ; restore filename
call Write ; print filename
lea DX,notfound ; not found message
call WriteLn ; print rest of message
jmp ExitGame ; exit
;
; Initialize game
;
InitGame: mov cursor,101h ; reset cursor position
mov gameVars,0 ; refresh variables
cmp [pieces+2],0 ; check winner
jz ClearScreen ; if no winner was set, continue game
mov AL,[pieces+2] ; get winner
and AX,1 ; remove potential 2nd bit
mov gameVars,AX ; set player
mov [pieces+2],0 ; unset winner
;
; Clear screen and redraw interface
;
ClearScreen: mov AL,[settings] ; get video mode
cmp AL,0 ; check if we're in text mode
je clsText ; redraw screen (text)
cmp AL,1 ; check if we're in PCjr/Tandy mode
je clsJr ; redraw screen (PCjr/Tandy 1000)
cmp AL,2 ; check if we're in CGA mode
je clsCGA ; redraw screen (CGA)
cmp AL,3 ; check if we're in EGA mode
je clsEGA ; redraw screen (EGA)
jmp startGame ; if not, start game as usual
clsText: call drawBoardText
jmp startGame
clsJr: call drawBoardJr
jmp startGame
clsCGA: call drawBoardCGA
jmp startGame
clsEGA: call drawBoardEGA
jmp startGame
;
; Main game loop
;
startGame: mov AL,[settings] ; get video mode
;
; Show whose turn it is
;
cmp AL,0 ; test text mode
je turnText ; print current player
cmp [pieces+2],0 ; check if someone has won
jnz userInput ; if so, we don't want to draw the "Your Move" sprites on TOP of the "Winner" sprites!
cmp AL,1 ; test PCjr/Tandy 1000 mode
je turnJr ; draw current player
cmp AL,2 ; test CGA mode
je turnCGA ; draw current player
cmp AL,3 ; test EGA mode
je turnEGA ; draw current player
jmp userInput ; if unknown mode, skip section
;
; Print player character to screen
;
turnText: mov AH,2 ; BIOS video function 2, set cursor position
xor BH,BH ; page 0
mov DX,0Bh ; row 0, column 11
int 10h ; call BIOS video
mov BX,[gameVars] ; load gameVars in BX
and BX,1 ; remove all but LSB
mov AL,[pieces+BX] ; get player char
mov AH,0Eh ; function 0E, output char to screen
xor BX,BX ; set BX to 0
int 10h ; call BIOS function 0E
jmp userInput ; procede to user input
;
; Draw player on screen
;
turnJr: mov AX,1B9Ch ; location where 'fancy' x and o go
mov BX,300h ; location of 'fancy x' sprite
test gameVars,1 ; check current player
jz turnJrCont ; if 0, do nothing
add BX,80h ; but if 1, move to 'fancy o' sprite
turnJrCont: call JrDrawSpr ; and finally draw it to screen
jmp userInput ; then procede to user input
;
; Draw player on screen
;
turnCGA: mov AX,1B8Eh ; location where 'fancy' x and o go
mov BX,180h ; location of 'fancy x' sprite
test gameVars,1 ; check current player
jz turnCGACont ; if 0, do nothing
add BX,40h ; but if 1, move to 'fancy o' sprite
turnCGACont: call CGADrawSpr ; and finally draw it to screen
jmp userInput ; then procede to user input
;
; Draw player on screen
;
turnEGA: mov AX,1B87h ; location where 'fancy' x and o go
mov BX,300h ; location of 'fancy x' sprite
test gameVars,1 ; check current player
jz turnEGACont ; if 0, do nothing
add BX,80h ; but if 1, move to 'fancy o' sprite
turnEGACont: call EGADrawSpr ; and finally draw it to screen
jmp userInput ; then procede to user input
;
; Get user input
;
userInput: mov AH,7 ; DOS function 07, get single character from keyboard (no echo)
int 21h ; Call DOS
; test code, ignore plz
;mov DX,62h ; 60h is the IO address of the 8255 PPI, the 3rd byte (C) is for input
;in AL,60h ; 60h is the 8255 PPI
;mov BL,AL
;in AL,61h
;mov AH,AL
;or AL,80h
;out 61h,AL
;xchg AH,AL
;out 61h,AL
;mov AL,BL
;
; Misc functions
;
cmp AL,1Bh ; compare AL to 1B which corresponds to the escape key
je endGame ; drop out of loop to end game
cmp AL,71h ; compare AL to 71 which corresponds to lowercase 'q'
je endGame ; gotta give people options :)
cmp AL,72h ; compare AL to 72 which corresponds to lowercase 'r'
je InitGame ; restarts game
cmp AL,3Fh ; compare AL to 3F which corresponds to '?'
je printHelp ; print help text
cmp AL,68h ; compare AL to 68 which corresponds to lowercase 'h'
je printHelp ; print help text
cmp AL,0 ; check for 0
je testSpecial
;
; Check for winner (if someone has won, allow no more board changes
;
cmp [pieces+2],0 ; see if there is a winner
jnz userInput ; if there is, then we won't allow any further changes to the board
cmp AL,20h ; check if space was pressesd
je cursorPlace ; if so, try move
cmp AL,0Dh ; check if enter was pressed
je cursorPlace
jmp uiNumbers ; if not, continue
cursorPlace: mov AX,cursor ; place cursor location in AX
mov BH,2 ; place 2 in BH
sub BH,AH ; so we can invert the row
mov AH,BH ; copy to BX
shl AH,1
add AH,BH ; AH * 3
add AL,AH ; add to AL
add AL,31h ; then add 31h as if a number had been pressed by the user
;
; Check if 1-9 were pressed, if so, attempt to update game board
;
uiNumbers: sub AL,31h ; ascii for 1 is 31h, so turn that into a 0
cmp AL,9 ; now subtract 9, that way if it is 0-8 we will trigger the carry flag
jae userInput ; if they did not input 1-9, then don't try anything (continue)
call tryMove ; attempt the requested move, if it is valid, the board will be updated, and the player will change
cmp DL,0 ; check if board was updated
jne userInput ; if not, don't redraw
jmp startGame ; refresh screen
;
; Test for special keys
;
testSpecial: int 21h ; Call DOS again
cmp AL,48h ; 48 is returned if the up arrow was pressed
je cursorUp ; move up
cmp AL,4Bh ; 4B is returned if the left arrow was pressed
je cursorLeft ; move left
cmp AL,4Dh ; 4D is returned if the right arrow was pressed
je cursorRight ; move right
cmp AL,50h ; 50 is returned if the down arrow was pressed
je cursorDown ; move down
jmp userInput ; return
;
; Move cursor
;
cursorUp: mov AX,cursor ; get current position
mov BX,AX ; copy into BX
dec AH ; increase row
cmp AH,2 ; check if we're still within the bounds
jb updateCur ; update
mov AH,2 ; if not, set row to 0
jmp updateCur ; then update
cursorLeft: mov AX,cursor ; get current position
mov BX,AX ; copy into BX
dec AL ; decrease column
cmp AL,2 ; check if we're still within the bounds
jb updateCur ; update
mov AL,2 ; if not, set column to 2
jmp updateCur ; then update
cursorRight: mov AX,cursor ; get current position
mov BX,AX ; copy into BX
inc AL ; increase column
cmp AL,3 ; check if we're still within the bounds
jb updateCur ; update
xor AL,AL ; if not, set column to 0
jmp updateCur ; then update
cursorDown: mov AX,cursor ; get current position
mov BX,AX ; copy into BX
inc AH ; decrease row
cmp AH,3 ; check if we're still within the bounds
jb updateCur ; update
xor AH,AH ; if not, set row to 2
jmp updateCur ; then update
;
; Draw cursor and update memory
;
updateCur: push AX ; backup new cursor position
mov AL,[settings] ; get video mode
cmp AL,0 ; test text mode
je ucText
cmp AL,1 ; test PCjr/Tandy 1000 mode
je ucJr
cmp AL,2 ; test CGA mode
je ucCGA
cmp AL,3 ; test EGA mode
je ucEGA
pop AX ; what we push, we must pop
jmp userInput ; can't determine video mode, so do nothing
;
; Print cursor to screen
;
ucText: mov AH,2 ; BIOS video function 2, set cursor - DOS function 2, print single char
mov DX,BX ; place old cursor position in DX
shl DX,1
shl DX,1 ; multiply row and column by 4
xor BH,BH ; page 0
add DX,301h ; add 3 to row, and 1 to column
int 10h ; call BIOS video
mov DL,20h ; space
int 21h ; call DOS
pop AX ; restore new cursor position
mov cursor,AX ; update memory
mov DX,AX ; and place in DX
shl DX,1
shl DX,1 ; cursor position * 4
mov AH,2 ; BIOS video function 2, set cursor - DOS function 2, print single char
xor BH,BH ; page 0
add DX,301h ; add 3 to row, and 1 to column
int 10h ; call BIOS video
mov DL,0DFh ; horizontal bar, upper
int 21h ; call DOS
jmp userInput
;
; Draw cursor on screen
;
ucJr: shl BX,1
shl BX,1 ; old cursor position * 4
mov DL,BH ; place row in DL
xor DH,DH ; 0 out DH
mov AX,280h ; 4 pixel rows = 280h
mul DX ; multiply by row
inc BL ; one more column
shl BL,1
shl BL,1
shl BL,1 ; column * 8
xor BH,BH ; remove BH
add AX,BX ; add offset to AX
mov BX,jrlast ; last sprite, blank
call JrDrawSpr
add AX,278h ; next sprite row, but 16 pixels left
call JrDrawSpr
add AX,10h ; move 32 pixels right
call JrDrawSpr
add AX,278h ; next sprite row, but 16 pixels left
call JrDrawSpr
pop AX ; restore new cursor position
mov cursor,AX ; update memory
mov BX,AX ; and place in BX
shl BX,1
shl BX,1 ; new cursor position * 4
mov DL,BH ; place row in DL
xor DH,DH ; 0 out DH
mov AX,280h ; 4 pixel rows
mul DX ; multiply by row
inc BL ; one more column
shl BL,1
shl BL,1
shl BL,1 ; column * 8
xor BH,BH ; remove BH
add AX,BX ; add offset to AX
mov BX,880h ; sprite 17, top cursor arrow
call JrDrawSpr
add AX,278h ; one sprite row down, and one sprite column left
mov BX,900h ; sprite 18, left cursor arrow
call JrDrawSpr
add AX,10h ; move 32 pixels right
mov BX,980h ; sprite 19, right cursor arrow
call JrDrawSpr
add AX,278h ; one sprite row down, and one sprite column left
mov BX,0A00h ; sprite 20, bottom cursor arrow
call JrDrawSpr
jmp userInput
;
; Draw cursor on screen
;
ucCGA: shl BX,1
shl BX,1 ; old cursor position * 4
mov DL,BH ; place row in DL
xor DH,DH ; 0 out DH
mov AX,280h ; 4 pixel rows = 140h
mul DX ; multiply by row
inc BL ; one more column
shl BL,1
shl BL,1 ; column * 4
xor BH,BH ; remove BH
add AX,BX ; add offset to AX
mov BX,cgalast ; last sprite, blank
call CGADrawSpr
add AX,27Ch ; next row, 16 pixels to the left
call CGADrawSpr
add AX,8 ; move 32 pixels right
call CGADrawSpr
add AX,27Ch ; next row, 16 pixels to the left
call CGADrawSpr
pop AX ; restore new cursor position
mov cursor,AX ; update memory
mov BX,AX ; and place in BX
shl BX,1
shl BX,1 ; new cursor position * 4
mov DL,BH ; place row in DL
xor DH,DH ; 0 out DH
mov AX,280h ; 4 pixel rows
mul DX ; multiply by row
inc BL ; one more column
shl BL,1
shl BL,1 ; column * 4
xor BH,BH ; remove BH
add AX,BX ; add offset to AX
mov BX,440h ; sprite 17, top cursor arrow
call CGADrawSpr
add AX,27Ch ; next row, 16 pixels to the left
mov BX,480h ; sprite 18, left cursor arrow
call CGADrawSpr
add AX,8 ; move 32 pixels right
mov BX,4C0h ; sprite 19, right cursor arrow
call CGADrawSpr
add AX,27Ch ; next row, 16 pixels to the left
mov BX,500h ; sprite 20, bottom cursor arrow
call CGADrawSpr
jmp userInput
;
; Draw cursor on screen
;
ucEGA: shl BX,1
shl BX,1 ; old cursor position * 4
mov DL,BH ; place row in DL
xor DH,DH ; 0 out DH
mov AX,280h ; 4 pixel rows = 280h
mul DX ; multiply by row
inc BL ; one more column
shl BL,1 ; column * 2
xor BH,BH ; remove BH
add AX,BX ; add offset to AX
mov BX,jrlast ; last sprite, blank
call EGADrawSpr
add AX,27Eh ; next row, 16 pixels to the left
call EGADrawSpr
add AX,4 ; move 32 pixels right
call EGADrawSpr
add AX,27Eh ; next row, 16 pixels to the left
call EGADrawSpr
pop AX ; restore new cursor position
mov cursor,AX ; update memory
mov BX,AX ; and place in BX
shl BX,1
shl BX,1 ; new cursor position * 4
mov DL,BH ; place row in DL
xor DH,DH ; 0 out DH
mov AX,280h ; 4 pixel rows
mul DX ; multiply by row
inc BL ; one more column
shl BL,1 ; column * 2
xor BH,BH ; remove BH
add AX,BX ; add offset to AX
mov BX,880h ; sprite 17, top cursor arrow
call EGADrawSpr
add AX,27Eh ; next row, 16 pixels to the left
mov BX,900h ; sprite 18, left cursor arrow
call EGADrawSpr
add AX,4 ; move 32 pixels right
mov BX,980h ; sprite 19, right cursor arrow
call EGADrawSpr
add AX,27Eh ; next row, 16 pixels to the left
mov BX,0A00h ; sprite 20, bottom cursor arrow
call EGADrawSpr
jmp userInput
;
; Help screen
;
printHelp: mov AL,[settings] ; get current video mode
cmp AL,0 ; test text mode
je phText
cmp AL,1 ; test PCjr/Tandy 1000
je phJr
cmp AL,2 ; test CGA
je phCGA
cmp AL,3 ; test EGA
je phEGA
jmp userInput
;
; Print help message
; Yes, this code is bad, just... consider it temporary I guess
;
phText: push DS ; backup data segment
mov BX,ES ; place video buffer in BX
mov DS,BX ; and then into DS
xor SI,SI ; we'll copy from the current video buffer
mov DI,0FA0h ; to another area in the video buffer that isn't displayed
cld ; go forward
mov CX,410h ; copy 13 screen rows of text
rep movsw
xor DI,DI ; start at 0
mov CX,410h ; copy into 13 screen rows of text
mov AX,0700h ; retain default color setting, but remove any characters
rep stosw
pop DS ; restore data segment (for Write)
mov AH,2 ; BIOS video function 2, set cursor position
xor BH,BH ; page 0
xor DX,DX ; row 0, column 0
int 10h ; call BIOS video
lea DX,helpText ; get address of help message
call Write ; print
mov AH,7 ; DOS function 07, get single char from keyboard (no echo)
int 21h ; Call DOS again (for user input)
push DS ; backup data segment
mov BX,ES ; place video buffer in BX
mov DS,BX ; and then into DS
mov SI,0FA0h ; this time we'll start in the hidden area
xor DI,DI ; and copy to the visible area
mov CX,410h ; copy 13 screen rows of text
rep movsw
pop DS ; restore data segment for the rest of the program
jmp userInput ; return to input
;
; Draw help message
;
phJr: mov BX,0A80h ; Sprite 21 - quit
mov AX,60h ; AX = Location to draw "Quit"
call JrDrawSpr
add BX,80h ; Sprite 22 - PCjr "Logo"
add AX,38h ; Location to draw logo
call JrDrawSpr
mov BX,0B80h ; Sprite 23 - 7
mov AX,288h ; Location of top left slot of board
call JrDrawSpr
add BX,80h ; Sprite 24 - 8
add AX,20h ; Top middle slot of board
call JrDrawSpr
add BX,80h ; Sprite 25 - 9
add AX,20h ; Top right slot of board
call JrDrawSpr
add BX,80h ; Sprite 26 - 4
add AX,9C0h ; Middle left slot of board
call JrDrawSpr
mov AH,7 ; DOS 07 - Read single char
int 21h ; Wait for user to press a key
mov BX,jrlast ; Last "sprite" - blank
mov AX,60h ; Location to un-draw "Quit"
call JrDrawSpr
add AX,38h ; Location to un-draw the logo
call JrDrawSpr
mov AX,gameVars ; Get game board data
shr AX,1 ; Remove current player bit
mov CX,288h ; Screen location of top left board slot
xor DX,DX ; Board index = 0, 0 (DH = row, DL = column)
phJrCol: push DX ; Backup board index
push AX ; Backup game board data
push CX ; Backup screen location
and AX,1Fh ; We only want the 5 LSBs
call getColumn ; Get column number specified by DL
mov BX,jrlast ; Address of last PCjr "sprite" (blank)
cmp AH,0 ; Check if this board slot is empty
jz phJrBlank ; If it is, draw the sprite now, otherwise get the player sprite:
dec AH ; Now AH = 0 for Player 1, and AH = 1 for Player 2
mov BL,AH ; Store the player that owns this slot in BL
xor BH,BH ; BH = 0
shl BX,7 ; If BX was 0, it's still 0, otherwise it went from 1 to 40h
phJrBlank: pop AX ; Restore screen location (in AX for sprite routine)
call JrDrawSpr ; Draw whatever sprite we've specified in BX
mov CX,AX ; Copy screen location back to CX
add CX,20h ; Screen location for next board slot (on same row)
pop AX ; Restore game board data
pop DX ; Restore board index
inc DL ; Next column
cmp DL,3 ; Check if we just drew the last column
jne phJrCol ; If not, draw this column
xor DL,DL ; Otherwise, set column to 0
shr AX,5 ; And we're done with this row, so remove it
add CX,9A0h ; New row, means new screen location, this should get us there
inc DH ; Next row
cmp DH,3 ; Check if we've finished
jne phJrCol ; If not, draw this row
jmp userInput ; Otherwise, return to userInput
;
; Draw help message
;
phCGA: mov BX,0540h ; Sprite 21 - quit
mov AX,30h ; AX = Location to draw "Quit"
call CGADrawSpr
add BX,40h ; Sprite 22 - CGA "Logo"
add AX,1Ch ; Location to draw the logo
call CGADrawSpr
mov BX,5C0h ; Sprite 23 - 7
mov AX,284h ; Location of top left slot of board
call CGADrawSpr
add BX,40h ; Sprite 24 - 8
add AX,10h ; Top middle slot of board
call CGADrawSpr
add BX,40h ; Sprite 25 - 9
add AX,10h ; Top right slot of board
call CGADrawSpr
add BX,40h ; Sprite 26 - 4
add AX,9E0h ; Middle left slot of board
call CGADrawSpr
add BX,40h ; Sprite 27 - 5
add AX,10h ; Middle slot of board
call CGADrawSpr
add BX,40h ; Sprite 28 - 6
add AX,10h ; Middle right slot of board
call CGADrawSpr
add BX,40h ; Sprite 29 - 1
add AX,9E0h ; Bottom left slot of board
call CGADrawSpr
add BX,40h ; Sprite 30 - 2
add AX,10h ; Bottom middle slot of board
call CGADrawSpr
add BX,40h ; Sprite 31 - 3
add AX,10h ; Bottom right slot of board
call CGADrawSpr
mov AH,7 ; DOS 07 - Read single char
int 21h ; Wait for user to press a key
mov BX,cgalast ; Last "sprite" - blank
mov AX,30h ; Location to un-draw "Quit"
call CGADrawSpr
add AX,1Ch ; Location to un-draw the logo
call CGADrawSpr
mov AX,gameVars ; Get game board data
shr AX,1 ; Remove current player bit
mov CX,284h ; Screen location of top left board slot
xor DX,DX ; Board index = 0, 0 (DH = row, DL = column)
phCGAcol: push DX ; Backup board index
push AX ; Backup game board data
push CX ; Backup screen location
and AX,1Fh ; We only want the 5 LSBs
call getColumn ; Get column number specified by DL
mov BX,cgalast ; Address of last CGA "sprite" (blank)
cmp AH,0 ; Check if this board slot is empty
jz phCGAblank ; If it is, draw the sprite now, otherwise get the player sprite:
dec AH ; Now AH = 0 for Player 1, and AH = 1 for Player 2
mov BL,AH ; Store the player that owns this slot in BL
xor BH,BH ; BH = 0
shl BX,6 ; If BX was 0, it's still 0, otherwise it went from 1 to 20h
phCGAblank: pop AX ; Restore screen location (in AX for sprite routine)
call CGADrawSpr ; Draw whatever sprite we've specified in BX
mov CX,AX ; Copy screen location back to CX
add CX,10h ; Screen location for next board slot (on same row)
pop AX ; Restore game board data
pop DX ; Restore board index
inc DL ; Next column
cmp DL,3 ; Check if we just drew the last column
jne phCGAcol ; If not, draw this column
xor DL,DL ; Otherwise, set column to 0
shr AX,5 ; And we're done with this row, so remove it
add CX,9D0h ; New row, means new screen location, this should get us there
inc DH ; Next row
cmp DH,3 ; Check if we've finished
jne phCGAcol ; If not, draw this row
jmp userInput ; Otherwise, return to userInput
;
; Draw help message
;
phEGA: mov BX,0A80h ; Sprite 21 - quit
mov AX,18h ; AX = Location to draw "Quit"
call EGADrawSpr
add BX,80h ; Sprite 22 - EGA "Logo"
add AX,0Eh ; Location to draw logo
call EGADrawSpr
mov BX,0B80h ; Sprite 23 -7
mov AX,282h ; Location of top left slot of board
call EGADrawSpr
mov AH,7 ; DOS 07 - Read single char
int 21h ; Wait for user to press a key
mov BX,jrlast ; Last "sprite" - blank
mov AX,18h ; Location to un-draw "Quit"
call EGADrawSpr
add AX,0Eh ; Location to un-draw the logo
call EGADrawSpr
mov BX,0B80h ; I don't know what the hell this is for
mov AX,282h ; And I'd rather figure it out later...
call EGADrawSpr
jmp userInput
;
; Main game loop end
;
endGame: xor AH,AH ; BIOS video function 0, set video mode
mov AL,[settings+1] ; get original video mode before program execution
int 10h ; call BIOS video
lea DX,copyright ; place address of copyright string in DX
call WriteLn ; print to screen
lea DX,thanks ; place address of thank you message in DX
call WriteLn ; print to screen
;
; Return to DOS
;
ExitGame: ;mov AX,0C00h
;int 21h
EXIT 0 ; Calls macro to terminate program and sets ERRORLEVEL to 0
main ENDP
;---------------------|
; Main Procedure ENDS |
;---------------------|
;------------------------------------------|
; Draw Board Procedure, for text mode |
;------------------------------------------|
drawBoardText PROC
;
; Clear Screen
;
cld ; clear direction
xor AX,AX ; set AX to 0
mov AX,0700h ; set color to 07, and clear the character
mov DI,0 ; begin at 0
mov CX,03E8h ; whole screen (80x25 / 2)
rep stosw ; fill with 0
;
; Print board
;
mov AH,1 ; BIOS video function 1, cursor control
mov CH,1Fh ; bit 5 disables cursor, 0-4 control cursor shape
int 10h ; call BIOS video
mov AH,2 ; BIOS video function 2, set cursor position
xor BH,BH ; page 0
xor DX,DX ; row 0, column 0
int 10h ; call BIOS video
lea DX,moveText ; Loads the address of the 'words' string, into the DX register
call WriteLn ; Ouputs moveText to console, followed by CRLF
lea BX,board ; board pattern 0
mov DX,0Bh ; 11 characters long
call WriteTextLn ; row 0
call WriteTextLn ; row 1
call WriteTextLn ; row 2
add BX,DX ; board pattern 1
call WriteTextLn ; row 3
sub BX,DX ; board pattern 0
call WriteTextLn ; row 4
call WriteTextLn ; row 5
call WriteTextLn ; row 6
add BX,DX ; board pattern 1
call WriteTextLn ; row 7
sub BX,DX ; board pattern 0
call WriteTextLn ; row 8
call WriteTextLn ; row 9
call WriteTextLn ; row 10
ret
drawBoardText ENDP
;------------------------------------------|
; Draw Board Procedure, for text mode ENDS |
;------------------------------------------|
;-------------------------------------------|
; Draw Board Procedure, for PCjr/Tandy |
;-------------------------------------------|
drawBoardJr PROC
;
; clear screen
;
cld ; clear direction
xor AX,AX ; set AX to 0
mov DI,0 ; begin at 0
mov CX,0F9Fh ; whole screen (bank 0) (1F3F / 2)
rep stosw ; fill with 0
add DI,0C1h ; next bank (2000 - 1F3F)
mov CX,0F9Fh ; whole screen (bank 1) (1F3F / 2)
rep stosw ; fill with 0
add DI,0C1h ; next bank
mov CX,0F9Fh ; whole screen (bank 2) (1F3F / 2)
rep stosw ; fill with 0
add DI,0C1h ; last bank
mov CX,0F9Fh ; whole screen (bank 3) (1F3F / 2)
rep stosw ; fill with 0
;
; draw board
;
mov BX,pattern ; pattern of rows
xor AX,AX ; begin at pixel 0,0
dbJrStart: cmp AX,1B80h ; check if we've drawn all 11 rows
jae dbJrEnd ; if so, draw "Your Move" sprites
test BX,1 ; check LSB
jz dbJr0 ; 0 represents a vertical bar row
jnz dbJr1 ; 1 represents a horizontal bar + cross row
dbJr0: push BX ; backup pattern
add AX,18h ; add 24
mov BX,680h ; sprite 13, vertical bar
call JrDrawSpr ; draw first bar
add AX,20h ; add 32
call JrDrawSpr ; draw second bar
add AX,248h ; beginning of next row
pop BX ; restore pattern
shr BX,1 ; next pattern
jmp dbJrStart ; next iteration
dbJr1: push BX ; backup pattern
mov BX,700h ; sprite 14, horizontal bar
call JrDrawSpr ; draw horizontal bar 1
add AX,8 ; next grid space
call JrDrawSpr ; draw horizontal bar 2
add AX,8 ; next grid space
call JrDrawSpr ; draw horizontal bar 3
add AX,8 ; next grid space
add BX,80h ; sprite 15, cross
call JrDrawSpr ; draw cross bar 1
sub BX,80h ; sprite 14, horizontal bar
add AX,8 ; next grid space
call JrDrawSpr ; draw horizontal bar 4