====== Fatal 15 ====== # Programma gioco del 15 .data # Segmento dati #/--------------------------------------------------------------/ # Stringhe generiche nordi: .asciiz "Hai perso,riprova!\n" ordi: .asciiz "Congratulazioni... HAI VINTO!!\n" gd15: .asciiz "Fatal Fifteen!" # il nome del gioco spazio: .asciiz " " #viene utilizzata per la grafica spazi5: .asciiz " " #idem istruz: .asciiz "Istruzioni del gioco:\n\nIl gioco consiste nello spostare le \ncaselle in modo da riordinare i numeri\ndella scacchiera.\n\nGli spostamenti vengono gestiti con le\nfrecce cursore del tastierino numerico.\n\nPer giocare attivare il tasto BLOCNUM\nPer uscire dal gioco premere ESC.\n" press: .asciiz "Premere un tasto per continuare..." mosse: .asciiz "Mosse:" perso: .asciiz"GAME OVER!\n\n Ritenta,la prossima volta sarai piu'\n fortunato(forse).\n\n" vinto: .asciiz "Congratulazioni,hai risolto il gioco!\n\n" info: .asciiz " Fatal fifteen (il gioco del 15 )\n Realizzato per il corso di:\n Laboratorio di informatica:\n\t Architettura.\n\n Gruppo di laboratorio numero 6:\n Barbara Bresci\n Flavio Casadei D.C.\n Marco Nenciarini.\n" #/--------------------------------------------------------------/ # Stringhe ANSI hansi: .byte 27, 91, 0 # i numeri 27 e 91 sono i caratteri # esc e parentesi rispettivamente # e vengono utilizzati per inizializzare # le sequenze ansi modo1: .asciiz "=1h" # modalità testo a colori 40*25 modo3: .asciiz "=3h" # modalità testo a colori 80*25 acca: .asciiz "H" # utilizzata per la gotoxy pev: .asciiz ";" # idem clrscr: .asciiz "2J" # cancella schermo noatt: .asciiz "0m" # attributi disattivati lamp: .asciiz "5m" # lampeggio # colori sfondo sfonero: .asciiz "40m" sforosso: .asciiz "41m" sfoverde: .asciiz "42m" sfobianco: .asciiz "47m" sfoblu: .asciiz "44m" # colori caratteri colnero: .asciiz"30m" colrosso: .asciiz"31m" colbianco: .asciiz "37m" colblu: .asciiz"34m" #/--------------------------------------------------------------/ # buffers .align 2 tabella: .space 16 # il vettore che contiene la scacchiera currblank: .word 0 # posizione della casella vuota ## costanti per la generazione di numeri random Z: .word 1103515245 # Base L: .word 1 # Seed I: .word 24337 # Incremento M: .word 65535 # Divisore # Segmento CODICE .text #/--------------------------------------------------------------/ ############################################### ## procedura write ## riceve in $a0 l'indirizzo della locazione di memoria a partitre ## dalla quale si trova la stringa che si desidera stampare ############################################### write: li $v0,4 #codice operativo per la stampa delle stringhe syscall # viene effettuata la chiamata di sistema j $ra #/--------------------------------------------------------------/ ############################################### ## procedura write_ansi ## riceve in $a0 l'indirizzo di memoria a partire dal quale si trova ## una stringa da stampare. ## Prima di stampare la stringa inizializza le sequenze di caratteri ansi ############################################### write_ansi: addi $sp, $sp, -8 sw $ra, 8($sp) sw $a0, 4($sp) la $a0, hansi # inizializzazione ansi jal write lw $a0, 4($sp) jal write lw $ra, 8($sp) addi $sp, $sp, 8 jr $ra #/--------------------------------------------------------------/ ############################################### ## procedura gotoxy ## riceve in $a1 ed in $a2 due valori numerici che indicano ## il punto dello schermo nel quale si desidera posizionare ## il cursore ############################################### gotoxy: addi $sp, $sp, -4 sw $ra, 4($sp) li $v0, 4 la $a0, hansi # inizializza la sequenza ansi syscall li $v0, 1 # chiamata di sistema per la stampa di un numero move $a0, $a2 # ordinata del cursore syscall la $a0, pev # carattere ';' jal write li $v0, 1 move $a0, $a1 # ascissa del cursore syscall la $a0, acca # lettera 'H' jal write lw $ra, 4($sp) addi $sp, $sp, 4 j $ra #/--------------------------------------------------------------/ ############################################### ## procedura clear_screen ## questa procedura cancella il contenuto dello schermo ############################################### clear_screen: addi $sp, $sp, -4 sw $ra, 4($sp) la $a0, clrscr jal write_ansi lw $ra, 4($sp) addi $sp, $sp, 4 jr $ra #/--------------------------------------------------------------/ ############################################### ## procedura schermo_blu ## questa procedura colora lo schermo di blu ############################################### schermo_blu: addi $sp, $sp, -4 sw $ra, 4($sp) la $a0, sfoblu jal write_ansi jal clear_screen lw $ra, 4($sp) addi $sp, $sp, 4 jr $ra #/--------------------------------------------------------------/ ############################################### ## procedura contorno ## traccia la cornice attorno alla scacchiera ############################################### contorno: addi $sp, $sp, -4 sw $ra, 4($sp) la $a0, sfonero # colore del contorno jal write_ansi li $a1, 10 # ascissa iniziale li $t0, 31 # ascissa finale la $t1, spazio # è il carattere ' ' che serve per colorere l'area forx: # traccia due linee orizzontali parallele a partire da $a1 fino a $t0 di ordinate $a2=3 e $a2=24 # disegnando un quadratino alla volta bgt $a1, $t0, exit_for_x # esco quando $a1 > $t0 li $a2, 3 # ordinata prima linea jal gotoxy # si posiziona alla coordinate ($a1,$a2) move $a0, $t1 jal write # stampa ' ' li $a2, 24 # ordinata seconda linea jal gotoxy move $a0, $t1 jal write addi $a1, $a1, 1 # incrementa l'ascissa sel cursore j forx exit_for_x: li $a2 3 # ordinata iniziale li $t0 24 # ordinata finale fory: # traccia due linee verticali parallele partire da $a2 fino a $t0 di ordinate $a1=10 e $a1=31 # disegnando un quadratino alla volta bgt $a2, $t0, exit_for_y #come sopra li $a1, 10 jal gotoxy move $a0, $t1 jal write li $a1, 31 jal gotoxy move $a0, $t1 jal write addi $a2, $a2, 1 j fory exit_for_y: lw $ra, 4($sp) addi $sp, $sp, 4 j $ra #/--------------------------------------------------------------/ ############################################### ## procedura resetscreen ## Questa procedura riporta lo schermo alla sua configurazione iniziale ## togliendo gli attributi a tutti i caratteri e ripristinando la modalità video ############################################### resetscreen: addi $sp, $sp, -4 # viene aggiustato lo stack sw $ra, 4($sp) # vengono salvati i registri la $a0, noatt # cancella gli attributi ai caratteri jal write_ansi la $a0, modo3 # modo testo 80x25 jal write_ansi jal clear_screen #ripulisce lo schermo lw $ra, 4($sp) # ripristino dei registri addi $sp, $sp, 4 # riposizionamento dello stack j $ra #/--------------------------------------------------------------/ ############################################### # Procedura Quadratino # Ricevce 3 parametri: # $a1 = x (ascissa) # $a2 = y (ordinata) # $a3 = numero (16 = casella vuota) # Traccia un quadrato di lato 5 a partire dalle coordinate (x, y), # e stampa il relativo numero. ############################################### quadratino: addi $sp, $sp, -4 sw $ra, 4($sp) ## Prima fase:selezione del colore del quadratino if_color: li $t0, 16 # la casella vuota la $a0, sfonero # imposta il colore nero ##Se il numero ricevuto in $a3 è 16 (casella vuota) allora il quadratino è di ##colore nero beq $t0, $a3, endif_color else_color: ## begin ## il numero non rappresenta la casella vuota quindi il colore viene scelto in base ## alla seguente formula colore :=((((numero - 1) div 4) + numero))mod 2) ## dove colore($a3) può prendere valore 0 oppure 1 ## implementazione della formula addi $t0, $a3, -1 # numero -1 srl $t0, $t0, 2 # (numero -1) div 4 add $t0, $t0, $a3 # ((numero - 1) div 4) + numero) andi $t0, $t0, 1 # ((numero - 1) div 4) + numero)mod 2) # fine selezione del colore la $a0, sfobianco # colore impostato a bianco if_color2: beqz $t0, endif_color2 # se $t0 <> 0 allora colore:=rosso la $a0, sforosso # colore impostato a rosso endif_color2: ## end # j endif_color endif_color: jal write_ansi # assegna il colore al quadratino ## inizio seconda fase: colora il quadratino addi $t0, $a2, 5 # 5 linee orizzontali for_sfondo: ## disegna 5 spazi colorato adiacenti su 5 righe partendo dalla riga indicata da $a2 fino ## a quella indicata da $a2 + 5 bge $a2, $t0, endfor_sfondo jal gotoxy la $a0, spazi5 # 5 spazi colorati jal write addi $a2, $a2, 1 j for_sfondo endfor_sfondo: ## inizio terza fase: stampa del numero addi $a1, $a1, 2 # viene posizionato il cursore per stampare il numero addi $a2, $a2, -3 jal gotoxy la $a0, colnero # il numero è di colore nero jal write_ansi li $v0, 1 move $a0, $a3 syscall # stampa il numero lw $ra 4($sp) addi $sp $sp 4 j $ra #/--------------------------------------------------------------/ ############################################### # procedura stampa_tabella # Stampa la scacchiera del gioco leggendo i valori in memoria ############################################### stampa_tabella: addi $sp, $sp, -16 sw $ra 16($sp) li $t0, 0 li $a2, 4 # ordinata di partenza per la scacchiera li $t2,24 # ordinata finale per la scacchiera for_y: bge $a2, $t2, endfor_y # esco se $a2 >= $t2 li $a1, 11 # ascissa iniziale li $t1, 31 # ascissa finale for_x: bge $a1, $t1, endfor_x # esce se $a1 >= $t1 lb $a3, tabella($t0) # carica il numero da stampare sulla casella sw $a1, 12($sp) sw $t0, 8($sp) sw $a2, 4($sp) jal quadratino lw $a2, 4($sp) lw $a1, 12($sp) lw $t0, 8($sp) addi $t0, $t0, 1 addi $a1, $a1, 5 j for_x endfor_x: addi $a2 $a2 5 j for_y endfor_y: la $a0, colbianco jal write_ansi la $a0, sfonero jal write_ansi lw $ra, 16($sp) addi $sp, $sp, 16 j $ra #/--------------------------------------------------------------/ ############################################### ##Procedura schermata finale ## A seconda del valore di $s1 (vettore ordinato) stampa un messaggio ############################################### schermata_finale: addi $sp, $sp, -4 sw $ra, 4($sp) la $a0, sforosso ## selezione colore di sfondo test_win1: beqz $s1, end_test_win1 la $a0, sfoverde end_test_win1: jal write_ansi la $a0, spazio jal write jal clear_screen ## selezione messaggio li $a1, 15 li $a2, 2 jal gotoxy la $a0, perso test_win2: beqz $s1, end_test_win2 li $a1, 2 li $a2, 2 jal gotoxy la $a0, vinto end_test_win2: jal write la $a0, info jal write lw $ra, 4($sp) addi $sp, $sp, 4 jr $ra #/--------------------------------------------------------------/ ############################################### # Procedura Schermata Iniziale # Stampa una schermata di introduzione al gioco ############################################### schermata_iniziale: addi $sp, $sp, -4 sw $ra, 4($sp) la $a0, modo1 jal write_ansi # Modalità video 40x25 li $a1, 10 li $a2, 3 jal gotoxy la $a0, istruz # Stampa le istruzioni jal write li $a1, 3 li $a2, 16 jal gotoxy la $a0, lamp jal write_ansi # Stampa premi un tasto (lampeggiante) la $a0, press jal write la $a0, noatt jal write_ansi # Ripristina gli attributi lw $ra, 4($sp) addi $sp, $sp, 4 jr $ra #/--------------------------------------------------------------/ ############################################### ## procedura ordinato ## questa procedura restituisce in $s1 un valore numerico: ## 1 se la scacchiera (il vettore tebella) è ordinata ## 0 altrimenti. ############################################### ordinato: li $s1, 1 # all'inizio si suppone che il vettore sia gi… ordinato li $t0, 0 # indice che scorre il vettore inizializzato a 0 li $t1, 15 # numero di elementi del vettore while_ord: bge $t0, $t1, endw_ord # if indice >= numero elementi beqz $s1, endw_ord # and ordinato(tabella) lbu $t2, tabella($t0) # carico l'elemento di indice $t0 in $t2 addi $t3, $t0, 1 # $t3 = $t0+1 if_ordin: beq $t3, $t2, increm # se $t2 <> $t3 allora la scacchiera non è ordinata else_ord: li $s1, 0 # $s1 prende 0 dato che una casella Š fuori posto increm: addi $t0, $t0, 1 j while_ord endw_ord: j $ra #/--------------------------------------------------------------/ ############################################### ## procedura inittab ## inizializza la scacchiera con i numeri da 1 a 15, mentre ## la casella vuota è rappresentata dal numero 16. ############################################### inittab: li $t0, 0 # contatore che scandisce il vettore li $t1, 15 # posizione massima dell'indice for_init: bgt $t0, $t1, exit_for_init # esco quando ho completato l'inserimento del vettore addi $t2, $t0, 1 # $t2 = $t0+1 sb $t2, tabella($t0) # viene memorizzato il valore di $t2 nella # casella corrispondente a $t0 addi $t0, $t0, 1 # $t0 viene incrementato di un unita' j for_init exit_for_init: li $t0, 15 # posizione corrente casella vuota! sw $t0, currblank # memorizzata in currblank jr $ra #/--------------------------------------------------------------/ ############################################### # Procedura Swap # swap riceve due indici della tabella $a2, $a3 e scambia # il loro contenuto ############################################### swap: lb $t0, tabella($a3) lb $t1, tabella($a2) beq $t0, $t1, fine_swap # se gli indici sono uguali non eseguo niente sb $t1, tabella($a3) # scambia il contenuto dei registri sb $t0, tabella($a2) li $t2, 16 #se uno di due registri è la posizione della casella vuota # è necessario riaggiornare la sua posizione if_vuoto1: bne $t1, $t2, else_vuoto1 # se $t1 è la casella vuota sw $a3, currblank # mette $a3 in currblank (posizione casella vuota) j endif_vuoto1 else_vuoto1: if_vuoto2: bne $t0, $t2, endif_vuoto2 # (idem) sw $a2, currblank # j endif_vuoto2 endif_vuoto2: endif_vuoto1: fine_swap: jr $ra #/--------------------------------------------------------------/ ############################################### # Procedura direzioni # riceve in $a1 un numero che rappresenta una delle 4 direzioni possibili # su giù destra sinistra # restituisce in $v1 0 se non Š possibile eseguire quelle mossa # 1 altrimenti ############################################### direzioni: li $v1, 1 # Š possibile spostarsi lw $t2, currblank case_dir: la $t0, case_dir_of sll $t1, $a1, 2 add $t1, $t0, $t1 jr $t1 case_dir_of: b su #0 b giu #1 b destra #2 b sinistra #3 su: li $t1, 3 # se la posizione della casella vuota è minore di 3 non è possibile # spostarsi bgt $t2, $t1, end_su li $v1, 0 end_su: j end_case_dir giu: li $t1, 12 # se la posizione della casella vuota è maggiore o uguale a 12 # non è possibile eseguire questo spostamento blt $t2, $t1, end_giu li $v1, 0 end_giu: j end_case_dir destra: andi $t1, $t2, 3 # currblank mod 4 li $t2, 3 bne $t1, $t2, end_destra # Se la posizione della casella vuota è congrua 3 modulo 4 # non si può andare a destra li $v1, 0 end_destra: j end_case_dir sinistra: andi $t1, $t2, 3 # currblank mod 4 # se la posizione della casella vuota è un multiplo # di 4 non si può andare a sinistra bnez $t1, end_sinistra li $v1, 0 end_sinistra: # j end_case_dir end_case_dir: jr $ra #/--------------------------------------------------------------/ ############################################### # Procedura waitcommand # Aspetta fino a che il giocatore non da # un comando valido, e lo # restituisce in $v0 ############################################### waitcommand: repeat_until_valid: li $t2, 1 # Tasto valido repeat_until_keypressed: lw $t0, 0xffff0000 # receiver control andi $t0, $t0, 1 # least significant bit beqz $t0, repeat_until_keypressed # a questo punto è stato premuto un tasto if_key: lw $t0, 0xffff0004 # receiver data (tasto letto) # viene confrontato il tasto letto con i tasti predefiniti per il gioco li $t1, 50 # freccia giu beq $t0, $t1, press_giu li $t1, 52 # freccia destra beq $t0, $t1, press_sin li $t1, 54 # freccia sinistra beq $t0, $t1, press_des li $t1, 56 # freccia su beq $t0, $t1, press_su li $t1, 27 # tasto esc beq $t0, $t1, press_esc tasto_errato: li $t2, 0 j endif_key press_giu: li $v0, 0 j endif_key press_su: li $v0, 1 j endif_key press_sin: li $v0, 2 j endif_key press_des: li $v0, 3 j endif_key press_esc: li $v0, 4 # j endif_key endif_key: beqz $t2, repeat_until_valid # Attende un tasto valido jr $ra #/--------------------------------------------------------------/ ############################################### #Random: # restituisce in $v0 un numero pseudocasuale fra # 0 e ($a0 - 1) compresi ############################################### random: lw $t0, Z # Base lw $t1, L # Seme lw $t2, I # Incremento lw $t3, M # divisore mul $t1, $t1, $t0 # seme:=seme*base add $t1, $t1, $t2 # seme:=seme+incremento divu $t1, $t3 # seme:=abs(seme mod divisore) mfhi $t1 sw $t1, L # memorizza il seme divu $t1, $a0 # $v0:=(seme mod $a0) mfhi $v0 j $ra ############################################### #initrand: # inizializza i numeri casuali con seed $a0 ############################################### initrand: sw $a0 , L # Salva il seme in L j $ra #/--------------------------------------------------------------/ ############################################### ## Perocedura permuta: ## Permuta le caselle della scacchiera in modo da otenere una configurazioen ## casuale. ############################################### permuta: addi $sp, $sp, -12 sw $ra, 4($sp) sw $s0, 8($sp) sw $s1, 12($sp) li $t0, 0 key_pressed: lw $t1, 0xffff0000 # receiver control addiu $t0, $t0, 1 # contatore che funge da temporizzatore andi $t1, $t1, 1 beqz $t1, key_pressed lw $t1, 0xffff0004 # tasto letto li $t2, 35 # trucchetto!!! if_tasto_segreto: beq $t1, $t2,endif_tasto_segreto # se viene premuto il tasto segreto, la scacchiera # non viene permutata else_tasto_segreto: move $a0, $t0 jal initrand # inizializza il seme con $a0 li $s0, 64 # mischia la scacchiera 64 volte (deve essere un numero pari) # Questo accorgimento permette di ottenere un numero di configurazioni # irrisolvibili limitato, in quanto ad ogni ciclo scambia un numero pari con uno dispari, quindi facendolo # un numero pari di volte la configurazione finale è risolvibile a # meno di eccezioni:vedi relazione. for_random: beqz $s0, endfor_random li $a0,8 # $a0 prende 8 jal random sll $s1, $v0 ,1 # $s1 ( primo numero casuale)=$v0 (risultato della random)*2 jal random sll $a2, $v0, 1 # come sopra ( $a2 è il secondo numero casuale) addi $a2, $a2,1 # $a2:=$a2+1 move $a3, $s1 jal swap # scambia i due indici addi $s0, $s0, -1 j for_random endfor_random: endif_tasto_segreto: lw $s1, 12($sp) lw $s0, 8($sp) lw $ra, 4($sp) addi $sp, $sp, 12 jr $ra #/--------------------------------------------------------------/ ############################################### ## Procedura gioco # $s1 contiene un valore che indica se la tabella e' ordinata # currblank contiene la posizione della casella vuota nel vettore # $v1 indica, a seconda della posizione della casella vuota, se lo spostamento scelto # e' possibile # $a1 mossa scelta: 0 sposta la casella vuota in alto # 1 sposta la casella vuota in basso # 2 sposta la casella vuota a destra # 3 sposta la casella vuota a sinistra # $s2 exit # indica se la procedura deve terminare per uno dei seguenti motivi # La tabella è ordinata # il giocatore ha premuto esc ## NOTA: gli spostamenti della casella vuota non vengono gestiti direttamente dalla pressione dei tasti freecia ## ma dalla procedura waitcommnd che trasforma tali tasti ( incluso l'escape ) in una mossa valida ############################################### gioco: addi $sp, $sp, -8 sw $s0, 4($sp) sw $ra, 8($sp) li $s0, 0 # numero mosse li $s2, 0 # exit = false li $s1, 0 # tabella non ordinata li $s4, 1000 # numero massimo di mosse while_: # While (not exit) and (not ordinato) and (mosse<1000) do bnez $s2, endwhile_ # se exit=true esce ble $s4, $s0, endwhile_ # se ho esaurito le mosse esce bnez $s1, endwhile_ # se la tabella è ordinata esce do_: la $a0, sfoblu jal write_ansi li $a1, 33 # stampa il numero di mosse li $a2, 4 jal gotoxy la $a0, mosse jal write li $a1, 35 li $a2, 5 jal gotoxy li $v0, 1 move $a0, $s0 syscall li $a1, 0 # posiziona il cursore in un area vuota li $a2, 23 jal gotoxy jal waitcommand # attende la pressione di un comando valido move $a1, $v0 # mossa li $t0, 4 # mossa=uscita if_carattere: blt $a1 $t0 else_if_carattere # se non e'stato premuto esc allora il gioco puo'continuare li $s2 1 # viene impostato $s2 ad 1 (uscita:=true) j endif_carattere else_if_carattere: jal direzioni # valuta se è possibile la mossa contenuta in $a1 if_mossa_possibile: beqz $v1 endif_mossa_possibile # Se la mossa non e'possiblie salta else_mossa_possibile: # calcolo della casella da scambiare lw $a2, currblank # mette in $a2 la posizione della casella vuota la $t0 case_swap # indirizzo della tabella case sll $t1 $a1 2 # l' indice ( la mossa ) e' moltiplicato per 4 perche' le istruzioni # distano di 4 bytes add $t1 $t1 $t0 # in $t1 sommiamo l'indirizzo della tabella con l'indice jr $t1 # salta all'istruzione adeguata case_swap: b swap_su # viene scambiata la casella vuota con quella in alto b swap_giu # viene scambiata la casella vuota con quella in basso b swap_sx # viene scambiata la casella vuota con quella a sinistra b swap_dx # viene scambiata la casella vuota con quella a destra ## Calcolo indici per lo swap swap_su: addi $a3 $a2 -4 j end_case_swap swap_giu: addi $a3 $a2 4 j end_case_swap swap_sx: addi $a3 $a2 1 j end_case_swap swap_dx: addi $a3 $a2 -1 # j end_case_swap end_case_swap: jal swap # scambio delle caselle addi $s0, $s0, 1 # una mossa in più jal stampa_tabella jal ordinato # controllo ordinamento della tabella endif_mossa_possibile: endif_carattere: j while_ endwhile_: lw $s0, 4($sp) lw $ra, 8($sp) addi $sp $sp 8 jr $ra #/--------------------------------------------------------------/ #/--------------------------------------------------------------/ #/--------------------------------------------------------------/ .globl main main: jal inittab jal schermata_iniziale jal permuta jal clear_screen jal schermo_blu li $a1 13 li $a2 1 jal gotoxy la $a0 gd15 jal write jal contorno jal stampa_tabella jal gioco jal schermata_finale jal waitcommand jal resetscreen li $v0, 10 syscall