Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ; -----------------------------------------------------
- ; MS-DOS Tron
- ;
- ; A WIP recreation of the game Tron for MS-DOS.
- ;
- ; Runs in CGA screen mode 4 using only 808x compatible
- ; instructions.
- ;
- ; Written by Graham Downey (2024)
- CPU 8086
- ORG 100h
- %define _UP 0
- %define _DOWN 1
- %define _LEFT 2
- %define _RIGHT 3
- %define _P1CLR 1
- %define _P2CLR 2
- %define _P1MASK 55h
- %define _P2MASK 0AAh
- section .text
- ; Startup code
- start:
- MOV AX, 0F00h ; Get and save current video mode
- INT 10h
- MOV [CS:oldVideoState], AL
- MOV AX, 0004h ; Set video mode to CGA 320x200x4
- INT 10h
- MOV AX, 0B800h ; CGA graphics seg
- MOV ES, AX
- MOV DX, CS ; Set data segment to code segment
- MOV DS, DX
- ; Test drawing 8 pixels top left corner
- MOV [ES:0000h], BYTE 0E4h
- MOV [ES:0001h], BYTE 1Bh
- drawBorder:
- ; Draw top and bottom borders
- horizontalBorder:
- MOV CX, 80 ; 80*4 pixels across
- MOV AL, 0E7h ; Cool border pattern
- XOR DI, DI ; Top border starts at 0000h
- MOV SI, 3EF0h-80 ; Bottom border starts here
- ; Render a cool arrow pattern
- .nextColumn:
- MOV [ES:DI], AL ; First row
- MOV [ES:SI], AL
- ROL AL, 1
- ROL AL, 1
- ADD DI, 80
- ADD SI, 80
- MOV [ES:DI], AL ; Third row
- MOV [ES:SI], AL
- XOR DI, 2000h
- XOR SI, 2000h
- ROL AL, 1
- ROL AL, 1
- MOV [ES:DI], AL ; 4th row
- MOV [ES:SI], AL
- SUB DI, 80
- SUB SI, 80
- ROL AL, 1
- ROL AL, 1
- MOV [ES:DI], AL ; Second row
- MOV [ES:SI], AL
- XOR DI, 2000h
- XOR SI, 2000h
- ROL AL, 1 ; Rotate 4px pattern
- ROL AL, 1
- INC DI
- INC SI
- LOOP .nextColumn
- ; Draw left and right borders
- verticalBorder:
- MOV CX, 99 ; 100*2 scanlines
- MOV AL, 0DBh
- MOV DI, 2000h
- .nextRow:
- MOV [ES:DI], AL ; left odd scanline
- ADD DI, 79
- MOV [ES:DI], AL ; right odd scanline
- ADD DI, 80
- XOR DI, 2000h
- MOV [ES:DI], AL ; right even scanline
- SUB DI, 79
- MOV [ES:DI], AL ; left even scanline
- OR DI, 2000h
- ; Creates a cool barber shop pole pattern
- ROL AL, 1
- ROL AL, 1
- LOOP .nextRow
- ; -------------------------------
- ; Main program loop start
- ;
- mainLoop:
- CALL movePlayers ; Do player movement logic
- CALL drawPlayers ; Render new player locations
- CALL checkKeyPress ; Get user input
- MOV CX, 2 ; Wait 2 VBlanks between frames (~15 fps)
- CALL waitVBlank ; So we don't get runaway speed on fast CPUs
- JMP mainLoop
- ; End Main Loop
- ; CGA Mode 4 reference
- ; https://www.chibialiens.com/8086/platform.php#LessonP1
- ; -----------------------------------
- ; Draw players on the screen
- ; Also checks for player collisions
- drawPlayers:
- ; Draw Player 1
- MOV DI, [p1Location]
- MOV CL, [p1Offset]
- SHL CL, 1 ; CL *= 2 because 2 bits per pixel
- MOV DL, 03h ; 1 pixel CGA bitmask (0000 0011b)
- SHL DL, CL ; Shift to correct pixel offset
- TEST [ES:DI], DL ; Check if something already passed here
- JNZ .P1Collision
- AND DL, _P1MASK ; Use mask to set DL to P1 color
- OR [ES:DI], DL ; Draw to screen
- ; Draw Player 2
- MOV DI, [p2Location]
- MOV CL, [p2Offset]
- SHL CL, 1 ; CL *= 2 because 2 bits per pixel
- MOV DL, 03h ; 1 pixel bitmask
- SHL DL, CL
- TEST [ES:DI], DL ; Check if something already passed here
- JNZ .P2Collision
- AND DL, _P2MASK ; Use mask to set DL to P1 color
- OR [ES:DI], DL ; Draw to screen
- RET
- .P1Collision:
- JMP end
- .P2Collision:
- JMP end
- ; -------------------
- ; Move the players
- movePlayers:
- ; Move player 1
- moveP1:
- MOV BL, [p1Direction]
- .left:
- CMP BL, _LEFT
- JNE .right
- INC BYTE [p1Offset]
- AND BYTE [p1Offset], 03h ; Only using 2 bits for offset (4px per byte)
- JNZ .done
- DEC WORD [p1Location]
- JMP .done
- .right:
- CMP BL, _RIGHT
- JNE .down
- DEC BYTE [p1Offset]
- TEST BYTE [p1Offset], 0FCh
- JZ .done
- MOV [p1Offset], BYTE 03h ; 4 pixels per byte (offset 0-3)
- INC WORD [p1Location]
- JMP .done
- .down:
- CMP BL, _DOWN
- JNE .up
- MOV BX, [p1Location]
- TEST BX, 2000h ; Check if we're odd or even scanline
- JZ .skipAdd
- ADD BX, 80
- .skipAdd:
- XOR BX, 2000h ; Toggle odd/even scanline bit
- MOV [p1Location], BX
- JMP .done
- .up:
- ;CMP BL, _UP ; Up direction assumed as last-case
- ;JNE .done
- MOV BX, [p1Location]
- TEST BX, 2000h
- JNZ .skipSub
- SUB BX, 80
- .skipSub:
- XOR BX, 2000h
- MOV [p1Location], BX
- .done:
- ; Move player 2
- moveP2:
- MOV BL, [p2Direction]
- .left:
- CMP BL, _LEFT
- JNE .right
- INC BYTE [p2Offset]
- AND BYTE [p2Offset], 03h ; Only using 2 bits for offset (4px per byte)
- JNZ .done
- DEC WORD [p2Location]
- JMP .done
- .right:
- CMP BL, _RIGHT
- JNE .down
- DEC BYTE [p2Offset]
- TEST BYTE [p2Offset], 0FCh
- JZ .done
- MOV [p2Offset], BYTE 03h ; 4 pixels per byte (offset 0-3)
- INC WORD [p2Location]
- JMP .done
- .down:
- CMP BL, _DOWN
- JNE .up
- MOV BX, [p2Location]
- TEST BX, 2000h ; Check if we're odd or even scanline
- JZ .skipAdd
- ADD BX, 80
- .skipAdd:
- XOR BX, 2000h ; Toggle odd/even scanline bit
- MOV [p2Location], BX
- JMP .done
- .up:
- ;CMP BL, _UP ; Up direction assumed as last-case
- ;JNE .done
- MOV BX, [p2Location]
- TEST BX, 2000h
- JNZ .skipSub
- SUB BX, 80
- .skipSub:
- XOR BX, 2000h
- MOV [p2Location], BX
- .done:
- RET
- ; ---------------------------------------
- ; Read and parse keyboard input
- checkKeyPress:
- MOV AX, 0100h ; Keyboard check for keystroke
- INT 16h
- JNZ checkKeyCode ; ZF not set if key is available
- RET
- checkKeyCode:
- XOR AX, AX ; Get keystroke and remove from buffer
- INT 16h ; AH = Scancode, AL = ASCII char
- ; --------------------------------------
- ; Global Keys
- .checkEsc:
- CMP AH, 1 ; Esc key
- JE end
- .checkPause:
- CMP AH, 25 ; P key
- JNE .isPaused
- NOT BYTE [paused] ; flip pause flag
- .isPaused:
- TEST [paused], BYTE 1
- JNE checkKeyCode ; Keep looping until pause key hit again
- ; --------------------------------------
- ; P1 controls
- .checkUp:
- CMP AH, 72 ; up arrow
- JNE .checkDown
- CMP [p1Direction], BYTE _DOWN ; Do not allow to backtrack
- JE .doneKeys
- MOV [p1Direction], BYTE _UP
- .checkDown:
- CMP AH, 80 ; down arrow
- JNE .checkLeft
- CMP [p1Direction], BYTE _UP ; Do not allow to backtrack
- JE .doneKeys
- MOV [p1Direction], BYTE _DOWN
- .checkLeft:
- CMP AH, 75 ; left arrow
- JNE .checkRight
- CMP [p1Direction], BYTE _RIGHT ; Do not allow to backtrack
- JE .doneKeys
- MOV [p1Direction], BYTE _LEFT
- .checkRight:
- CMP AH, 77 ; right arrow
- JNE .checkW
- CMP [p1Direction], BYTE _LEFT ; Do not allow to backtrack
- JE .doneKeys
- MOV [p1Direction], BYTE _RIGHT
- ; --------------------------------------
- ; P2 controls
- .checkW:
- CMP AH, 17 ; W key
- JNE .checkA
- CMP [p2Direction], BYTE _DOWN ; Do not allow to backtrack
- JE .doneKeys
- MOV [p2Direction], BYTE _UP
- .checkA:
- CMP AH, 30 ; A key
- JNE .checkS
- CMP [p2Direction], BYTE _RIGHT ; Do not allow to backtrack
- JE .doneKeys
- MOV [p2Direction], BYTE _LEFT
- .checkS:
- CMP AH, 31 ; S key
- JNE .checkD
- CMP [p2Direction], BYTE _UP ; Do not allow to backtrack
- JE .doneKeys
- MOV [p2Direction], BYTE _DOWN
- .checkD:
- CMP AH, 32 ; D key
- JNE .doneKeys
- CMP [p2Direction], BYTE _LEFT ; Do not allow to backtrack
- JE .doneKeys
- MOV [p2Direction], BYTE _RIGHT
- .doneKeys:
- JMP checkKeyPress ; Keep looping until kb buffer clear
- ; -----------------------------------------------
- ; Clears the keyboard buffer so we don't dump
- ; random key presses into the console when we
- ; quit the program
- clearKBBuff:
- MOV AX, 0040h ; Buffer is at seg 0040h
- MOV DS, AX
- ; To clear the buffer, set
- ; [0040:001A] = [0040:001C]
- MOV AL, BYTE [001Ch]
- MOV [001Ah], AL
- MOV AX, CS ; Restore DS to CS
- MOV DS, AX
- RET
- ; ----------------------------------------------
- ; Pauses the program while it waits for 'n' vBlanks
- ; to pass.
- ;
- ; CX = 'n' vsyncs to wait for
- ; trashes: DX, AX, CX
- waitVBlank:
- MOV DX, 03DAh ; port vsync pulse is sent to
- .waitVSync:
- IN AL, DX ; read vsync status
- TEST AL, 8 ; when we read !8 from the port
- JNE .waitVSync ; vsync in progress
- .waitVBlank:
- IN AL, DX
- TEST AL, 8 ; when we read 8 from the port
- JE .waitVBlank ; vsync is done
- DEC CX ; next vsync?
- JNZ waitVBlank
- RET
- ; -----------------------------------------------
- ; Restore video state and terminate program
- end:
- CALL clearKBBuff
- XOR AH, AH ; Restore to initial video mode
- MOV AL, [CS:oldVideoState]
- INT 10h
- XOR AX, AX ; Quit to DOS
- INT 21h
- section .data
- ; Store DOS' video state at program's start
- oldVideoState: db 03h
- paused: DB 00h ; Variable to hold paused/unpaused state
- ; Player 1 direction & location
- p1Direction: db _LEFT
- p1Location: dw 0FA0h-1 ; ptr to screen mem location
- p1Offset: db 03h ; offset of which pixel in byte
- ; Player 2 direction & location
- p2Direction: db _RIGHT
- p2Location: dw 0F50h+1 ; ptr to screen mem location
- p2Offset: db 04h ; offset of which pixel in byte
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement