JavascriptProva

Visualizzazione post con etichetta Assembly. Mostra tutti i post
Visualizzazione post con etichetta Assembly. Mostra tutti i post

sabato 17 dicembre 2016

Tornando a Giobe...

Ricominciamo dalla firma di Zbikovsky...
C:\Arch-Lab\LAVORO~1>debug es1.xxx
-d
17BC:0100  4D 5A 16 01 02 00 00 00-20 00 00 00 FF FF 00 00   MZ...... .......
17BC:0110  00 00 00 00 00 01 00 00-1E 00 00 00 01 00 00 00   ................
17BC:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
Andando indietro vado al PSP:
-d 17bc:0000
17BC:0000  CD 20 FF 9F 00 9A EE FE-1D F0 4F 03 20 12 8A 03   . ........O. ...
17BC:0010  20 12 17 03 20 12 2C 07-01 01 01 00 02 FF FF FF    ... .,.........
17BC:0020  FF FF FF FF FF FF FF FF-FF FF FF FF A0 11 4E 01   ..............N.
17BC:0030  E0 16 14 00 18 00 BC 17-FF FF FF FF 00 00 00 00   ................
17BC:0040  05 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:0050  CD 21 CB 00 00 00 00 00-00 00 00 00 00 20 20 20   .!...........
17BC:0060  20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20           .....
17BC:0070  20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00           ........
-d
17BC:0080  00 0D 65 73 31 2E 78 78-78 0D 65 78 65 0D 20 49   ..es1.xxx.exe. I
17BC:0090  35 20 44 31 20 50 33 33-30 20 54 33 0D 74 2E 65   5 D1 P330 T3.t.e
17BC:00A0  78 65 0D 00 00 00 00 00-00 00 00 00 00 00 00 00   xe..............
17BC:00B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:00C0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:00D0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:00E0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17BC:00F0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-

sabato 21 settembre 2013

Passaggio di parametri tramite stack con indicazione dei parametri nella direttiva PROC.

Ripassati CMP, alcuni salti condizionati, diverse flags, e riscoperta la tecnica di Giobe per ottenere la stampa a video dei codici ASCII dei numeri, mediante la quale egli realizza la procedura Byt2Asc, ho pensato di creare una Byt2Asc nella quale il parametro sia passato mediante lo stack, aggiungendo alla direttiva PROC il parametro.
Ecco la procedura:
public Byt2Asc
text segment byte public
Byt2Asc proc near C,num:WORD
 mov ax,num
 push ax
 shr al,1
 shr al,1
 shr al,1
 shr al,1
 
 cmp al,0AH
 jb X1
 add al,7
X1: add al,30h

 mov ah,0eh
 int 10h
 
 pop ax
 and al,0Fh
 
 cmp al,0AH
 jb X2
 add al,7
X2: add al,30h

 mov ah,0eh
 int 10h

 ret 2
Byt2Asc endp
text ends
end
Per specificare i parametri nella direttiva PROC, bisogna dichiarare anche la "convenzione di chiamata", che può essere C o di altro tipo (la ripasserò in seguito) in quanto la procedura deve conoscere l'ordine nel quale ritirare i parametri dallo stack.
Ho dichiarato C, ma mi sono cominciato a scontrare con una persistente segnalazione di errore, che non riuscivo a capire, di "unresolved external". Poi, ricordando l'associazione di idee di convenzione di chiamata C con l'underscore iniziale, ho capito che il problema era che nel programma chiamante avrei dovuto far precedere il nome Byt2Asc da un underscore, così ho fatto e ho risolto il problema:
extrn Cancella:near
extrn _Byt2Asc:near
extrn KeyWait:near

text segment public

.....
Ho assemblato e linkato il tutto, e funziona.
Dal momento che l'aggiunta di parametri automatizza parte del codice, vediamo qual è il vero codice in DEBUG:
AX=00F4  BX=0000  CX=0450  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=001E   NV UP EI PL NZ NA PO NC
1781:001E 55            PUSH    BP
-p

AX=00F4  BX=0000  CX=0450  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=001F   NV UP EI PL NZ NA PO NC
1781:001F 8BEC          MOV     BP,SP
-p

AX=00F4  BX=0000  CX=0450  DX=0000  SP=03FA  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=0021   NV UP EI PL NZ NA PO NC
1781:0021 8B4604        MOV     AX,[BP+04]                         SS:03FE=00F4
-p

........


AX=0E34  BX=0000  CX=0450  DX=0000  SP=03FA  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=0048   NV UP EI PL NZ NA PO NC
1781:0048 5D            POP     BP
-p

AX=0E34  BX=0000  CX=0450  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1786  CS=1781  IP=0049   NV UP EI PL NZ NA PO NC
1781:0049 C20200        RET     0002
-p
Ecco: Le istruzioni "automatiche" sono:
PUSH BP
MOV BP,SP

.....

POP BP
Ossia il salvataggio nello stack del Base Pointer, l'attribuzione al Base Pointer del valore di SP, e poi, alla fine, presupponendo che SP sia tornato al valore corrispondente a BP mediante il corretto equilibrio dei pushaggi e dei poppaggi, l'istruzione che riprende il valore precedente di BP.

Registri flag di segno e di parità.

Ecco, approfondiamo un po' queste belle flags.
In particolare, mi incuriosisce la flag di segno.
Vuoi vedere che questa flag diventa NG semplicemente se il risultato di un'operazione ha il primo bit pari a 1?
Per verificarlo, faccio una somma che mi dia il primo bit pari a 1:
text segment public
start:
 mov al,01111111B
 mov bl,10000000B
 add al,bl
 mov ah,4ch
 int 21h
 
text ends
end start
-r
AX=0000  BX=0000  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1781  CS=1781  IP=0000   NV UP EI PL NZ NA PO NC
1781:0000 B07F          MOV     AL,7F
-t

AX=007F  BX=0000  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1781  CS=1781  IP=0002   NV UP EI PL NZ NA PO NC
1781:0002 B380          MOV     BL,80
-t

AX=007F  BX=0080  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1781  CS=1781  IP=0004   NV UP EI PL NZ NA PO NC
1781:0004 02C3          ADD     AL,BL
-t

AX=00FF  BX=0080  CX=000A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1781  CS=1781  IP=0006   NV UP EI NG NZ NA PE NC
1781:0006 B44C          MOV     AH,4C
-
Sì! Verificato! Ovviamente, se stiamo facendo un'operazione senza segno, ce ne freghiamo.

C'è un'altra flag che cambia, la seconda, che da PO diventa PE. Che significa? Giobe, aiutami tu!

Ecco: sarebbe il Parity Flag. Ricordando che in inglese Even e Odd significano Pari e Dispari, PO significa che nel risultato c'è un numero dispari di 1 mentre PE significa numero pari di 1.

Ripasso dell'istruzione CMP e di alcune flags.

Ripassiamo un po' l'istruzione CMP e le flags

Ecco un programmuscolo:
text segment public
start:
 mov al,3
 cmp al,8
 
 mov ah,4ch
 int 21h
 
text ends
end start
In debug, col comando traccia:
c:\Assembly>debug cmp.exe
-t

AX=0003  BX=0000  CX=0008  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1781  CS=1781  IP=0002   NV UP EI PL NZ NA PO NC
1781:0002 3C08          CMP     AL,08
-t

AX=0003  BX=0000  CX=0008  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1781  CS=1781  IP=0004   NV UP EI NG NZ AC PO CY
1781:0004 B44C          MOV     AH,4C
-
Rivediamo un po' queste flags...

La flag di riporto esprime un riporto o un prestito, e nella sottrazione di numero maggiore da numero minore si genera un prestito, per cui in questo caso la prima flag viene modificat da NC a CY.
La terza flag è quella di riporto ausiliario che esprime il riporto nell'ambito del nibble. In questo caso il riporto c'è, e quindi da NA diventa AC.
La quinta flag è quella del segno, che prende, se non vado errato, il valore del bit più significativo, che in questo caso è 1, e quindi da PL diventa NG.

Vediamo di ricostruire cosa avviene nello specifico in questa sottrazione di numeri binari.
3 - 8 si traduce in binario con 00000011 - 00001000.
Mettiamolo in colonna:

00000011 -
00001000 =
____________
11111011

che, nella lettura con segno equivale a -5, giusto risultato di 8 - 3.

Il mio "trucco" per leggere velocemente i numeri binari negativi è questo. Quando dal bit più significativo c'è una fila ininterrotta di 1, io vado a vedere l'ultimo 1 della fila, e vedo a quale numero "corrisponde", ossia quale valore avrebbe il numero binario senza segno se questo 1 fosse l'unico: in questo caso, se l'ultimo numero della fila fosse l'unico, ossia se fosse 00001000, il byte avrebbe valore di 8.
Quindi vado avanti e considero i restanti 1 come se fossero soli nel byte: in questo caso sono la penultima e l'ultima cifra del byte a essere 1, e se fossero soli il byte avrebbe valore di 3.
Quindi sottraggo al numero calcolato precedentemente, ossia a 8, il secondo numero calcolato, ossia il 3, e cambio di segno il risultato, ottenendo -5.

Veriabili locali e stack

Variabili locali.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 
 call Cancella

 call prc

 call KeyWait
 
 mov ah,4ch
 int 21H
 
prc proc near
 push bp
 mov bp,sp
 
 ;crea la variabile locale
 sub sp,2
 
 mov [bp-2],41h
 mov ah,0eh
 mov al,[bp-2]
 int 10h
 
 ;cancella la variabile locale
 mov sp,bp
 pop bp
 ret 
prc endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
Ecco. Molto semplice.
Una volta create le variabili locali è fondamentale resettare lo SP quando si chiude la procedura riportandolo al valore contenuto in BP.
Quindi la locazione puntata da BP è quella che separa, nello stack, i parametri dalle variabili locali.
Cerchiamo di costruire un programma che usi sia parametri sia variabili locali, in modo da studiarne lo stack.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 
 call Cancella
 
 mov al,'A'

 push ax
 call prc
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
prc proc near
 push bp
 mov bp,sp
 
 ;crea la variabile locale.
 sub sp,2
 mov [bp-2],2
 
 mov ax,[bp+4]
 add ax,[bp-2]
 
 mov ah,0eh
 int 10h
 
 ;cancella la variabile locale
 mov sp,bp
 pop bp
 ret 2
prc endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
Ecco: il parametro passato è la lettera A. La variabile locale è 2.
La procedura aggiunge la variabile locale al parametro ottenendo 42H, ossia la lettera C, che stampa a video.
Funziona:
C
c:\Assembly>

Vediamo un po' come è congegnato lo stack con parametri, variabili locali, indirizzo di ritorno e BP originario salvato:

AX=0041  BX=0000  CX=0440  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0010   NV UP EI PL NZ NA PO NC
1781:0010 55            PUSH    BP
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0011   NV UP EI PL NZ NA PO NC
1781:0011 8BEC          MOV     BP,SP
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=03FA  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0013   NV UP EI PL NZ NA PO NC
1781:0013 83EC02        SUB     SP,+02
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=03F8  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0016   NV UP EI PL NZ NA PO NC
1781:0016 C746FE0200    MOV     WORD PTR [BP-02],0002              SS:03F8=3302
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=03F8  BP=03FA  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=001B   NV UP EI PL NZ NA PO NC
1781:001B 8B4604        MOV     AX,[BP+04]                         SS:03FE=0041
-
-d ss:03f0
1785:03F0  FA 03 1E 00 81 17 C4 11-02 00 00 00 09 00 41 00   ..............A.
1785:0400  C2 02 00 00 55 8B EC 57-56 8B 7E 04 39 3E 7C 01   ....U..WV.~.9>|.
1785:0410  75 06 C7 06 7E 01 FF FF-39 3E 80 01 75 05 6A 00   u...~...9>..u.j.
1785:0420  E8 0F 12 6B DF 0E 8B B7-2E 4B C1 E6 02 03 36 B4   ...k.....K....6.
1785:0430  52 EB 18 8B C6 2B 06 B4-52 C1 F8 02 50 E8 66 A1   R....+..R...P.f.
1785:0440  8B 04 C1 E0 02 03 06 B4-52 8B F0 83 3C FF 75 E3   ........R...<.u.
1785:0450  6B DF 0E C7 87 2E 4B FF-FF 33 C0 5E 5F C9 C2 02   k.....K..3.^_...
1785:0460  00 00 55 8B EC 56 8B 5E-04 6B DB 0E 8D 87 2E 4B   ..U..V.^.k.....K
-
Ecco: SP punta la variabile locale, BP punta il suo valore originario salvato nello stack.

Riportare al valore iniziale il puntatore dello stack dopo il passaggio di parametri tramite stack

Nel mio programmino rimane un conto in sospeso con lo stack.
Le due WORD pushate prima di chiamare la procedura, contenenti i parametri, non vengono poi ripoppate o smaltite in qualche modo, cosicchè SP non torna ai valori iniziali fino alla fine del programma.
Seguo in DEBUG i valori prima e dopo la chiamata della procedura Stampa:
AX=0003  BX=0000  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0003   NV UP EI PL NZ NA PO NC
1781:0003 B041          MOV     AL,41
-p

AX=0041  BX=0000  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0005   NV UP EI PL NZ NA PO NC
1781:0005 B30C          MOV     BL,0C
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0007   NV UP EI PL NZ NA PO NC
1781:0007 50            PUSH    AX
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0008   NV UP EI PL NZ NA PO NC
1781:0008 53            PUSH    BX
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0009   NV UP EI PL NZ NA PO NC
1781:0009 E80700        CALL    0013
-
Il valore di base di SP è 0400.
Con il pushaggio dei parametri diventa poi 03FE e 03FC.

-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0013   NV UP EI PL NZ NA PO NC
1781:0013 55            PUSH    BP
-
Con la CALL diventa 03FA perchè viene pushato l'indirizzo di ritorno.

-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0014   NV UP EI PL NZ NA PO NC
1781:0014 8BEC          MOV     BP,SP
-t>
Con il PUSH BP all'interno della procedura diventa 03F8.

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0016   NV UP EI PL NZ NA PO NC
1781:0016 8B5E04        MOV     BX,[BP+04]                         SS:03FC=000C
-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0019   NV UP EI PL NZ NA PO NC
1781:0019 8B4606        MOV     AX,[BP+06]                         SS:03FE=0041
-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0019   NV UP EI PL NZ NA PO NC
1781:0019 8B4606        MOV     AX,[BP+06]                         SS:03FE=0041
-t

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=001C   NV UP EI PL NZ NA PO NC
1781:001C B409          MOV     AH,09
-t

AX=0941  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=001E   NV UP EI PL NZ NA PO NC
1781:001E B700          MOV     BH,00
-t

AX=0941  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 B90100        MOV     CX,0001
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0023   NV UP EI PL NZ NA PO NC
1781:0023 CD10          INT     10
-p

A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0025   NV UP EI PL NZ NA PO NC
1781:0025 5D            POP     BP
-p
E resta tale fino ad ora. Quindi il POP BP lo riporta a 03FA.
AX=0941  BX=000C  CX=0001  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0026   NV UP EI PL NZ NA PO NC
1781:0026 C3            RET
-


E con il RET, col recupero dell'indirizzo di ritorno, va a 03FC.
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000C   NV UP EI PL NZ NA PO NC
1781:000C E81800        CALL    0027
-


...per poi restare uguale fino alla fine del programma.
-p

AX=1C0D  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000F   NV UP EI PL NZ NA PO NC
1781:000F B44C          MOV     AH,4C
-p

AX=4C0D  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0011   NV UP EI PL NZ NA PO NC
1781:0011 CD21          INT     21
-p

Program terminated normally
-
C'è quindi un conto in sospeso con lo stack, che rimane 4 bytes, ossia 2 words, più in basso rispetto a quello che dovrebbe essere.

Come "aggiustarlo" alla fine della procedura?
Come "riassorbire", cioè, i due parametri che erano stati pushati prima della chiamata della procedura?
Mi pare di ricordare che posporre un numero a RET determina un riassestamento di SP.
Controlliamo...

extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 push bp
 mov bp,sp
 mov bx,[bp+4]
 mov ax,[bp+6]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 pop bp
 ret 4
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 

AX=0941  BX=000C  CX=0440  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 B90100        MOV     CX,0001
-p

AX=0941  BX=000C  CX=0001  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0023   NV UP EI PL NZ NA PO NC
1781:0023 CD10          INT     10
-p
A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0025   NV UP EI PL NZ NA PO NC
1781:0025 5D            POP     BP
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0026   NV UP EI PL NZ NA PO NC
1781:0026 C20400        RET     0004
-p

AX=0941  BX=000C  CX=0001  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000C   NV UP EI PL NZ NA PO NC
1781:000C E81A00        CALL    0029
-
Ecco. Aggiungendo a RET il numero di bytes che sono stati occupati dai parametri passati, oltre a "rimangiarsi" l'indirizzo di ritorno, si resetta SP 4 bytes più in su, al punto di partenza!

Mi pare che ci fosse anche la possibilità di riportare lo stack al valore di base mediante un'istruzione specifica, ma ciò andrebbe fatto dopo il "rientro" nel programma di base.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 add sp,4
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 push bp
 mov bp,sp
 mov bx,[bp+4]
 mov ax,[bp+6]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 pop bp
 ret 
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
AX=0941  BX=000C  CX=0001  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0028   NV UP EI PL NZ AC PE NC
1781:0028 5D            POP     BP
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0029   NV UP EI PL NZ AC PE NC
1781:0029 C3            RET
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000C   NV UP EI PL NZ AC PE NC
1781:000C 83C404        ADD     SP,+04
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000F   NV UP EI PL NZ AC PE NC
1781:000F E81800        CALL    002A
-
Ottimo!

Un BAT per assemblare e linkare in un attimo

Mi costruisco un "pipistrello" per l'assemblaggio e il linkaggio, roba di due minuti, perfezionando esegui.bat.
@echo off
ml /c /Zm %1.asm
link %1,,nul,%2,,
Perfetto! Così mi basta inserire il nome del sorgente e quello della libreria per ottenere tutto immediatamente.

c:\Assembly>dir uno*
 Il volume nell'unità C non ha etichetta.
 Numero di serie del volume: ECE6-4560

 Directory di c:\Assembly

21/09/2013  12.34               500 uno.asm
               1 File            500 byte
               0 Directory  12.490.698.752 byte disponibili

c:\Assembly>esegui uno,libreria
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994
Copyright (C) Microsoft Corp 1984-1993.  All rights reserved.


c:\Assembly>dir uno*
 Il volume nell'unità C non ha etichetta.
 Numero di serie del volume: ECE6-4560

 Directory di c:\Assembly

21/09/2013  12.34               500 uno.asm
21/09/2013  12.44             1.600 uno.exe
21/09/2013  12.44               153 uno.obj
               3 File          2.253 byte
               0 Directory  12.490.694.656 byte disponibili

c:\Assembly>
Perfetto!

venerdì 20 settembre 2013

Uso di MOV per recuperare i parametri dallo stack

Bene.
Anzichè con il POP, posso prendere i parametri pushati nello stack mediante l'istruzione MOV.
Ho provato (memoria corta!) a porre questi valori in un qualunque registro con l'istruzione MOV, prendendo come riferimento la locazione puntata dal registro SP, secondo il ragionamento che sommando 2 a questo valore si salta l'indirizzo di ritorno e si prende l'ultimo parametro pushato, poi sommando 4 si va a prendere il primo parametro
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov bx,[sp+2]
 mov ax,[sp+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
E ottengo un errore dall'assemblatore:
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm
uno.asm(24) : error A2031: must be index or base register
uno.asm(25) : error A2031: must be index or base register

c:\Assembly>

Andiamo a rivedere i registri.
Ecco: registri puntatore e registri indice

Questo significa che per questo scopo, ossia per trasferire dati prelevati dallo stack, si possono usare solo BP, DI e SI.
Allora, invece di usare BP proviamo a usare DI o SI.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov si,sp
 mov bx,[si+2]
 mov ax,[si+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>
Ancora, con DI:
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov di,sp
 mov bx,[di+2]
 mov ax,[di+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>
Sì, con questi non mi dà errore.

Comunque, siccome classicamente il registro deputato a questa funzione è il BP, seguiamo la via "ortodossa":
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 mov bp,sp
 mov bx,[bp+2]
 mov ax,[bp+4]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>
Bene. Adesso assembliamo, linkiamo e vediamo come funziona.
c:\Assembly>esegui uno
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>link uno

Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994
Copyright (C) Microsoft Corp 1984-1993.  All rights reserved.

Run File [uno.exe]:
List File [nul.map]:
Libraries [.lib]: libreria
Definitions File [nul.def]:

c:\Assembly>
A
c:\Assembly>
Ottimo.

Non utilizzare SP è fondamentale in modo da poter continuare a usare lo stack anche nel contesto della procedura, ovvio!
Un ulteriore passaggio: BP potrebbe essere "occupato" con un valore importante, quindi per non perderlo lo pushiamo nello stack prima di usarlo come puntatore dei parametri.
Ovviamente, nel puntare i parametri bisogna aggiungere 2 in più a BP perchè oltre all'indirizzo di ritorno va saltato anche il valore precedente di BP pushato per ultimo nello stack.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 push bp
 mov bp,sp
 mov bx,[bp+4]
 mov ax,[bp+6]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 


Adesso "sistemiamo" anche il recupero dello stack...
Intanto assemblo e linko per poi seguirmi tutte le istruzioni con -t e -p in debug. Non c'è bisogno di fare la "dimostrazione pratica" dell'errore, perchè incontro una finestra di messaggio secondo cui NTVDM ha incontrato un errore.

Ovviamente, lo stack non perdona! Finchè l'ultimo valore pushato è l'indirizzo di ritorno, con l'istruzione RET esso viene poppato e usato per far "rientrare" il flusso nel programma principale, ma adesso se non ripoppo il valore in BP questo viene ripoppato dall'istruzione RET e scambiato per l'indirizzo di ritorno, dirottando il programma vattelappesca dove!
Rimedio:
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push ax
 push bx
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 push bp
 mov bp,sp
 mov bx,[bp+4]
 mov ax,[bp+6]
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 pop bp
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
assemblando e linkando...
A
c:\Assembly>
Perfetto... per il momento.
Ma andiamo a seguire in debug: c'è qualcosa che non mi torna: sono stati pushati due valori che non vengono poppati...
AX=0041  BX=0000  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0005   NV UP EI PL NZ NA PO NC
1781:0005 B30C          MOV     BL,0C
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0007   NV UP EI PL NZ NA PO NC
1781:0007 50            PUSH    AX
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0008   NV UP EI PL NZ NA PO NC
1781:0008 53            PUSH    BX
-p

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0009   NV UP EI PL NZ NA PO NC
1781:0009 E80700        CALL    0013
-t
Ecco, con il "pushaggio" di AX e BX lo Stack Pointer è giustamente arretrato di 4 bytes.

AX=0041  BX=000C  CX=0440  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0013   NV UP EI PL NZ NA PO NC
1781:0013 55            PUSH    BP
-p
e adesso è andato ancora più indietro, a 03FAH, per il "pushaggio" dell'indirizzo di ritorno.

AX=0041  BX=000C  CX=0440  DX=0000  SP=03F8  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0014   NV UP EI PL NZ NA PO NC
1781:0014 8BEC          MOV     BP,SP
-p
E ancora più indietro, in quanto nel contesto della procedura viene pushato anche BP.

Seguiamo lo svolgersi della procedura...
.....

-p
A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03F8  BP=03F8  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0025   NV UP EI PL NZ NA PO NC
1781:0025 5D            POP     BP
-p

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0026   NV UP EI PL NZ NA PO NC
1781:0026 C3            RET
-p
Ecco, con il "poppaggio" di BP per restituire a questo registro il suo valore originario, SP è "risalito" a 03FAH.

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000C   NV UP EI PL NZ NA PO NC
1781:000C E81800        CALL    0027
-p
...e con il "poppaggio" dell'indirizzo di ritorno ad opera dell'istruzione RET SP viene aumentato ancora di 2, andando a 03FCH.

Continuiamo a seguire il programma...
AX=1970  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=000F   NV UP EI PL NZ NA PO NC
1781:000F B44C          MOV     AH,4C
-
-p

AX=4C70  BX=000C  CX=0001  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1785  CS=1781  IP=0011   NV UP EI PL NZ NA PO NC
1781:0011 CD21          INT     21
-p

Program terminated normally
-
...laddove c'è un conto in sospeso con lo stack!!!

Come si fa a pareggiare i conti con lo stack?

Parametri tramite stack: due erroracci che si compensano!

Ecco trattata la procedura Stampa mediante passaggio di parametri per mezzo dello stack.
extrn Cancella:near

extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 push bx
 push ax
 call Stampa
 
 
 call KeyWait
 
 mov ah,4ch
 int 21H
 
stampa proc near
 pop ax
 mov bx,ax
 pop ax
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp
text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
Funziona.
Vediamo nel dettaglio cosa accade:
AX=0003  BX=0000  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0003   NV UP EI PL NZ NA PO NC
1781:0003 B041          MOV     AL,41
-p

AX=0041  BX=0000  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0005   NV UP EI PL NZ NA PO NC
1781:0005 B30C          MOV     BL,0C
-
qui in AL e BL vengono posti rispettivamente il carattere e il colore.
AX=0041  BX=0000  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0005   NV UP EI PL NZ NA PO NC
1781:0005 B30C          MOV     BL,0C
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0007   NV UP EI PL NZ NA PO NC
1781:0007 53            PUSH    BX
-d 1784:03f0
1784:03F0  00 00 00 00 03 00 41 00-00 00 07 00 81 17 C4 11   ......A.........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Prima che i parametri vengano passati tramite stack, ottengo un dump della "cima" dello stack.
Adesso vediamo come cambia dopo il primo push:
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0008   NV UP EI PL NZ NA PO NC
1781:0008 50            PUSH    AX
-d 1784:03f0
1784:03F0  00 00 00 00 41 00 00 00-08 00 81 17 C4 11 0C 00   ....A...........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Ecco: in cima allo stack è stato depositato il colore presente in BH. SP è stato decrementato di 2 (è stata inserita una WORD)
Secondo push:
AX=0041  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0009   NV UP EI PL NZ NA PO NC
1781:0009 E80700        CALL    0013
-d 1784:03f0
1784:03F0  00 00 41 00 00 00 09 00-81 17 C4 11 41 00 0C 00   ..A.........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
E adesso in cima allo stack è stato depositato anche il carattere (41H, codice ASCII della A), e SP è stato decrementato ancora di 2.

Successivamente, con il CALL (NEAR), viene depositato nello stack l'indirizzo "di ritorno" calcolato:
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0009   NV UP EI PL NZ NA PO NC
1781:0009 E80700        CALL    0013
-t

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FA  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0013   NV UP EI PL NZ NA PO NC
1781:0013 58            POP     AX
-d1784:03f0
1784:03F0  41 00 00 00 13 00 81 17-C4 11 0C 00 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
E guardacaso, una fortunata (o sfortunata) coincidenza fa sì che casualmente il programma "funzioni"! adesso faccio POP AX:
-p

AX=000C  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0014   NV UP EI PL NZ NA PO NC
1781:0014 8BD8          MOV     BX,AX
-d 1784:03f0
1784:03F0  41 00 0C 00 00 00 14 00-81 17 C4 11 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Infatti l'indirizzo di ritorno è 000C, che è uguale alla word pushata che contiene il codice di colore rosso (0CH), e che viene posta in AX per poi essere passata in BX, passando in BL il codice di colore per la procedura Stampa.

Infatti non avevo tenuto conto, nel manipolare lo stack dalla procedura, che bisognava considerare l'indirizzo di ritorno messo in cima allo stack!
Mi meraviglio del fatto che il programma funzioni normalmente, ma forse ho capito perchè. Vado avanti:
-p

AX=000C  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0014   NV UP EI PL NZ NA PO NC
1781:0014 8BD8          MOV     BX,AX
-d 1784:03f0
1784:03F0  41 00 0C 00 00 00 14 00-81 17 C4 11 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-p

AX=000C  BX=000C  CX=0430  DX=0000  SP=03FC  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0016   NV UP EI PL NZ NA PO NC
1781:0016 58            POP     AX
-d 1784:03f0
1784:03F0  41 00 0C 00 00 00 16 00-81 17 C4 11 41 00 0C 00   A...........A...
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-p

AX=0041  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0017   NV UP EI PL NZ NA PO NC
1781:0017 B409          MOV     AH,09
-d 1784:03f0
1784:03F0  41 00 0C 00 41 00 00 00-17 00 81 17 C4 11 0C 00   A...A...........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Il secondo POP AX mette in AX il valore della lettera 41H.
-p

AX=0941  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0019   NV UP EI PL NZ NA PO NC
1781:0019 B700          MOV     BH,00
-p

AX=0941  BX=000C  CX=0430  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=001B   NV UP EI PL NZ NA PO NC
1781:001B B90100        MOV     CX,0001
-p

AX=0941  BX=000C  CX=0001  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=001E   NV UP EI PL NZ NA PO NC
1781:001E CD10          INT     10
-p
A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 C3            RET
-p
E già: ho erroneamente invertito l'ordine dei parametri immessi nello stack! Secondo errore, che ha compensato il primo! Infatti ho pushato prima il colore e dopo la lettera, mentre nella procedura ho poppato prima il colore e dopo la lettera, cosa che se non avessi avuto casualmente l'indirizzo di ritorno contenente esattamente il codice del colore nella parte bassa della WORD avrebbe generato una "stranezza".

Adesso RET riprende quello che nelle mie erronee intenzioni doveva essere il colore, e che coincide con l'indirizzo di ritorno per puro caso, e reindirizza IP nel modo giusto.
-p
A
AX=0941  BX=000C  CX=0001  DX=0000  SP=03FE  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=0020   NV UP EI PL NZ NA PO NC
1781:0020 C3            RET
-t

AX=0941  BX=000C  CX=0001  DX=0000  SP=0400  BP=0000  SI=0000  DI=0000
DS=1771  ES=1771  SS=1784  CS=1781  IP=000C   NV UP EI PL NZ NA PO NC
1781:000C E81200        CALL    0021
-d 1784:03f0
1784:03F0  41 00 0C 00 41 09 41 09-00 00 0C 00 81 17 C4 11   A...A.A.........
1784:0400  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0410  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0420  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0430  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0440  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0450  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1784:0460  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
...pronto a chiamare la procedura KeyWait.

Simpatico, questo errore compensato da un altro errore!!!

Così, anziche poppare i parametri dallo stack, ho capito che devo usare istruzioni diverse per poterli richiamare dallo stack nel corpo della procedura, perchè per poppare i parametri non posso fare a meno di poppare anche l'indirizzo di ritorno, creando casini!

Uso di procedure in libreria.

Adesso veniamo a una procedura, quella che ho già chiamato X, per perfezionarla...

Diversamente dalle altre che ho già "impacchettato", questa procedura necessita della preparazione di alcuni registri prima di essere chiamata.

Ho già fatto in modo che il valore in AL, in cui va messo il carattere da stampare, sia preparato prima, e adesso devo fare in modo che anche BL, in cui va messo il colore, sia trattato nello stesso modo. La rinomino "stampa".
public stampa
text segment byte public

stampa proc near
 mov ah,09h
 mov bh,00h
 mov cx,1
 int 10h
 ret
stampa endp

text ends
end
Eseguo il solito "impacchettamento"
c:\Assembly>esegui stampa
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: stampa.asm
MASM : fatal error A1000: cannot open file : stampa.asm

c:\Assembly>esegui stampa
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: stampa.asm

c:\Assembly>lib libreria + stampa

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

List file:
Output library:

c:\Assembly>
c:\Assembly>lib libreria

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

Operations:
List file: con
CANCELLA..........cancella          KEYWAIT...........KeyWait
STAMPA............stampa            X.................proced


proced            Offset: 00000010H  Code and data size: cH
  X

cancella          Offset: 00000070H  Code and data size: 7H
  CANCELLA

KeyWait           Offset: 000000d0H  Code and data size: 5H
  KEYWAIT

stampa            Offset: 00000130H  Code and data size: aH
  STAMPA

c:\Assembly>
(esegui è un file BAT che ho costruito per assemblare senza stare a ripetere gli switch ogni volta)

Bene.
Adesso uso il ptogramma principale con queste procedure.
extrn Cancella:near
extrn Stampa:near
extrn KeyWait:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 call Cancella
 
 mov al,'A'
 mov bl,0Ch
 call Stampa
 
 call KeyWait
 
 mov ah,4ch
 int 21H

text ends

stack segment para stack
 db 1024 dup(00H)

stack ends

end start
 
E funziona benissimo!

Creazione di procedure e immissione in libreria.

Ecco, i dati.
Nei modelli di Giobe i dati sono compresi fra l'inizio del programma principale e l'indirizzo cui si salta partendo dall'inizio.
Prima però mi ricostruisco le procedure e le metto nella libreria.
public Cancella
text segment byte public
Cancella proc near
 mov ah,00h
 mov al,03h
 int 10h
 ret
Cancella endp
text ends
end


c:\Assembly>esegui cancella
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: cancella.asm

c:\Assembly>lib libreria+cancella

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

List file:
Output library:

c:\Assembly>
c:\Assembly>lib libreria

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

Operations:
List file: con
CANCELLA..........cancella          X.................proced


proced            Offset: 00000010H  Code and data size: cH
  X

cancella          Offset: 00000070H  Code and data size: 7H
  CANCELLA

c:\Assembly>

public KeyWait
text segment byte public
KeyWait proc near
mov ah,00h
int 16h
ret
KeyWait endp
text ends
end
c:\Assembly>lib libreria+KeyWait

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

List file:
Output library:

c:\Assembly>lib libreria

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

Operations:
List file: con
CANCELLA..........cancella          KEYWAIT...........KeyWait
X.................proced

proced            Offset: 00000010H  Code and data size: cH
  X

cancella          Offset: 00000070H  Code and data size: 7H
  CANCELLA

KeyWait           Offset: 000000d0H  Code and data size: 5H
  KEYWAIT

c:\Assembly>


Ecco...

giovedì 19 settembre 2013

Ripasso librerie in assembly

Ho ripassato le procedure esterne NEAR (ossia facenti parte dello stesso segmento).
Ecco i due sorgenti, quello del programma principale e quello di una procedura esterna:
extrn X:near
;definiamo il segmento
text segment public
;inizio del programma
start:
 mov ah,00h
 mov al,03h
 int 10h
 
 mov al,'X'
 call X
 
 mov ah,00h
 int 16h
 
 mov ah,4ch
 int 21H

text ends
end start
 
public X
text segment public

X proc near
 
 mov ah,09h
 mov bh,00h
 mov bl,0ch
 mov cx,1
 int 10h
 ret
X endp
text ends
end
assemblo e linko insieme l'uno e l'altro:

c:\Assembly>ml /Zm /c uno.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

c:\Assembly>ml /Zm /c proced.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: proced.asm

c:\Assembly>link uno proced

Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994
Copyright (C) Microsoft Corp 1984-1993.  All rights reserved.

Run File [uno.exe]:
List File [nul.map]:
Libraries [.lib]:
Definitions File [nul.def]:
LINK : warning L4021: no stack segment

c:\Assembly>
e funziona:
X
c:\Assembly>

Adesso, invece di linkare due files oggetto insieme uso una libreria:
Costruisco una libreria.

c:\Assembly>lib libreria

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

Library does not exist.  Create? (y/n) y
Operations:
List file:

c:\Assembly>dir libreria*
 Il volume nell'unità C non ha etichetta.
 Numero di serie del volume: ECE6-4560

 Directory di c:\Assembly

20/09/2013  00.31             1.033 libreria.lib
               1 File          1.033 byte
               0 Directory  12.002.516.992 byte disponibili

c:\Assembly>
Adesso ci agginugo il file oggetto proced.obj.
Troviamolo...
c:\Assembly>dir proced*
 Il volume nell'unità C non ha etichetta.
 Numero di serie del volume: ECE6-4560

 Directory di c:\Assembly

19/09/2013  22.07               137 proced.asm
20/09/2013  00.33                77 proced.obj
               2 File            214 byte
               0 Directory  12.002.582.528 byte disponibili

c:\Assembly>
Eccolo!
Aggiungiamolo:
c:\Assembly>lib libreria + proced

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

List file:
Output library:

c:\Assembly>
Leggiamo la libreria per vedere se contiene la procedura:
c:\Assembly>lib libreria

Microsoft (R) Library Manager  Version 3.40
Copyright (C) Microsoft Corp 1983-1993.  All rights reserved.

Operations:
List file: con
X.................proced

proced            Offset: 00000010H  Code and data size: cH
  X

c:\Assembly>
Perfetto!

Procedura nello stesso modulo

Andiamo avanti...

;definiamo il segmento
text segment 
;inizio del programma
start:
 mov ah,00h
 mov al,03h
 int 10h
 
 mov ah,09h
 mov al,'A'
 mov bh,00h
 mov bl,0ch
 mov cx,1
 int 10h
 
 mov ah,00h
 int 16h
 
 mov ah,4ch
 int 21H
 
text ends
end start
C:\Assembly>ml /Zm /c uno.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: uno.asm

C:\Assembly>link uno

Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994
Copyright (C) Microsoft Corp 1984-1993.  All rights reserved.

Run File [uno.exe]:
List File [nul.map]:
Libraries [.lib]:
Definitions File [nul.def]:
LINK : warning L4021: no stack segment

C:\Assembly>
A
C:\Assembly>


Il ciclo LOOP

Adesso mi condenso l'istruzione per la stampa in una procedura...
;definiamo il segmento
text segment 
;inizio del programma
start:
 mov ah,00h
 mov al,03h
 int 10h
 
 call X
 mov ah,00h
 int 16h
 
 mov ah,4ch
 int 21H
 
 
X: mov ah,09h
 mov al,'A'
 mov bh,00h
 mov bl,0ch
 mov cx,1
 int 10h
 ret

text ends
end start
 
Non ho usato la sintassi con PROC, ma da vaghe reminiscenze mi sembra di ricordare che per le procedure vicine, ossia nello stesso segmento, si possa usare anche una semplice LABEL...

A
C:\Assembly>
funziona lo stesso.

Meglio poter scegliere in anticipo il carattere, no?
;definiamo il segmento
text segment 
;inizio del programma
start:
 mov ah,00h
 mov al,03h
 int 10h
 
 mov al,'X'
 call X
 
 mov ah,00h
 int 16h
 
 mov ah,4ch
 int 21H
 
 
X: mov ah,09h
 mov bh,00h
 mov bl,0ch
 mov cx,1
 int 10h
 ret

text ends
end start
 
sarebbe meglio scegliere in anticipo anche il colore, ma adesso quello che mi interessa è ripassare l'uso delle procedure.
X
C:\Assembly>
Bene.

Infatti, quello che mi interessa è usare le procedure esterne.
Riscrivo tutto con la sintassi PROC...
;definiamo il segmento
text segment 
;inizio del programma
start:
 mov ah,00h
 mov al,03h
 int 10h
 
 mov al,'X'
 call X
 
 mov ah,00h
 int 16h
 
 mov ah,4ch
 int 21H
 
X proc near
 
 mov ah,09h
 mov bh,00h
 mov bl,0ch
 mov cx,1
 int 10h
 ret
X endp

text ends
end start
 
E adesso si viene alle librerie...

mercoledì 18 settembre 2013

E vai con l'assembly!!!

Ho copiato, in assenza di una soluzione di condivisione di cartelle e appunti, tramite DropBox, alcuni files fondamentali della cartella Arch-Lab dal SO "di base" a questo virtuale.
Ho così creato la cartella "Assembly" in C:\
C:\Users\Antonello>cd..\..\

C:\>cd assembly

C:\Assembly>dir
 Il volume nell'unità C non ha etichetta.
 Numero di serie del volume: ECE6-4560

 Directory di C:\Assembly

19/09/2013  01.31    <DIR>          .
19/09/2013  01.31    <DIR>          ..
28/06/2006  14.30           134.144 LIB.EXE
28/06/2006  14.30           364.544 LINK.EXE
15/09/2013  18.42             7.916 MASM_1.COM
15/09/2013  18.42             4.106 MASM_IN1.COM
15/09/2013  18.42             9.687 ML.ERR
15/09/2013  18.42           372.736 ML.EXE
15/09/2013  18.42            38.202 NG.EXE
               7 File        931.335 byte
               2 Directory  18.526.797.824 byte disponibili

C:\Assembly>
Ora vediamo di farci qualcosina...
Non sapendo usare ML come linker, ci avevo messo un vecchio linker. Compilo sul blocco note (non ho copiato QEditor) la parte fondamentale del primo esercizio della Palestra Apprendisti (roba elementare):
prog segment byte public

inizio:

mov ah,00h
mov al,03h
int 10h

mov ah,0eh
mov al,'A'
int 10h

mov ah,00h
int 16h

mov ah,4ch
int 21h



prog ends
end  inizio
e quindi sottopongo il sorgente al processo di assemblaggio e linkaggio...
C:\Assembly>ml /Zm /c esa1.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: esa1.asm

C:\Assembly>link esa1

Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994
Copyright (C) Microsoft Corp 1984-1993.  All rights reserved.

Run File [esa1.exe]:
List File [nul.map]:
Libraries [.lib]:
Definitions File [nul.def]:
LINK : warning L4021: no stack segment

C:\Assembly>dir esa*
 Il volume nell'unità C non ha etichetta.
 Numero di serie del volume: ECE6-4560

 Directory di C:\Assembly

19/09/2013  01.41               181 esa1.asm
19/09/2013  01.41               532 esa1.exe
19/09/2013  01.41                69 esa1.obj
               3 File            782 byte
               0 Directory  18.052.173.824 byte disponibili

C:\Assembly>
Perfetto!

Eseguiamo...
A
C:\Assembly>
Okay! Fantastico!

VirtualBox, Vista e i programmi a 16 bit

Per ristudiare e approfondire l'assembly a 16 bit dal sito di Giobe e altri, non riuscendo a ottenere da Windows 8 a 64 bit l'esecuzione di programmi a 16 bit, ho trovato questa soluzione, ancora in divenire, che mi sembra piuttosto soddisfacente...

Tramite VirtualBox, ho aperto su questo computer una finestra di Windows Vista a 32 bit, con la quale dovrei riuscire a eseguire tutto ciò che mi serve, e anche a pubblicare in rete i risultati.

Adesso proverò a continuare questo post da quell'altro ambiente.
Eccomi qua.
Sto continuando da Vista, sempre con lo stesso computer.
Adesso posterò la mia bella finestrona DOS con l'esecuzione (finalmente) di Debug, che in Windows 8 a 64 bit non esiste.
C:\Users\Antonello>debug
-d
1760:0100  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1760:0110  00 00 00 00 00 00 00 00-00 00 00 00 34 00 4F 17   ............4.O.
1760:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1760:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1760:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1760:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1760:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1760:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=1760  ES=1760  SS=1760  CS=1760  IP=0102   NV UP EI NG NZ NA PO NC
1760:0102 0000          ADD     [BX+SI],AL                         DS:0000=CD
-
Non ho ancora installato l'"ambiente Assembly", ma questo è già un ottimo passo avanti.
Non sono ancora riuscito a risolvere il problema della condivisione di cartelle e clipboard fra la VirtualBox e il sistema operativo del computer...

Ottimo, comunque!

lunedì 16 settembre 2013

Chiamata a una procedura nello stesso segmento

Continuiamo a ripassare...

prog SEGMENT BYTE PUBLIC 'CODE'
INIZIO:
JMP x
x: MOV AH,00H
MOV AL,03H
INT 10H
CALL prc
MOV AH,00H
INT 16H
MOV AH,4CH
INT 21h

prc PROC NEAR
MOV AH,09H
MOV AL,'X'
MOV BH,00H
MOV CX,0001H
MOV BL,1FH
INT 10H
RET
prc ENDP

prog ENDS
END INIZIO
Ecco, ho messo una chiamata a una procedura contenuta nello stesso segmento.
Mi sono dannato perchè non avevo messo l'istruzione RET... Succede, quando non hai fresche le nozioni.

Adesso come faccio a vedere cosa accade nello stack quando si fa una chiamata? Il DEBUG non funziona, qui...

Ripasso elementare di Assembly dalla palestra di Giobe.

Bene.
Con questo sistema operativo a 64 bit, che non supporta nulla a 16 bit, mi era impossibile ripassare l'assembly a 16 bit.
Ho risolto usando un editor qualunque, facendo assemblare e linkare da ML e LINK con la finestra DOS e facendo eseguire gli eseguibili con DosBox.
Funziona!

Ripassiamo da capo...

Ecco, definiamo il SEGMENTO.
prog SEGMENT BYTE PUBLIC 'CODE'



prog ENDS

E pian piano, ripassando i vari "interrupt", ho ricostruito un programmino:
prog SEGMENT BYTE PUBLIC 'CODE'
INIZIO:

MOV AH,00H
MOV AL,03H
INT 10H

MOV AH,09H
MOV AL,'X'
MOV BH,00H
MOV CX,0001H
MOV BL,1FH
INT 10H

MOV AH,00H
INT 16H

MOV AH,4CH
INT 21h


prog ENDS
END INIZIO
Già: un ripasso non è mai dover reimparare tutto da zero, anche se può sembrare.

sabato 14 settembre 2013

Un "Cretino" in Assembly detto da me medesimo

Ecco come un programmino in Assembly mi suona un file sonoro:
.386
.MODEL Flat,STDCALL
option casemap:none
;include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib 
include \masm32\include\winmm.inc
includelib \masm32\lib\winmm.lib
.DATA
comando db "open C:\users\antonello\desktop\ciccio.wav alias suono",0
comando2 db "play suono wait",0
.DATA?
.CONST
.CODE
start:

invoke mciSendString, addr comando, 0, 0, 0
invoke mciSendString, addr comando2, 0, 0, 0

invoke ExitProcess,0
end start
...e mi sento dire "CRETINO" con la mia voce, registrata su un file wav...

Ripassare l'Assembly!

Ed eccoci al ripasso dell'Assembly... mi sembra di non ricordare più quasi niente...

Il tutorial è questo.

Il codice è questo, ricopiato pedissequamente dal tutorial:
.386
.MODEL Flat,STDCALL
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib 
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.DATA
MsgBoxCaption  db "Iczelion Tutorial No.2",0
MsgBoxText       db "Win32 Assembly is Great!",0
.DATA?
.CONST
.CODE
start:

invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK 
    invoke ExitProcess,0
end start
che viene assemblato e linkato così, seguendo pedissequamente le istruzioni del tutorial:
C:\masm32>ml /c /coff /Cp prova.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: prova.asm

C:\masm32>ml /c /coff /Cp prova.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: prova.asm

***********
ASCII build
***********


C:\masm32>link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib prova
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


C:\masm32>
senza errori, dunque...

Lo avvio:
C:\masm32>prova

C:\masm32>
...e mi dà la messagebox... che carina!


Adesso mi faccio un viaggio all'interno di windows.inc e vediamo cosa c'è... Incontro la definizone delle costanti NULL e MB_OK... Sta' a vedere che questo file viene incluso soltanto per avere le definizioni delle costanti? Ho vaghe reminiscenze che...

Bene. "Commentiamo" la riga del codice che include windows.inc e vediamo che succede.

.386
.MODEL Flat,STDCALL
option casemap:none
;include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib 
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.DATA
MsgBoxCaption  db "Iczelion Tutorial No.2",0
MsgBoxText       db "Win32 Assembly is Great!",0
.DATA?
.CONST
.CODE
start:

invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK 
    invoke ExitProcess,0
end start
...assembliamo...
C:\masm32>ml /c /coff /Cp prova.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: prova.asm
prova.asm(17) : error A2006: undefined symbol : MB_OK
prova.asm(17) : error A2114: INVOKE argument type mismatch : argument : 4
prova.asm(17) : error A2006: undefined symbol : NULL
prova.asm(17) : error A2114: INVOKE argument type mismatch : argument : 1

C:\masm32>
Bene: riga 17 ottengo "simbolo indefinito: MB_OK" e un errore relativo agli argomenti di INVOKE, precisamente all'argomento 4. Stessa cosa per il simbolo NULL, argomento 1.
Questo conferma la mia reminiscenza circa il fatto che nel windows.inc siano contenute le costanti.
Bene, andiamo a vedere il valore delle costanti sul file windows.inc...
Copio uno stralcio del file nel quale è presente NULL:
TRUE                                 equ 1
FALSE                                equ 0
NULL                                 equ 0
Normal                               equ 000000h
ReadOnly                             equ 000001h
Bene, NULL è pari a zero.

Copiamo un altro stralcio dove è presente la costante MB_OK:
SB_DISABLE_DOWN                     equ 2h
ESB_DISABLE_LTUP                     equ ESB_DISABLE_LEFT
ESB_DISABLE_RTDN                     equ ESB_DISABLE_RIGHT
MB_OK                                equ 0h
MB_OKCANCEL                          equ 1h
MB_ABORTRETRYIGNORE                  equ 2h
MB_YESNOCANCEL                       equ 3h
MB_YESNO                             equ 4h
MB_RETRYCANCEL                       equ 5h
MB_ICONHAND                          equ 10h
MB_ICONQUESTION                      equ 20h
Bene, MB_OK è pari a zero.

Allora, se la sua funzione in questo caso è solo quella di "tradurre" le costanti in valori numerici, dovrei rendere inutile l'inclusione di windows.inc se sostituisco nel codice sorgente questi simboli con i loro valori numerici.
.386
.MODEL Flat,STDCALL
option casemap:none
;include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib 
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.DATA
MsgBoxCaption  db "Iczelion Tutorial No.2",0
MsgBoxText       db "Win32 Assembly is Great!",0
.DATA?
.CONST
.CODE
start:

invoke MessageBox, 0, addr MsgBoxText, addr MsgBoxCaption, 0 
    invoke ExitProcess,0
end start
Salvo e provo a riassemblare:
C:\masm32>ml /c /coff /Cp prova.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: prova.asm

C:\masm32>
Perfetto! Allora linkiamo pure e vediamo se funziona.
C:\masm32>link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib prova.obj
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

LINK : fatal error LNK1104: cannot open file "prova.exe"

C:\masm32>
AYAYAYAYAY!!!!! E che è successo???

Un momento! Il programma è in esecuzione! E certo! E' una causa frequente di errore nel linkaggio!
Chiudo la MessageBox che sta ancora lì...

C:\masm32>link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib prova.obj
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


C:\masm32>
Benissimo. Il problema era proprio quello.

Faccio partire il programma.
C:\masm32>prova

C:\masm32>
Ed ecco che funziona egregiamente!

sabato 2 giugno 2012

Dissezione del file oggetto: il campo Locat del subrecord FIXUP del record FIXUPP

Dunque...

Vediamo cosa abbiamo ricavato finora.

I record SEGDEF definiscono le caratteristiche dei vari segmenti.
In pratica, definiscono:
  • L'allineamento;
  • Il rango (combine);
  • la lunghezza.

I record LEDATA, per quanto ricordo, contengono il "contenuto" vero e proprio dei segmenti.

La parte più difficile è il record FIXUPP.
Esso, a parte i primi tre bytes di rito, contiene un subrecord che, a quanto ho capito, può essere di due tipi: FIXUP o THREAD (non meglio da me conosciuti, prendiamoli per buoni).
Se si tratti di un subrecord FIXUP o THREAD, dipende dal primo byte di questo subrecord.
Vediamo sul pratico...

Il codice che identifica il record FIXUPP è il 9C.
00 B4 00 CD 16 B4 4C CD-21 F2 9C 0F 00 C8 03 54
01 C4 10 54 01 C4 15 50-02 07 00 DA 8A 06 00 C1


Eccolo isolato:
9C 0F 00 C8 03 54 01 C4 10 54 01 C4 15 50 02 07 00 DA


...e depurato dei tre bytes rituali iniziali e dell'ultimo (checksum):
C8 03 54 01 C4 10 54 01 C4 15 50 02 07 00

Ora, questa sequenza di bytes è fatta di un unico sottorecord, che può essere di tipo THREAD o di tipo FIXUP, e la differenza è specificata dal primo bit.
Mi sembra ovvio che, essendo il primo bytes C8, il primo bit è pari a 1, il che significa che si tratta di un sottorecord di tipo FIXUP.

Salto tutto il capitolo del documento relativo ai subrecord di tipo THREAD e vado a qualli di tipo FIXUP.

Il primo campo del subrecord FIXUP si chiama Locat, ed è lungo due bytes.
Eccolo isolato:
C8 03 54 01 C4 10 54 01 C4 15 50 02 07 00
  • Il primo bit del campo Locat è quello che identifica il record come di tipo FIXUP.
  • Il secondo bit del campo Locat distingue fra self-relative e segment-relative fizup. Non ricordo bene cosa significhi, ho una vaga idea...
  • I successivi 4 bit formano il sottocampo Location e specificano il tipo di aggiustamento che va fatto: credo si tratti di specificare se quello da aggiustare è l'offset o l'indirizzo di segmento o ambedue...
  • I restanti 10 bits formano il Segment Data Offset, ossia indicano la posizione dove effettuare il riaggiustamento nel record LEDATA precedente.

Che macello!!!.
Vediamo il campo Locat di questo subrecord di tipo FIXUP:
C8 03 = 1100-1000 0000-0011
Dividiamo i sottocampi:
1-1-0010-0000000011.
Il primo bit è 1, il che identifica questo sottorecord come un sottorecord di tipo FIXUP e non THREAD.
Il secondo bit è 1, il che significa che si tratta di un aggiustamento segment-relative.
Il campo successivo è Location, e indica il tipo di aggiustamento che va fatto, in questo caso il suo valore è 2, e significa che è un aggiustamento 16 bit-base.
Vediamolo con dispObj:
FIXUPP:  Type 9C, Offset 006A
         Segment-relative base location 003 (003),
           Frame & Target segment DATA
         Segment-relative word offset location 010 (010),
           Frame & Target segment DATA
         Segment-relative word offset location 015 (015),
           Frame & Target segment TEXT, offset 0007
    0000:9C 0F 00 C8 03 54 01 C4-10 54 01 C4 15 50 02 07 ­ ...╚.T.─.T.─.P..
    0010:00                                              ­ .
Ho beccato il primo di tre aggiustamenti.
"Segment-relative base location".

E abbiamo visto i primi tre sottocampi di Locat.
Adesso vediamo l'altro, che è 00000000011, ossia 3, e indica in quale offset del LEDATA precedente va fatto l'aggiustamento.
Corrisponde a quanto detto dal dispObj, ossia 003.
Andiamo a vedere il LEDATA:
0000:EB 07 BA 00 00 8E DA FA-FA B4 00 B0 03 CD 10 A1
0010:00 00 2E 8B 1E 00 00 B4-00 CD 16 B4 4C CD 21
Eccolo!

Ora mi esercito con gli altri, sempre sul campo Locat.
Riprendo il record, e lo separo nei vari sottorecords FIXUP:
C8 03 54 01 
C4 10 54 01 
C4 15 50 02 07 00
(l'ultimo sottorecord FIXUPP è più lungo, non so perchè, lo vedrò poi) Ecco i campi Locat.
1100-1000 0000-0003

1100-0100 0001-0000

1100-0100 0001-0101


Dividiamoli in sottocampi:
1 1 0010 0000000003

1 1 0001 0000010000

1 1 0001 0000010101
Traduzione: Nel primo campo Locat il sottorecord è FIXUP, la correzione è segment-relative, si tratta di una rilocazione dell'indirizzo di segmento (base location). La posizione nel LEDATA è 003.

Nel secondo campo Locat il sottorecord è FIXUP, la correzione è segment-relative, si tratta di una rilocazione di offset. La posizione nel LEDATA è 010.

Nel terzo campo Locat il sottorecord è FIXUP, la correzione è segment-relative, si tratta di una rilocazione di offset. La posizione nel LEDATA è 015.

Confrontiamo col responso di dispObj:
FIXUPP:  Type 9C, Offset 006A
         Segment-relative base location 003 (003),
           Frame & Target segment DATA
         Segment-relative word offset location 010 (010),
           Frame & Target segment DATA
         Segment-relative word offset location 015 (015),
           Frame & Target segment TEXT, offset 0007
Esatto!!!