software 1996 · whisky-ware · otra birra

Tutorial de ASM — Capítulo II

Segundo capítulo del tutorcillo. Distribuido como whisky-ware por la BBS Public Enemy, circa 1996. El texto es el original, sin tocar.

 ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
 ░▒▓ Tutorcillo chencho de ASM ▓▒░
 ░▒▓      Segundo Capitulo     ▓▒░
 ░▒▓ Whisky-whare del Sami 3;*)▓▒░
 ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓

             -*-

 En este capitulo ya aprendes
a ir programando y demas. Supongo
que hara falta otro capitulillo
mas (o igual hasta 2)... pero
ahora estoy un poco liado por
lo que ir "entrenando" con este
y el anterior... Si hay dudas...
ya sabes, estoy por Public Enemy.

             -*-

( aun no se para que tengo el
 warlock) };)
░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░
░▒▓                                                                        ▓▒░
░▒▓                           Tutorial de ASM                              ▓▒░
░▒▓                                                                        ▓▒░
░▒▓                            (II de momento)                             ▓▒░
░▒▓                                                                        ▓▒░
░▒▓  Sami 1.996 (Manolo Buitre por aquello de mantener anonimato paterno)  ▓▒░
░▒▓                                                                        ▓▒░
░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░

Este es el segundo capitulo (y como sea como el anterior en 3 capitulos nos cepillamos el curso ;-)... espero que el anterior os haya ido igual que a mi el verano. Voy a ver que dejamos pendiente del otro dia........

01 — Comenzando con experiencias y pasando de la teoria... -;)

Bueno... pasamos a programar... ¿que te gustaria hacer?... ¡ lo estoy oyendo ! ¿un motor 3D?... bien... eso creo que es lo que en Basic se llama PRINT -3;)... pero antes un pelin mas de teoria (que en realidad es practica, vamos). Antes de seguir, ¿recuerdas lo de los opcodes?... si no lo recuerdas repapalos, porque aqui voy un poco mas alla:

; Compila a partir de aqui
; Segundo programa de XXXXXX

        .MODEL MediuM           ; En este caso mejor un Tiny
        .DATA                   ; Sobra (pero sigue leyendo!)
        .CODE
                mov ah,4Ch      ; Salida al dOS con "errorlevel"
                int 21h         ; (es un servicio de interrupcion)
         END

Venga, a que sabes lo que hace el programa de ahi arriba?... pues ahora vayamos con una orden del *compilador* (no del procesador)... en realidad son varias pero todas hacen cosas muy parecidas:

Te presento a DB, DW, DD, DQ y DT (aunque hay alguna mas, es ==)

Antes de que te cagues en algun santo, te aviso que ya me cague yo, asi que a callar... ¿Que que hace DB?... Mira:

        .MODEL MediuM           ; Como tengo datos y codigo mejor el medium
        .DATA                   ; para este ejemplo no sobra, eh! 3;)

                Mi_letra  DB            'A'     ; Mete el codigo ASCII de A
                Mi_letra2 DB            65      ; (que es este)
                Entero    DW            1234h   ; definimos 16bits
                Mi_letra3 DB            41h

        .CODE

                mov al,byte ptr Mi_letra        ; Mete en al 41h
                mov bl,byte ptr Mi_letra2       ; Mete en bl 41h
                mov cl,byte ptr Mi_letra3       ; Mete en cl 41h

                mov ah,4Ch      ; Salida al dOS con "errorlevel"
                int 21h         ; (es un servicio de interrupcion)
         END

Vale tio! no te pongas asi... mal ejemplo, ya lo se... jugaremos un poco con el para que lo cojas. Primero ten tu tablita de ASCII al lado ¿ya?... sigamos...

Empezemos analizando el programa:

- ".MODEL MediuM"
        Quiero un segmentiko de datos y otro de codigo.

- ".DATA"
        Lo que haya hasta ".CODE" sera mi segmento de datos,
        donde metere los datos que quiera (hasta 64 Kb)

- "Mi_letra     DB     'A'"
        Asigna una variable de 1 byte que contenga el codigo
        ASCII de la letra "A" (mayuscula), el cual es 65 en decimal
        o 41h (que es el mismo, claro). Las siguentes asignaciones
        con DB son iguales. Si pusieramos DW, "Definieriamos un Word",
        usease un entero de 16 bits, con DD un doble etc.

        Analicemos esto mas a fondo ya que es muuuuyyyyyy importante:

        El primer DB se pondra al principio del segmento de datos,
        el segundo DB en el segundo byte de este segmento, al poner
        el DW en el tercero y cuarto byte quedara esto:

                         3   4  <== offset dentro del segmento de datos
                        -------
                        34h 12h

        No me he confundido... sino que el almacenamiento de word,
        dobles y demas se hace de manera invertida, es decir, la
        parte mas "estupida" delante, y la parte alta del valor
        (MSB me parece que le llaman en ingles, por aquello del
        Most Significant Byte) detras. Es algo muuuyyyy a tener en
        cuenta si por ejemplo has definido algo con un DW y luego
        quieres leer solo un byte de esa posicion de memoria.

- "mov al,byte ptr Mi_letra        ; Mete en al 41h"

        Bueno, por si lo recalco poco, el ";" ese es un comentario.
        y luego vamos con eso que pone de "byte ptr"... lo que hace
        es decir que meta un byte de la posicion de memoria "Mi_letra"
        Si le dijeramos un "mov ax,word ptr Mi_letra", en ax
        tendriamos este byte y el siguiente. (es sencillo, ¿no?)...
        Bueno, quiza esto sea una tonteria ya que si pones "mov al,XXX"
        se sobreentiende (por ser AL de 8 bits), que solo quieres coger
        un byte. De hecho CREO que a los compiladores modernos no les
        hace falta esto (yo como voy con mi viejiiiisimo TASM pues
        prefiero ponerlo por clarificar).

¿donde estabamos?... ah... pues nada... que el programa ya ha acabado. Como ejercicio de este mini-tema... te pillas el Turbo Debugger y cargas este programa (.EXE). Para ello tendras que crear el .ASM con un editor de textos normal, luego pasarselo por el TASM ("TASM mi_prog.asm") y por ultimo linkarlo ("TLINK mi_prog.obj"). Los puntos de ruptura en el TD se ponen con F2, con F9 se ejecuta el programa hasta el final y con F7 vas "traceando" paso a paso lo que va haciendo el bicho. Como consejos... juega a ver la memoria, o tu programa cargado en memoria (en forma de opcodes) etc. El TD (turbo debugger) sera una de las herramientas mas utiles que vas a tener (bueno, la mejor) por lo que al principio no sabras ni lo que es, pero cuando juegues un poco con el ya veras que sencillo. :)

Otro ejercicio (un poco estupido tal vez) es coger un desensamblador y crear otro ASM (pero partiendo del ejecutable)... esto te vale para que sepas lo que hacen las directivas esas de .MODEL y demas, aparte de que te daras cuenta del monton de maneras que tienes de poner una cosa en ASM.

Aunque en el anterior capitulo te engañe diciendo que no habria mas teoria (creo que te ido engañando todo el "camino" ;)... en este ya no te va a "entrar" mas (que no quiere decir que no vaya a haber mas, pero sera mas "discreta")... en este capitulo tengo previsto que juegues un poco con el 8086 usandolo para hacer calculos sencillos etc. Usaras algunas de las interrupciones mas utiles y demas... asi que adelante :)

Vamos a empezar sacando un texto a pantalla:

        .MODEL MediuM           ; 64Kb de datos y 64Kb de codigo
        .DATA                   ; Segmento de datos

           Cadena       DB      "Esto esta en pantalla$"
                                ; La cadena va entre comillas, y si usas
                                ; el DOS para imprimir una cadena (cadena
                                ; es una tira de bytes), el fin de cadena
                                ; se señala con un "$".

        .CODE                   ; Segmento de codigo

           mov ax,@DATA         ; Configura el DS => @DATA es el segmento
           mov ds,ax            ; de datos que pones con la directiva ".DATA"
                                ; pero DS no es accesible directamante (como
                                ; los demas registros de segmento) y por eso
                                ; metemos el segmento en AX.

           mov dx,offset Cadena ; El servicio 9 de los servicios del DOS
           mov ah,9             ; (int 21h) imprime una cadena hasta que
           int 21h              ; encuentra un "$".. es decir... un 36.
                                ; Para indicarle que cadena quieres sacar
                                ; por la pantalla, el segmento de donde
                                ; esta la cadena se mete en DS, y el offset
                                ; en DX. Luego se llama a la interrupcion 21h.

           mov ah,4Ch           ; Acaba el programa
           int 21h

        END                     ; Fin del programa

Este es el "listing" del programa de arriba (se hace con: TASM uu.asm /la /zi ... y se genera el uu.lst):

      1  0000                   .MODEL MediuM
      2  0000                   .DATA
      4  0000  45 73 74 6F 20 65 73+  Cadena  DB  "Esto esta en pantalla$"
      7  24
     13  0016                   .CODE
     15  0000  B8 0000s         mov ax,@DATA
     16  0003  8E D8            mov ds,ax
     21  0005  BA 0000r         mov dx,offset Cadena
     22  0008  B4 09            mov ah,9
     23  000A  CD 21            int 21h
     29  000C  B4 4C            mov ah,4Ch
     30  000E  CD 21            int 21h
     32                         END

02 — Plantillas para tus programas mas simples

PARA HACER UN .COM

Codigo Segment
        assume CS:Codigo, DS:Codigo

        org     100h

                                ; AQUI VA TU CODIGO Y TUS DATOS.

     Comienzo:
        mov     ax,4C00h        ; Salida al dos con errorlevel
        int     21h

Codigo EndS
       End Comienzo

Compilar:   Tasm mifich.asm
Linkar:     Tlink/t mifich.obj

PARA HACER UN .EXE

        .MODEL medium
        .386
        .DATA

                ejemplo DB "Hola$"

        .CODE
                mov     ax,4C00h        ; Salida al dos con errorlevel
                int     21h
        .END

Compilar:   Tasm mifich.asm
Linkar:     Tlink/3 mifich.obj

PARA LAS .QLB (para Quick Basic 4.x)

        .MODEL medium,Basic
        .386
        .DATA

                ejemplo DB "Hola$"

        .CODE
             Public MiSubrutina

                MiSubrutina Proc Far, parametro:word
                        MOV     BX,parametro
                        MOV     AX,[BX]
                        RET
                MiSubrutina EndP

        .END

Compilar:       Tasm mifich.asm
Linkar en LIB:  Lib milib.lib +mifich.obj
Crear QLB:      Link/QU milib.lib, milib.qlb,nul,bqlb45.lib

03 — Las puertas logicas

Existen fisica y logicamente... forman la base mas simple del ordenador, desde las memorias hasta el propio procesador, por lo que te podras imaginar la importancia que tienen.

Las instrucciones del procesador son: AND, OR, XOR y NOT

Al principio te pareceran que no valen para nada, que no te acuerdas y que en caso de que te acuerdes no sabes donde usarlas, pero eso va junto con la practica 3;D

NOT ==> 0 - 1 / 1 - 0 — Niega: si tienes un 1 te da un 0 y viceversa:

        .MODEL tiny
        .CODE
                mov ax,11111111b ; o lo que es lo mismo 00FFh
                not ax           ; En AL tendremos todo a 0 y en AH todo a 1
                mov ah,4Ch       ; Salida al DOS
                int 21h
        END

Lo mejor con las puertas es que tu mismo llegues a tus conclusiones, ya que yo de primeras me las aprendi, de segundas las olvide, de terceras saque mis conclusiones y por fin las use ;DDDDD. Pruebalas con el Turbo Debugger, y si no lo tienes, ya sabes... };)

OR ==> 0-0=0 / 0-1=1 / 1-0=1 / 1-1=1:

                mov al,00010001b
                or  al,00000010b

        El bit numero 1 se pondra a 1 pero sin cambiar los demas
        bits. El resultado, como en todas las instrucciones de ASM,
        se ponen en el primer operando, es decir, en este caso, AL.

XOR ==> si son iguales da 0, si son distintas da 1. Por eso se usa: "xor ax,ax" para borrar AX... puesto como AX y AX son iguales (no te jode...)...

AND ==> Ambos a 1 si quieres 1. Por eso se usa para hacer comparaciones de bit en bit:

                mov al,01001001b
                and al,00000001b        ; Esto dara 1... :)

                mov al,00000000b
                and al,00000001b        ; Y esto dara 0... :)

En ensamblador tambien existe otra AND. Se diferencia de esta en que TEST (que asi se llama esta nueva instruccion), no guarda el resultado en ningun sitio... y puesto que te puedes preguntar que para que sirve pues (a que siiiiii... ..... ;DDD)... vayamos a MAS PRACTICA!!!

04 — Saltos y demas (lo que se llaman bifurcaciones)

Hasta ahora los poquitos programas que hemos hecho han sido lineales. Comenzaban en un principio. Y acababan en un final. Pero claro... pocos programas pueden ser lineales y deben hacer una cosa u otra (segun unas condiciones, claro).

Dejemonos de rollos que tengo que merendar y son ya las 7:

        .MODEL medium
        .DATA

                h1       DB "Pues el bit 0 estaba puesto a 1$"
                h2       DB "Pues el bit 0 no estaba puesto a 1$"
                variable DB 00h

        .CODE

                mov ax,@data                    ; Prepara DS con @DATA
                mov ds,ax

                mov byte ptr variable,00110101b ; mete esto en "variable"
                mov al,byte ptr variable        ; en al esta 00110101b

                test al,00000001b               ; una AND con un 1
                jz   no_uno                     ; si el resultado da 0...
                                                ; ...salta a la etiqueta
                mov ah,9                        ; no_uno (Jump if Zero)
                mov dx,offset h1                ;         ^       ^ ==> JZ
                int 21h                         ; ... y si no es 0, seguir
                jmp final                       ; ... y saltar a final

              no_uno:
                mov ah,9
                mov dx,offset h2
                int 21h

              final:
                mov ah,4ch                      ; Sale al DOS
                int 21h
        END

Veamos la explicacion de este "fenomeno"... resulta que donde pone "jmp final", cuando se esta ejecutando esta instruccion, el CS:IP (ya sabras por fin que es esto)... pues digamos que es 6000:0020 (inventao, por supuesto). "Final" es un simbolo, una etiqueta..."algo" que marca una posicion de memoria (que es la de "mov ah,4ch")... por lo que si ponemos que salte a Final (jmp final)... saltara a la posicion de memoria donde este "mov ah,4ch"... existen tres tipos de saltos...

el short: que permite un salto *** RELATIVO *** (a donde estes
          en ese momento) de 8 bits... es decir, entre -128 y
          +127. Normalmente no te tienes que preocupar del tipo
          de salto, porque lo suele poner el compilador gracias
          a las directivas. Ocupa el opcode de JMP + 1 byte.

el near : un salto intrasegmental, dentro del mismo segmento
          de codigo. Por lo que solo modifica el IP. Solo ocupa
          por tanto un byte mas que el anterior.

el far  : un salto intersegmental... entre segmentos.. modifica
          el CS:IP, por lo que ocupa 2 bytes mas que el anterior.

Es importante saber que tipo de salto usar puesto que contra mas "lejano" sea, mas lento es el salto, y mas espacio ocupa en memoria (la instruccion, por supuesto).

05 — Inciso especial: los flags ;)

Los flags es otro registro de 16...... siiiiii.... oooootroooo 3:). ... pero este es especial... no se puede poner "mov ax,flags" ni nada asi. Esto son las flags (a ver si no me equivoco demasiao ;):

       15                                                0 ==> Nº de bit
        *  *  *  * | *  *  *  * | *  *  *  * | *  *  *  *
                     OF DF IF TF  SF ZF    AF     PF    CF

Tranquilooooooooo.... que no es nada!!!

Si esto lo lees por primera vez, no te preocupes si no entiendes nada, este inciso esta aqui por referencia, no por explicacion ;)... la explicacion se ve muuuucho mejor en la practica que en la teoria. Cada bit de estos sirven para indicar una cosa u la otra (por eso son bits). Aqui va la lista... si un bit esta a 1:

CF: "carry flag" ==> el acarreo al sumar
OF: "OverFlow"   ==> cuando te pasas y no cabe en 8 o 16 bits un valor
ZF: "Zero Flag"  ==> (este es lo del JZ)... 1 si una comparacion
                     es cierta.
SF: "¿Sign Flag?"==> bla,bla,bla
AF: "¿?"         ==> lo mismo que el carry pero cuando curras en BCD
                     ( ya veras que es eso, por ahora sigue dando
                       saltitos pequeños ;)
DF: "Direction flag" ==> indica si las cadenas se almacenan hacia
                     arriba o hacia abajo. (piensa en ello)

IF: si las interrupciones estan activadas (o no, claro)
TF: el "trapping"... para ejecutar programas "paso a paso"... esta
    pijorra la usa el Turbo Debugger por ejemplo, cuando estas dandole
    al F7 y F8.

Hay mas en procesadores superiores al 8086, peeeroo.... el Samy tiene tanta informacion de PC superiores al 8086 como vosotros... por lo que cuando pasemos del 8086 vereis como mis "conocimientos" ("aunque mi mami dice que no tengo") se van degradando 3;(

06 — Saltos y demas (lo que se llaman bifurcaciones) ][

Y como no hay 2 sin una (y me juego lo que quieras a que tengo que hacer el ][I... y seguro que gano claro X-D)... aqui van MAS SALTOS!!! Pues como antes solo te enseñao el salto no-condicional, ese que salta sin condicion (jmp) y el JZ (si el resultado es 0)... pues te habras mosqueao, pero aqui vienen mas:

JA      : jump if above => salta si es mas grande
JAE     : jump if above or equal => si mayor o igual
JB      : (ah, perroooo) jump if below => si es menor
JBE     : jump if below or equal => o menor o igual
JE      : jump if equal => salta si es igual

Y ahora los contrarios a estos:

JNA     : si NO es mas grande
JNB     : si no es mas pequeño

... bueno, todos negaos, pero con la N despues de la J ;)

Seguiremoooooos... hay muchiiisimos mas CX-DDDDDDDDDD, pero antes entrenemos estos un poco puesto que son los mas sencillos y los mas usados:

        .MODEL medium
        .DATA

                h1 DB "es igual a 1$"
                h2 DB "No es igual a 1$"

        .CODE

                mov ax,@data                    ; Prepara DS con @DATA
                mov ds,ax

                mov al,1
                cmp al,1                ; compara al con 1
                jne no_uno              ; y SEGUIDAMENTE... si no es igual
                                        ; salta a no_uno

                                        ; INCISO: "CMP" es una instruccion
                                        ; del procesador, no es el test
                                        ; de antes (que tambien es del proce-
                                        ; sador). Y casi siempre lleva el
                                        ; Jxxx detras.

                mov ah,9
                mov dx,offset h1
                int 21h
                jmp final

              no_uno:
                mov ah,9
                mov dx,offset h2
                int 21h

              final:
                mov ah,4ch                      ; Sale al DOS
                int 21h
        END

Lo siento por no poner comentarios pero es que es igual que el anterior :) Los demas tipos de estos saltos, ya sabeis, practicarlos 3:D

La semana que viene seguiremos con otros tipos de saltos, subrutinas y demas. Crearemos nuestra primera interrupcion etc. Pero os dejo deberes (X-DDDDDDDDDD).... probar todo lo que hemos dado hasta ahora, sin ningun miedo porque no os podeis cargar nada (mientras no toques la INT 13h, que es la de disco)... segundo: conseguiros de una vez la lista de interrupciones, que esta en Public Enemy (53-18-07)... la de Ralf Brown mismo. Probar cosillas con ellas, que se aprende mucho y luego os cogeis la lista de instrucciones del procesador (y si no la conseguis pues me la pedis a mi). Y creo que no se me olvida nada mas... que os cuideis y feliz (va con segundas, por supuesto) fin de semana 3:*DDDD...... ¡¡¡FAFAN PAGATE ALGO!!!

PD: Documento guiski-ware, share-whiski o como tu quieras }:*D~~