;========================================================================== ; TETRIS 85 ;-------------------------------------------------------------------------- ; TETRIS 85 v1.0 by David Boozer, 1/3/95 ;========================================================================== LCD_ADDR .EQU $FC00 ; Address of LCD display GRAPH_ADDR .EQU $8641 ; Address of graphics screen QUIT_ADDR .EQU $0462 ; Jump here to exit program TEXT_CURSOR .EQU $800C ; column|row of text cursor MENU_CURSOR .EQU $8333 ; row|column of menu cursor PRINT_TXT .EQU $3E68 ; Print a ASCIIZ text string (normal font) PRINT_MENU_TXT .EQU $3BE0 ; Print a ASCIIZ text string (menu font) UNPACK_HL .EQU $009A ; Strip of a decimal digit of HL to A KEY_ADDR .EQU $01BE ; Input a keystroke to A CLEAR_LCD .EQU $3DFC ; Clear the LCD display BUSY_SIGNAL_OFF .EQU $3EA4 ; Turn of the busy indicator DOWN_KEY .EQU $01 LEFT_KEY .EQU $02 RIGHT_KEY .EQU $03 UP_KEY .EQU $04 SECOND_KEY .EQU $36 EXIT_KEY .EQU $37 .ORG GRAPH_ADDR ; ; Set up the CUSTOM menu data structure ; .BYTE $41, $00, $07, $C3 .WORD Start .TEXT "Tetris" .BYTE $00 ; ; Shape bitmaps are stored in a 5 x 7 word table ; Shape_table Shape_A .WORD $6600, $FFFE Max_delay_time .WORD $0000 Delay_time .WORD $0000 Score .WORD $0000 Shape_B .WORD $F000, $4444, $FFFC Current_bitmap .WORD $E004 Current_ptr .WORD Shape_E Shape_C .WORD $C600, $C804, $FFFC Next_ptr .WORD Shape_F Line_count .WORD $0000 Shape_D .WORD $6C00, $6204, $FFFC Random1 .WORD $E642 Random2 .WORD $2CF9 Shape_E .WORD $E004, $6404, $E400, $C404, $FFF8 Shape_F .WORD $E800, $440C, $E002, $4604, $FFF8 Shape_G .WORD $E200, $4C04, $E008, $4406, $FFF8 Tetris_count .WORD $0000 Level .BYTE $00 Number_location .WORD $0000 Number_buffer .BYTE $00, $00, $00, $00, $00, $00 Lines_txt .WORD $0D64 .TEXT "Lines" .BYTE $00 Level_txt .WORD $250B .TEXT "Level" .BYTE $00 Score_txt .WORD $0D0B .TEXT "Score" .BYTE $00 Game_txt .WORD $0802 .TEXT "GAME" .BYTE $00 Over_txt .WORD $0803 .TEXT "OVER" .BYTE $00 Title .WORD $0602 .TEXT "TETRIS 85" .BYTE $00 Name .WORD $0304 .TEXT "By David Boozer" .BYTE $00 Start: CALL BUSY_SIGNAL_OFF ; Turn off the busy indicator CALL CLEAR_LCD ; Clear the LCD display LD HL, Title Wait_screen: CALL Print_string CALL Print_string Wait_loop: CALL KEY_ADDR ; Wait for a keystroke CP $05 ; Is it an arrow key? JR C, Wait_loop ; If so, loop back CP EXIT_KEY ; Is it the EXIT key? JR Z, Exit_code ; If so, exit the program ; ; Draw a background pattern ; LD HL, LCD_ADDR LD A, %10101010 LD B, $40 ; There are $40 rows Draw_loop1: PUSH BC LD B, $10 ; There are $10 bytes per row Draw_loop2: LD (HL), A INC HL DJNZ Draw_loop2 XOR $FF ; %10101010 <--> %01010101 POP BC DJNZ Draw_loop1 CALL Draw_middle ; Clear a stripe down the middle LD HL, $FCB1 ; Draw the score window CALL Draw_window LD HL, $FCBC ; Draw the lines window CALL Draw_window LD HL, $FE31 ; Draw the level window CALL Draw_window ; LD HL, $FE3C ; Draw the next shape window ; CALL Draw_window LD HL, Lines_txt ; Draw "Lines", "Level", "Score" LD B, $03 Setup_displays: CALL Print_menu_string DJNZ Setup_displays LD HL, $0000 LD (Line_count), HL LD (Score), HL LD A, $FF LD (Level), A LD HL, $2000 LD (Max_delay_time), HL JP Begin Reset_delay: LD HL, (Max_delay_time) LD (Delay_time), HL Redraw: CALL Draw_word_bitmap Key_loop: LD HL, (Delay_time) DEC HL LD (Delay_time), HL LD A, H OR L JR Z, Animate_shape CALL KEY_ADDR CP DOWN_KEY JR Z, Animate_shape CP LEFT_KEY JR Z, Left_code CP RIGHT_KEY JR Z, Right_code CP SECOND_KEY JR Z, Second_code CP EXIT_KEY JR Z, Exit_code JR Key_loop Exit_code: CALL CLEAR_LCD LD HL, $0000 LD (TEXT_CURSOR), HL JP QUIT_ADDR Left_code: LD E, C DEC E JR Move_shape Right_code: LD E, C INC E JR Move_shape Second_code: CALL Draw_word_bitmap CALL Rotate_bitmap EX DE, HL PUSH DE CALL Test_collision POP DE OR A JR NZ, Redraw LD (Current_bitmap), HL LD (Current_ptr), DE JR Redraw ;========================================================================== ; Move a shape left or right ;========================================================================== Move_shape: PUSH DE CALL Draw_word_bitmap ; Erase the old shape POP DE PUSH BC LD C, E LD HL, (Current_bitmap) CALL Test_collision OR A JR NZ, Collision_found POP DE JR Redraw Collision_found: POP BC JR Redraw ;========================================================================== ; Animate a falling shape ;========================================================================== Animate_shape: CALL Draw_word_bitmap ; Remove the old shape INC B ; Increment the row LD HL, (Current_bitmap) CALL Test_collision ; Is there a collision? OR A JR NZ, New_shape ; If so, get a new shape LD D, $0D LD A, H AND $0F JR Z, Keep_D DEC D Keep_D: LD A, D CP B JR C, New_shape JP Reset_delay New_shape: DEC B CALL Draw_word_bitmap ; Replace the old shape LD HL, $0000 LD (Tetris_count), HL ; ; Remove up to four completed rows ; LD C, B LD B, $04 Check_row_loop: PUSH BC LD B, C CALL Check_row POP BC INC C DJNZ Check_row_loop LD HL, (Score) INC HL LD DE, (Tetris_count) ADD HL, DE LD (Score), HL Begin: LD DE, $1464 ; Update the lines display LD HL, (Line_count) CALL Print_HL LD DE, $140B ; Update the score display LD HL, (Score) CALL Print_HL LD HL, (Line_count) ; Update the level display CALL UNPACK_HL LD A, (Level) CP L JR Z, Same_level LD A, L LD (Level), A LD DE, $2C0B CALL Print_HL LD HL, (Max_delay_time) LD DE, $FE00 ADD HL, DE LD (Max_delay_time), HL Same_level: LD HL, (Next_ptr) LD (Current_ptr), HL ; ; Randomly generate a number from 0 to 6 (Number in stored in A) ; Random: LD HL, (Random1) LD DE, (Random2) LD (Random2), HL XOR A RR A RR H RR L RR D RR E LD HL, (Random1) LD A, D XOR H LD H, A LD A, E XOR L LD L, A LD (Random1), HL LD A, L AND $07 JR Z, Random DEC A LD HL, Shape_table LD E, A SLA A SLA A ADD A, E SLA A LD E, A LD D, $00 ADD HL, DE LD (Next_ptr), HL ; ; Update the next shape display ; LD HL, $FE3C CALL Draw_window LD HL, (Next_ptr) LD E, (HL) INC HL LD D, (HL) LD (Current_bitmap), DE LD BC, $0819 LD A, $E0 CP D JR NZ, Not_E INC B Not_E: CALL Draw_word_bitmap LD HL, (Current_ptr) LD E, (HL) INC HL LD D, (HL) LD (Current_bitmap), DE LD BC, $000F CALL Test_collision OR A JR NZ, Game_over JP Reset_delay Game_over: CALL Draw_middle LD HL, Game_txt JP Wait_screen ;========================================================================== ; Clear a strip down the middle of the screen ;========================================================================== Draw_middle: LD HL, LCD_ADDR+$0005 LD DE, $000A LD B, $20 Middle_loop1: PUSH BC DEC HL LD (HL), $AB INC HL LD B, $06 Middle_LoopA: LD (HL), $00 INC HL DJNZ Middle_LoopA ADD HL, DE LD B, $06 Middle_LoopB: LD (HL), $00 INC HL DJNZ Middle_LoopB LD (HL), $D5 ADD HL, DE POP BC DJNZ Middle_loop1 RET ;========================================================================== ; Print a text string ;-------------------------------------------------------------------------- ; Calling Registers: ; HL = Pointer to string data structure (row|column word + string) ; ; Return Registers: ; HL = Pointer to next byte after 00 at end of string ;========================================================================== Print_string: LD E, (HL) INC HL LD D, (HL) INC HL LD (TEXT_CURSOR), DE RES 1, (IY+$0D) CALL PRINT_TXT RET ;========================================================================== ; Print a menu string ;-------------------------------------------------------------------------- ; Calling Registers: ; HL = Pointer to string data structure (column|row word + string) ; ; Return Registers: ; HL = Pointer to next byte after 00 at end of string ;========================================================================== Print_menu_string: LD E, (HL) INC HL LD D, (HL) INC HL LD (MENU_CURSOR), DE RES 3, (IY+$05) CALL PRINT_MENU_TXT RET ;========================================================================== ; Draw a window ;-------------------------------------------------------------------------- ; Calling Registers: ; HL = Pointer into video memory ;========================================================================== Draw_window: LD DE, $000E LD B, $03 Top_loop: LD (HL), $FF INC HL DJNZ Top_loop DEC HL ADD HL, DE LD B, $10 Middle_loop: LD (HL), $80 INC HL LD (HL), $00 INC HL LD (HL), $01 ADD HL, DE DJNZ Middle_loop LD B, $03 Bottom_loop: LD (HL), $FF INC HL DJNZ Bottom_loop RET ;========================================================================== ; Print the number in HL ;========================================================================== Print_HL LD (Number_location), DE LD DE, Number_buffer+$04 LD B, $05 Unpack_loop: CALL UNPACK_HL ADD A, '0' LD (DE), A DEC DE DJNZ Unpack_loop LD B, $04 Pad_loop: INC DE LD A, (DE) CP '0' JR NZ, Skip_pad LD A, ' ' LD (DE), A DJNZ Pad_loop Skip_pad: LD HL, Number_location CALL Print_menu_string RET ;========================================================================== ; Remove a completed row ;-------------------------------------------------------------------------- ; Calling Regisers: ; B = Row number to check ;========================================================================== Check_row: LD C, $0A CALL Get_video_ptr LD B, $06 Check_loop: LD A, (HL) INC HL CP $FF RET NZ DJNZ Check_loop PUSH HL LD HL, (Line_count) INC HL LD (Line_count), HL LD HL, (Tetris_count) ADD HL, HL LD DE, 20 ADD HL, DE LD (Tetris_count), HL POP HL DEC HL Erase_loop1: LD E, L LD D, H LD BC, $FFC0 ADD HL, BC EX DE, HL LD A, D CP $FC RET C LD B, $04 Erase_loop2: PUSH BC PUSH DE LD B, $06 Erase_loop3: LD A, (DE) LD (HL), A DEC HL DEC DE DJNZ Erase_loop3 LD BC, $0016 ADD HL, BC POP DE POP BC DJNZ Erase_loop2 EX DE, HL JR Erase_loop1 ;========================================================================== ; Get the rotated bitmap of the current block ;-------------------------------------------------------------------------- ; Return Registers: ; DE = New bitmap ; HL = Pointer to new bitmap ;========================================================================== Rotate_bitmap: LD HL, (Current_ptr) INC HL INC HL Load_bitmap: LD E, (HL) INC HL LD D, (HL) DEC HL LD A, $FF CP D RET NZ ADD HL, DE JR Load_bitmap ;========================================================================== ; Test for a collision ;-------------------------------------------------------------------------- ; Calling Registers: ; B = y-coordinate ; C = x-coordinate ; HL = Bimap to test against ; ; Return Registers: ; A = 00 if no collision, nonzero otherwise ;========================================================================== Test_collision: ;-------------------------------------------------------------------------- ; Get the word bitmap at coordinates (C,B) in DE ;-------------------------------------------------------------------------- PUSH BC PUSH HL CALL Get_video_ptr CALL Get_byte_bitmap PUSH AF CALL Get_byte_bitmap POP DE LD E, A POP HL POP BC ;-------------------------------------------------------------------------- LD A, L AND D RET NZ LD A, H AND E RET ;========================================================================== ; Convert the coordinates of a block into a word-sized pointer into video ; memory via the equation: ; ; HL = LCD_ADDR + (40 * y-coordinate) + (x-coordinate DIV 2) ; ;-------------------------------------------------------------------------- ; Calling Registers: ; B = y-coordinate ; C = x-coordinate ; ; Return Registers: ; HL = Pointer into video memory ; D = 00 if x-coordinate is even, 01 if x-coordinate is odd ;========================================================================== Get_video_ptr: PUSH BC INC B LD HL, LCD_ADDR-$0040 PUSH DE LD DE, $0040 Loop_y: ADD HL, DE DJNZ Loop_y POP DE LD D, $00 SRA C ; 0 -> C -> CF RL D ; D = 01 if CF was set, 00 otherwise ADD HL, BC ; Note: B = 00 POP BC RET ;========================================================================== ; Get the byte bitmap at pointer HL ;-------------------------------------------------------------------------- ; Calling Registers: ; HL = Pointer to bitmap ; D = 00 or 01 ; ; Return Registers: ; A = Bitmap byte ; BC = Destroyed ; HL = (Pointer to bitmap)+0080 ;========================================================================== Get_byte_bitmap: CALL Get_nybble_bitmap SLA A SLA A SLA A SLA A PUSH AF CALL Get_nybble_bitmap POP BC OR B RET ;========================================================================== ; Get the nybble bitmap at pointer HL ;-------------------------------------------------------------------------- ; Calling Registers: ; HL = Pointer to bitmap ; D = 00 or 01 ; ; Return Registers: ; A = Bitmap nybble (in low order 4 bits, high order 4 bits are zero) ; BC = 0040 ; HL = (Pointer to bitmap)+0040 ;========================================================================== Get_nybble_bitmap: PUSH DE PUSH HL LD E, $00 LD B, D INC B INC B Bitmap_loop: LD A, (HL) PUSH AF SLA E AND $F0 JR Z, Skip_1 INC E Skip_1: POP AF SLA E AND $0F JR Z, Skip_2 INC E Skip_2: INC HL DJNZ Bitmap_loop RR D JR NC, Skip_shift SRA E Skip_shift: LD A, E AND $0F POP HL POP DE LD BC, $0040 ADD HL, BC RET ;========================================================================== ; Draw a word bitmap at location (C,B) ;-------------------------------------------------------------------------- ; Calling Registers: ; B = y-coordinate ; C = x-coordinate ; ; Return Registers: ; None ;========================================================================== Draw_word_bitmap: PUSH BC LD DE, (Current_bitmap) PUSH DE CALL Get_video_ptr CALL Draw_nybble_bitmap CALL Draw_nybble_bitmap POP AF LD E, A CALL Draw_nybble_bitmap CALL Draw_nybble_bitmap POP BC RET ;========================================================================== ; Draw the nybble in the high order position of E to HL ;-------------------------------------------------------------------------- ; Calling Registers: ; HL = Pointer into video memory ; D = 00 or 01 ; E = Bitmap ; ; Return Registers: ; E = Bitmap << 4 ; BC = 0040 ; HL = (Pointer into video memory)+0040 ;========================================================================== Draw_nybble_bitmap: PUSH HL LD C, D LD B, $04 Expand_bit: LD A, (HL) SRA D JR C, Odd XOR $F0 INC D PUSH HL JR Even Odd: XOR $0F INC HL PUSH HL DEC HL Even: SLA E JR NC, Skip_draw PUSH HL PUSH DE PUSH BC LD DE, $0010 LD B, $04 Draw_loop: LD (HL), A ADD HL, DE DJNZ Draw_loop POP BC POP DE POP HL Skip_draw: POP HL DJNZ Expand_bit LD D, C LD BC, $0040 POP HL ADD HL, BC RET .END