%include "util.mac"
%include "vxdn.inc"
%include "icedump.inc"
%include "wiat.inc"


global Parse_Tetris


extern sdata
extern Parser.error
extern Parser.errorMsg
extern ParseExpression
extern GetVideoMem


bits 32


segment _LTEXT

; original: Tetris v1.0 by Joe Wingbermuehle
;           19981226
;           http://www.usmo.com/~joewing
;           joewing@usmo.com
;
; adaptation: The Owl 2000/07
;
; keys: left/right arrows to move the block
;       up arrow to rotate it
;       down arrow to make it fall faster
;       esc to end the game
;
;

;-------------------------------------------------------------------------------
; TETRIS [<delay>]
;-------------------------------------------------------------------------------

Parse_Tetris:
; get <delay>
	call	ParseExpression		; parse <delay>
	jnc	@F

	mov	eax,150

@@
	mov	[delay],eax

; clear screen
	mov	ecx,[bLINES_current]
	movzx	ecx,byte [ecx]
	mov	[LINES],ecx

	mov	eax,[dWIDTH_current]
	imul	ecx,[eax]
	mov	eax,[eax]
	mov	[WIDTH],eax

	xor	edx,edx
	mov	ebx,0x20
	call	[pPrintChar]

; init timer/kbd/board
	and	dword [ticks],byte 0

	cmp	dword [seed],byte 0
	jnz	@F

	VMMCall	Get_Last_Updated_System_Time
	mov	[seed],eax

@@
	call	[pEmptyKbdBuffer]

	mov	eax,[WIDTH]
	sub	eax,[board.width]
	shr	eax,1
	mov	[board.x],eax

	mov	eax,[LINES]
	sub	eax,[board.lines]
	shr	eax,1
	mov	[board.y],eax

	call	DrawBoard
	call	DrawNewBlock

	and	dword [stats.score],byte 0
	and	dword [stats.lines],byte 0
	and	dword [stats.blocks],byte 0

; game loop
.main:
	call	Tick
	mov	eax,[ticks]
	cmp	eax,[delay]
	jb	@F

	and	dword [ticks],byte 0

	call	ScrollDown
	jnc	.main

	call	DrawNewBlock
	jnc	.main

	jmp	short .exit

@@
	call	[pReadFromKbdBuffer_char]

	cmp	al,KBD_ESC
	jz	.exit

	push	dword .main

	cmp	al,KBD_LEFT
	jz	near .scrollLeft

	cmp	al,KBD_RIGHT
	jz	near .scrollRight

	cmp	al,KBD_DOWN
	jz	near .fastDown

	cmp	al,KBD_UP
	jz	near .rotateBlock

	retn				; back to .main

.exit:
	call	[pUpdateAllWindows]

	push	dword [stats.score]
	push	dword [stats.lines]
	push	dword Format_Result
	call	[pPrintf]
	add	esp,byte 12

	popad
	retn


.rotateBlock:
	call	EraseBlock

	push	dword [block.id]
	push	dword [block.lines]

	inc	dword [block.id]
	test	byte [block.id],3
	jnz	@F

	sub	dword [block.id],byte 4

@@
	imul	eax,[block.id],byte blocks.01-blocks.00
	add	eax,blocks+2*4*4
	mov	eax,[eax]
	mov	[block.lines],eax

	call	DrawBlock
	jc	@F

	add	esp,byte 8
	retn

@@
	pop	dword [block.lines]
	pop	dword [block.id]
	call	DrawBlock
	retn


.scrollLeft:
	call	EraseBlock

	dec	dword [block.x]
	call	DrawBlock
	jnc	@F

	inc	dword [block.x]
	call	DrawBlock

@@
	retn


.scrollRight:
	call	EraseBlock

	inc	dword [block.x]
	call	DrawBlock
	jnc	@F

	dec	dword [block.x]
	call	DrawBlock

@@
	retn


.fastDown:
	call	ScrollDown
	jnc	@F

	call	DrawNewBlock
	jnc	@F

	mov	dword [esp], .exit

@@
	retn


Tick:
	mov	eax,1
	call	[pDelayMilliSec]
	inc	dword [ticks]
	retn


DrawBoard:
	mov	dl,[board.x]
	dec	dl
	mov	dh,[board.y]
	add	dh,[board.lines]
	mov	ecx,[board.width]
	add	ecx,byte 2
	mov	ebx,0x7F20
	call	DrawHLine

	mov	dl,[board.x]
	dec	dl
	mov	dh,[board.y]
	mov	ecx,[board.lines]
	inc	ecx
	mov	ebx,0x7F20
	call	DrawVLine

	mov	dl,[board.x]
	add	dl,[board.width]
	mov	dh,[board.y]
	mov	ecx,[board.lines]
	inc	ecx
	mov	ebx,0x7F20
	call	DrawVLine
	retn


;-------------------------------------------------------------------------------
; stc if board is full
;-------------------------------------------------------------------------------
DrawNewBlock:
	mov	eax,[board.width]
	sub	eax,byte 2
	shr	eax,1
	add	eax,[board.x]
	mov	[block.x],eax

	mov	eax,[board.y]
	mov	[block.y],eax

	call	GetRandomBlockID
	mov	[block.id],eax

	imul	eax,byte blocks.01-blocks.00
	add	eax,blocks+2*4*4
	mov	eax,[eax]
	mov	[block.lines],eax

	call	DrawBlock
	jc	@F

	inc	dword [stats.blocks]	; preserves carry

@@
	retn


GetRandomBlockID:
	push	edx

	xor	edx,edx
	mov	eax,[seed]
	idiv	dword [q]
	imul	eax,[r]
	imul	edx,[a]
	sub	edx,eax
	lea	eax,[edx+0x7FFFFFFF]
	test	edx,edx
	jle	@F

	mov	eax,edx

@@
	mov	[seed],eax

	xor	edx,edx
	idiv	dword [NumOfBlocks]
	lea	eax,[4*edx]

	pop	edx
	retn


;-------------------------------------------------------------------------------
; dl: left.x
; dh: left.y
; ecx: length
; bl: char
; bh: color
;-------------------------------------------------------------------------------
DrawHLine:
	call	[pPrintChar]
	retn


;-------------------------------------------------------------------------------
; dl: top.x
; dh: top.y
; ecx: length
; bl: char
; bh: color
;-------------------------------------------------------------------------------
DrawVLine:
	push	ecx

	mov	ecx,1
	call	[pPrintChar]

	inc	dh
	pop	ecx
	loop	DrawVLine

	retn


;-------------------------------------------------------------------------------
; stc if block cannot be drawn
;-------------------------------------------------------------------------------
DrawBlock:
	push	ebx
	push	ecx
	push	edx
	push	esi
	push	edi

	mov	ebx,[WIDTH]

	call	GetVideoMem
	mov	edi,eax

	mov	eax,[block.y]
	imul	eax,ebx
	add	eax,[block.x]
	lea	edi,[2*eax+edi]		; edi: upper left corner of blockwindow

	imul	esi,[block.id],byte blocks.01-blocks.00
	add	esi,blocks		; esi: upper left corner of blockdata

	mov	edx,esi
	mov	ecx,[block.lines]

.loop_check:
	lodsd
	and	eax,[edi]
	and	eax,0x0F000F00
	jz	@F

	stc
	jmp	.ret

@@
	lodsd
	and	eax,[edi+4]
	and	eax,0x0F000F00
	jz	@F

	stc
	jmp	short .ret

@@
	lea	edi,[edi+2*ebx]
	loop	.loop_check

; now let's draw it
	imul	eax,[block.lines],2
	imul	eax,ebx
	sub	edi,eax			; edi: upper left corner of blockwindow
	mov	esi,edx			; esi: upper left corner of blockdata

	mov	ecx,[block.lines]

.loop_draw:
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi],ax

@@
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi+2],ax

@@
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi+4],ax

@@
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi+6],ax

@@
	lea	edi,[edi+2*ebx]
	loop	.loop_draw

	call	[pUpdateScreen]

	clc

.ret:
	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	ebx
	retn


EraseBlock:
	push	ebx
	push	ecx
	push	edx
	push	esi
	push	edi

	mov	ebx,[WIDTH]

	call	GetVideoMem
	mov	edi,eax

	mov	eax,[block.y]
	imul	eax,ebx
	add	eax,[block.x]
	lea	edi,[2*eax+edi]		; edi: upper left corner of blockwindow

	imul	esi,[block.id],byte blocks.01-blocks.00
	add	esi,blocks		; esi: upper left corner of blockdata

	mov	edx,0x20
	mov	ecx,[block.lines]

.loop_draw:
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi],dx

@@
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi+2],dx

@@
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi+4],dx

@@
	lodsw
	test	ax,ax
	jz	@F

	mov	[edi+6],dx

@@
	lea	edi,[edi+2*ebx]
	loop	.loop_draw

	call	[pUpdateScreen]

	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	ebx
	retn


;-------------------------------------------------------------------------------
; stc if new block is needed
;-------------------------------------------------------------------------------
ScrollDown:
	call	EraseBlock

	inc	dword [block.y]
	call	DrawBlock
	jnc	@F

	dec	dword [block.y]
	call	DrawBlock

	call	CompactLines

	stc

@@
	retn


CompactLines:
	push	ebx
	push	ecx
	push	edx
	push	esi
	push	edi

	mov	ebx,[WIDTH]

	call	GetVideoMem
	mov	esi,eax

	mov	eax,[block.y]
	imul	eax,ebx
	add	eax,[board.x]
	lea	esi,[2*eax+esi]		; esi: left border of blockwindow

	mov	ecx,[block.lines]

.loop_outer:
	push	ecx
	mov	edx,esi
	mov	ecx,[board.width]

.loop_inner:
	lodsw
	test	ax,0x0F00
	loopnz	.loop_inner

	mov	esi,edx

	jz	.nextline

; compact a line
	inc	dword [stats.lines]
	mov	eax,[stats.blocks]
	add	[stats.score],eax

	test	byte [delay],111b	; gets faster after 8 completed lines
	jnz	@F

	cmp	dword [delay],byte 1	; no that if anyone ever got this far...
	jbe	@F

	dec	dword [delay]

@@
	mov	ecx,[block.y]
	sub	ecx,[board.y]
	inc	ecx

.loop_moveline:
	push	ecx

; move down upper neighbour

	mov	edi,esi
	sub	esi,ebx
	sub	esi,ebx
	push	esi

	mov	ecx,[board.width]
	rep	movsw

	pop	esi
	pop	ecx
	loop	.loop_moveline

	mov	esi,edx

.nextline:
	lea	esi,[2*ebx+esi]

	pop	ecx
	loop	.loop_outer

	call	[pUpdateScreen]

	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	ebx
	retn


segment _LDATA
Format_Result:	db 'lines completed: %d,    score: %d',0

	align 4
ticks:		dd 0
seed:		dd 0
a:		dd 16807
r:		dd 2836
q:		dd 127773
delay:		dd 200
LINES:		dd 0
WIDTH:		dd 0
NumOfBlocks:	dd 7
stats:
.score:		dd 0
.lines:		dd 0
.blocks:	dd 0

board:
.x:		dd 0
.y:		dd 0
.lines:		dd 20
.width:		dd 12

block:
.x:		dd 0
.y:		dd 0
.id:		dd 0
.lines:		dd 0

blocks:
.00:
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dd 4

.01:
	dw 0x7F20, 0x7F20, 0x7F20, 0x7F20
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 1

.02:
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dw 0x7F20, 0x0000, 0x0000, 0x0000
	dd 4

.03:
	dw 0x7F20, 0x7F20, 0x7F20, 0x7F20
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 1

.10:
	dw 0xAF20, 0x0000, 0x0000, 0x0000
	dw 0xAF20, 0x0000, 0x0000, 0x0000
	dw 0xAF20, 0xAF20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3
.11:
	dw 0x0000, 0x0000, 0xAF20, 0x0000
	dw 0xAF20, 0xAF20, 0xAF20, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.12:
	dw 0xAF20, 0xAF20, 0x0000, 0x0000
	dw 0x0000, 0xAF20, 0x0000, 0x0000
	dw 0x0000, 0xAF20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.13:
	dw 0xAF20, 0xAF20, 0xAF20, 0x0000
	dw 0xAF20, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.20:
	dw 0x5F20, 0x0000, 0x0000, 0x0000
	dw 0x5F20, 0x5F20, 0x0000, 0x0000
	dw 0x5F20, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.21:
	dw 0x0000, 0x5F20, 0x0000, 0x0000
	dw 0x5F20, 0x5F20, 0x5F20, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.22:
	dw 0x0000, 0x5F20, 0x0000, 0x0000
	dw 0x5F20, 0x5F20, 0x0000, 0x0000
	dw 0x0000, 0x5F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.23:
	dw 0x5F20, 0x5F20, 0x5F20, 0x0000
	dw 0x0000, 0x5F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.30:
	dw 0x4F20, 0x0000, 0x0000, 0x0000
	dw 0x4F20, 0x4F20, 0x0000, 0x0000
	dw 0x0000, 0x4F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.31:
	dw 0x0000, 0x4F20, 0x4F20, 0x0000
	dw 0x4F20, 0x4F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.32:
	dw 0x4F20, 0x0000, 0x0000, 0x0000
	dw 0x4F20, 0x4F20, 0x0000, 0x0000
	dw 0x0000, 0x4F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.33:
	dw 0x0000, 0x4F20, 0x4F20, 0x0000
	dw 0x4F20, 0x4F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.40:
	dw 0x0000, 0x3F20, 0x0000, 0x0000
	dw 0x0000, 0x3F20, 0x0000, 0x0000
	dw 0x3F20, 0x3F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.41:
	dw 0x3F20, 0x3F20, 0x3F20, 0x0000
	dw 0x0000, 0x0000, 0x3F20, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.42:
	dw 0x3F20, 0x3F20, 0x0000, 0x0000
	dw 0x3F20, 0x0000, 0x0000, 0x0000
	dw 0x3F20, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.43:
	dw 0x3F20, 0x0000, 0x0000, 0x0000
	dw 0x3F20, 0x3F20, 0x3F20, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.50:
	dw 0x0000, 0x2F20, 0x0000, 0x0000
	dw 0x2F20, 0x2F20, 0x0000, 0x0000
	dw 0x2F20, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.51:
	dw 0x2F20, 0x2F20, 0x0000, 0x0000
	dw 0x0000, 0x2F20, 0x2F20, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.52:
	dw 0x0000, 0x2F20, 0x0000, 0x0000
	dw 0x2F20, 0x2F20, 0x0000, 0x0000
	dw 0x2F20, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 3

.53:
	dw 0x2F20, 0x2F20, 0x0000, 0x0000
	dw 0x0000, 0x2F20, 0x2F20, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.60:
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.61:
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.62:
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2

.63:
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x9F20, 0x9F20, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dw 0x0000, 0x0000, 0x0000, 0x0000
	dd 2
