; B-tris
; Can I make it < 1kbytes or better... 800 bytes? (Beat Andy Hertzfeld?)
; Nah... but 1k, YES!
; Wow!! Spaghetti code! Drool, drool!

; 30/10/96: Broke the 900 bytes barrier!!!

.model tiny
.code
.286

theight equ 20
twidth equ 10
x_start equ 10
y_start equ 2
level_lines equ 10

org 100h
btris:
;	push cs
;	push cs
;	pop ds
;	pop es

	mov al, 2; assume ax= 0
	int 10h

	; hide cursor
	mov ah, 2
	;assume bh = 0
	mov dx, 25*256
	int 10h

	; show title
;	mov al, 7h
;	mov vid_attr, al ; 7h by default
;	xor di, di
	mov si, offset title_msg
	call printf

	; empty board
	mov di, offset start_zero;offset topempty;board
	mov cx, ((theight+1) * twidth / 2) + ((zero_elem/2) +1)
	xor ax, ax
	rep stosw

	; draw border
;	cld ; assume cld'd
	push 0b800h
	pop es
	mov di, (x_start - 2*2) + (y_start - 1)*160
;  mov cx, (twidth+2)*2
	mov ax, ' ' + (113 *256)
;  rep stosw

;  mov di, (x_start - 2*2) + (y_start)*160
;  mov cx, theight
;draw_border_loop:
;  stosw
;  stosw
;  add di, twidth * 4
;  stosw
;  stosw
;  add di, 160 - ((twidth + 2)*4)
;  loop draw_border_loop

;  mov di, (x_start - 2*2) + (y_start + theight)*160
;  mov cx, (twidth+2)*2
	;assume df=c
;  rep stosw

  mov bx, theight + 2
dbl:
  mov cx, (twidth+2)*2
  rep stosw
  add di, 160-((twidth + 2) * 4)
  dec bx
  jnz short dbl

	push cs
	pop es
	; end draw border

	;randomize!
	push ds
	push 40h
	pop ds
	mov ax, ds:[6ch]
	xor ax, ds:[6eh]
	pop ds
;	mov rand_seed, ax
;	jmp short bum_2

	;Main loop
main_loop:
	;rand
;	mov ax, rand_seed
bum_2:
	xor cx, cx
	mov bx, 31
	mul bx ; dx:ax = bx * ax
	add ax, 1
	adc dx, cx ; dx+=cf
	dec cx ; cx=0xffffh
  div cx; 0ffffh ; ax = dx:ax / 65535; dx = dx:ax % 65535
  mov word ptr rand_seed, dx
  xchg dx, ax; mov ax, dx
	; ax = random number

	mov bl, 7
  mov ah, 0
  div bl ; al = al / 7; ah = al % 7
	;piece = ah
	mov bx, (twidth/2)*256 + (1) ; bh = x, bl = y
	mov al, 0; al=o = 0

	call fit_piece
	jnc no_end_game
end_game_jmp:
	;end main loop
end_game:
	mov si, offset end_msg
;	mov di, (23*160)
	call printf ; game over! msg

	; show cursor at row 24
	mov ah, 2
	mov bh, 0
	mov dx, 23*256
	int 10h

	; exit to dos, retcode 0
;  mov ax, 4c00h
;  int 21h
  ret

no_end_game:

	call place_piece
	call show_scorenboard
;	push ax
	call clock

;  add cx, word ptr speed; code size bum (see data near eof)
  db 81h, 0c1h
speed: ; default = 16
  dw 16

	mov tim, cx
	xor cx, cx;	mov k, 0
	mov k, cx
;	pop ax

	mov cx, 16

;  sub cx, word ptr level; code size bum (see data near eof)
  db 81h, 0e9h
level: ; default = 1
  dw 1

	mov dx, cx
	shl dx, 1
	add cx, dx
	mov pscore, cx

	;middle loop
mid_loop:
;	push cx
	call clock
	cmp cx, tim
;	pop cx
	jbe mid_no_down

	call remove_piece
	inc bx;bl ; actually inc bl, but bx is smaller, just hope no overflow
;	push cx
	call clock
	add cx, word ptr speed ; code size bum (see data near eof)
	mov tim, cx
;	pop cx
	call fit_piece
	jnc mid_no_end
	dec bx;bl ;actually dec bl, but...
	call place_piece
	jmp end_mid_loop

mid_no_end:
	call place_piece
	call draw_board

mid_no_down:
	push ax
	mov ah, 1
	int 16h
	mov k, ax
	pop ax
	jz mid_loop
	push ax
	mov ah, 0
	int 16h
	pop ax

	call remove_piece

	; switch
	mov dx, k
	;cmp dl, 'j' ; left
	cmp dh, 4bh ; left
	jz left_key
	cmp dh, 4dh ; right
	jz right_key
	cmp dh, 48h ; up
	jz up_key
	cmp dh, 50h ; down

;	jz down_key

;	pusha
;	push ds
;	mov ax, 0b800h
;	mov ds, ax
;	mov ax, dx
;	mov bx, 10
;	mov cl, 7
;	mov di, 50*2 + (5*160)+1
;	call show_dec
;	pop ds
;	popa
	jnz short mid_end_switch

down_key0:
	inc pscore
down_key:
	inc bx;bl ; actually inc bl, but bx smaller, just hope no overflow
	call fit_piece
	jnc down_key0

down_key1:
	dec bx;bl ;actually bl

mid_end_switch:
	call place_piece
	call draw_board
	mov dx, k
	cmp dh, 50h ; down
	je end_mid_loop
	cmp dh, 1 ; exit (esc)
	jne mid_loop
	jmp end_game
left_key:
	dec bh
	call fit_piece
	jc left_key_1
bum_1:
	dec pscore
	jmp short mid_end_switch
left_key_1:
	inc bh
	jmp short mid_end_switch

right_key:
	inc bh
	call fit_piece
	jnc bum_1
	dec bh
	jmp short mid_end_switch

up_key:
	mov dx, ax ; oo = o and p
	inc ax;al ; actually inc al, but ax smaller

;---
;  mov cx, offset piece_size
;  add cl, ah
;  adc ch, 0
;  mov si, cx
;  cmp al, [si]

  push bx
  mov bl, ah
  mov bh, 0
  cmp al, [piece_size + bx]
  pop bx
;---

  jnae short up_key_1
	mov al, 0

up_key_1:
	call fit_piece
  jnc short bum_1
	mov al, dl
	jmp short mid_end_switch

	;end middle loop
end_mid_loop:
	mov ax, pscore
	sub ax, 5

  cmp ax, word ptr level
  jnl short add_sc
  mov ax, word ptr level
add_sc:
	add ax, 5
	add score, ax

	call show_scorenboard

	;--- kill lines loop ---
	xor dx, dx ; dx = hits
kl_again:
	mov si, offset board + ((theight-1) * twidth)
;	mov cl, theight
	mov cx, theight
kl_loop:
	mov bx, twidth-1
kl_loop2:
  cmp byte ptr [si+bx], ch ; assume ch = 0
  jz short no_kill

	dec bx
  jns short kl_loop2

	; kill it
	inc dx; hits++
	dec si
	mov di, si
	add di, twidth
	mov bh, cl
kl_again2:
	mov cx, twidth
	std
	rep movsb
	dec bh
  jnz short kl_again2
;	mov cx, twidth
;	mov al, 0
;	rep stosb
	cld
	jmp short kl_again

no_kill:
	sub si, twidth
	loop kl_loop
	;--- end Kill lines loop ---

	add lines, dx
	add llines, dl
	mov dh, 1
	mov cl, dh
	dec cx; cl ;actually cl, but...
	shl dh, cl
	mov al, 100
	mul dh
	add score, ax

	cmp llines, level_lines
  jb short no_inc_level
	sub llines, level_lines
  inc word ptr level
  sub word ptr speed, 2
no_inc_level:
	call show_scorenboard

  mov ax, rand_seed

	jmp main_loop

;================================================================ Game procs =
show_scorenboard proc near
draw_board:
	pusha
	push ds
  push es

	push 0b800h ; (1)
	mov ax, score
	mov bx, 10
;	mov cl, 7 ; attrib
	mov di, 50*2 + (2*160)+1
	pop ds ; (1)
	call show_dec

  mov ax, word ptr cs:level
	mov di, 50*2 + (3*160)+1
	call show_dec

  mov ax, cs:lines

	mov di, 50*2 + (4*160)+1
	call show_dec

;  pop ds
;  popa
	;ret ; no ret!
;  endp

;draw_board proc near
;  pusha
;  push es

;  pop ds
;  push 0b800h
;  pop es

  push ds
  pop es
  pop ds

	mov di, x_start + (y_start)*160
	mov si, offset board
	mov cx, theight
	mov al, 219 ; ' ' ; filled block

draw_board_loop:
	mov bl, twidth

	draw_board_loop2:
	mov ah, [si]
	inc si
	stosw
	stosw
	dec bl ; cannot bx!!
	jnz draw_board_loop2

	add di, 160 - (twidth * 4)

	loop draw_board_loop

	pop es
;  pop ds
	popa
	ret
	endp

get_piece_size:
;  mov dx, offset piece_size
;  dec ah
;  add dl, ah
;  adc dh, 0
;  mov si, dx

  dec ah
  push bx
  mov bl, ah
  mov bh, 0
  lea si, [piece_size + bx]
  pop bx
	ret

;------------
get_piece_map_loop:
	dec ah
	add al, byte ptr [si]
	dec si

get_piece_map1: ; entry
	or ah, ah
;  js get_piece_map_exit_loop1
  jns get_piece_map_loop

;  jmp short get_piece_map1

get_piece_map_exit_loop1:

	shl al, 2 ; change
	mov ah, 0
	mov bp, offset piece_map
	add bp, ax ; bp= piece_map[o]
	mov ch, 4
	ret

;-- get_piece_map
get_piece_map:
	mov al, [bp]
  mov ah, al
  and ah, 0fh
  shr al, 4
	sub ah, 2
	sub al, 2 ; end CHANGE
	add ah,bl;ah=yy
	ret

get_board_xy:
  mov si, offset board
  mov al, ah ; al = y
  mov cl, twidth
  mul cl ; ax = al * twidth
  add al, dl
  adc ah, 0
  add si, ax

	ret

;----------------------------------------------------------------- fit_piece -
fit_piece proc near; ah=p, al=o, bh=x,bl=y
	pusha
	call get_piece_size

fp_loop1:
	call get_piece_map1

fp_loop2:
	call get_piece_map

	js fp_ret1 	;yy<0
	cmp ah, theight 	;yy>=theight
	jae fp_ret1
	add al,bh;al=xx
	;xx<0
	js fp_ret1
	;xx>=twidth
	cmp al, twidth
	jae fp_ret1
	;board[yy][xx]
	mov dx, ax ; al = x, ah = y, dl = x

	call get_board_xy

	cmp byte ptr [si], 0
	jnz fp_ret1
	inc bp
	dec ch
	jnz fp_loop2

fp_ret0:
;	clc ; assume cf already 0, since ch = 0

;  popa
;  ret
  db 0b6h; mov dl, xx
fp_ret1:
	stc
  popa
	ret
	endp

;-------------------------------------------------------------- remove_piece -
remove_piece proc near
	pusha
  mov dh, -1
  jmp short pp_entry
	endp

;--------------------------------------------------------------- place_piece -
place_piece proc near
	pusha
	mov dh, ah
pp_entry:
	call get_piece_size
	inc dh

pp_loop1:
	call get_piece_map1

pp_loop2:
	call get_piece_map
	add al,bh;al=xx

	;board[yy][xx]
	mov dl, al ; al = x, ah = y, dl = x
	call get_board_xy
	mov byte ptr [si], dh

	inc bp
	dec ch
	jnz pp_loop2

pp_ret:
  popa
  ret
	endp

;============================================================ Non game procs =
show_dec proc near
dec_loop:
  xor dx, dx
  div bx ; ax = dx:ax/bx, dx = dx:ax%bx
  add dl, '0'
  dec di
  mov [di], dl
  dec di
  or ax, ax
  jnz dec_loop

	ret
	endp

printf proc near ; ds:si = source
	push es
	push 0b800h
	pop es

;	cld ; assume cld'd
printf_again2:
	lodsw
	xchg di, ax

printf_again:
	lodsb
	cmp al, 1
	jbe printf_exit
	stosb
;  mov al, vid_attr
  mov al, 7
	stosb
	jmp short printf_again

printf_exit:
	jne short printf_again2
	pop es
	ret
	endp

clock proc near
	push ds
	push 40h
	pop ds
	mov cx, ds:[6ch]
	pop ds
	ret
	endp

;====================================================================== Data =
;.data
; use ".data" means align 2

title_msg label byte
	dw 0
  db 'B''s TinyTris  Use ', 18h, 19h, 1bh, 1ah, ' Esc to quit', 00
score_msg label byte
	dw 160 * 2 + 35 * 2
	db 'Score:', 00
level_msg label byte
	dw 160 * 3 + 35 * 2
	db 'Level:', 00
line_msg label byte
	dw 160 * 4 + 35 * 2
	db 'Lines:', 01
end_msg label byte
	dw (23*160)
	db 'Game Over!', 01

piece_map label byte
 db 012h, 022h, 032h, 002h ; 0
 db 021h, 022h, 023h, 024h

 db 012h, 022h, 032h, 033h ; 2
 db 021h, 022h, 023h, 031h
 db 011h, 012h, 022h, 032h
 db 021h, 022h, 023h, 013h

 db 012h, 022h, 032h, 013h ; 6
 db 021h, 022h, 023h, 033h
 db 012h, 022h, 032h, 031h
 db 021h, 022h, 023h, 011h

 db 012h, 022h, 032h, 023h ; 10
 db 021h, 022h, 023h, 032h
 db 012h, 022h, 032h, 021h
 db 021h, 022h, 023h, 012h

 db 022h, 032h, 023h, 033h ; 14

 db 012h, 022h, 023h, 033h ; 15
 db 013h, 012h, 022h, 021h

 db 013h, 023h, 022h, 032h ; 17
 db 021h, 022h, 032h, 033h

piece_size label byte
	db 2, 4, 4, 4, 1, 2, 2

;vid_attr db 7
;level dw 1
;speed db 16

start_zero label byte
;speed2 db ? ; code size bum -- speed's size = 1 instead of 2 now, since
						; speed is actually word long

llines db ?
lines dw ?
score dw ?

k dw ?
tim dw ?
rand_seed dw ?
pscore dw ?

topempty db twidth dup (?)
board label byte
zero_elem equ offset topempty - offset start_zero

end btris

