commit 2a94dfb8d5052b9cef22f8ba060c05a0add27d68 Author: Elmar Kresse Date: Thu Jul 3 15:53:46 2025 +0200 Add initial source files for Tic-Tac-Toe game implementation Signed-off-by: Elmar Kresse diff --git a/MAIN.EXE b/MAIN.EXE new file mode 100644 index 0000000..d136404 Binary files /dev/null and b/MAIN.EXE differ diff --git a/MAIN.MAP b/MAIN.MAP new file mode 100644 index 0000000..5d13b3e --- /dev/null +++ b/MAIN.MAP @@ -0,0 +1,9 @@ + + Start Stop Length Name Class + + 00000H 0090EH 0090FH _TEXT CODE + 00910H 009ECH 000DDH _DATA DATA + 009F0H 00AEFH 00100H STACK STACK + +Program entry point at 0000:08A6 + diff --git a/MAIN.OBJ b/MAIN.OBJ new file mode 100644 index 0000000..17d36b9 Binary files /dev/null and b/MAIN.OBJ differ diff --git a/MAIN.TR b/MAIN.TR new file mode 100644 index 0000000..1aacd97 Binary files /dev/null and b/MAIN.TR differ diff --git a/data.asm b/data.asm new file mode 100644 index 0000000..89bb731 --- /dev/null +++ b/data.asm @@ -0,0 +1,49 @@ +;**************************************** +; Elmar Kresse 19-INB-1 (2021) +; data.asm +; file contains variables and constant +; for music, drawing, text and game +; +;**************************************** + + +;Sounds +placeSound DD 4070, 100000 ; Note with pause +placeSoundLength = $ - placeSound + +gameOverSound DD 4560, 100000, 4304, 50000, 7239, 100000, 9121, 200000 +gameOverSoundLength = $ - gameOverSound + + +;coordinates and size +x_wert DW ? ;16 Bit variable +y_wert DW ? ;16 Bit variable +d_wert DW ? ;16 Bit variable + + +spieler DW ? ;current player (0,1) +field DW ? ;selected field (0-8) +balance dw 0 ;balance for check won (3 = 3 in a row) + + +;Player Arrays +player1 DW 0,0,0,0,0,0,0,0,0 +player2 DW 0,0,0,0,0,0,0,0,0 + +;main menue strings +title_str DB "TIC-TAC-TOE$" +signature_str DB "[Elmar Kresse 19INB]$" +choose1_str DB "1 - Mensch gegen Mensch$" +choose2_str DB "2 - Verlassen$" +cross_win DB "Kreuz hat gewonnen$" +circle_win DB "Kreis hat gewonnen$" +draw DB "Unentschieden$" + +;reset ISR var +oldIOFF DW ? +oldISeg DW ? + +;timer vector vars +old_timer DW 0 ;original int 1c vector +old_segment DW 0 ;original int 1c segment +timer_ticks DW 0 ;timer_ticks \ No newline at end of file diff --git a/draw.asm b/draw.asm new file mode 100644 index 0000000..72649be --- /dev/null +++ b/draw.asm @@ -0,0 +1,344 @@ +;**************************************** +; Elmar Kresse 19-INB-1 (2021) +; draw.asm +; file contains source code for +; drawing and maths for circle +; +; - draw_pixel MACRO x, y, c +; - circle_draw macro x, y, radius +; Reference Digital differential analyzer (graphics algorithm) +; https://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm) +; +; - cross_draw MACRO x, y, d +; - rectangle_draw MACRO x, y, d +; - line_print_down PROC +; - line_print_straight PROC +; - paint_menu PROC FAR +; - prepare_display PROC +;**************************************** + +draw_pixel MACRO x, y, c + push ax + push bx + push cx + push dx + MOV AH, 0Ch ;setzen der Konfig fürs schreiben + MOV AL, c ; Farbe + MOV BH, 00h ;Set Seitennummber + MOV CX, x + MOV DX, y + INT 10h + pop dx + pop cx + pop bx + pop ax + ENDM + +circle_draw macro x, y, radius + push dx + push cx + push bx + push ax + + ;Werte zurücksetzen + mov balance, 0 + mov ax, 0 + mov bx, 0 + + ;Verschiebung Mitte + add x, 60 + add y, 60 + + mov bx, radius + mov balance, radius + neg balance + + + zeichne_kreis_schleife: + + ;0 to 45 + mov cx, x + add cx, bx + mov dx, y + sub dx, ax + draw_pixel cx, dx, 0Fh + ;90 to 45 + mov cx, x + add cx, ax + mov dx, y + sub dx, bx + draw_pixel cx, dx, 0Ch + ;90 to 135 + mov cx, x + sub cx, ax + mov dx, y + sub dx, bx + draw_pixel cx, dx, 04h + ;180 to 135 + mov cx, x + sub cx, bx + mov dx, y + sub dx, ax + draw_pixel cx, dx, 06h + ;180 to 225 + mov cx, x + sub cx, bx + mov dx, y + add dx, ax + draw_pixel cx, dx, 12h + ;270 to 225 + mov cx, x + sub cx, ax + mov dx, y + add dx, bx + draw_pixel cx, dx, 0Bh + ;270 to 315 + mov cx, x + add cx, ax + mov dx, y + add dx, bx + draw_pixel cx, dx, 01h + ;360 to 315 + mov cx, x + add cx, bx + mov dx, y + add dx, ax + draw_pixel cx, dx, 0Dh + + + push dx + mov dx, balance + add dx, ax + add dx, ax + mov balance, dx + pop dx + + + cmp balance, 0 + jl balance_negative + ;balance_positive: + dec bx + + push dx + mov dx, balance + sub dx, bx + sub dx, bx + mov balance, dx + pop dx + + + balance_negative: + inc ax + + cmp ax, bx + jg end_drawing + jmp zeichne_kreis_schleife + + + end_drawing: + ; pause the screen for dos compatibility: + + pop ax + pop bx + pop cx + pop dx + +endm + +cross_draw MACRO x, y, d + push ax + push bx + push cx + push dx + + + ;Erste FOR Schleife nach rechts unten + mov cx, x + mov dx, y + + xor cx,cx ; cx-register is the counter, setn to 0 + loop1: + push cx + mov cx, cx + mov dx, y + add dx, cx + add cx, x + draw_pixel cx, dx, 0Fh + pop cx + inc cx + cmp cx, d ; cx vergleich mit der Größe (Dimension d) + jle loop1 ; Loop while less or equal + + + xor cx,cx ; cx-register is the counter, setn to 0 + loop2: + push cx + mov cx, cx + mov dx, y + sub dx, cx + add cx, x + add dx, d + draw_pixel cx, dx, 0Dh + pop cx + inc cx + cmp cx, d ; cx vergleich mit der Größe (Dimension d) + jle loop2 ; Loop while less or equal + + pop ax + pop bx + pop cx + pop dx + + ENDM + +rectangle_draw MACRO x, y, d + ;Verwaltet move und call + mov cx, x + mov dx, y + mov x_wert, cx + mov y_wert, dx + mov d_wert, d + call line_print_down + call line_print_straight + ENDM + +line_print_down PROC + xor cx,cx ; cx-register is the counter, setn to 0 + loop3: + push cx + mov cx, cx + mov dx, y_wert + add cx, x_wert + draw_pixel cx, dx, 0Fh + add dx, d_wert + draw_pixel cx, dx, 0Fh + pop cx + inc cx + cmp cx, d_wert ; cx vergleich mit der Größe (Dimension d) + jle loop3 ; Loop while less or equal + ret +line_print_down endp + +line_print_straight PROC + xor dx,dx ; cx-register is the counter, setn to 0 + loop4: + push dx + mov dx, dx + mov cx, x_wert + add dx, y_wert + draw_pixel cx, dx, 0Fh + add cx, d_wert + draw_pixel cx, dx, 0Fh + pop dx + inc dx + cmp dx, d_wert ; cx vergleich mit der Größe (Dimension d) + jle loop4 ; Loop while less or equal + ret +line_print_straight endp + +paint_menu PROC FAR + + push ax + push bx + push dx + ;set pointer + mov ah,2 + mov dh,5 + mov dl,14 + mov bh,0 + int 10h + mov dx,offset title_str + mov ah,9 + int 21h + ;set pointer + mov ah,2 + mov dh,6 + mov dl,10 + mov bh,0 + int 10h + mov dx,offset signature_str + mov ah,9 + int 21h + + + ;set pointer + mov ah,2 + mov dh,7 + mov dl,11 + mov bh,0 + int 10h + + cmp cx, 2 + mov dx,offset cross_win + je zeige_gewinner + + + cmp cx, 3 + mov dx,offset circle_win + je zeige_gewinner + + + cmp cx, 4 + jne kein_gewinner + + mov ah,2 + mov dh,7 + mov dl,13 + mov bh,0 + int 10h + mov dx,offset draw + je zeige_gewinner + + zeige_gewinner: + mov ah,9 + int 21h + + kein_gewinner: + + + ;set pointer + mov ah,2 + mov dh,9 + mov dl,10 + mov bh,0 + int 10h + mov dx,offset choose1_str + mov ah,9 + int 21h + + ;set pointer + mov ah,2 + mov dh,10 + mov dl,10 + mov bh,0 + int 10h + mov dx,offset choose2_str + mov ah,9 + int 21h + + pop ax + pop bx + pop dx + + ret +paint_menu endp + +prepare_display PROC + ;Videomode clear alles entfernen + ;Spielfeld initialisieren + rectangle_draw 80, 10, 405 ; Kreuz MACRO + rectangle_draw 84, 14, 130 ; 1 + rectangle_draw 218, 14, 130 ; 2 + rectangle_draw 352, 14, 130 ; 3 + + rectangle_draw 84, 148, 130 ; 4 + rectangle_draw 218, 148, 130 ; 5 + rectangle_draw 352, 148, 130 ; 6 + + rectangle_draw 84, 282, 130 ; 7 + rectangle_draw 218, 282, 130 ; 8 + rectangle_draw 352, 282, 130 ; 9 + ret +prepare_display ENDP + +;---------------------------------------- DRAW ENDE --------------------------------------- \ No newline at end of file diff --git a/game.asm b/game.asm new file mode 100644 index 0000000..7153124 --- /dev/null +++ b/game.asm @@ -0,0 +1,401 @@ +;**************************************** +; Elmar Kresse 19-INB-1 (2021) +; game.asm +; file contains source code for the +; basic functions of the game logic +; +; - conkf MACRO x_kord, y_kord +; - conkf_prog PROC FAR +; - game_logic PROC +; - check_won_value MACRO a, b, c +; - check_draw PROC +; - reset_player_data PROC +; - check_won PROC +; - reset_timerisr PROC +; - start_game PROC +;**************************************** + +conkf MACRO x_kord, y_kord ;MACRO for coordinates to field + push dx + push cx + + + mov cx, x_kord + mov dx, y_kord + mov field, 0h + call conkf_prog + + pop cx + pop dx + ENDM + +conkf_prog PROC FAR ;PROC for coordinates to field +push ax +push bx +push cx +push dx + ;horizontal x + cmp cx, 482 + jg out_of_range + cmp cx, 352 + mov x_wert, 357 + jnl x_3 + cmp cx, 218 + mov x_wert, 223 + jnl x_2 + cmp cx, 84 + mov x_wert, 89 + jnl x_1 + jmp out_of_range + + x_3: + mov ax, 2 + jmp y_kordi + x_2: + mov ax, 1 + jmp y_kordi + x_1: + mov ax, 0 + +y_kordi: + ;vertical y + cmp dx, 412 + jg out_of_range + cmp dx, 282 + mov y_wert, 287 + jnl y_3 + cmp dx, 148 + mov y_wert, 153 + jnl y_2 + cmp dx, 14 + mov y_wert, 19 + jnl y_1 + jmp out_of_range + + y_3: + mov bx, 6 + jmp calc_field + y_2: + mov bx, 3 + jmp calc_field + y_1: + mov bx, 0 + + calc_field: + add ax, bx ;(A = ax) * (B = bx) = (goal = AX) + mov field, ax + jmp kord_final + + out_of_range: + mov field, 10 ;not in range(0-8) 10 is set for later checks + + kord_final: + + + pop dx + pop cx + pop bx + pop ax + + + + + ret +conkf_prog ENDP + +game_logic PROC + +conkf x_wert, y_wert + +;check valid field 0-8 +cmp field, 0ah +je end_game_logic + +;Feld frei + push ax + push si + mov si, field + shl si, 1 + cmp player1[si], 1 + pop si + pop ax + je end_game_logic + + push ax + push si + mov si, field + shl si, 1 + cmp player2[si], 1 + pop si + pop ax + je end_game_logic + + + +mov ax, spieler +cmp ax, 0 +je kreuz_spieler +jne kreis_spieler + +kreuz_spieler: + cross_draw x_wert, y_wert, 120 + + ;Sound + PUSH AX BX CX DX + MOV BX, OFFSET placeSound + MOV CX, OFFSET placeSoundLength + CALL playbeep + POP DX CX BX AX + + mov spieler, 1 ;Spieler umschalten + + ;Geklicketes Feld belegen + push ax + push si + mov si, field + shl si, 1 + mov player1[si], 1 + pop si + pop ax + + push cx + mov cx, offset player1 + call check_won + pop cx + cmp ax, 1 + jne end_game_logic + mov cx, 2 + call reset_timerisr + call start_programm + +kreis_spieler: + ;Kreis zeichnen + circle_draw x_wert, y_wert, 60 + + + ;Sound + PUSH AX BX CX DX + MOV BX, OFFSET placeSound + MOV CX, OFFSET placeSoundLength + CALL playbeep + POP DX CX BX AX + + mov spieler, 0 ;Spieler umschalten + + ;Geklicketes Feld belegen + push ax + push si + mov si, field + shl si, 1 + mov player2[si], 1 + pop si + pop ax + + push cx + mov cx, offset player2 + call check_won + pop cx + + cmp ax, 1 + jne end_game_logic + mov cx, 3 + call reset_timerisr + call start_programm + +end_game_logic: + +call check_draw +ret +game_logic ENDP + +check_won_value MACRO a, b, c +push ax +push bx + mov bx, 0 + mov ax, a; + shl ax, 1 + add bp, ax + add bx, ds:[bp] + mov bp, cx + mov ax, b; + shl ax, 1 + add bp, ax + add bx, ds:[bp] + mov bp, cx + mov ax, c; + shl ax, 1 + add bp, ax + add bx, ds:[bp] + mov bp, cx + cmp bx, 3 +pop bx +pop ax + je got_winner +ENDM + +check_draw PROC + + ;Überprüfen ob Unentschieden + push bx ;Counter ob alle Felder belegt sind + push ax ;Zahlvariable für Shiften + push cx ;Pointer für das Array des jeweiligen Spielers + push dx ;Zählvariable für Schleifen + push bp + mov bx, 0 + mov ax, 0 + mov dx, 0 + + mov cx, offset player1 ;pointer for player1 + + player1_field_loop: + mov ax, dx + shl ax, 1 + mov bp, cx + add bp, ax + add bx, ds:[bp] + inc dx + cmp dx, 9 + jne player1_field_loop + + mov cx, offset player2 ;pointer for player2 + mov dx, 0 + player2_field_loop: + mov ax, dx + shl ax, 1 + mov bp, cx + add bp, ax + add bx, ds:[bp] + inc dx + cmp dx, 9 + jne player2_field_loop + + + cmp bx, 9 + + pop bp + pop dx + pop cx + pop ax + pop bx + + je draw_end + jmp check_draw_end + +draw_end: + mov cx, 4 ; 4 = Unentschieden + call reset_timerisr + call start_programm + +check_draw_end: +ret +check_draw ENDP + +reset_player_data PROC + + push ax + push si + push bx ;Schleifen Zähler + mov bx, 0 + +resetloop_player1: + mov si, bx + shl si, 1 + mov player1[si], 0 + inc bx + cmp bx, 9 + jne resetloop_player1 + + mov bx, 0 +resetloop_player2: + mov si, bx + shl si, 1 + mov player2[si], 0 + inc bx + cmp bx, 9 + jne resetloop_player2 + + pop bx + pop si + pop ax +ret +reset_player_data ENDP + +check_won PROC +push bp +mov bp, cx + +;vertical + check_won_value 0, 3, 6 + check_won_value 1, 4, 7 + check_won_value 2, 5, 8 + +;horizontal + + check_won_value 2, 1, 0 + check_won_value 3, 4, 5 + check_won_value 6, 7, 8 + +;cross + check_won_value 0, 4, 8 + check_won_value 6, 4, 2 + jmp ende_check_won + +got_winner: +pop bp + +;play gameover sound +MOV BX, OFFSET gameOverSound +MOV CX, gameOverSoundLength +CALL playbeep + +mov ax, 1 ;var for save winner exists +ret + +ende_check_won: +pop bp +mov ax, 0 +ret +check_won ENDP + +reset_timerisr PROC + PUSH CX + PUSH DS + PUSH AX + PUSH DX + MOV AX, 251Ch ; Restore old user timer interrupt vector + MOV DX, [CS:old_timer] + MOV CX, [CS:old_segment] + MOV DS, CX + INT 21h + POP DX + POP AX + POP DS + POP CX + ret +reset_timerisr ENDP + +start_game PROC + CLI + + mov ax,00h + int 33h + or ax,ax + + call reset_player_data ;Spieler Array reset + mov ax,12h ;Videomodus + int 10h + + call prepare_display + + mov ax, 1 ;Curser an + int 33h + + ;Ersten Spieler setzten + + call prepare_mouse + mov spieler, 0 + call prepare_display + + STI + ret +start_game endp \ No newline at end of file diff --git a/logic.asm b/logic.asm new file mode 100644 index 0000000..8a6b2bf --- /dev/null +++ b/logic.asm @@ -0,0 +1,140 @@ +;**************************************** +; Elmar Kresse 19-INB-1 (2021) +; logic.asm +; file contains source code for the +; logic (mouse, timer, programm) +; +; - handle_timer_tick PROC +; - prepare_timer_tick PROC +; - prepare_mouse PROC +; - mouseProc PROC FAR +; - start_programm PROC +;**************************************** + +handle_timer_tick PROC + ; Call the original interrupt vector + ; by pushing flags register and doing a FAR CALL to old vector + PUSHF + CALL DWORD ptr [CS:old_timer] + INC WORD ptr [CS:timer_ticks] ; Increase timer counter + + IRET +handle_timer_tick ENDP + +prepare_timer_tick PROC + + PUSH DS + MOV AX, 351Ch ; Get user timer interrupt vector + INT 21h + ; ES:BX now has the address of the interrupt vector + MOV [CS:old_timer], BX + MOV AX, ES + MOV [CS:old_segment], AX + + MOV AX, 251ch ; Set user timer interrupt vector + PUSH CS + POP DS + MOV DX, OFFSET handle_timer_tick + INT 21h + + POP ds + + ret +prepare_timer_tick ENDP + +prepare_mouse PROC + + mov dx, OFFSET mouseProc ;jump to click_event_handler + mov ax, 0Ch + mov cx, 00000010b ; on left click + push cs ;es:dx + pop es + int 33h + + mov ax, 1 ;show cursor + int 33h + + ret +prepare_mouse ENDP + +mouseProc PROC FAR ;other doc segment therefore FAR + enter 0, 0 + push ax + push bx + push cx + push dx + push es + push ds + + mov ax, 2 ;hide cursor + int 33h + + ;dx contains coordinates of "rows" + mov y_wert, dx ;save y coordinate in variable + ;cx contains coordinates of "columns" + mov x_wert, cx ;save y coordinate in variable + + call game_logic + + xor bx, bx ;button pressed clear result + + mov ax, 1 ;show cursor + int 33h + + pop ds + pop es + pop dx + pop cx + pop bx + pop ax + leave + ret +mouseProc ENDP + +start_programm PROC + + mov AX, video_seg + mov es, AX + + + mov ax, @DATA + mov ds, ax + + + mov ax, 12h + int 10h + + mov dx, 03C4h ;address of the index port (share) + mov al, 2 ;index of control register + out dx, al ;Set index: output out :: dx to port al(2) + inc dx ;index of dataport + mov al, 0Fh ;value (1 = bit plane enabled, here all) fi + out dx, al + + + call prepare_timer_tick + + mov ax,0Dh + int 10h + + call paint_menu + + check_mode_loop: + xor ax, ax ;delete ah + int 16h ;keyboard query + + cmp al,'1' + je short playgame + + cmp al,'2' + je ende + + jmp check_mode_loop + + playgame: + call start_game + + +ret +start_programm ENDP + diff --git a/main.asm b/main.asm new file mode 100644 index 0000000..3d0300c --- /dev/null +++ b/main.asm @@ -0,0 +1,133 @@ +;**************************************** +; Elmar Kresse 19-INB-1 (2021) +; main.asm +; file contains source code for the +; main operations +; +;**************************************** + +.model small +.STACK 100h +.486 +esc_code = 1Bh ;esc key +video_seg = 0B800h + +.data +INCLUDE data.asm + + +.code +INCLUDE draw.asm +INCLUDE game.asmn +INCLUDE sound.asm +INCLUDE logic.asm + +ISR1Ch: push ds + push ax + mov ax, @DATA + mov ds, ax + pop ax + pop ds + iret + +;start of the main program +;---------------------------------------------------------------------------------------------------------- +Beginn: + + mov ax, @DATA + mov ds, ax + mov ax, 3 + int 10h + ;read old VT entry + mov al, 1Ch + mov ah, 35h + int 21h + ;es:bx address old ISR + ;save + mov oldIOFF, bx + mov oldISeg, es + ;store new ISR + push ds ; store ds + push cs + pop ds ; ds <- cs + mov dx, OFFSET ISR1Ch ; address in ds:dx + mov al, 1Ch ; + mov ah, 25h + int 21h + pop ds ; restore ds + + + call start_programm + +;-------------------------------------------------------------------------------------------------------- + +;Endlosschleife +;-------------------------------------------------------------------------------------------------------- +endlloop: + xor ax, ax ;delete ah + int 16h ;Keyboard query, waits for any key is pressed + cmp al, esc_code ;If esc is not pressed, the program remains in the endless loop + jne short endlloop ;jmp not equal + call reset_timerisr + mov cx, 0 + call start_programm + +;------------------------------------------------------------------------------------------------------- + +;End of the program and return to DOS +;----------------------------------------------------------------------------------------------------- +ende: + + PUSH DS + MOV AX, 251Ch ;Restore old user timer interrupt vector + MOV DX, [CS:old_timer] + MOV CX, [CS:old_segment] + MOV DS, CX + INT 21h + POP DS + + mov ax, 0 ;reset mouse programm + int 33h ;mouse interrupt + + MOV ax, 3 ;Reset screen mode + INT 10h + + mov dx, oldIOFF ;order is important + mov ax, oldISeg + mov ds, ax + mov al, 1Ch + mov ah, 25h + int 21h + + mov ah, 4Ch ;Backflow to DOS + int 21h + .stack 100h + end Beginn +;--------------------------------------------------------------------------------------------------- + + + + + + +; Convert X: Y coordinates to fields +; Check whether the field is free +; if free field +; test whether the player has won + +; 1 2 3 0 1 2 +; 1|2 3 4 0|0 1 2 +; 4|5 6 7 3|3 4 5 +; 7|8 9 10 6|6 7 8 + +;8 test cases +; 0,3,6 +; 1,4,7 +; 2,5,8 + +; 0,1,2 +; 3,4,5 +; 6,7,8 + +; 0,4,8 +; 6,4,2 \ No newline at end of file diff --git a/sound.asm b/sound.asm new file mode 100644 index 0000000..f17789e --- /dev/null +++ b/sound.asm @@ -0,0 +1,45 @@ +;**************************************** +; Elmar Kresse 19-INB-1 (2021) +; sound.asm +; file contains source code for +; only playing sounds +; +; - playbeep PROC +;source: http://muruganad.com/8086/8086-assembly-language-program-to-play-sound-using-pc-speaker.html +;**************************************** + + +playbeep PROC + MOV AL, 182 + OUT 43h, AL + beep: + + ;set frequency + MOV AX, [BX] + OUT 42h, AL + MOV AL, AH + OUT 42h, AL + ;sound on + IN AL, 61h + OR AL, 00000011b + OUT 61h, AL + + ADD BX, 4 + MOV WORD ptr [CS:timer_ticks], 0 + + ;break + timer_loop: + CMP WORD ptr [CS:timer_ticks], 3 + jne timer_loop + MOV WORD ptr [CS:timer_ticks], 0 + + ;sound off + IN AL, 61h + AND AL, 11111100b + OUT 61h, AL + + ADD BX, 4 + SUB CX, 8 + JNE beep + ret +playbeep ENDP