; *** 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 : 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 : 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