JavascriptProva

mercoledì 30 maggio 2012

Dissezione del file oggetto: il record SEGDEF

Mi sono arenato.
Non ricordo più i concetti di classe e combine di un segmento.

Scrivo un sorgentino con le classi.
text SEGMENT "CODE"

inizio: JMP     Procedura

Var     DW 0FAFAH

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

        MOV     AX,[Var]
        MOV     BX,Var
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Ecco il record LNAMES del file oggetto:
 96 0C 00 00 04 54 45 58 54 04 43 4F 44 45 F6 
Ecco, ho ottenuto in sequenza il nome del segmento e il nome della classe, TEXT e CODE.

Avendo retrodatato il computer riesco a far funzionare anche il programma dispObj:
LNAMES:  Type 96, Offset 000C
            1: Name
            2: Name TEXT
            3: Name CODE
    0000:96 0C 00 00 04 54 45 58-54 04 43 4F 44 45       ­ .....TEXT.CODE
Quindi vado a SEGDEF:
SEGDEF:  Type 98, Offset 001B
         Reloc para-aligned, combine type none, length 001B
         Segment name TEXT, class CODE
    0000:98 07 00 60 1B 00 02 03-01                      ­ ...`.....
Dove l'allineamento al paragrafo è specificato dal primo sottocampo di tre bytes pari a 3, la combine "none" dal secondo sottocampo di tre bytes pari a zero.
Vengono quindi:
-la lunghezza del segmento pari a 001B (27) bytes;
-l'indice del nome del segmento pari a 02 (ossia TEXT)
-l'indice del nome della classe pari a 03 (ossia CODE).


Adesso mi sbizzarrisco a creare un sorgente con più segmenti:
data SEGMENT "DATI"
uno     dw 1234h
due     dw 3456h
data ENDS

text SEGMENT "CODE"

inizio: JMP     Procedura

Var     DW 0FAFAH

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

        MOV     AX,[Var]
        MOV     BX,Var
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Vediamo dispObj:
LNAMES:  Type 96, Offset 000C
            1: Name
            2: Name DATA
            3: Name TEXT
            4: Name CODE
            5: Name DATI
    0000:96 16 00 00 04 44 41 54-41 04 54 45 58 54 04 43 ­ .....DATA.TEXT.C
    0010:4F 44 45 04 44 41 54 49-                        ­ ODE.DATI
SEGDEF:  Type 98, Offset 0025
         Reloc para-aligned, combine type none, length 0004
         Segment name DATA, class DATI
    0000:98 07 00 60 04 00 02 05-01                      ­ ...`.....
SEGDEF:  Type 98, Offset 002F
         Reloc para-aligned, combine type none, length 001B
         Segment name TEXT, class CODE
    0000:98 07 00 60 1B 00 03 04-01                      ­ ...`.....
Ecco: vengono elencati in LNAMES tutti i nomi dei segmenti e delle classo, ed essendoci due segmenti, vengono creati due records SEGDEF.

Il primo è questo:
98 07 00 60 04 00 02 05-01
che si legge:
  • allineamento al paragrafo perchè il primo sottocampo di tre bytes è pari a 3
  • combine=none perchè il secondo sottocampo di tre bytes è pari a zero
  • lunghezza del segmento di 0004 bytes
  • indice del nome del segmento 02, corrispondente a DATA
  • indice della classe del segmento 05 corrispondente a DATI

Il secondo è questo:
98 07 00 60 1B 00 03 04-01
che si legge:
  • allineamento al paragrafo perchè il primo sottocampo di tre bytes è pari a 3
  • combine=none perchè il secondo sottocampo di tre bytes è pari a zero
  • lunghezza del segmento di 001B (27) bytes
  • indice del nome del segmento 03, corrispondente a TEXT
  • indice della classe del segmento 04, corrispondente a CODE.

Quindi, dopo un record LNAMES, che elenca i nomi e le classi dei segmenti, i records SEGDEF specificano per ogni segmento gli attributi (allineamento, combine), la lunghezza di ogni segmento e la corrispondenza con i nomi e le classi specificati in LNAMES.
In pratica, dopo essersi reso conto di tutti i nomi, l'assemblatore elenca i segmenti con nome, classe, lunghezza, allineamento e combine.

Faticosetto, l'argomento!

martedì 29 maggio 2012

Dissezione del file oggetto: record SEGDEF (98H), campo ATTRIBUTI, sottocampo ALLINEAMENTO

Il record SEGDEF (98H) è sicuramente quello che al primo studio di questa roba mi ha dato più grattacapi nel cercare di capirci qualcosa.

SEGDEF:98 07-00 60 1B 00 02 01 01 E2
Il macello sta nel fatto che c'è un campo formato da una serie di lunghezza variabile di bytes che esprimono gli attributi del segmento.
Nella fattispecie abbiamo un byte che vale 60.
Lo scrivo in binario:
01100000
Andiamo per ordine:
Il primo sottocampo è di tre bytes, e vale 011, ossia 3.
Questo è il campo dell'ALIGNMENT del segmento, che può avere i seguenti valori:
  • 0: segmento assoluto (non ricordo più cosa significa)
  • 1: segmento allineato al byte
  • 2: segmento allineato alla word
  • 3: segmento allineato al paragrafo
  • 4: segmento allineato alla pagina
  • 5: segmento allineato alla doubleword
Bene.
Acquisito questo, adesso mi metto a giocare creando segmenti variamente allineati e vedere come varia questo byte.
Anzi parto dall'intenzione di volere un certo valore di questo byte e vediamo se ci riesco variando il sorgente.


Adesso il sottocampo dovrà essere 1, quindi il byte sarà 20.
dati segment byte
dato1 dw 1234h
dato2 dw 5678h
dati ends

text segment
assume cs:text,ds:dati
inizio:
        mov     ah,00h
        mov     al,03h
        int     10h
        
        mov     ah,00h
        int     16h
        
        mov     ah,4ch
        int     21h
       
text ends
end inizio
 80 0A 00 08 6D 69 6F 32-2E 61 73 6D 88 96 0C 00
 00 04 54 45 58 54 04 44-41 54 49 EF 98 07 00 20
Bene!
Adesso il sottocampo dovrà essere 2, quindi il byte sarà 40.
dati segment word
dato1 dw 1234h
dato2 dw 5678h
dati ends

text segment
assume cs:text,ds:dati
inizio:
        mov     ah,00h
        mov     al,03h
        int     10h
        
        mov     ah,00h
        int     16h
        
        mov     ah,4ch
        int     21h
       
text ends
end inizio
80 0A 00 08 6D 69 6F 32-2E 61 73 6D 88 96 0C 00
00 04 54 45 58 54 04 44-41 54 49 EF 98 07 00 40
Ottimo.
Adesso specifichiamo l'allineamento al paragrafo così da trasformare il byte in 60.
Ho capito che l'allineamento di default dei segmenti è al paragrafo.
dati segment para
dato1 dw 1234h
dato2 dw 5678h
dati ends

text segment
assume cs:text,ds:dati
inizio:
        mov     ah,00h
        mov     al,03h
        int     10h
        
        mov     ah,00h
        int     16h
        
        mov     ah,4ch
        int     21h
       
text ends
end inizio
80 0A 00 08 6D 69 6F 32-2E 61 73 6D 88 96 0C 00
00 04 54 45 58 54 04 44-41 54 49 EF 98 07 00 60
Perfetto!
Adesso il sottocampo sarà 4, quindi il byte sarà 80.
dati segment page
dato1 dw 1234h
dato2 dw 5678h
dati ends

text segment
assume cs:text,ds:dati
inizio:
        mov     ah,00h
        mov     al,03h
        int     10h
        
        mov     ah,00h
        int     16h
        
        mov     ah,4ch
        int     21h
       
text ends
end inizio
80 0A 00 08 6D 69 6F 32-2E 61 73 6D 88 96 0C 00
00 04 54 45 58 54 04 44-41 54 49 EF 98 07 00 80
Benissimo!
Adesso il sottocampo sarà 5, quindi il byte sarà A0.
dati segment dword
dato1 dw 1234h
dato2 dw 5678h
dati ends

text segment
assume cs:text,ds:dati
inizio:
        mov     ah,00h
        mov     al,03h
        int     10h
        
        mov     ah,00h
        int     16h
        
        mov     ah,4ch
        int     21h
       
text ends
end inizio
80 0A 00 08 6D 69 6F 32-2E 61 73 6D 88 96 0C 00
00 04 54 45 58 54 04 44-41 54 49 EF 98 07 00 A0
Perfetto!!!

Dissezione del file oggetto: il record LNAMES (96H)

Riporto ancora qui il mio schema:
THEADR:80 09 00 07 6D 69 6F 2E-61 73 6D BC
LNAMES:96 07 00 00 04 54 45 58 54 1A
SEGDEF:98 07-00 60 1B 00 02 01 01 E2
LEDATA:A0 1F 00 01 00 00 EB 02 FA FA B4 00 B0 03 CD 10 2E A1 00 00 2E 8B 1E 00 00 B4 00 CD 16 B4 4C CD 21 F0
FIXUPP:9C 0D 00 C4 0C 50-01 02 00 C4 11 50 01 02 00 0C
MODEND:8A 06 00 C1 50 01-00 00 5E
Dopo i primi tre bytes "rituali", che esprimono il tipo di record e la sua lunghezza, prendiamo il corpo del record.
Esso è fatto di un byte che esprime la lunghezza del nome del segmento e un numero variabile di bytes, che ne esprimono il nome, ambedue questi campi ripetuti per il numero di segmenti che il programma comprende, con uno fisso iniziale di lunghezza zero.

Ne faccio un codice colore:
LNAMES:96 07 00 00 04 54 45 58 54 1A
C'è un primo byte, marcato in celeste, che esprime la lunghezza di una stringa di lunghezza zero, quindi un byte, marcato in celeste, che esprime la lunghezza della stringa contenente il nome dell'unico segmento, marcato in verde: TEXT. Adesso ho scritto rapidamente un sorgentino con due segmenti, e prendo il record LNAMES:
96 0C 00 00 04 44 41 54 49 04 54 45 58 54 EF
. Qui la sequenza lunghezza della stringa-stringa con il nome del segmento è ripetuta due volte: per il segmento DATI e per il segmento TEXT il cui nome appare nei bytes marcati in verde.
Il record LNAMES, espresso dal numero 96H, è un semplice elenco dei nomi dei segmenti di cui è composto il programma.

Dissezione del file oggetto formato OMF

Scaricare il PDF col formato dell'OMF, che se mi sbaglio a digitarlo sul motore di ricerca mi appare tutta la lista dei siti che parlano dell'Ordine dei Frati Minori (OFM)!!!
80H THEADR
Ecco il record:
80 09 00 07 6D 69 6F 2E-61 73 6D BC
Dunque, dunque... qual è la tredicesima lettera dell'alfabeto? La M. E già, ricordiamo anche la firma di Mark Zbikowski 4D: minuscola diventa 6D.

Ecco: la traduzione di questo record è:
  • 80H: questo è il record THEADR
  • 0900: questo record è lungo 9 bytes (escluso il byte che lo identifica e questi stessi due bytes che ne indicano la lunghezza)
  • 07: la stringa riportata in questo record è lunga 7 bytes
  • 6D 69 6F 2E 61 73 6D: "mio.asm"
  • BC: checksum (che devo ancora capire bene come viene calcolato...)

Veniamo a quello successivo.

Voglio stabilire un preciso codice di colore per identificare i campi del record. Ci provo...
C:\Arch-Lab\Lavoro>debug mio.obj
-d
17B1:0100  80 09 00 07 6D 69 6F 2E-61 73 6D BC 96 07 00 00   ....mio.asm.....
17B1:0110  04 54 45 58 54 1A 98 07-00 60 1B 00 02 01 01 E2   .TEXT....`......
17B1:0120  A0 1F 00 01 00 00 EB 02-FA FA B4 00 B0 03 CD 10   ................
17B1:0130  2E A1 00 00 2E 8B 1E 00-00 B4 00 CD 16 B4 4C CD   ..............L.
17B1:0140  21 F0 9C 0D 00 C4 0C 50-01 02 00 C4 11 50 01 02   !......P.....P..
17B1:0150  00 0C 8A 06 00 C1 50 01-00 00 5E 00 00 00 00 00   ......P...^.....
17B1:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
17B1:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
Ecco, con l'aiuto del documento PDF che ho scaricato, me li sono identificati tutti.
Ora li riporto singolarmente...
THEADR:80 09 00 07 6D 69 6F 2E-61 73 6D BC
LNAMES:96 07 00 00 04 54 45 58 54 1A
SEGDEF:98 07-00 60 1B 00 02 01 01 E2
LEDATA:A0 1F 00 01 00 00 EB 02 FA FA B4 00 B0 03 CD 10 2E A1 00 00 2E 8B 1E 00 00 B4 00 CD 16 B4 4C CD 21 F0
FIXUPP:9C 0D 00 C4 0C 50-01 02 00 C4 11 50 01 02 00 0C
MODEND:8A 06 00 C1 50 01-00 00 5E
Bene. Ora sarà più facile ragionarci!

venerdì 25 maggio 2012

initialCS nell'header del file .EXE

Ora una domandina che mi sono posta e risolta un'infinità di volte...

Che cosa fa sì che il registro CS contenga l'indirizzo "alto" del segmento principale del programma?

Per il momento non voglio reimpicciarmi la testa, e parto da un presupposto fondamentale:
Quando il programma viene caricato in memoria, nel registro CS della CPU viene "automaticamente", in qualche modo, scritto il numero corrispondente al segmento principale. Punto!

Rivediamo ancora il famoso header dei files .exe...

Ecco il procedimento . che ricordo piuttosto bene...che ricordo piuttosto bene.
C:\Arch-Lab\old>copy mio.exe mio.xxx
        1 file copiati.

C:\Arch-Lab\old>dir mio.*
 Il volume nell'unità C è Acer
 Numero di serie del volume: F4A2-EF6D

 Directory di C:\Arch-Lab\old

25/05/2012  12:46               406 mio.asm
25/05/2012  12:47               556 MIO.EXE
25/05/2012  12:47               126 MIO.OBJ
25/05/2012  12:47               556 mio.xxx
               4 File          1.644 byte
               0 Directory   7.817.498.624 byte disponibili

C:\Arch-Lab\old>
ho ottenuto un file con estensione fasulla.
Ora lo debuggo e ottengo l'header, riconoscibile per la firna di Mark Zbikowski:
C:\Arch-Lab\old>debug mio.xxx
-d
13A3:0100  4D 5A 2C 00 02 00 00 00-20 00 00 00 FF FF 00 00   MZ,..... .......
13A3:0110  00 00 0B F3 00 00 01 00-1E 00 00 00 01 00 00 00   ................
13A3:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
13A3:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
13A3:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
13A3:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
13A3:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
13A3:0170  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-
E' solo una parre dell'header, dovrebbe essere solo un quarto, ma è sufficiente.
Ora rivediamo sulla Bibbia le nozioni relative all'header dei files .exe...

Ecco. Per il momento non voglio fare tutto un ripasso mnemonico sull'header, ma solo rivedere quello che mi interessa a proposito del valore iniziale del registro CS.
 4D 5A 2C 00 02 00 00 00-20 00 00 00 FF FF 00 00
 00 00 0B F3 00 00 01 00-1E 00 00 00 01 00 00 00
quelli marcati in rosso sarebbero i valori initialIP e initialCS.
Il valore initialCS è impostato a 1 probabilmente perchè il segmento codice è il secondo.
Per verificare l'ipotesi scrivo un programmino in cui il segmento codice è il primo, e poi magari uno in cui è il terzo...
text SEGMENT
assume CS:text,DS:dati
inizio: JMP     Procedura

Var     DW 0FAFAH

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

        MOV     AX, Var
        MOV     BX,Dato1
        
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS


dati SEGMENT
Dato1 DW 0BABAH
Dato2 DW 0CACAH

dati ENDS
END inizio
...ed ecco l'header ottenuto con il procedimento di prima:
13A3:0100  4D 5A 24 00 02 00 00 00-20 00 00 00 FF FF 00 00   MZ$..... .......
13A3:0110  00 00 00 30 00 00 00 00-1E 00 00 00 01 00 00 00   ...0............
come volevasi dimostrare, initialCS è ora 0000.

Un'ulteriore prova:
dati SEGMENT
Dato1 DW 0BABAH
Dato2 DW 0CACAH

dati ENDS

altro SEGMENT

altrodato1 DW 0E0E0H
altrodato2 DW 0ABCDH

altro ENDS



text SEGMENT
assume CS:text,DS:dati
inizio: JMP     Procedura

Var     DW 0FAFAH

Procedura PROC   
        MOV AH,00H
        MOV AL,03H

        INT 10H

        MOV     AX, Var
        MOV     BX,Dato1
        
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
13A3:0100  4D 5A 3B 00 02 00 00 00-20 00 00 00 FF FF 00 00   MZ;..... .......
13A3:0110  00 00 3A A3 00 00 02 00-1E 00 00 00 01 00 00 00   ..:.............
Ancora una volta, come volevasi dimostrare!

Programmi con più segmenti e ASSUME: ripasso definitivo.

In realtà non ha molto senso assumere diversi registri di segmento per un segmento. Scrivo adesso un programmino che utilizza più di un segmento
dati SEGMENT
Dato1 DW 0BABAH
Dato2 DW 0CACAH


dati ENDS

text SEGMENT
assume CS:text,DS:dati
inizio: JMP     Procedura

Var     DW 0FAFAH

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

        MOV     AX, Var
        MOV     BX,Dato1 
        
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
-u
141E:0000 EB03          JMP     0005
141E:0002 90            NOP
141E:0003 FA            CLI
141E:0004 FA            CLI
141E:0005 B400          MOV     AH,00
141E:0007 B003          MOV     AL,03
141E:0009 CD10          INT     10
141E:000B 2E            CS:
141E:000C A10300        MOV     AX,[0003]
141E:000F 8B1E0000      MOV     BX,[0000]
141E:0013 B400          MOV     AH,00
141E:0015 CD16          INT     16
141E:0017 B44C          MOV     AH,4C
141E:0019 CD21          INT     21
AX=0003  BX=0000  CX=002B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141E  IP=000B   NV UP EI PL NZ NA PO NC
141E:000B 2E            CS:
141E:000C A10300        MOV     AX,[0003]                          CS:0003=FAFA
-p

AX=FAFA  BX=0000  CX=002B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141E  IP=000F   NV UP EI PL NZ NA PO NC
141E:000F 8B1E0000      MOV     BX,[0000]                          DS:0000=20CD
-p

AX=FAFA  BX=20CD  CX=002B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141E  IP=0013   NV UP EI PL NZ NA PO NC
141E:0013 B400          MOV     AH,00
-
In pratica ho detto all'assemblatore di assumere come parte alta dell'indirizzo il registro CS per le variabili contenute nel segmento text, e di assumere come parte alta dell'indirizzo il registro DS per le variabili contenute nel segmento dati.

. Posso anche associare il segmento dati con ES, volendo:
dati SEGMENT
Dato1 DW 0BABAH
Dato2 DW 0CACAH


dati ENDS

text SEGMENT
assume CS:text,ES:dati
inizio: JMP     Procedura

Var     DW 0FAFAH

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

        MOV     AX, Var
        MOV     BX,Dato1
        
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
-u
141E:0000 EB03          JMP     0005
141E:0002 90            NOP
141E:0003 FA            CLI
141E:0004 FA            CLI
141E:0005 B400          MOV     AH,00
141E:0007 B003          MOV     AL,03
141E:0009 CD10          INT     10
141E:000B 2E            CS:
141E:000C A10300        MOV     AX,[0003]
141E:000F 26            ES:
141E:0010 8B1E0000      MOV     BX,[0000]
141E:0014 B400          MOV     AH,00
141E:0016 CD16          INT     16
141E:0018 B44C          MOV     AH,4C
141E:001A CD21          INT     21
AX=0003  BX=0000  CX=002C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141E  IP=000B   NV UP EI PL NZ NA PO NC
141E:000B 2E            CS:
141E:000C A10300        MOV     AX,[0003]                          CS:0003=FAFA
-p

AX=FAFA  BX=0000  CX=002C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141E  IP=000F   NV UP EI PL NZ NA PO NC
141E:000F 26            ES:
141E:0010 8B1E0000      MOV     BX,[0000]                          ES:0000=20CD
-p

AX=FAFA  BX=20CD  CX=002C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141E  IP=0014   NV UP EI PL NZ NA PO NC
141E:0014 B400          MOV     AH,00
-
In pratica, tanto per rendermi sicuro di aver capito (ci riuscirò mai, con la mia incertezza cronica?) ecco il significato della direttiva ASSUME:

La direttiva ASSUME dà istruzioni all'assemblatore su quale registro prendere per stabilire la parte "alta" dell'indirizzo specificato nelle variabili per un contenuto della memoria.

Una gerarchia fra registri di segmento impiegati nella direttiva ASSUME?

Dunque abbiamo assodato che le istruzioni di indirizzamento indiretto con registro non vengono influenzate dalla direttiva ASSUME. Vediamo ora se esiste un ordine di preferenza per i registri di segmento quando questi vengono "assunti" da ASSUME.
text SEGMENT
assume CS:text,DS:text,ES:text
inizio: JMP     Procedura

Var     DW 0FAFAH

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

        MOV     AX,[Var]
        MOV     BX,Var
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
141D:000B A10300        MOV     AX,[0003]
141D:000E 8B1E0300      MOV     BX,[0003]
AX=0003  BX=0000  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141D  IP=000B   NV UP EI PL NZ NA PO NC
141D:000B A10300        MOV     AX,[0003]                          DS:0003=009F
-t

AX=009F  BX=0000  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=140D  ES=140D  SS=141D  CS=141D  IP=000E   NV UP EI PL NZ NA PO NC
141D:000E 8B1E0300      MOV     BX,[0003]                          DS:0003=009F
Bene: quando sono assunti ambedue i registri ES e DS, quindi, le istruzioni di indirizzamento diretto preferiscono quindi la codifica con il registro di segmento DS, mentre le istruzioni di indirizzamento indiretto con registro non vengono influenzate dalla direttiva ASSUME.

Ora vediamo di creare un programmino con più di un segmento...

giovedì 24 maggio 2012

La direttiva ASSUME e le parti alte degli indirizzi nelle istruzioni di indirizzamento diretto e indiretto con registro.

Riepiloghiamo...

Con il vecchio MASM le idee si fanno più chiare...

Se c'è ASSUME CS:text:
  • l'indirizzamento diretto, con i nomi delle variabili, prende come parte alta dell'indirizzo il registro CS.
  • l'indirizzamento indiretto con registro prende come parte alta dell'indirizzo il registro DS.
text SEGMENT
ASSUME CS:text
inizio: JMP     Procedura

Var     DW 0FAFAH

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

       
        MOV     BX,Var        
        MOV     AX,[BX]
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
-u
17C2:0000 EB03          JMP     0005
17C2:0002 90            NOP
17C2:0003 FA            CLI
17C2:0004 FA            CLI
17C2:0005 B400          MOV     AH,00
17C2:0007 B003          MOV     AL,03
17C2:0009 CD10          INT     10
17C2:000B 2E            CS:
17C2:000C 8B1E0300      MOV     BX,[0003]
17C2:0010 8B07          MOV     AX,[BX]
17C2:0012 B400          MOV     AH,00
17C2:0014 CD16          INT     16
17C2:0016 B44C          MOV     AH,4C
17C2:0018 CD21          INT     21
AX=0003  BX=0000  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=000B   NV UP EI PL NZ NA PO NC
17C2:000B 2E            CS:
17C2:000C 8B1E0300      MOV     BX,[0003]                          CS:0003=FAFA
-p

AX=0003  BX=FAFA  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=0010   NV UP EI PL NZ NA PO NC
17C2:0010 8B07          MOV     AX,[BX]                            DS:FAFA=0000
-p

AX=0000  BX=FAFA  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=0012   NV UP EI PL NZ NA PO NC
17C2:0012 B400          MOV     AH,00
-

Se c'è ASSUME CS:text, DS:text:
  • l'indirizzamento diretto, con i nomi delle variabili, prende come parte alta dell'indirizzo il registro DS;
  • l'indirizzamento indiretto con registro prende come parte alta dell'indirizzo il registro DS.
text SEGMENT
ASSUME CS:text, DS:text
inizio: JMP     Procedura

Var     DW 0FAFAH

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

       
        MOV     BX,Var        
        MOV     AX,[BX]
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
-u
17C2:0000 EB03          JMP     0005
17C2:0002 90            NOP
17C2:0003 FA            CLI
17C2:0004 FA            CLI
17C2:0005 B400          MOV     AH,00
17C2:0007 B003          MOV     AL,03
17C2:0009 CD10          INT     10
17C2:000B 8B1E0300      MOV     BX,[0003]
17C2:000F 8B07          MOV     AX,[BX]
17C2:0011 B400          MOV     AH,00
17C2:0013 CD16          INT     16
17C2:0015 B44C          MOV     AH,4C
17C2:0017 CD21          INT     21
AX=0003  BX=0000  CX=0019  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=000B   NV UP EI PL NZ NA PO NC
17C2:000B 8B1E0300      MOV     BX,[0003]                          DS:0003=009F
-p

AX=0003  BX=009F  CX=0019  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=000F   NV UP EI PL NZ NA PO NC
17C2:000F 8B07          MOV     AX,[BX]                            DS:009F=7865
-p

AX=7865  BX=009F  CX=0019  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=0011   NV UP EI PL NZ NA PO NC
17C2:0011 B400          MOV     AH,00
-


E se uso altri registri?

Proviamo a usare ES al posto di DS:
text SEGMENT
ASSUME CS:text, ES:text
inizio: JMP     Procedura

Var     DW 0FAFAH

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

       
        MOV     BX,Var        
        MOV     AX,[BX]
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
-u
17C2:0000 EB03          JMP     0005
17C2:0002 90            NOP
17C2:0003 FA            CLI
17C2:0004 FA            CLI
17C2:0005 B400          MOV     AH,00
17C2:0007 B003          MOV     AL,03
17C2:0009 CD10          INT     10
17C2:000B 26            ES:
17C2:000C 8B1E0300      MOV     BX,[0003]
17C2:0010 8B07          MOV     AX,[BX]
17C2:0012 B400          MOV     AH,00
17C2:0014 CD16          INT     16
17C2:0016 B44C          MOV     AH,4C
17C2:0018 CD21          INT     21
AX=0003  BX=0000  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=000B   NV UP EI PL NZ NA PO NC
17C2:000B 26            ES:
17C2:000C 8B1E0300      MOV     BX,[0003]                          ES:0003=009F
-p

AX=0003  BX=009F  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=0010   NV UP EI PL NZ NA PO NC
17C2:0010 8B07          MOV     AX,[BX]                            DS:009F=7865
-p

AX=7865  BX=009F  CX=001A  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17B2  ES=17B2  SS=17C2  CS=17C2  IP=0012   NV UP EI PL NZ NA PO NC
17C2:0012 B400          MOV     AH,00
-
Sì.
Anche se si usa ASSUME CS:text,ES:text:
  • l'indirizzamento diretto, con i nomi delle variabili, prende come parte alta dell'indirizzo il registro ES.
  • l'indirizzamento indiretto con registro prende come parte alta dell'indirizzo il registro DS.


Il quadro è piuttosto chiaro...

mercoledì 23 maggio 2012

Ripasso sui registri di segmento e la direttiva ASSUME...

E ora affrontiamo il ripasso e la rimessa a punto delle nozioni sui segmenti.

Nel caso dell'indirizzamento diretto e indiretto con registro, quelli in cui si usa (o si dovrebbe usare) la parentesi quadra, si specifica un indirizzo, ma nel sistema in cui ci muoviamo un indirizzo non si può specificare con un numero a 16 bit!.

Tralasciando per ora il ripasso della famosa questione degli indirizzi logici e di quelli fisici (che ricordo bene, anche se come al solito ia mia mente iper-iper-ipercritica mi fa sempre credere di non ricordare mai un tubo costringendomi al ripasso ossessivo perpetuo), ribadisco il concetto secondo cui un indirizzo ha bisogno, per essere specificato, anche di un altro valore di 16 bit che è contenuto nei registri di segmento.
Come dice il Profeta, nel momento in cui l'assemblatore incontra delle etichette, o dei nomi di variabili, deve puntare le etichette con un preciso registro di segmento, ossia deve comprendere per filo e per segno l'indirizzo completo, e quindi ha bisogno anche dell'altra metà dell'indirizzo, che deve essere messa su un registro di segmento (CS, DS, ES, SS).
(In realtà per qualunque istruzione è necessario avere il valore di un registro di segmento, in quanto ogni istruzione viene messa in un indirizzo preciso, che non può essere specificato se non si fa riferimento anche all'"altra metà", ma adesso non mi voglio impicciare la testa...

Questo assemblatore che sto usando io usa tranquillamente il registro CS per tutte le sue operazioni, anche per ottenere l'indirizzo completo specificato dalle variabili. Lo fa automaticamente.
Ecco perchè genera istruzioni come queste:
802:000A 2E            CS:
802:000B A10200        MOV     AX,[0002]
802:000E 2E            CS:
802:000F 8B1E0200      MOV     BX,[0002]
Se vado a usare il vecchio assemlatore MASM, invece, ecco cosa accade nel suo lavoro con il medesimo sorgente:
C:\Arch-Lab\Lavoro\old>masm mio
Microsoft (R) Macro Assembler Version 5.10
Copyright (C) Microsoft Corp 1981, 1988.  All rights reserved.

Object filename [mio.OBJ]:
Source listing  [NUL.LST]:
Cross-reference [NUL.CRF]:
mio.ASM(3): error A2062: Missing or unreachable CS
mio.ASM(7): error A2062: Missing or unreachable CS
mio.ASM(13): error A2068: Cannot address with segment register

  48604 + 394623 Bytes symbol space free

      0 Warning Errors
      3 Severe  Errors

C:\Arch-Lab\Lavoro\old>
L'errore di cui parla il "Profeta".
Questo assemblatore non riconosce automaticamente il registro CS come portatore dell'"altra metà dell'indirizzo" delle istruzioni e delle etichette varie, e pertanto segnala errore.

Ecco la correzione:
text SEGMENT
ASSUME CS:text
inizio: JMP     Procedura

Var     DW 0FAFAH

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


        MOV     AX,Var
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
che dice all'assemblatore di considerare come "altra metà degli indirizzi" il numero situato nel registro CS.
C:\Arch-Lab\Lavoro\old>masm mio
Microsoft (R) Macro Assembler Version 5.10
Copyright (C) Microsoft Corp 1981, 1988.  All rights reserved.

Object filename [mio.OBJ]:
Source listing  [NUL.LST]:
Cross-reference [NUL.CRF]:

  48604 + 394623 Bytes symbol space free

      0 Warning Errors
      0 Severe  Errors

C:\Arch-Lab\Lavoro\old>
E vediamo com'è adesso l'istruzione che prevede un indirizzamento diretto, tramite una variabile:
1802:000B 2E            CS:
1802:000C A10300        MOV     AX,[0003]

Mettiamoci pure un'istruzione che prevede un indirizzamento indiretto tramite registro.
text SEGMENT
ASSUME CS:text
inizio: JMP     Procedura

Var     DW 0FAFAH

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


        MOV     AX,Var
        MOV     BX,OFFSET Var
        MOV     CX,[BX]
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Disassemblato:
1802:000B 2E            CS:
1802:000C A10300        MOV     AX,[0003]
1802:000F BB0300        MOV     BX,0003
1802:0012 8B0F          MOV     CX,[BX]
Esecuzione passo-passo:
AX=0003  BX=0000  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000B   NV UP EI PL NZ NA PO NC
1802:000B 2E            CS:
1802:000C A10300        MOV     AX,[0003]                          CS:0003=FAFA
-p

AX=FAFA  BX=0000  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000F   NV UP EI PL NZ NA PO NC
1802:000F BB0300        MOV     BX,0003
-p

AX=FAFA  BX=0003  CX=001C  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=0012   NV UP EI PL NZ NA PO NC
1802:0012 8B0F          MOV     CX,[BX]                            DS:0003=009F
-p

AX=FAFA  BX=0003  CX=009F  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=0014   NV UP EI PL NZ NA PO NC
1802:0014 B400          MOV     AH,00
-
Ecco: per l'istruzione di un indirizzamento indiretto con registro, invece, come "altra metà" dell'indirizzo viene assunto il registro DS, non il CS nonostante sia stata usata la direttiva ASSUME per quest'ultimo.
C'è ancora da ragionare...

Pedanterie sull'indirizzamento diretto...

Ecco.
Mi hanno sempre messo in crisi le cose "imprecise", e allora sento l'esigenza di precisare qualcosa a proposito della notazione assembly dell'indirizzamento diretto.
Dal momento che il disassemblato del debug mostra un numero fra parentesi quadre, provo a fare così per vedere se la notazione, nel sorgente, possa essere scritta usando parentesi quadre per il nome di una variabile.
Uso utti e due i tipi di notazione:
text SEGMENT

inizio: JMP     Procedura

Var     DW 0FAFAH

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

        MOV     AX,[Var]
        MOV     BX,Var
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
E vediamo se nel disassemblato esse figurano allo stesso modo:
-u
1802:0000 EB02          JMP     0004
1802:0002 FA            CLI
1802:0003 FA            CLI
1802:0004 B400          MOV     AH,00
1802:0006 B003          MOV     AL,03
1802:0008 CD10          INT     10
1802:000A 2E            CS:
1802:000B A10200        MOV     AX,[0002]
1802:000E 2E            CS:
1802:000F 8B1E0200      MOV     BX,[0002]
1802:0013 B400          MOV     AH,00
1802:0015 CD16          INT     16
1802:0017 B44C          MOV     AH,4C
1802:0019 CD21          INT     21
Sì: figurano allo stesso modo, e a giudicare dall'esecuzione passo-passo hanno anche, ovviamente, il medesimo effetto:
AX=0003  BX=0000  CX=001B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000A   NV UP EI PL NZ NA PO NC
1802:000A 2E            CS:
1802:000B A10200        MOV     AX,[0002]                          CS:0002=FAFA
-p

AX=FAFA  BX=0000  CX=001B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000E   NV UP EI PL NZ NA PO NC
1802:000E 2E            CS:
1802:000F 8B1E0200      MOV     BX,[0002]                          CS:0002=FAFA
-p

AX=FAFA  BX=FAFA  CX=001B  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=0013   NV UP EI PL NZ NA PO NC
1802:0013 B400          MOV     AH,00
-
Esatto!
Quindi il fatto di scrivere MOV AX,Var può essere considerato una tollerante eccezione alla regola secondo cui viene usato il valore presente all'indirizzo specificato dal valore di una variabile.
Nel caso invece dell'uso del valore presente all'indirizzo specificato dal valore di un registro, la parentesi quadra è d'obbligo!

martedì 22 maggio 2012

Ripasso di indirizzamento immediato, diretto, a registro e indiretto con registro

Adesso un po' di ripasso e messa a posto sulle modalità di indirizzamento.
Vediamo quante ne conosciamo...

Ora mi scrivo un programmuscolo con indirizzamento immediato, diretto e a registro.
text SEGMENT

inizio: JMP     Procedura
Var     DW 0BABAH


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


        MOV     AX,0DADAH
        MOV     AX,Var
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Vediamone la traduzione nel debug:
-u
1802:0000 EB02          JMP     0004
1802:0002 BABAB4        MOV     DX,B4BA
1802:0005 00B003CD      ADD     [BX+SI+CD03],DH
1802:0009 10B8DADA      ADC     [BX+SI+DADA],BH
1802:000D 2E            CS:
1802:000E A10200        MOV     AX,[0002]
1802:0011 B400          MOV     AH,00
1802:0013 CD16          INT     16
1802:0015 B44C          MOV     AH,4C
1802:0017 CD21          INT     21
Accidenti: traduzione fasulla!
Ecco:
-u 0004
1802:0004 B400          MOV     AH,00
1802:0006 B003          MOV     AL,03
1802:0008 CD10          INT     10
1802:000A B8DADA        MOV     AX,DADA
1802:000D 2E            CS:
1802:000E A10200        MOV     AX,[0002]
1802:0011 B400          MOV     AH,00
1802:0013 CD16          INT     16
1802:0015 B44C          MOV     AH,4C
1802:0017 CD21          INT     21
Il valore della variabile è messo fra parentesi quadra. Questo significa che va preso in considerazione l'indirizzo corrispondente a qual numero.
Ed ecco l'esecuzione passo-passo:
AX=0003  BX=0000  CX=0019  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000A   NV UP EI PL NZ NA PO NC
1802:000A B8DADA        MOV     AX,DADA
-p

AX=DADA  BX=0000  CX=0019  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000D   NV UP EI PL NZ NA PO NC
1802:000D 2E            CS:
1802:000E A10200        MOV     AX,[0002]                          CS:0002=BABA
-p

AX=BABA  BX=0000  CX=0019  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=0011   NV UP EI PL NZ NA PO NC
1802:0011 B400          MOV     AH,00
-
Il valore della variabile, riportato nella tabella dei simboli, è:
Symbols:

                N a m e                 Type     Value    Attr

Var  . . . . . . . . . . . . . . Word  0002   text 
inizio . . . . . . . . . . . . . L Near  0000   text 
Sì: come già visto, è bastato inserire il nome di una variabile nell'istruzione MOV per far sì che si avesse un indirizzamento diretto e non immediato: non viene posto nel registro il valore della variabile come riportato nella tabella dei simboli del listato, ma il numero posto nell'indirizzo corrispondente al valore della variabile.

Bene.
Adesso cerchiamo di fare un indirizzamento a registro.

text SEGMENT

inizio: JMP     Procedura



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


        MOV     BX,0DADAH
        MOV     AX,BX
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Eccolo in debug:
-u
1802:0000 EB00          JMP     0002
1802:0002 B400          MOV     AH,00
1802:0004 B003          MOV     AL,03
1802:0006 CD10          INT     10
1802:0008 BBDADA        MOV     BX,DADA
1802:000B 8BC3          MOV     AX,BX
1802:000D B400          MOV     AH,00
1802:000F CD16          INT     16
1802:0011 B44C          MOV     AH,4C
1802:0013 CD21          INT     21
1802:0015 B44C          MOV     AH,4C
1802:0017 CD21          INT     21
Ed eccone l'esecuzione passo-passo:
AX=0003  BX=0000  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=0008   NV UP EI PL NZ NA PO NC
1802:0008 BBDADA        MOV     BX,DADA
-p

AX=0003  BX=DADA  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000B   NV UP EI PL NZ NA PO NC
1802:000B 8BC3          MOV     AX,BX
-p

AX=DADA  BX=DADA  CX=0015  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000D   NV UP EI PL NZ NA PO NC
1802:000D B400          MOV     AH,00
-


Facciamo un po' di filosofia...
Per farla intendere agli idioti come me:
Se io ho un numeretto che si chiama Pasquale, nel caso in cui nell'istruzione io scrivo MOV AX,Pasquale, metto in AX il valore di Pasquale. Credo che questa si possa considerare come una forma di indirizzamento immediato: se io scrivo il numeretto direttamente o scrivo il nome del numeretto che si chiama Pasquale, la cosa non cambia.
Ora nel caso in cui il numeretto si trovi nel registro BX posso considerare di avere un numeretto che si chiama BX: Se io scrivo MOV AX,BX è come se scrivo il nome del numeretto quando il numeretto si chiama Pasquale, e quindi questa sarebbe assimilabile alla situazione di un indirizzamento immediato...
...credo!

C'è poi l'indirizzamento diretto: anzichè usare nell'istruzione il valore del numeretto che si chiama Pasquale, uso il valore che sta scritto all'indirizzo corrispondente al valore di Pasquale, e questo si specifica con la parentesi quadra.
Stessa cosa per l'indirizzamento indiretto con registro: non voglio il valore del numeretto che si chiama BX, ma voglio il valore che sta scritto all'indirizzo corrispondente al valore del numeretto che si chiama BX.

Quindi possiamo stabilire una cosa del genere:

immediatodiretto
a registroindiretto con registro

Ora facciamo un programuscolo con un indirizzamento indiretto con registro:
text SEGMENT

inizio: JMP     Procedura

Var     DW 0FAFAH

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


        MOV     BX,OFFSET Var
        MOV     AX,CS:[BX]
        
        
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
    
Traduzione (disassemblato):
--u
1802:0000 EB02          JMP     0004
1802:0002 FA            CLI
1802:0003 FA            CLI
1802:0004 B400          MOV     AH,00
1802:0006 B003          MOV     AL,03
1802:0008 CD10          INT     10
1802:000A BB0200        MOV     BX,0002
1802:000D 2E            CS:
1802:000E 8B07          MOV     AX,[BX]
1802:0010 B400          MOV     AH,00
1802:0012 CD16          INT     16
1802:0014 B44C          MOV     AH,4C
1802:0016 CD21          INT     21
1802:0018 2100          AND     [BX+SI],AX
1802:001A 0000          ADD     [BX+SI],AL
1802:001C 0000          ADD     [BX+SI],AL
1802:001E 0000          ADD     [BX+SI],AL
-
Ed esecuzione passo-passo:
AX=0003  BX=0000  CX=0018  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000A   NV UP EI PL NZ NA PO NC
1802:000A BB0200        MOV     BX,0002
-p

AX=0003  BX=0002  CX=0018  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=000D   NV UP EI PL NZ NA PO NC
1802:000D 2E            CS:
1802:000E 8B07          MOV     AX,[BX]                            CS:0002=FAFA
-p

AX=FAFA  BX=0002  CX=0018  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=17F2  ES=17F2  SS=1802  CS=1802  IP=0010   NV UP EI PL NZ NA PO NC
1802:0010 B400          MOV     AH,00
-
Ecco. In pratica, questo indirizzamento indiretto con registro è in qualche modo simile all'indirizzamento diretto che fa uso dell'indirizzo contenuto nel valore di una variabile: qui l'indirizzo è contenuto nel valore di un registro.

C'è poi tutta una serie di concetti da ripassare e rimettere a posto a proposito di quel CS: che appare prima delle istruzioni di indirizzamento diretto e indiretto con registro...

Indirizzamento immediato e diretto

Può darsi che mi sbaglio, ma...

Creare un programmino con due metodi di indirizzamento, immediato e diretto.

text SEGMENT

inizio: JMP     Procedura
Var1 DW 4142H


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


        MOV     AX,5152H
        MOV     BX,Var1
       
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Simboli:
Symbols:

                N a m e                 Type     Value    Attr

Var1 . . . . . . . . . . . . . . Word  0002   text 
inizio . . . . . . . . . . . . . L Near  0000   text 


Traduzione delle istruzioni nel debug:
1802:000A B85251        MOV     AX,5152
1802:000D 2E            CS:
1802:000E 8B1E0200      MOV     BX,[0002]
Conclusione: Quando l'operando di un'istruzione MOV è un numero, esso viene preso come tale; quando l'operando è il nome di una variabile, viene preso il contenuto dell'indirizzo rappresentato dal nome della variabile.. E se usassi il nome di una LABEL piuttosto che quello di una variabile?
text SEGMENT

inizio: JMP     Procedura
Var1 DW 4142H


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


xxx00:  MOV     AX,5152H
        MOV     BX,xxx00
       
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Ecco:
1802:000A B85251        MOV     AX,5152
1802:000D BB0A00        MOV     BX,000A
Bene: Quando nell'istruzione MOV l'operando è un'etichetta, viene preso il contenuto della locazione indicata dall'etichetta solo se questa è un nome di variabile, e non se è un'etichetta "coi due punti" Nel primo caso si opera un indirizzamento diretto, mentre nel secondo caso si opera un indirizzamento immediato come nel caso in cui l'operando sia un numero: infatti viene usato il numero rappresentato dall'etichetta, che è il suo indirizzo.

Almeno credo...!

Etichette e tabella dei simboli...ripasso generale.

Torno a ripassare gli elementi sulle variabili.
Dunque le etichette in assembly vengono messe, come già avevo studiato in passato, nella tabella dei simboli in questo modo:
Symbols:

                N a m e                 Type     Value    Attr

Variab . . . . . . . . . . . . . Word  0002   text 
XXX00  . . . . . . . . . . . . . L Near  0004   text 
XXX01  . . . . . . . . . . . . . L Near  000D   text 
inizio . . . . . . . . . . . . . L Near  0000   text 
 
Di esse vengono ricordati soltanto il tipo, il valore e il segmento.
Per quanto riguarda il valore, esso è invariabilmente l'indirizzo di memoria e non il contenuto della locazione di memoria.
E' poi l'istruzione che decide se prendere l'indirizzo o il contenuto.
Come dice Giobe, l'assemblatore ritiene etichette non incluse nelle sue tabelle di conversione.
Le etichette vengono distinte in:
  • etichette con i due punti;
  • etichette senza i due punti, di cui:
    • costanti, definite dalla direttiva EQU
    • variabili, definite dalle direttive DB o DW.
Bene.
Quelle con i due punti, in questo programmuscolo di cui sto trattando, sono "schedate" come L Near, mentre quella definita con la direttiva DW è "schedata" come Word.
Adesso mi faccio un programmuscolo-esercizio con i seguenti scopi:
Fare in modo che nella tabella dei simboli mi appaiano etichette di tutti i tipi.
text SEGMENT

inizio: JMP     XXX00
Var1 DW 4142H
Var2 DB 43H
Cost EQU 44H

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

 MOV CX,0AH

XXX01:  MOV AH,0EH
 MOV AL,41H
        INT 10H


        LOOP    XXX01
        
        MOV     AX,Var1
        MOV     BX,OFFSET Var1
        LEA     CX,Var1
        MOV     DX,XXX01
        LEA     AX,XXX01
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H


text ENDS
END inizio
Ecco la tabella dei simboli:
Symbols:

                N a m e                 Type     Value    Attr

Cost . . . . . . . . . . . . . . Number  0044h  
Var1 . . . . . . . . . . . . . . Word  0002   text 
Var2 . . . . . . . . . . . . . . Byte  0004   text 
XXX00  . . . . . . . . . . . . . L Near  0005   text 
XXX01  . . . . . . . . . . . . . L Near  000E   text 
inizio . . . . . . . . . . . . . L Near  0000   text
Bene.

E che ne è delle direttive che delimitano le procedure?
Ripassiamo...
text SEGMENT

inizio: JMP     Procedura
Var1 DW 4142H
Var2 DB 43H
Cost EQU 44H

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

 MOV CX,0AH

XXX01:  MOV AH,0EH
 MOV AL,41H
        INT 10H


        LOOP    XXX01
        
        MOV     AX,Var1
        MOV     BX,OFFSET Var1
        LEA     CX,Var1
        MOV     DX,XXX01
        LEA     AX,XXX01
        MOV AH,00H
        INT 16H

        MOV AH,4CH
        INT 21H

Procedura ENDP
text ENDS
END inizio
Ecco la tabella:
Procedures,  parameters and locals:

                N a m e                 Type     Value    Attr

Procedura  . . . . . . . . . . . P Near  0005   text Length= 002B Private


Symbols:

                N a m e                 Type     Value    Attr

Cost . . . . . . . . . . . . . . Number  0044h  
Var1 . . . . . . . . . . . . . . Word  0002   text 
Var2 . . . . . . . . . . . . . . Byte  0004   text 
XXX01  . . . . . . . . . . . . . L Near  000E   text 
inizio . . . . . . . . . . . . . L Near  0000   text 
Procedura viene catalogata non come simbolo ma come procedura, e identificata come P Near.
Non ricordo quando è che viene usato un P Far e un L Far... Mi pare di ricordare che esistessero anche queste...
Comunque quello che mi interessa adesso è vedere bene le variabili.
Rivediamo il programmuscolo...

Queste istruzioni:
        MOV     AX,Var1
        MOV     BX,OFFSET Var1
        LEA     CX,Var1
mettono rispettivamente, nei vari registri, il contenuto di Var1, l'indirizzo di Var1 e ancora l'indirizzo di Var1.
Come vengono tradotte dal debug?
1802:0016 2E            CS:
1802:0017 A10200        MOV     AX,[0002]
1802:001A BB0200        MOV     BX,0002
1802:001D 8D0E0200      LEA     CX,[0002]
...laddove Var1 viene tradotto come l'indirizzo fra parentesi quadre quando non preceduto da OFFSET, viene tradotto come l'indirizzo semplice quando preceduto da OFFSET, viene tradotto ancora fra parentesi quadre quando l'istruzione è LEA.
Coms si può rendere tutto questo in termini umani?

L'istruzione MOV è riferita all'indirizzo. Di per sè, MOV mette nel registro il numero che le viene presentato.
Facciamo un'aggiunta con l'immissione in un registro di un numero puro e isoliamo l'istruzione:
MOV     AX,51H
che viene tradotta dal debugger:
1802:0016 B85100        MOV     AX,0051
Semplicemente "metti in AX il numero 0051.
Semplice, lineare.
Ma allora perchè
MOV     AX,Var1
...è tradotta come
1802:0019 2E            CS:
1802:001A A10200        MOV     AX,[0002]
...? Lasciamo stare il CS, che si riferisce al segmento...
Semplicemente perchè quando l'istruzione MOV ha a che fare con un'etichetta probabilmente prende per buono che debba scrivere il valore contenuto all'indirizzo dell'etichetta, non il valore dell'indirizzo dell'etichetta!
E devo rispolverarmi tutte le nozioni relative all'indirizzamento.

lunedì 21 maggio 2012

Overloading dei costruttori in VB.NET

Adesso creo un costruttore con parametri.
Public Class Form1


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim QuestaClasse As New MiaClasse(45678, "Ciao, Bestia")
        QuestaClasse.Mostra()

    End Sub
End Class

Class MiaClasse
    Public proprieta As Integer
    Dim frase As String
    Public Sub New(ByVal a, ByVal b)
        proprieta = a
        frase = b
    End Sub

    Public Sub Mostra()
        MsgBox(proprieta & " " & frase)
    End Sub
End Class
...che funziona:



Dunque non è necessario avere contemporaneamente un costruttore di default esplicitamente dichiarato, se è presente un costruttore con parametri, come invece è in C++.
Ora creo anche il costruttore di default:
Public Class Form1


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim QuestaClasse As New MiaClasse(45678, "Ciao, Bestia")
        QuestaClasse.Mostra()
        Dim AltraClasse As New MiaClasse
        AltraClasse.Mostra()

    End Sub
End Class

Class MiaClasse
    Public proprieta As Integer
    Dim frase As String

    Public Sub New()
        proprieta = 23
        frase = "Sei un cretino"
    End Sub
    Public Sub New(ByVal a, ByVal b)
        proprieta = a
        frase = b
    End Sub

    Public Sub Mostra()
        MsgBox(proprieta & " " & frase)
    End Sub
End Class
...e funzionano tutti e due:





Bene, bene...

Costruttori in VB.NET

Prima di sbizzarrirmi con i controlli di VB.NET è il caso che impari i rudimenti di questo linguaggio, in quanto sono completamente diversi da quelli del VB6 con il quale ero diventato bravo.

Questo linguaggio è basato su classi, come il C++ e come il Java, e possiede sicuramente potenzialità molto maggiori rispetto al VB6 quanto a ereditarietà, che con il VB6 è molto limitata rispetto agli altri linguaggi.

Sembra però che sia più interessante...
Creo sullo stesso modulo dove è dichiarata la classe Form1 un'altra classe...
Public Class Form1


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub
End Class

Class MiaClasse
    Dim proprieta As Integer
    Dim frase As String


End Class
Esistono i costruttori?

Sì! Ecco il costruttore!
Public Class Form1


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim QuestaClasse As New MiaClasse
        QuestaClasse.Mostra()

    End Sub
End Class

Class MiaClasse
    Public proprieta As Integer
    Dim frase As String
    Public Sub New()
        proprieta = 12345
        frase = "Ciao ciccio bello"
    End Sub
    Public Sub Mostra()
        MsgBox(proprieta & " " & frase)
    End Sub
End Class


Ed ecco la prova che il costruttore fa il suo lavoro:

Aggiunta di righe alla DataGridView

Codice per l'aggiunta di righe alla DataGridView (con nomenclatura squisitamente dialettale abruzzese)
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Griglia.ColumnCount = 3
        With Griglia
            .Columns(0).Name = "Nome"
            .Columns(1).Name = "Cognome"
            .Columns(2).Name = "Professione"
        End With
        Dim riga As String() = New String() {"Carmine", "Vattacicerchia", "tritamarrocche"}
        Griglia.Rows.Add(riga)
        riga = New String() {"Mariangelo", "Piagnaculo", "sbuciamelangole"}
        Griglia.Rows.Add(riga)
        riga = New String() {"Tonino", "Lu Cafone", "zompafossi"}
        Griglia.Rows.Add(riga)

    End Sub
End Class




Intuitivo, intuitivo!

DataGridView (Visual Basic)

Bene.
Vediamo come si creano le colonne e le righe in un DataGridView.
Per cominciare, con questo codice:
Public Class Form1
  
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        MsgBox(Griglia.ColumnCount)

    End Sub
End Class
ottengo uno zero assoluto: la mia griglia non ha colonne.

Vediamo qual è il codice per crearle...
Ecco: basta un semplice ordine.
"Cara griglia, tu hai tre colonne!". La proprietà ColumnCount non è di sola lettura come credevo, ma può anche essere impostata.
Come si dice in termini più "tecnici"? Di "lettura-scrittura", credo...
Public Class Form1
    Private Stringa As String

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Griglia.ColumnCount = 3
        MsgBox(Griglia.ColumnCount)

    End Sub
End Class
Bene. Ottengo un numero 3 nella MessageBox, ed ecco la mia griglietta in fase nascente:



Bastava un semplice ordine...

Ora provvediamo a denominare le colonne.
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Griglia.ColumnCount = 3
        With Griglia
            .Columns(0).Name = "Cavolate"
            .Columns(1).Name = "Fesserie"
            .Columns(2).Name = "Pinzillacchere"
        End With

    End Sub
End Class
Perfetto!



...e per il momento ci possiamo accontentare!

domenica 20 maggio 2012

Rispolverando Assembly: generazione del listato dall'assemblatore

Un veloce ripasso di Assembly (dovrei averlo già riposto in un angolo remoto del cervello...)

Le procedure che si chiamavano con INT... vediamo... Tabella dei vettori, ecco...

INT 10H sono le funzioni per la gestione del video.
Ripassiamo...
text SEGMENT
MOV AH,00H
MOV AL,03H
INT 10H

MOV AH,00H
INT 16H

MOV AH,4CH
INT 21H


text ENDS
END
Una cosa brevissima...

Quello che mi interessa è rivedere un po' il listato, con la tabella dei simboli.

Come si fa per far generare il listato dall'assemblatore? Ecco, per simulare il vecchio MASM, viene eseguito ML con queste opzioni:
C:\Arch-Lab\Lavoro>masm mio.asm
Microsoft (R) MASM Compatibility Driver
Copyright (C) Microsoft Corp 1993.  All rights reserved.

 Invoking: ML.EXE /I. /Zm /c /Ta mio.asm

Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: mio.asm

C:\Arch-Lab\Lavoro>
Un ripasso di queste opzioni:
C:\Arch-Lab\Lavoro>ml /help
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.


        ML [ /options ] filelist [ /link linkoptions ]

/AT Enable tiny model (.COM file)         /nologo Suppress copyright message
/Bl<linker> Use alternate linker          /Sa Maximize source listing
/c Assemble without linking               /Sc Generate timings in listing
/Cp Preserve case of user identifiers     /Sf Generate first pass listing
/Cu Map all identifiers to upper case     /Sl<width> Set line width
/Cx Preserve case in publics, externs     /Sn Suppress symbol-table listing
/coff generate COFF format object file    /Sp<length> Set page length
/D<name>[=text] Define text macro         /Ss<string> Set subtitle
/EP Output preprocessed listing to stdout /St<string> Set title
/F <hex> Set stack size (bytes)           /Sx List false conditionals
/Fe<file> Name executable                 /Ta<file> Assemble non-.ASM file
/Fl[file] Generate listing                /w Same as /W0 /WX
/Fm[file] Generate map                    /WX Treat warnings as errors
/Fo<file> Name object file                /W<number> Set warning level
/FPi Generate 80x87 emulator encoding     /X Ignore INCLUDE environment path
/Fr[file] Generate limited browser info   /Zd Add line number debug info
/FR[file] Generate full browser info      /Zf Make all symbols public
/G<c|d|z> Use Pascal, C, or Stdcall calls /Zi Add symbolic debug info
/H<number> Set max external name length   /Zm Enable MASM 5.10 compatibility
/I<name> Add include path                 /Zp[n] Set structure alignment
/link <linker options and libraries>      /Zs Perform syntax check only


C:\Arch-Lab\Lavoro>
Ecco:
  • L'opzione /I. è relativa al percorso (me la rivedrò dopo);
  • L'opzione /Zm abilita la compatibilità con MASM 5.10 (non ho mai capito bene cosa sia);
  • L'opzione /c significa assemblare senza linkare;
  • L'opzione /Ta significa "assemblare files non .ASM": forse abilita ML ad assemblare files senza l'estensione esplicitamente specificata...


L'opzione richiesta per generare il listato è /Fl.
Proviamo...
C:\Arch-Lab\Lavoro>ml /c /Zm /Fl mio.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: mio.asm

C:\Arch-Lab\Lavoro>dir mio*
 Il volume nell'unità C è ACER
 Numero di serie del volume: AC40-F131

 Directory di C:\Arch-Lab\Lavoro

20/05/2012  13.22               111 mio.asm
20/05/2012  13.22               526 mio.exe
20/05/2012  15.35               666 mio.lst
20/05/2012  15.35                65 mio.obj
               4 File          1.368 byte
               0 Directory   5.926.289.408 byte disponibili

C:\Arch-Lab\Lavoro>
Ecco il listato!
Ora me lo analizzo un po'...

E che accidenti vuoi analizzare? Non ho simboli in questo microprogrammino idiota!
Così andrà meglio:
text SEGMENT

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

MOV AH,00H
INT 16H

MOV AH,4CH
INT 21H


text ENDS
END inizio
Ecco il listato:
Microsoft (R) Macro Assembler Version 6.14.8444      05/20/12 15:42:29
mio.asm            Page 1 - 1


 0000    text SEGMENT

 0000    inizio:
 0000  B4 00   MOV AH,00H
 0002  B0 03   MOV AL,03H
 0004  CD 10   INT 10H

 0006  B4 00   MOV AH,00H
 0008  CD 16   INT 16H

 000A  B4 4C   MOV AH,4CH
 000C  CD 21   INT 21H


 000E    text ENDS
    END inizio
 Microsoft (R) Macro Assembler Version 6.14.8444      05/20/12 15:42:29
mio.asm            Symbols 2 - 1




Segments and Groups:

                N a m e                 Size     Length   Align   Combine Class

text . . . . . . . . . . . . . . 16 Bit  000E   Para   Private 


Symbols:

                N a m e                 Type     Value    Attr

inizio . . . . . . . . . . . . . L Near  0000   text 

    0 Warnings
    0 Errors
Ed ecco la tabella dei simboli:
Symbols:

                N a m e                 Type     Value    Attr

inizio . . . . . . . . . . . . . L Near  0000   text 
in cui c'è un simbolo solo, l'etichetta inizio.

Non male come inizio!

sabato 19 maggio 2012

Arrays e puntatori...

Dopo aver rinfrescato i puntatori è bene rinfrescare gli arrays.
Dichiaro un array e poi metto a video l'identificatore dell'array:
#include<stdio.h>
int mioArray[5]={1,2,3,4,5};

void main(){
 printf("%x",mioArray);
 getc(stdin);
 
}
...curando di far stampare il valore in notazione esadecimale, con l'uso dello specificatore %x nell'istruzione printf. Ottengo:
1077000






Ora vado in OllyDbg, e guardo la finestra Dump:
01077000 >01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00   ... ... ... ...
01077010  05 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00   ....... ... ...
01077020 >01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00   ... ... .......
01077030 >FE FF FF FF 01 00 00 00 FF FF FF FF FF FF FF FF  þÿÿÿ ...ÿÿÿÿÿÿÿÿ
01077040  00 00 00 00 4E E6 40 BB B1 19 BF 44 00 00 00 00  ....Næ@»± ¿D....
Perfetto! Quello marcato è il mio array!
L'indirizzo di memoria al quale si trova è lo stesso risultato dato dal programma nel mettere a video l'identificatore dell'array.
Quindi l'identificatore di un array, contenendo l'indirizzo del primo elemento dell'array stesso, non è altro che un puntatore all'array!

venerdì 18 maggio 2012

Variabile e puntatore locali nello stack in OllyDbg

Adesso dichiariamo una variabile e un puntatore locali, sempre inizializzando il puntatore con l'indirizzo della variabile.
#include<stdio.h>

void main(){
 
 int variabile=0xCACACACA;;
 int *puntatore=&variabile;
 getc(stdin); 
}
Ecco in OllyDbg una parte dello stack durante l'esecuzione della funzione main, prima delle istruzioni che dichiarano la variabile e il puntatore:
0016FBB0   CCCCCCCC
0016FBB4   CCCCCCCC
0016FBB8   CCCCCCCC
0016FBBC   CCCCCCCC
0016FBC0   CCCCCCCC
0016FBC4   CCCCCCCC
0016FBC8   CCCCCCCC
0016FBCC   CCCCCCCC
0016FBD0   CCCCCCCC
0016FBD4   CCCCCCCC
0016FBD8   CCCCCCCC
0016FBDC   CCCCCCCC
0016FBE0   CCCCCCCC
0016FBE4   CCCCCCCC
0016FBE8   CCCCCCCC
0016FBEC  /0016FBF8
0016FBF0  |76A04B29  RETURN to kernel32.76A04B29
0016FBF4  |7FFDE000
0016FBF8  ]0016FC38
0016FBFC  |77C3E1C6  RETURN to ntdll.77C3E1C6
Dopo che viene dichiarata la variabile, di valore 0xCACACACA:

0016FBB0   CCCCCCCC
0016FBB4   CCCCCCCC
0016FBB8   CCCCCCCC
0016FBBC   CCCCCCCC
0016FBC0   CCCCCCCC
0016FBC4   CCCCCCCC
0016FBC8   CCCCCCCC
0016FBCC   CCCCCCCC
0016FBD0   CCCCCCCC
0016FBD4   CCCCCCCC
0016FBD8   CCCCCCCC
0016FBDC   CCCCCCCC
0016FBE0   CCCCCCCC
0016FBE4   CACACACA
0016FBE8   CCCCCCCC
0016FBEC  /0016FBF8
0016FBF0  |76A04B29  RETURN to kernel32.76A04B29
0016FBF4  |7FFDE000
0016FBF8  ]0016FC38
0016FBFC  |77C3E1C6  RETURN to ntdll.77C3E1C6
E dopo che viene inizializzato il puntatore:
0016FBB0   CCCCCCCC
0016FBB4   CCCCCCCC
0016FBB8   CCCCCCCC
0016FBBC   CCCCCCCC
0016FBC0   CCCCCCCC
0016FBC4   CCCCCCCC
0016FBC8   CCCCCCCC
0016FBCC   CCCCCCCC
0016FBD0   CCCCCCCC
0016FBD4   CCCCCCCC
0016FBD8   0016FBE4
0016FBDC   CCCCCCCC
0016FBE0   CCCCCCCC
0016FBE4   CACACACA
0016FBE8   CCCCCCCC
0016FBEC  /0016FBF8
0016FBF0  |76A04B29  RETURN to kernel32.76A04B29
0016FBF4  |7FFDE000
0016FBF8  ]0016FC38
0016FBFC  |77C3E1C6  RETURN to ntdll.77C3E1C6
...che contiene l'indirizzo nello stack della variabile locale!
Il codice in Assembly delle due istruzioni che dichiarano variabile e puntatore è:
00D413AE   C745 F8 CACACACA MOV DWORD PTR SS:[EBP-8],CACACACA
00D413B5   8D45 F8          LEA EAX,DWORD PTR SS:[EBP-8]
00D413B8   8945 EC          MOV DWORD PTR SS:[EBP-14],EAX
L'indirizzo della variabile locale nello stack viene copiato nel registro EAX e quindi copiato da questa nello stack.

Puntatore globale in OllyDbg

Ora uso un puntatore.
Dichiaro il puntatore, e contemporaneamente lo inizializzo con l'indirizzo della variabile globale.
#include<stdio.h>
int variabile=0xCACACACA;;
int *puntatore=&variabile;
void main(){
 
 variabile=0xBABABABA;
 getc(stdin);
 
}
Ed ecco il dump in OllyDbg:
00247000 >CA CA CA CA 00 70 24 00 00 00 00 00 01 00 00 00  ÊÊÊÊ.p$..... ...
00247010  01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00   ... ... ... ...
00247020 >00 00 00 00 FE FF FF FF 01 00 00 00 FF FF FF FF  ....þÿÿÿ ...ÿÿÿÿ
00247030 >FF FF FF FF 00 00 00 00 4E E6 40 BB B1 19 BF 44  ÿÿÿÿ....Næ@»± ¿D
Il puntatore è stato dichiarato (e definito, in quanto è stata allocata per lui della memoria già all'inizio, quando il programma è stato caricato in memoria) subito dopo la variabile globale, e trova posto esattamente dopo la variabile globale, puntando all'indirizzo della variabile stessa.

Le variabili globali in C++ studiate con OllyDbg

Le variabili.
Iniziamo con una variabile semplice semplice
#include<stdio.h>
int variabile;

void main(){
 
 variabile=0xCACACACA;
 getc(stdin);
 
}
La variabile viene dichiarata (e definita) inizialmente. E' una variabile globale in quanto viene dichiarata al di fuori di funzioni.
Nel contesto della funzione main viene inizializzata.
Ecco in OllyDbg quello che accade.

Questo è il codice assembly della funzione main:
00061390 > 55               PUSH EBP
00061391   8BEC             MOV EBP,ESP
00061393   81EC C0000000    SUB ESP,0C0
00061399   53               PUSH EBX
0006139A   56               PUSH ESI
0006139B   57               PUSH EDI
0006139C   8DBD 40FFFFFF    LEA EDI,DWORD PTR SS:[EBP-C0]
000613A2   B9 30000000      MOV ECX,30
000613A7   B8 CCCCCCCC      MOV EAX,CCCCCCCC
000613AC   F3:AB            REP STOS DWORD PTR ES:[EDI]
000613AE   C705 38710600 CA>MOV DWORD PTR DS:[variabile],CACACACA
000613B8   8BF4             MOV ESI,ESP
000613BA   FF15 B4820600    CALL DWORD PTR DS:[<&MSVCR100D.__iob_fun>; MSVCR100.__iob_func
000613C0   3BF4             CMP ESI,ESP
000613C2   E8 6AFDFFFF      CALL Studio.00061131
In rosso l'istruzione che inizializza la variabile globale.
il nome della variabile è riportato nel disassemblato.
Mediante un'istruzione del menu popup di OllyDbg posso andare nella finestra del Dump alla locazione della variabile, prima ancora di eseguire l'istruzione nel debugger.
Ecco il disassemblato:
00067138 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067148 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067158 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067168 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067178 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067188 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067198  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
Ora imposto un punto di origine dell'esecuzione all'inizio della procedura main. Ed eseguo con il tasto F8 passo-passo:
00061390 > 55               PUSH EBP
00061391   8BEC             MOV EBP,ESP
00061393   81EC C0000000    SUB ESP,0C0
00061399   53               PUSH EBX
0006139A   56               PUSH ESI
0006139B   57               PUSH EDI
0006139C   8DBD 40FFFFFF    LEA EDI,DWORD PTR SS:[EBP-C0]
000613A2   B9 30000000      MOV ECX,30
000613A7   B8 CCCCCCCC      MOV EAX,CCCCCCCC
000613AC   F3:AB            REP STOS DWORD PTR ES:[EDI]
000613AE   C705 38710600 CA>MOV DWORD PTR DS:[variabile],CACACACA
000613B8   8BF4             MOV ESI,ESP
000613BA   FF15 B4820600    CALL DWORD PTR DS:[<&MSVCR100D.__iob_fun>; MSVCR100.__iob_func
000613C0   3BF4             CMP ESI,ESP
Sono arrivato a questa istruzione, e il dump è ancora come prima.
La eseguo ed ecco come cambia il dump:
00067138 >CA CA CA CA 00 00 00 00 00 00 00 00 00 00 00 00  ÊÊÊÊ............
00067148 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067158 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067168 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067178 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067188 >00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00067198  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Conclusione: La variabile globale dichiarata e definita all'inizio, ha occupato un posto ben preciso, che è noto prima ancora che venga chiamata l'istruzione che la inizializza.


Adesso inizializzo la variabile globale al di fuori della funzione.
#include<stdio.h>
int variabile=0xCACACACA;;

void main(){
 
 variabile=0xBABABABA;
 getc(stdin);
 
}
Vediamo in Olly:
009C7000 >CA CA CA CA 01 00 00 00 01 00 00 00 01 00 00 00  ÊÊÊÊ ... ... ...
009C7010  01 00 00 00 01 00 00 00 00 00 00 00 FE FF FF FF   ... .......þÿÿÿ
009C7020 >01 00 00 00 FF FF FF FF FF FF FF FF 00 00 00 00   ...ÿÿÿÿÿÿÿÿ....
009C7030 >4E E6 40 BB B1 19 BF 44 00 00 00 00 00 00 00 00  Næ@»± ¿D........
009C7040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
Dopo l'istruzione che ne cambia il valore:
009C7000 >BA BA BA BA 01 00 00 00 01 00 00 00 01 00 00 00  ºººº ... ... ...
009C7010  01 00 00 00 01 00 00 00 00 00 00 00 FE FF FF FF   ... .......þÿÿÿ
009C7020 >01 00 00 00 FF FF FF FF FF FF FF FF 00 00 00 00   ...ÿÿÿÿÿÿÿÿ....
009C7030 >4E E6 40 BB B1 19 BF 44 00 00 00 00 00 00 00 00  Næ@»± ¿D........
009C7040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
Benissimo!
Una conferma in più che la locazione della variabile è predestinata già quando il programma è stato caricato in memoria.
Qui ho una tabella della mappa di memoria che contiene i vari segmenti del programma:
Memory map
Address    Size       Owner      Section    Contains      Type   Access    Initial   Mapped as
00010000   00010000                                       Map    RW        RW
00030000   00004000                                       Map    R         R
00040000   00001000                                       Map    R         R
0022D000   00001000                                       Priv   RW  Guar  RW
0022E000   00002000                         stack of mai  Priv   RW  Guar  RW
00390000   00005000                                       Priv   RW        RW
00490000   0037F000                                       Map    R         R         \Device\HarddiskVolume2\Windows\System32\locale.nls
009B0000   00001000   Studio                PE header     Imag   R         RWE
009B1000   00010000   Studio     .textbss   code          Imag   R         RWE
009C1000   00004000   Studio     .text      SFX           Imag   R         RWE
009C5000   00002000   Studio     .rdata                   Imag   R         RWE
009C7000   00001000   Studio     .data      data          Imag   R         RWE
009C8000   00001000   Studio     .idata     imports       Imag   R         RWE
009C9000   00001000   Studio     .rsrc      resources     Imag   R         RWE
009CA000   00001000   Studio     .reloc     relocations   Imag   R         RWE
00B70000   00004000                                       Priv   RW        RW
53280000   00001000   MSVCR100              PE header     Imag   R         RWE
53281000   0015D000   MSVCR100   .text      code,imports  Imag   R         RWE
533DE000   00006000   MSVCR100   .data      data          Imag   R         RWE
533E4000   00001000   MSVCR100   .rsrc      resources     Imag   R         RWE
533E5000   0000D000   MSVCR100   .reloc     relocations   Imag   R         RWE
769C0000   00001000   kernel32              PE header     Imag   R         RWE
769C1000   000CD000   kernel32   .text      code,imports  Imag   R         RWE
76A8E000   00003000   kernel32   .data      data          Imag   R         RWE
76A91000   00001000   kernel32   .rsrc      resources     Imag   R         RWE
76A92000   0000A000   kernel32   .reloc     relocations   Imag   R         RWE
77C00000   00001000   ntdll                 PE header     Imag   R         RWE
77C01000   000C3000   ntdll      .text      code,exports  Imag   R         RWE
77CC4000   00001000   ntdll      RT                       Imag   R         RWE
77CC5000   0000B000   ntdll      .data      data          Imag   R         RWE
77CD0000   00053000   ntdll      .rsrc      resources     Imag   R         RWE
77D23000   00005000   ntdll      .reloc     relocations   Imag   R         RWE
7F6F0000   00006000                                       Map    R         R
7FFB0000   00023000                                       Map    R         R
7FFDE000   00001000                         data block o  Priv   RW        RW
7FFDF000   00001000                                       Priv   RW        RW
7FFE0000   00001000                                       Priv   R         R
la quale dimostra che l'indirizzo attribuito alla variabile globale è l'indirizzo di inizio del segmento data.

martedì 15 maggio 2012

Filosofando sui puntatori...

Ora ripassiamo i puntatori, questo argomento maledetto che mi ha lasciato sempre dei dubbi.
Giustiziamoli definitivamente!

#include<stdio.h>
void funzione(){
 int variabile=123;
 int *puntatore;
 puntatore=&variabile;

 printf("%u", *puntatore);
 getc(stdin);
}
void main(){
 funzione(); 
}
con int variabile dichiaro una variabile "normale".
Con int *puntatore dichiaro una variabile che ha per contenuto l'indirizzo di un'altra variabile.
Ma *puntatore non è un indirizzo, ma il contenuto di quell'indirizzo, tant'è vero che se stampo a video *puntatore ottengo il valore della variabile, non del suo indirizzo.
Nel dichiarare il valore, io uso *puntatore, in quanto mi interessa il contenuto dell'indirizzo puntato
Se mi servisse il valore dell'indirizzo, dovrei usare puntatore e basta.

Il distruttore

Il distruttore viene chiamato quando l'oggetto cessa di esistere.
Quando cessa di esistere un oggetto?

Non so rispondere.

Ecco: sono arrivato a una conclusione.
Un oggetto per il quale venga allocata la memoria in modo... statico (?) si distrugge solo alla fine del programma.
Credo...
Ecco: ho creato una classe con un distruttore, nel quale vi sono le istruzioni di stampare qualcosa a video e poi attendere nuovamente la pressione di un tasto.
Il distruttore entra in azione alla fine del programma, quando l'oggetto cessa di esistere.
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 Rettangolo();
 ~Rettangolo();
 int area();
};


Rettangolo::Rettangolo(){
 base=3;
 altezza=5;
}
Rettangolo::~Rettangolo(){
 printf("distruggo");
 getc(stdin);
}
int Rettangolo::area(){
 return(base*altezza);
}
void main(){
 Rettangolo BaseRettangolo;
 
 printf("%u\n",BaseRettangolo.area());
 
 getc(stdin);
}
E adesso mi ripercorro gli eventi sulla finestra:
15



La finestra è in attesa dell'input (istruzione getc(stdin)).
Ora schiaccio Invio ed ecco cosa accade:
15

distruggo


Viene eseguito, nel momento in cui la procedura main() è cessata, il programma termina e l'oggetto cessa di esistere, il codice del distruttore, con stampa a video della stringa "distruggo" e nuova attesa di un input (nuova istruzione getc(stdin)).
Schiacciando di nuovo Invio, la finestra sparisce e il programma è terminato.

Costruttore predefinito e altro costruttore

Dunque, per dare un valore ai membri dato, variabile nel corso dell'esecuzione, overloadiamo il costruttore:
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 Rettangolo();
 Rettangolo(int, int);
 int area();
};


Rettangolo::Rettangolo(){}
Rettangolo::Rettangolo(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}
void main(){
 Rettangolo MioRettangolo(6,4);
 printf("%u",MioRettangolo.area());
 getc(stdin);
}
In verde, il costruttore predefinito, in blu il nuovo costruttore, con parametri, e il codice che istanzia la classe usando questo costruttore.
Posso anche stabilire che se istanziato senza parametri il rettangolo abbia valori dei membri dato di default di 5 e 3, mentre se istanziato con parametri assuma i valori specificati in questi parametri:
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 Rettangolo();
 Rettangolo(int, int);
 int area();
};


Rettangolo::Rettangolo(){
 base=3;
 altezza=5;
}
Rettangolo::Rettangolo(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}
void main(){
 Rettangolo BaseRettangolo;
 Rettangolo MioRettangolo(6,4);
 printf("%u\n",BaseRettangolo.area());
 printf("%u",MioRettangolo.area());
 getc(stdin);
}
Ecco: la classe viene istanziata senza parametri, secondo il costruttore predefinito, e allora assume il valore predefinito di 5 e 3 per base e altezza, con area corrispondente; poi viene istanziata con parametri, attribuendo ai membri dato i valori di 6 e 4 per base e altezza, con area corrispondente.

Costruttore, overloading obbligatorio del costruttore.

Il costruttore è in pratica un membro funzione, o metodo, che viene chiamato al momento in cui la classe viene istanziata in un oggetto.
Ecco il rettangolo di cui sopra:
#include<stdio.h>
class Rettangolo{
	int base, altezza;
public: 
	void setValues(int b, int a);
	int area();
};


void Rettangolo::setValues(int b, int a){
	base=b;
	altezza=a;
}
int Rettangolo::area(){
	return(base*altezza);
}
void main(){
	Rettangolo MioRettangolo;
	MioRettangolo.setValues(3,5);
	printf("%u",MioRettangolo.area());
	getc(stdin);
}
...in cui prima di stampare a video l'area, valore restituito dal metodo area(), bisogna chiamare esplicitamente il metodo setValues(int,int) che dia un valore ai membri dato base e altezza.
Se non si chiama prima il metodo setValues(int,int) il metodo area() non può dare alcun risultato.

Anzichè chiamare esplicitamente il metodo setValues(int,int) dispongo un metodo che viene chiamato automaticamente al momento dell'istanziazione della classe, ossia il costruttore:
#include<stdio.h>
class Rettangolo{
	int base, altezza;
public: 
	Rettangolo(int,int);
	int area();
};


Rettangolo::Rettangolo(int b, int a){
	base=b;
	altezza=a;
}
int Rettangolo::area(){
	return(base*altezza);
}
void main(){
	Rettangolo MioRettangolo;

	printf("%u",MioRettangolo.area());
	getc(stdin);
}
Ottengo un errore!

Perchè?

Il messaggio di errore è nessun costruttore predefinito

Ah, già! Ricordo che è assolutamente necessario che ci sia un costruttore predefinito per ogni classe! Questo costruttore deve essere privo di parametri.

Ovviamo!

#include<stdio.h>
class Rettangolo{
	int base, altezza;
public: 
	Rettangolo();
	int area();
};


Rettangolo::Rettangolo(){
	base=5;
	altezza=3;
}
int Rettangolo::area(){
	return(base*altezza);
}
void main(){
	Rettangolo MioRettangolo;

	printf("%u",MioRettangolo.area());
	getc(stdin);
}
Ecco: il costruttore senza parametri mi viene accettato. Nel suo contesto posso inserire i valori da dare ai membri dato all'atto dell'istanziazione o anche lasciarlo vuoto.
Se non vi è altro costruttore, esso può essere anche omesso, ma se vi è un altro costruttore esso deve essere dichiarato esplicitamente, anche privo di codice.
In pratica, ogni inserimento di costruttore differente da quello predefinito deve essere inteso come un overloading del costruttore!

sabato 12 maggio 2012

Improprietà nella dichiarazione del prototipo della funzione

Ho commesso un'improprietà

Nel prototipo del membro funzione, dichiarato nel contesto della dichiarazione della classe, ho specificato gli identificatori dei parametri, cosa di cui posso fare benissimo a meno.
Ho scritto:
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 void setValues(int b, int a);
 int area();
} PrimoRettangolo;


void Rettangolo::setValues(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}
quando invece avrei potuto scrivere semplicemente:
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 void setValues(int,int);
 int area();
} PrimoRettangolo;


void Rettangolo::setValues(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}
in quanto nella dichiarazione del prototipo non interessa al compilatore sapere gli identificatori dei parametri, ma interessa soltanto sapere che il membro funzione avrà due parametri di tipo int.

Modi diversi di istanziare la classe

Adesso invece di istanziare la classe subito dopo la sua dichiarazione, la istanzio nel contesto della procedura main.
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 void setValues(int b, int a);
 int area();
};


void Rettangolo::setValues(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}
void main(){
 Rettangolo MioRettangolo;
 MioRettangolo.setValues(3,5);
 printf("%u",MioRettangolo.area());
 getc(stdin);
}


Si tratta solo di un modo diverso di istanziare una classe.
Ora la istanzio in più oggetti, una nel contesto della dichiarazione e una dopo:
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 void setValues(int b, int a);
 int area();
} PrimoRettangolo;


void Rettangolo::setValues(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}
void main(){
 Rettangolo SecondoRettangolo;
 PrimoRettangolo.setValues(3,5);
 SecondoRettangolo.setValues(4,6);
 printf("%u\n",PrimoRettangolo.area());
 printf("%u",SecondoRettangolo.area());
 getc(stdin);
}
E' uguale, la classe viene istanziata sia nell'oggetto PrimoRettangolo che in quello SecondoRettangolo.

Incapsuliamo

Che cosa si intende per incapsulamento nella programmazione a oggetti?

Ecco, è esattamente quello che ricordavo: ossia il rendere invisibili all'esterno alcuni membri, lasciandone visibili solo alcuni, prevalentemente di tipo funzione, in modo da operare sull'oggetto che istanzia la classe tenendo le fila di questi metodi che hanno accesso ai membri invisibili all'esterno.

Ho trovato il termine di modello black box, che è descrivibile in base a "come reagisce" ma i cui ingranaggi interni non sono visibili.
Questo dovrebbe essere un principio fondamentale nella programmazione a oggetti, da ottenere mediante l'incapsulamento.
Per la classe Rettangolo abbiamo i membri dato base e altezza che sono, fin qui, stati visibili all'esterno in quanto compresi sotto lo specificatore di accesso public.
Rendendoli privati, e lasciando pubblici soltanto i metodi che su di essi agiscono, li incapsuliamo: ci basta, dall'esterno, muovere le giuste leve e queste agiranno sui membri dato interni e ci daranno il risultato.

Incapsuliamo:
#include<stdio.h>
class Rettangolo{
 int base, altezza;
public: 
 void setValues(int b, int a);
 int area();
} MioRettangolo;


void Rettangolo::setValues(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}
void main(){
 MioRettangolo.setValues(3,5);
 printf("%u",MioRettangolo.area());
 getc(stdin);
}
Ecco: ho marcato anche lo specificatore di accesso public in modo da evidenziare che i membri dato base e altezza sono precedenti ad esso, senza alcuno specificatore, il che significa che essi sono privati (la condizione di default dei membri di una classe quando non preceduti da nessuno specificatore esplicito). Agendo sui membri funzione setValues e area si opera sui dati "segreti" dell'oggetto che istanzia la classe e si mostra a video il risultato. Essi sono vere e proprie "leve" accessibili dall'esterno per operare sui meccanismi interni all'oggetto.

Definizione di un membro all'esterno della dichiarazione di una classe

Adesso mettiamo il membro funzione fuori della dichiarazione della classe.
#include<stdio.h>
class Rettangolo{
public: 
 int base,altezza;
 void setValues(int b, int a);
 int area(){return (base*altezza);}
} MioRettangolo;

void Rettangolo::setValues(int b, int a){
 base=b;
 altezza=a;
}

void main(){
 MioRettangolo.setValues(3,5);
 printf("%u",MioRettangolo.area());
 getc(stdin);
}
Ecco: la definizione del membro funzione SetValues è stata messa esternamente alla dichiarazione della classe Rettangolo, mentre all'interno di questa dichiarazione ne è stato messo il prototipo (marcato in verde).
Proviamo adesso a lasciare all'interno solo i prototipi di tutti i membri funzione:
#include<stdio.h>
class Rettangolo{
public: 
 int base,altezza;
 void setValues(int b, int a);
 int area();
} MioRettangolo;


void Rettangolo::setValues(int b, int a){
 base=b;
 altezza=a;
}
int Rettangolo::area(){
 return(base*altezza);
}

void main(){
 MioRettangolo.setValues(3,5);
 printf("%u",MioRettangolo.area());
 getc(stdin);
}
E ripassiamo la nomenclatura dei doppi due punti, che si chiamano operatore di scope.

Membri dato e membri funzione.

I membri di una classe possono essere dati o funzioni.

Scrivo ora una classe con dei membri dati (in rosso) e membri funzione (in blu).
Evidenzio anche su sfondo giallo il nome dell'oggetto che istanzia la classe.
#include<stdio.h>
class Rettangolo{
public: 
 int base,altezza;
 void setValues(){base=5;altezza=3;}
 int area(){return (base*altezza);}
} MioRettangolo;


void main(){
 MioRettangolo.setValues();
 printf("%u",MioRettangolo.area());
 getc(stdin);
}

Meglio, mettiamo i valori da attribuire a base e altezza sotto forma di parametri del membro funzione SetValues della classe Rettangolo:
#include<stdio.h>
class Rettangolo{
public: 
 int base,altezza;
 void setValues(int b, int a){base=b;altezza=a;}
 int area(){return (base*altezza);}
} MioRettangolo;


void main(){
 MioRettangolo.setValues(3,5);
 printf("%u",MioRettangolo.area());
 getc(stdin);
}
un tantino più flessibile... direi!!! (frase da intendere in senso piuttosto ironico: è ovvio che stabilire i valori nell'ambito della dichiarazione della classe è semplicemente ridicolo!)

Ripassiamo il C++ ancora e ancora...

#include<stdio.h>
class MiaClasse{
public: 
 int membro1,membro2;
} oggetto,oggettino;


void main(){
 oggettino.membro1=123;
 oggettino.membro2=456;
 printf("%u",oggettino.membro2);
 getc(stdin);
}


Nomenclatura:
class MiaClasse{
public: 
 int membro1,membro2;
} oggetto,oggettino;
il nome della classe è l'identificatore
poi c'è lo specificatore di accesso.