; *** TETRIS ***
; per Spectrum 48K
; sviluppato da:
; Antonio Di Stefano
; Ivo Pannizzo
; Silvia Benzi
; Francesco Agostaro

CLS	.EQU	$0DAF		; routine: cancella schermo
OPEN	.EQU	$1601		; routine: apre canale di dati
PPOS	.EQU	$0DD9		; routine: posiziona cursore 
KSCAN	.EQU	$02BF		; routine: legge tastiera
STRING	.EQU	$203C		; routine: stampa stringa

REPDEL	.EQU	$5C09		; var: ritardo tastiera
UDG	.EQU	$5C7B		; var: contiene puntatore inizio UDG
ATTR_P	.EQU	$5C8D		; var: attributi caratteri perm.
ATTR_T	.EQU	$5C8F		; var: attributi caratteri temp.
DF_SZ	.EQU	$5C6B		; var: dimensione area di edit
LASTK	.EQU	$5C08		; var: ultimo tasto premuto
FRAMES	.EQU	$5C78		; var: contatore di tempo

	.ORG	$8000

;*******************************
; Cancella lo schermo,
; copia gli UDG, setta i ritardi
; di tastiera
	
	LD	IX,XPOS		; inizio area variavili
	XOR	A		; azzera A
	OUT	($FE),A		; bordo nero

	EI			; abilita le interruzioni

	LD	HL,REPDEL	; setta ritardi di tastiera
	LD	(HL),15		; REPDEL (delay before repeat)
	INC	HL
	LD	(HL),5		; REPPER (repeatition delay)

	LD	BC,16		; copia gli UDG
	LD	DE,(UDG)	; inizio area UDG
	LD	HL,UDG0		; inizio definizione UDG
	LDIR

	LD	A,%00000010	; sfondo nero, penna rossa
	LD	(ATTR_P),A	; memorizza in ATTR_P

	CALL	CLS		; chiama CLS


;*******************************
; Disegna sfondo fisso
;

	LD	A,2		; apre canale "S" (schermo superiore)
	CALL	OPEN		; chiama OPEN

	XOR	A		; azzera dimensione area di edit
	LD	(DF_SZ),A	; scrive in DF_SZ

	LD	(IX+8),144	; carattere mattoncino
	LD	DE,$2118	; da coord. (33,24)
	LD	HL,$0001	; a coord. (0,1)
	CALL	CLR2		; stampa tutto lo schermo

	XOR	A		; sfondo nero, penna nera
	LD	(ATTR_T),A	; memorizza in ATTR_T

	LD	(IX+8),32	; carattere blank (spazio)
	LD	DE,$0D17	; da (13,23)
	LD	HL,$0103	; a (1,3)
	CALL	CLR2		; stampa area rettangolare di info

	LD	B,5		; stampa le stringhe di stato
	LD	HL,SSCORE
STLOP	CALL	PTSTR
	DJNZ	STLOP


;*******************************
; Set intro & reset vars
;

INTRO	CALL	CLEAR		; cancella area di gioco
	LD	BC,16		; resetta le 16 variabili 
	LD	HL,DEFVAR	; copiandole da DEFVAR
	LD	DE,XPOS		; a XPOS (e seguenti)
	LDIR

	CALL	SETP2		; sceglie primo pezzo

	LD	B,13		; stampa le 13 stringhe dell'intro
	LD	HL,MSG1
ITLOP	CALL	PTSTR
	DJNZ	ITLOP

	CALL	WKEY		; aspetta un tasto


;*******************************
; Gestione partita
;

	CALL	CLEAR		; cancella area di gioco
	CALL	UPDTE		; aggiorna info
	CALL	SETP		; prepara pezzo
	CALL	NEXTP		; disegna prossimo pezzo

MAIN	LD	A,(TIMLV)	; carica tempo di livello
	LD	(CYCL),A	; nella variabile di ciclo
	CALL	PRNT		; stampa pezzo

; *** loop di gestione tastiera ***

MAIN2	XOR	A		; azzera LASTK
	LD	(LASTK),A

	HALT			; attesa 20ms + scansione tastiera (RST 38H)

	LD	A,(LASTK)	; legge LASTK
	LD	D,(IX+0)	; posizione attuale diventa
	LD	(IX+2),D	; posizione passata

	CP	$61		; tasto "A": sposta pezzo a sx.
	JR	NZ,KEYD		; altrimenti salta
	CALL	PRNT		; cancella
	DEC	(IX+0)		; decrementa XPOS
	JR	HHIT

KEYD	CP	$64		; tasto "D": sposta pezzo a dx.
	JR	NZ,KEYSP	; altrimenti salta
	CALL	PRNT		; cancella
	INC	(IX+0)		; incrementa XPOS
	JR	HHIT

KEYSP	CP	$20		; tasto <SPACE>: ruota pezzo
	JR	NZ,KEYET	; altrimenti salta
	CALL	PRNT		; cancella

	; *** rotazione ***
	LD	A,(ORNT)	; prende in A il byte di orientazione
	INC	A		; incrementa A
	AND	%00000011	; maschera: 000000xx
	LD	(ORNT),A	; rimette il risultato il ORNT
	LD	(IX+8),1	; MODE=hittest
	CALL	PRNT		; esegue hittest
	JR	Z,ESC0		; se zero esce
	LD	A,(ORNT)
	DEC	A		; altrimenti decrementa A
	AND	%00000011	; maschera: 000000xx
	LD	(ORNT),A	; rimette il risultato il ORNT

ESC0	CALL	PRNT		; stampa
	JR	ENDCS

KEYET	CP	$0D		; tasto <ENTER>: discesa veloce
	JR	NZ,KEYP		; altrimenti salta
	LD	(IX+3),1	; imposta tempo di discesa (ciclo)
	JR	ENDCS

KEYP	CP	$70		; tasto "P": pausa
	JR	NZ,KEYQ		; altrimenti salta
	LD	HL,SPAUS	; stampa stringa "Pause!"
	CALL	PTSTR	
	CALL	WKEY		; aspetta la pressione di un tasto
	LD	HL,BLANK	; cancella stringa
	CALL	PTSTR
	JR	ENDCS

KEYQ	CP	$71		; tasto "Q": fine partita
	JR	NZ,ENDCS	; altrimenti salta a fine casi
	CALL	NEXTP		; cancella l'area "NEXT"
	JP	INTRO		; ritorna all'intro

HHIT	LD	(IX+8),1	; MODE=hittest
	CALL	PRNT		; esegue hittest
	JR	Z,ESC1		; se zero salta
	LD	D,(IX+2)	; altrimenti riprende posizione vecchia
	LD	(IX+0),D	; e la rimette a posto
ESC1	CALL	PRNT		; stampa

ENDCS	DEC	(IX+3)		; contatore ciclo di temporizz.
	JP	NZ, MAIN2

; *** incrementa posizione verticale ***

	CALL	PRNT		; cancella
	INC	(IX+1)		; incrementa pos. vert.
	LD	(IX+8),1	; MODE=hittest
	CALL	PRNT		; esegue hittest
	JP	Z,MAIN		; se hittest=0 OK, ricomincia ciclo

	; *** deposita pezzo ***
	DEC	(IX+1)		; altrimenti->posizione precedente
	CALL	PRNT		; stampa pezzo
	
; *** calcola linee occupate ***

	LD	A,24		; A=24-YPOS
	SUB	(IX+1)
	CP	(IX+14)		; confronta con LINES
	JP	M,ESC2		; se < di altezza pila salta
	LD	(LINES),A	; altrimenti aggiorna
	CP	20		; se si sono raggiunte 20 linne
	JP	M,ESC2

	; *** perdita di una vita ***
	CALL	DELAY		; piccolo ritardo
	CALL	CLEAR		; cancella area di gioco
	LD	(IX+14),0	; azzera numero di linee
	DEC	(IX+13)		; decrementa vite
	JR	NZ,ESC2		; se LIVES non  0 continua partita

	; *** perdita della partita ***
	LD	HL,MSG4		; altrimenti...
	CALL	PTSTR		; stampa game over
	CALL	WKEY		; aspetta la pressione di un tasto
	CALL	NEXTP		; cancella l'area "NEXT"
	JP	INTRO		; ritorna all'intro

; *** controllo linee e livelli ***

	; *** ricerca e canc. linee complete ***
ESC2 	LD	B,23		; inizia dal basso...
LIN1	LD	A,%11111111	; maschera di bit
	LD	C,18		; ...a destra
LIN2	CALL	CCORD		; calcola loc. colore
	AND	(HL)		; AND con i colori sullo schermo
	DEC	C
	JR	NZ,LIN2		; loop di colonna
	OR	A		; se A=0 la linea non  completa
	JR	NZ,DELOP	; se  completa la cancella
	DJNZ	LIN1		; loop di riga
	JR	FWD2		; <-- uscita!

DELOP	LD	C,3		; loop di cancellazione e spostamento
	PUSH	BC
	CALL	CCORD
	LD	BC,18		; copia 18 blocchi
	PUSH	HL		; mette nello stack HL
	LD	DE,32
	SBC	HL,DE		; sottrae ad HL 32 (riga sopra)
	POP	DE		; prende dallo stack HL in DE
	LDIR			; copia riga
	POP	BC
	DEC	B
	LD	A,22
	SUB	B		; linea attuale = 22-B
	CP	(IX+14)		; ha raggiunto LINES?
	JR	NZ,DELOP
	CALL	DELAY		; piccolo ritardo

	LD	DE,100		; 100 punti per ogni linea cancellata!
	CALL	SCRFN		; aggiorna il punteggio
	DEC	(IX+14)		; decrementa linee nella pila
	DEC	(IX+12)		; decrementa linee da completare
	JR	Z,FWD1		; esce se completate tutte le linee
	JR	ESC2		; altrimenti ripete

	; *** Livello completo ***
FWD1	CALL	CLEAR
	LD	HL,MSG5		; stampa "livello completo"
	CALL	PTSTR
	LD	B,100		; attende 2 sec.
	CALL	WLP1		; routine di attesa
	DEC	(IX+11)		; dec. tempo di livello
	LD	A,(LEVEL)	; livello
	CP	20		; controlla se  l'ultimo livello
	JR	Z,FINAL		; se si -> schermata finale
	INC	A		; altrimenti inc. livello
	LD	DE,1000		; +1000 punti!
	CALL	SCRFN		; somma al punteggio
	LD	(LEVEL),A
	ADD	A,3		; per livello succ.
	LD	(LINLV),A	; LINLV=LEVEL+3
	LD	(IX+14),0	; azzera altezza pila
	CALL	CLEAR

	; *** passa al prossimo pezzo ***
FWD2	LD	DE,20		; 20 punti per ogni pezzo caduto
	CALL	SCRFN		; aggiorna il punteggio
	CALL	NEXTP		; cancella l'area "NEXT"
	CALL	SETP		; sceglie nuovo pezzo
	CALL	UPDTE		; aggiorna info
	CALL	NEXTP		; disegna prossimo pezzo

	LD	(IX+0),9	; reimposta POSX
	LD	(IX+1),1	; POSY
	LD	(IX+2),9	; MPOSX
	JP	MAIN		; ricomincia

; *** Schermata di fine gioco ***

FINAL	CALL	CLEAR		; cancella area
	LD	B,2		; stampa le 2 stringhe
	LD	HL,MSG6
FNLOP	CALL	PTSTR
	DJNZ	FNLOP
	CALL	WKEY		; aspetta un tasto
	CALL	NEXTP		; cancella area next
	JP	INTRO		; ricomincia da capo



;******************************
; Aspetta che sia premuto
; un tasto

WKEY	CALL	DELAY		; piccolo ritardo di 500ms

KLOOP	CALL	KSCAN		; chiama KEY_SCAN
	CP	$FF		; controlla se A=$FF (nessun tasto premuto)
	JR	Z,KLOOP		; se lo  -> loop
	RET



;******************************
; Ritardo di 500 ms
;
 
DELAY	LD	B,25		; 25*20ms=500ms
WLP1	HALT
	DJNZ	WLP1



;*******************************
; Aggiorna i dati visualizzati
;

UPDTE	LD	HL,SPALL	; disegna le vite (palline)
	CALL	PTSTR

	LD	A,9
	ADD	A,(IX+13)
	LD	C,A		; coord. X cursore
	LD	B,$11		; coord. Y cursore
	CALL	PPOS
	LD	B,3		; stampa i tre spazi
SLP1	LD	A,32
	RST	10H
	DJNZ	SLP1

	LD	BC,$0E0B	; sposta cursore
	CALL	PPOS
	LD	H,0
	LD	L,(IX+12)	; Legge LINES	
	CALL	PRTNUM

	LD	BC,$0B0B	; sposta cursore
	CALL	PPOS
	LD	H,0
	LD	L,(IX+15)	; Legge LEVEL	
	CALL	PRTNUM		; scrive livello

	LD	BC,$140B	; sposta cursore
	CALL	PPOS
	LD	HL,(SCORE1)	; punta a SCORE1

PRTNUM	LD	E,32		; flag di stampa numeri
	PUSH	DE
	PUSH	HL
	LD	BC,-10000
	CALL	$192A		; routine di stampa numeri
	JP	$1A30		; con ritorno indiretto



;*******************************
; incrementa il punteggio di DE
;

SCRFN	LD	HL,(SCORE1)	; carica il punteggio
	ADD	HL,DE		; somma	il nuovo punteggio
	LD	(SCORE1),HL	; rimette a posto il risultato
	RET

	

;*******************************
; Disegna nell'area "NEXT:"
; il prossimo pezzo o lo cancella
;

NEXTP	LD	(IX+7),0	; ORNT
	LD	BC,$1318	; coord. pezzo
	LD	HL,(NEXT)	; locaz. prox. pezzo
	LD	D,(IX+18)	; legge NCOL
	LD	E,(IX+6)	; legge CCOL
	LD	(IX+6),D	; scambia
	PUSH	DE
	CALL	PRNT2		; entrata secondaria di PRNT
	POP	DE
	LD	(IX+6),E	; scambia di nuovo
	LD	(IX+18),D
	RET



;*******************************
; Cancella area di gioco
; o riempe un area rettangolare
; che va da (H,L) a (D,E) con
; un carattere passato in MODE
; usando il colore ATTR_T
;

CLEAR	LD	(IX+8),32	; carattere blank (spazio)
	LD	DE,$1F18	; da (31,24)
	LD	HL,$010F	; a (0,15)
	XOR	A		; sfondo nero, penna nera
	LD	(ATTR_T),A	; memorizza in ATTR_T

CLR2	LD	C,D		; disegna area rettancolare
HLP2	LD	B,E
	PUSH	DE
VLP2	PUSH	HL
	CALL	PPOS
	POP	HL
	LD	A,(MODE)	; carattere blank (spazio)
	RST	10H
	DEC	B
	LD	A,B
	CP	H
	JR	NZ,VLP2
	POP	DE
	DEC	C
	LD	A,C
	CP	L
	JR	NZ,HLP2

	LD	A,%01000110	; sfondo nero, penna gialla chiara
	LD	(ATTR_T),A	; memorizza in ATTR_T

	RET



;*******************************
; Stampa o cancella un pezzo
; puntato da CRPZ nelle coord.
; (XPOS, YPOS) usando il colore CCOL
; oppure esegue hit test se MODE=1
;

PRNT	XOR	A		; azzera registri A o A'
	EX	AF,AF'
	LD	HL,(CRPZ)	; carica indirizzo del pezzo corrente
	LD	D,0		; prepara somma a 16 bit
	LD	E,(IX+7)	; con l'offset di direzione pezzo
	ADD	HL,DE		; somma

	LD	B,(IX+1)	; coordinata x attuale
	LD	C,(IX+0)	; coordinata y attuale
PRNT2	LD	A,(HL)		; legge la descrizione del pezzo
	CALL	CCORD		; calcola coorinate schermo -> HL

INIT	LD	C,2		; pezzo di due "righe"
CLOP	LD	B,4		; e quattro "colonne"

	PUSH	HL		; conserva il valore ad inizio riga
	
	BIT	0,(IX+7)	; controlla direz. in ORNT
	LD	DE,32		
	JR	NZ,LLOP		; imposta DE=1 se il bit=0, else DE=32
	LD	DE,1

LLOP	SRL	A		; scorre a dx. per leggere i bit
	JR	NC, BLNK	; se non c' carry non scrive niente
	EX	AF,AF'		; altrimenti conserva A
	
	BIT	0,(IX+8)	; MODO: 0=stampa, 1=hittest
	JR	Z,STMP
	
	OR	(HL)		; controlla i colori sullo schermo
	OR	A		; controlla se A=0 e setta il flag Z 
	JR	EXT1
	
STMP	LD	A,(IX+6)	; carica il colore attuale
	XOR	(HL)		; XOR con lo schermo
	LD	(HL),A		; disegna (o cancella) blocco
EXT1	EX	AF,AF'		; riprende A

BLNK	ADD	HL,DE		; avanza di col/rig sullo schermo
	DJNZ	LLOP		; loop interno
	
	POP	HL

	BIT	0,(IX+7)	; controlla direz. in ORNT
	LD	DE,32		
	JR	Z,EXT2		; imposta DE=32 se il bit=0, else DE=1
	LD	DE,1

EXT2	ADD	HL,DE		; avanza di col/rig sullo schermo

	DEC	C		; decrementa contatore ciclo
	JR	NZ,CLOP		; ciclo di colonna

	EX	AF,AF'
	LD	(IX+8),0	; resetta MODE
	RET



;*******************************
; Calcolo coordinate schermo
; memoria colori formula:
; $5800+C+32*B -> HL
; usa: HL, DE
;

CCORD	LD	H,0		; prepara posizione Y a 16 bit
	LD	L,B	
	DEC	L
	PUSH	BC

	LD	B,5		; moltiplica x 32
MULT	ADD	HL,HL
	DJNZ	MULT

	LD	DE,$5800	; inizio memoria colori
	ADD	HL,DE		; somma posizione X

	POP	BC
	LD	D,0		; prepara posizione X a 16 bit
	LD	E,C
	DEC	E	
	ADD	HL,DE		; somma X all'indirizzo
	RET



;*******************************
; Genera un pezzo casuale, copia
; il prossimo nell'attuale, e
; resetta ORNT
;

SETP	LD	(IX+7),0	; resetta ORNT (orientaz.)
	LD	HL,(NEXT)	; copia prossimo pezzo
	LD	(CRPZ),HL	; nel pezzo attuale
	LD	A,(NCOL)	; copia prossimo colore
	LD	(CCOL),A	; nel colore attuale

SETP2	LD	HL,(FRAMES)	; LSD della variabile FRAMES
	LD	A,H
	ADD	A,L
	AND	%00000111	; conserva solo i 3 LSB
	CP	7		; controlla
	JR	NZ,CNT		; se non  7 salta
	DEC	A		; 7->6
CNT	SLA	A		; moltiplica x4
	SLA	A

	LD	E,A		; prepara somma a 16 bit
	LD	D,0

	LD	HL,PZZO		; indirizzo primo pezzo
	ADD	HL,DE		; somma l'offset casuale
	LD	(NEXT),HL	; memorizza come prossimo pezzo

	SLA	A		; calcolo del colore
	ADD	A,8
	OR	%01000111	; sovrappone attributi (pen=7, bright)
	LD	(NCOL),A	; memorizza come prossimo colore
	RET



;*******************************
; Stampa una stringa puntata da
; HL. La struttura contiene:
; X, Y, lungh., "stringa"
;

PTSTR	PUSH	BC		; conserva (soprattutto) B
	LD	B,(HL)		; legge riga
	INC	HL
	LD	C,(HL)		; legge colonna
	PUSH	HL
	CALL	PPOS		; sposta cursore
	POP	HL
	INC	HL
	LD	B,0		; legge lunghezza stringa (max 255)
	LD	C,(HL)
	INC	HL
	EX	DE,HL
	CALL	STRING		; stampa stringa
	EX	DE,HL		; serve in caso di pi stringhe
	POP	BC
	RET



;*******************************
; Definizione variabili e pezzi
;

; *** inizio area variabili ***

XPOS	.BYTE	9		; 0) posizione X del pezzo
YPOS	.BYTE	1		; 1) posizione Y del pezzo
MXPO	.BYTE	9		; 2) posizione passata
CYCL	.BYTE	21		; 3) contatore di ciclo di ritardo
CRPZ	.BYTE	0,0		; 4) indirizzo base pezzo attuale
CCOL	.BYTE	0		; 6) colore pezzo attuale
ORNT	.BYTE	0		; 7) orientazione pezzo
MODE	.BYTE	0		; 8) modo: 0=stampa, 1=hittest
SCORE1	.BYTE	0,0		; 9) punti (max 65535)
TIMLV	.BYTE	21		; 11) tempo di livello
LINLV	.BYTE	4		; 12) linee di livello
LIVES	.BYTE	3		; 13) vite
LINES	.BYTE	0		; 14) linee nella pila
LEVEL	.BYTE	1		; 15) livello attuale
NEXT	.BYTE	0,0		; 16) puntatore prossimo pezzo
NCOL	.BYTE	79		; 18) colore prossimo pezzo


DEFVAR	.BYTE	9,1,9,2,21,0,0,0,0,0,0,21,4,3,0,1


; *** Descrizione pezzi ***

PZZO	.BYTE	%00110011	; pezzo tipo O
	.BYTE	%00110011
	.BYTE	%00110011
	.BYTE	%00110011

PZZT	.BYTE	%01110010	; pezzo tipo T
	.BYTE	%01110010
	.BYTE	%00100111
	.BYTE	%00100111

PZZJ	.BYTE	%00010111	; pezzo tipo J
	.BYTE	%01000111
	.BYTE	%01110100
	.BYTE	%01110001

PZZL	.BYTE	%01110001	; pezzo tipo L
	.BYTE	%01110100
	.BYTE	%01000111
	.BYTE	%00010111

PZZS	.BYTE	%00110110	; pezzo tipo S
	.BYTE	%01100011
	.BYTE	%00110110
	.BYTE	%01100011

PZZZ	.BYTE	%01100011	; pezzo tipo Z
	.BYTE	%00110110
	.BYTE	%01100011
	.BYTE	%00110110

PZZI	.BYTE	%00001111	; pezzo tipo I
	.BYTE	%00001111
	.BYTE	%00001111
	.BYTE	%00001111


; *** Tabella delle stringhe ***

SSCORE	.BYTE	21, 11, 6, "Score:"
SLIVES	.BYTE	18, 11, 6, "Lives:"
SLINES	.BYTE	15, 11, 6, "Lines:"
SLEVEL	.BYTE	12, 11, 6, "Level:"
SNEXT	.BYTE	9, 11, 6, "Next: "
SPAUS	.BYTE	3, 11, 6, "Pause!"
BLANK	.BYTE	3, 11, 6, "      "
SPALL	.BYTE	17, 9, 3, 145,145,145
MSG1	.BYTE	21, 29, 12, "** TETRIS **"
MSA1	.BYTE	19, 24, 2, "by"
MSA2	.BYTE	17, 29, 13, "A. Di Stefano"
MSA3	.BYTE	16, 29, 11, "I. Pannizzo"
MSA4	.BYTE	15, 29, 8, "S. Benzi"
MSA5	.BYTE	14, 29, 11, "F. Agostaro"
MSG2	.BYTE	11, 29, 11, "Press a key"
MSK1	.BYTE	8, 30, 9, "Keyboard:"
MSK2	.BYTE	7, 30, 13, "A/D: move L/R"
MSK3	.BYTE	6, 30, 13, "SPACE: rotate"
MSK4	.BYTE	5, 30, 11, "ENTER: down"
MSK5	.BYTE	4, 30, 8, "P: pause"
MSK6	.BYTE	3, 30, 13, "Q: abort game"
MSG4	.BYTE	15, 28, 10, "Game Over!"
MSG5	.BYTE	15, 30, 14, "Level complete"
MSG6	.BYTE	13, 30, 14, "GREAT CHAMPION"
MSG7	.BYTE	16, 30, 14, "Game complete!"


; *** Descrizione UDGs ***

UDG0	.BYTE	%11111011	; carattere 0: mattone
	.BYTE	%11111011
	.BYTE	%00000000
	.BYTE	%11011111
	.BYTE	%11011111
	.BYTE	%11011111
	.BYTE	%00000000
	.BYTE	%11111011

UDG1	.BYTE	%00000000	; carattere 1: pallina
	.BYTE	%00000000
	.BYTE	%00111100
	.BYTE	%01111110
	.BYTE	%01111110
	.BYTE	%00111100
	.BYTE	%00000000
	.BYTE	%00000000

.END
