;AETC Pràctica: Quinze-Puzle ;Cognoms i Nom: Virgili Llop, Joaquim ; ;Lliurar el fitxer amb el nom: cognom1_cognom2_nom.asm ;Data de lliurament: ---------- .model small .stack 1024 DOSSERVICE EQU 21H ; serveis DOS BIOSSERVICE EQU 10H ; serveis BIOS TicksSegon EQU 18 ; Ticks per segon VECT_R EQU 08h ; vector de rellotge .data Tauler db 'Tauler de Joc' ;Tauler de joc que es mostra a l'inici del programa db ' 0 1 2 3 ' db ' ÚÄÂÄÂÄÂÄ¿ ' db ' 0³ ³ ³ ³ ³ ' db ' ÃÄÅÄÅÄÅÄ´ ' db ' 1³ ³ ³ ³ ³ ' db ' ÃÄÅÄÅÄÅÄ´ ' db ' 2³ ³ ³ ³ ³ ' db ' ÃÄÅÄÅÄÅÄ´ ' db ' 3³ ³ ³ ³ ³ ' db ' ÀÄÁÄÁÄÁÄÙ ' db '00 Moviments' db '00 Segons ' ;Quan lliureu la pràctica deixeu la matriu inicialitzada amb aquests valors. Fitxa db 'ABCD' ;Matriu 4x4 on tenim les fitxes db 'FGHI' db 'JK Z' db 'OPNM' Tecla db 0 ; Variable per emmagatzemar la tecla pitjada Estat db 1 ; 0: Hem pitjat la tecla 'q' per sortir, sortir del programa. ; 1: Continuem jugant. ; 2: Guanyat, Totes les lletres estan ordenades, indicar-ho i acabar. ; 3: Temps exhaurit, indicar-ho i acabar. ; 4: Nombre de moviments exhaurits, indicar-ho i acabar ; 5: Masses caselles en blanc, indicar-ho i acabar. ;FilCur i ColCur indicaran la posició del cursor en tot moment. FilCur db ? ; '0': Fila 0, '3': Fila 3 ColCur db ? ; '0': Columna 0, '3': Columna 3 FilPos db ? ; Posició de la Fila 0 en pantalla ColPos db ? ; Posició de la Columna 0 en pantalla FilEspai db ? ; Posició de l'Espai en la fitxa, Fila ColEspai db ? ; Posició de l'Espai en la fitxa, Columna Moviments db 99 ; Moviments que podem realitzar. Ticks db 0 ;variable que indica els Ticks de rellotge transcorreguts Segons dw 0 ;variable per comptar els segons RAI_R_Seg dw ? ; adreça de segment de la RAI de rellotge original RAI_R_Dir dw ? ; desplaçament dins del segment de la RAI de rellotge original MsgPerdut5 db 'Tauler incorrecte, masses caselles en blanc.','$' MsgPerdut4 db 'Has perdut! Moviments exhaurits','$' MsgPerdut3 db 'Has perdut! Temps exhaurit ','$' MsgGuanya db 'Ho has aconseguit, tot ordenat!','$' MsgSortir db 'Has premut (q) per sortir ','$' .code ;******************************************************************************* ; Esborra la pantalla completament escrivint espais en blanc ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* EsborraPantalla: push AX push BX push CX push DX ;Inicialització per posicionar el cursor mov bh,00h ;pagina mov dh,00h ;fila inicial mov dl,00h ;columna inicial mov ah,02h ;servei per posicionar el cursor int BIOSSERVICE mov bh,00h ;pagina mov bl,07h ;atribut mov cx,2000 ;80*25nombre de caracters a escriure mov al,' ' ;caracter mov ah,09h ;servei per escriure caracter. int BIOSSERVICE pop DX pop CX pop BX pop AX Ret ;******************************************************************************* ; Mostra el Tauler de joc sense Dades, és a dir, mostra la matriu Tauler ; ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* MostraTauler: push AX push BX push CX push DX push SI ;Inicialitzar registres mov bh,00h ;pagina mov dh,06 ;fila inicial mov dl,33 ;columna inicial mov bl,07h ;atribut mov cx,1 ;escriurem 1 caracters cada cop mov si,0 ;index per accedir al tauler bucleMT: mov ah,02h ;servei per posicionar el cursor int BIOSSERVICE mov al,Tauler[SI] ;caracter mov ah,09h ;servei per escriure caracter. int BIOSSERVICE inc si ;incrementem l'índex per accedir a les dades ;Actualitzem posició del cursor (fila i columna) inc dl ;columna cmp dl, 46 ;Quan arribem a la columna 46 ens posem a l'inici jl bucleMT ;de la següent fila. mov dl, 33 inc dh ;fila cmp dh, 19 ;Quan arribem a la fila 19 vol dir que hem acabat jl bucleMT ;de dibuixar el tauler. pop SI pop DX pop CX pop BX pop AX ret ;******************************************************************************* ; Buscar posició Inicial per començar a jugar. ; Buscar on hi ha l'espai en blanc i verifica que només hi hagi un espai en ; blanc a la matriu Fitxa. Si només hi ha un espai en blanc posiciona el cursor ; en aquella casella. Si hi ha més d'un o cap espai en blanc posem Estat=5 per acabar. ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* PosIni: push AX push BX push CX push DX push SI push DI mov si,0 mov di, 0 mov dh, 0 mov dl, 0 bucleIni: ;Bucle de lectura d'espais en la fitxa mov al,Fitxa[SI] cmp al, 32 ; comprobar si el caràcter de la fitxa que s'està llegint és un espai jne bucleContinua inc di mov [FilEspai], dh ; si ho és en guardarem la posició mov [ColEspai], dl bucleContinua: inc si cmp dl, 3 jge ColSet inc dl jmp ContinuaCol ColSet: inc dh mov dl, 0 ContinuaCol: cmp si, 16 jl bucleIni cmp di, 1 je bucleb ;comprobar que només hi ha un espai mov [Estat], 5 ;sino posem Estat = 5 i sortim de la subrutina jmp buclec bucleb: ; dibuixem cursor mov dh, [FilEspai] mov dl, [ColEspai] mov [FilCur], dh mov [ColCur], dl mov [FilPos], 9 mov [ColPos], 36 mov bh,00h mov bl,07h mov dh,[FilPos] mov dl,[ColPos] add dh,[FilCur] add dl,[ColCur] add dh,[FilCur] add dl,[ColCur] mov ah, 02h int BIOSSERVICE buclec: pop DI pop SI pop DX pop CX pop BX pop AX ret ;******************************************************************************* ; Actualitza el contingut del Tauler de Joc amb les dades de la matriu Fitxa ; i mostra els moviments que podem realitzar a pantalla cridant a la subrutina ; MostraDigits. El cursor ha de quedar en el mateix lloc que estava. ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* ActualitzaTauler: push ax push bx push cx push dx push si mov dh,[FilPos] mov dl,[ColPos] mov bh,00h mov bl,07h mov cx,1 mov si,0 bucleAT: ;Bucle d'actualització de la fitxa en el tauler mov ah,02h int BIOSSERVICE mov al,Fitxa[SI] mov ah,09h int BIOSSERVICE inc si add dl, 2 cmp dl, 44 jl bucleAT mov dl, 36 add dh, 2 cmp dh, 17 jl bucleAT lea bx, Moviments ; Enviem els moviments i la posició on mostrarlos en pantalla push bx mov dl, 33 mov dh, 17 push dx Call MostraDigits pop dx pop bx mov bh,00h mov bl,07h mov dh,[FilPos] mov dl,[ColPos] add dh,[FilCur] add dl,[ColCur] add dh,[FilCur] add dl,[ColCur] mov ah, 02h int BIOSSERVICE ; Posicionem cursor pop si pop dx pop cx pop bx pop ax ret ;******************************************************************************* ; Converteix un valor decimal (entre 0 i 99) en dos caràcters ASCII. ; S’ha de dividir el valor entre 10, el quocient representarà les desenes i ; el residu les unitats, després s’han de convertir a ASCII. ; Mostra els 2 digits ASCII a la posició de pantalla indicada a través de la pila. ; El valor i la posició es passen per la pila. ; Paràmetres d'entrada: valor [bp+6] i posició (fila,columna) [bp+4] ; Paràmetres de sortida: cap ;******************************************************************************* MostraDigits: push bp mov bp,sp push ax push bx push cx push dx push si mov dx,[bp+4] mov cx, 1 mov bh,00h mov bl,07h mov ah,02h int BIOSSERVICE mov si, 0 mov bx,[bp+6] mov al,[BX][SI] mov ah, 0 mov cl, 10 div cl mov si, dx mov dh, ah add al, 48 ; obtenim la primera xifra en ascii mov bh,00h mov bl,07h mov cx, 1 mov ah,09h int BIOSSERVICE mov al, dh add al, 48 ;obtenim la segona xifra en ascii mov dx, si inc dl mov ah,02h int BIOSSERVICE mov ah,09h int BIOSSERVICE mov dh,[FilPos] mov dl,[ColPos] add dh,[FilCur] add dl,[ColCur] add dh,[FilCur] add dl,[ColCur] mov ah, 02h int BIOSSERVICE ;posicionem cursor pop si pop dx pop cx pop bx pop ax pop bp ret ;******************************************************************************* ; Llegim una tecla utilitzant el servei 08h de les crides al DOS. ; Segons la tecla llegida cridarem al procediment corresponent. ; [‘i’,’j’,’k’ o ’l’] cridar al procediment MouCursor ; ‘’ cridar al procediment MouFitxa ; ‘q’ posa Estat=0. ; (per la lectura de teclar utilitzar només crides als serveis del DOS). ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* LlegirTecla: push ax push dx mov al,0 mov ah,08h int DOSSERVICE mov [Tecla], al cmp [Segons], 100 ;comprobar si el temps s'ha exhaurit, jl ContinuaLlegint ;en cas que així sigui mov [Estat], 3 ;posem Estat=3 jmp quitb ;i sortim de la subrutina ContinuaLlegint: cmp [Tecla], 32 jne ContinuaLlegintB Call MouFitxa ;quan premem espai cridem la subrutina MouFitxa ContinuaLlegintB: cmp [Tecla], 105 jl quit cmp [Tecla], 108 jg quit mov ah,0 mov al, [Tecla] Call MouCursor ;quan premem tecla entre "i" i "l" cridem subrutina MouCursor jmp quitb quit: cmp [Tecla], 113 ;quan la Tecla es q posem Estat = 0 jne quitb mov [Estat], 0 quitb: pop dx pop ax ret ;******************************************************************************* ; Mou el cursor segons la direcció indicada per AL i actualitza FilCur i ColCur ; segons la direcció que s’ha seleccionat ; Paràmetres d'entrada: AL indica la direcció del moviment ('i', 'j', 'k' o 'l') ; Paràmetres de sortida: Cap ;******************************************************************************* MouCursor: push ax push bx push cx push dx cmp al, 105 ; moviment si s'ha pitjat "i" jne CursorA cmp [FilCur], 0 jle CursorA dec [FilCur] CursorA: cmp al, 106 ; moviment si s'ha pitjat "j" jne CursorB cmp [ColCur], 0 jle CursorB dec [ColCur] CursorB: cmp al, 107 ; moviment si s'ha pitjat "k" jne CursorC cmp [FilCur], 3 jge CursorC inc [FilCur] CursorC: cmp al, 108 ; moviment si s'ha pitjat "l" jne CursorD cmp [ColCur], 3 jge CursorD inc[ColCur] CursorD: mov bh,00h mov bl,07h mov dh,[FilPos] mov dl,[ColPos] add dh,[FilCur] add dl,[ColCur] add dh,[FilCur] add dl,[ColCur] mov ah, 02h int BIOSSERVICE ; posicionem cursor pop dx pop cx pop bx pop ax ret ;******************************************************************************* ; Mira si hi ha una casella buida al costat (dalt, baix, esquerra o dreta) de ; la casella actual del cursor, si és així fem el moviment de la fitxa i ; decrementa en un el número de moviments, si estem a la casella buida o si ; la casella buida no està al costat de la casella on estem, no moure cap fitxa ; ni descomptar moviments. ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* MouFitxa: push ax push bx push cx push dx push si mov dh, [FilEspai] ; comprobem que no estem a la casella buida cmp [FilCur],dh jne SeguirMovA mov dh, [ColEspai] cmp [ColCur],dh jne SeguirMovA jmp NoMov SeguirMovA: ; casella buida a dalt? mov dh, [ColEspai] cmp [ColCur],dh jne SeguirMovC mov dh, [FilCur] sub dh, 1 cmp dh, [FilEspai] jne SeguirMovB jmp FerMov SeguirMovB: ; casella buida a baix? mov dh, [FilCur] add dh, 1 cmp dh, [FilEspai] jne NoMov jmp FerMov SeguirMovC: ; casella buida a l'esquerra? mov dh, [FilEspai] cmp [FilCur],dh jne NoMov mov dh, [ColCur] sub dh, 1 cmp dh, [ColEspai] jne SeguirMovD jmp FerMov SeguirMovD: ; casella buida a la dreta? mov dh, [ColCur] add dh, 1 cmp dh, [ColEspai] jne NoMov jmp FerMov FerMov: ; Hem trobat la casella buida al costat, realitzar moviment mov bx, 4 mov dh, 0 mov ah, 0 mov al, [FilCur] mul bx add al, [ColCur] mov dl, al mov si, dx mov ch, Fitxa[SI] ; obtenim caràcter a intercanviar mov ah, 0 mov al, [FilEspai] mul bx add al, [ColEspai] mov dl, al mov si, dx mov Fitxa[SI], ch ; col·loquem el caràcter a la casella buida mov ah, 0 mov al, [FilCur] mul bx add al, [ColCur] mov dl, al mov si, dx mov Fitxa[SI], 32 ; "buidem" la casella on tenim el cursor mov al, [FilCur] mov [FilEspai], al mov al, [ColCur] mov [ColEspai], al ; guardem la nova posició de l'espai dec [Moviments] NoMov: pop si pop dx pop cx pop bx pop ax ret ;******************************************************************************* ; Verifica si el tauler està ordenat, és a dir, les fitxes estan ordenades ; d’esquerra a dreta i de dalt a baix, quedant la casella buida a la última ; posició (baix-dreta), si és així canviar l’estat a 2 (Guanya). ; Verifica si s'han esgotat els moviments disponibles, si és així posa estat a 4 ; (moviments exhaurits) ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* VerificaFi: push dx push si push di mov si, 0 mov di, 1 BucleFi: ;Bucle on mirem si cada caràcter de la fitxa no és més petit en Ascii ;que l'anterior (per tant, si estan ordenats alfabèticament) mov dh, Fitxa[DI] cmp Fitxa[SI], dh jg BucleNoFi inc si inc di cmp di, 14 jle BucleFi mov [Estat], 2 ;si està la fitxa ordenada, Estat = 2 i sortim de la subrutina jmp FiVerificada BucleNoFi: cmp [Moviments], 0 ; en cas que haguem exhaurit els moviments, Estat=4 jg FiVerificada mov [Estat], 4 FiVerificada: pop di pop si pop dx ret ;******************************************************************************* ; Mostra el missatge indicat per SI. ; Paràmetres d'entrada: SI: adreça del missatge que volem mostrar ; Paràmetres de sortida: Cap ;******************************************************************************* MostraMissatge: push AX push DX push SI mov ah,02h int BIOSSERVICE mov dx,SI ;adreça del missatge mov ah,09h int DOSSERVICE pop SI pop DX pop AX ret ;******************************************************************************* ; Instal·lació de la rutina d'atenció d'interrupció de rellotge ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* InstalRAI: push es push ax xor ax,ax mov es,ax mov [Segons], 1 mov ax,es:[VECT_R*4] mov [RAI_R_Dir],ax mov ax,es:[VECT_R*4+2] mov [RAI_R_Seg],ax cli lea ax,[RAIrellotge] mov es:[VECT_R*4],ax mov es:[VECT_R*4+2],cs sti pop ax pop es ret ;******************************************************************************* ; Desinstal·lació de la rutina d'atenció d'interrupció de rellotge ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* DesInstalRAI: push es push ax xor ax,ax mov es,ax cli mov ax,[RAI_R_Dir] mov es:[VECT_R*4],ax mov ax,[RAI_R_Seg] mov es:[VECT_R*4+2],ax sti pop ax pop es ret ;******************************************************************************* ; Rutina d'atenció de l'interrupció del rellotge ; Paràmetres d'entrada: Cap ; Paràmetres de sortida: Cap ;******************************************************************************* RAIrellotge : push ax push bx push dx push ds mov ax,@DATA mov ds,ax ;codi de la RAI inc [Ticks] mov al, [Ticks] cmp al, TicksSegon jle FiRAIrel mov [Ticks],0 cmp [Segons], 100 jge FiRAIrel lea bx,Segons push bx mov dl, 33 mov dh, 18 push dx Call MostraDigits ; Mostrar segons en pantalla pop dx pop bx inc [Segons] FiRAIrel: mov al,20h out 20h,al pop ds pop dx pop bx pop ax iret ;******************************************************************************* ; Programa Principal ;******************************************************************************* inici: STARTUPCODE Call InstalRAI Call EsborraPantalla Call MostraTauler Call PosIni push bx push dx push si mov bh, 00h ;pàgina mov bl, 07h ;atribut mov dh, 19 mov dl, 33 BuclePrincipal: Call ActualitzaTauler Call VerificaFi cmp [Estat], 1 je bloc cmp [Estat], 5 jne Msg5 lea si, MsgPerdut5 Call MostraMissatge Msg5: cmp [Estat], 4 jne Msg4 lea si, MsgPerdut4 Call MostraMissatge Msg4: cmp [Estat], 3 jne Msg3 lea si, MsgPerdut3 Call MostraMissatge Msg3: cmp [Estat], 2 jne Msg2 lea si, MsgGuanya Call MostraMissatge Msg2: cmp [Estat], 0 jne Msg0 lea si, MsgSortir Call MostraMissatge Msg0: jmp final bloc: Call LlegirTecla jmp BuclePrincipal final: Call DesInstalRAI pop si pop dx pop bx EXITCODE 0 end inici