JavascriptProva

lunedì 30 settembre 2013

Altri appunti sul file WAV

-d
1760:0100  52 49 46 46 05 2A 00 00-57 41 56 45 66 6D 74 20   RIFF.*..WAVEfmt
1760:0110  10 00 00 00 01 00 01 00-11 2B 00 00 11 2B 00 00   .........+...+..
1760:0120  01 00 08 00 64 61 74 61-E1 29 00 00 80 80 7F 80   ....data.)......
1760:0130  80 80 80 7F 80 80 80 80-80 80 80 80 80 80 80 80   ................
1760:0140  80 80 7F 80 80 80 80 80-80 80 80 80 81 80 7F 80   ................
1760:0150  80 80 80 80 80 80 80 80-80 80 80 80 80 80 80 80   ................
1760:0160  80 80 7F 80 80 80 80 80-80 80 80 80 80 80 80 80   ................
1760:0170  81 80 80 80 80 80 80 80-81 80 80 80 81 81 80 80   ................
-
57 41 56 45 = WAVE

66 6D 74 20 = fmt 

10 00 00 00 dovrebbe essere la grandezza di ogni chunk... dovrebbe essere sempre 16 tranne in casi che al momento sfuggono alla mia comprensione.

01 00 = compression code. In questo caso sarebbe 1 quindi "uncompressed".

01 00 = number of channels. Avrebbe quindi un solo canale.

11 2B 00 00 = sample rate: in questo caso è 11025, se non sbaglio.

11 2B 00 00 = average bytes per second, bytes medi al secondo. E' uguale al sample rate.

Basta così.
Adesso dobbiamo vedere bene cosa si intende per sample rate, channels e bytes per second.

Iniziamo a studiare i files WAV

Iniziamo a smontare pezzo-pezzo un file wav...

C:\Users\Antonello\Downloads>debug ciccio.wav
-d
1760:0100  52 49 46 46 05 2A 00 00-57 41 56 45 66 6D 74 20   RIFF.*..WAVEfmt
1760:0110  10 00 00 00 01 00 01 00-11 2B 00 00 11 2B 00 00   .........+...+..
1760:0120  01 00 08 00 64 61 74 61-E1 29 00 00 80 80 7F 80   ....data.)......
1760:0130  80 80 80 7F 80 80 80 80-80 80 80 80 80 80 80 80   ................
1760:0140  80 80 7F 80 80 80 80 80-80 80 80 80 81 80 7F 80   ................
1760:0150  80 80 80 80 80 80 80 80-80 80 80 80 80 80 80 80   ................
1760:0160  80 80 7F 80 80 80 80 80-80 80 80 80 80 80 80 80   ................
1760:0170  81 80 80 80 80 80 80 80-81 80 80 80 81 81 80 80   ................
-
In relazione a questa guida abbiamo queste parti:
52 49 46 46 = i caratteri ASCII per RIFF

05 2A 00 00 = la grandezza del file meno gli otto bytes di questa voce e della precedente.
00002A05 a quanto corrisponde? Mano alla calcolatrice!
10757.

Verifichiamo le dimensioni del file:
Clicco col destro sul file, e ottengo le proprietà:
10,5 KB (10.765 byte)
che corrisponde alla perfezione con la grandezza del file. 10757 + 8 bytes = 10765.
Grande!!!

Inizio a usare DirectSound.

Procediamo, come al solito con linquaggio da ignorante, a cercare di capire qualcosa su come si usa DirectSound.

Dunque, io creo un Device. Non ho ancora capito bene che accidenti sia, ma lo creo ugualmente, toh!

Imports Microsoft.DirectX
Imports Microsoft.DirectX.DirectSound
Public Class Form1
    Dim _dev As Device
    Dim _buffer As SecondaryBuffer
Eccolo dichiarato.
Dichiaro anche un buffer.

Vediamo come si istanzia questo Device.

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

        _dev = New Device
        _dev.SetCooperativeLevel(Me.Handle, CooperativeLevel.Priority)
Semplicemente con New Device. Il costruttore del Device non ha parametri.

Bene, ora che abbiamo dato vita a un Device, impostiamo il Cooperative Level.
Che accidenti è, per la precisione, questo Cooperative Level?

Credo che abbia a che fare con le altre applicazioni, ossia con la competizione con altre applicazioni per qualcosa, come le periferiche... ma non ne sono molto sicuro.

In ogni caso, sintetizziamo e andiamo avanti: Si dichiara e si istanzia un Device, che ha un semplice costruttore senza parametri, e quindi si imposta il Cooperative Level del Device stesso.
Quindi, dichiarato il buffer, si istanzia. Questo ha nel costruttore due parametri, uno che specifica il file, e l'altro che specifica il Device.
Forse si tratta di un discorso del tipo: "Creo un buffer nel quale immagazzino i dati del file e lo associo al tale Device.

Bene... con un linguaggio da caprone inizio a capire la cosa. Poi con calma cercherò di affinare sia la comprensione sia il linguaggio.
Se aspetto di comprendere tutto, non vado più avanti.

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!

martedì 17 settembre 2013

Il battesimo del C#

Conoscendo le basi della sintassi di C++, Java e anche VB.NET, il C# è molto intuitivo:
public class miaClasse
{
    public miaClasse(){
        this.funzione();
    }
    public void funzione(){
        System.Console.WriteLine("ciao");
        System.Console.ReadKey();
        return;
    }

}

public class Principale

{
    public static void Main(){
        miaClasse Classe=new miaClasse();
        
    }
}
Ho creato una classe miaClasse che nel costruttore chiama la sua funzione che stampa a video un messaggio.
Poi un'altra classe contenente la funzione static Main, la quale istanzia miaClasse, con evocazione immediata della funzione di stampa (in quanto "scatta" il costruttore).
Facilissimo, finora!

lunedì 16 settembre 2013

VB.NET: Sintassi di Declare per la dichiarazione delle API di Windows.

Cerchiamo di fissare bene la sintassi di Declare.

Ho trovato uno schema: Declare Function (function name) Lib (library) (arguments)
Proviamo a seguirlo...
Public Class Form1
    Declare Function Bippa Lib "kernel32" Alias "Beep" (ByVal dwFreq As Integer, ByVal dwDuration As Integer) As Integer

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Bippa(440, 2000)
        Bippa(800, 2000)
    End Sub
End Class
Ecco la funzione Beep della libreria "kernel32.dll", che ho rinominato alla buona "Bippa".
Dopo la specifica della libreria e prima degli argomenti va inserito l'Alias sotto forma di stringa, ossia il nome con cui la funzione figura nella libreria.

Proviamo con MessageBox.
Public Class Form1
    Declare Function Messaggio Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Integer, ByVal message As String, ByVal capt As String, ByVal uType As Integer) As Integer

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Messaggio(0, "Ciao, ciccio", "Messaggio", 0)
    End Sub

End Class
...che funziona, ovviamente!

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.

domenica 15 settembre 2013

Ripasso da zero di C++

Che cosa dobbiamo includere? Che cosa sono gli header? Credo che siano soltanto dei files in cui sono scritti i prototipi delle funzioni.

Per il momento includo stdio.h.

stdio.h contiene i prototipi di funzioni come printf che è il caso di andare a vedere.

(Mado', quanto sono ignorante in C++!)

#include 

void main(){
 printf("%X \n",12);
 getchar();
}
è un codice incredibilmente banale.
C


Praticamente ho scritto il numero 12 in codice esadecimale.

Ora, articoliamo un po' la cosa.

#include 

void main(){
 funzione();
}

void funzione(){
 printf("%X \n",12);
 getchar();
}
Ecco: se scrivo così ottengo un errore.
1>uno.cpp(4): error C3861: 'funzione': identificatore non trovato
Praticamente la funzione main() non conosce l'identificatore funzione.
Che fare?
Proviamo così:
#include 


void funzione(){
 printf("%X \n",12);
 getchar();
}

void main(){
 funzione();
}
C


...e funziona.


Proviamo in un altro modo:
#include 

void funzione();

void main(){
 funzione();
}

void funzione(){
 printf("%X \n",12);
 getchar();
}
Così funziona, perchè ho scritto prima il prototipo (mi pare che si chiamasse così) della funzione funzione().
Complichiamo un altro po' le cose.

#include 

void funzione(int);

void main(){
 funzione(12);
}

void funzione(int numero){
 printf("%X \n",numero);
 getchar();
}
Ecco, la funzione mi accetta un parametro di tipo int, e ho dovuto mettere nel prototipo un int per significare che accetta un parametro di quel tipo.

Adesso mettiamo due parametri.
#include 

void funzione(int, int);

void main(){
 funzione(12, 25);
}

void funzione(int numero, int numero2){
 printf("%X %d \n",numero, numero2);
 getchar();
}
C 25


Ovviamente, nel prototipo ci saranno due int.

...e così via.

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!

venerdì 13 settembre 2013

Riesumando il C++...

Riprendiamo in mano il C++, di cui ho dimenticato praticamente tutto...

Creo un progetto vuoto con il C++ Express 2010.

#include <stdio.h>

void main(){
 printf("Ciao bestia");
 getchar();
}
Estremissimamente semplice:
Ciao bestia

E per ricominciare, andiamo già bene...
Aggiungiamo la vecchia sintassi del file di intestazione iostream
#include <iostream>

void main(){
 using namespace std;
 cout<< "Ciao bestione";
 getchar();
}

StopWatch

Che cosa è StopWatch?

Ecco un codice, ricopiato pari pari da un esempio in rete:
        Dim sw As New Stopwatch
        sw.Start()
        Thread.Sleep(2000)
        sw.Stop()
        MsgBox("tempooooo!")
Dopo due secondi appare la MessageBox con scritto "tempooooo!".
Funziona.
L'ho applicata al mio tentativo di realizzare un' "eco" con VB.NET, e la cosa è riuscita.

martedì 10 settembre 2013

Gestione compilazione di un progetto VB.NET per il tipo di CPU

Ecco dove devo andare per impostare il tipo di CPU per il quale programmare.
Questa CPU è a 64 bit, e nel preparare le classi di riconoscimento e sintesi vocale ho incontrato problemi con il settaggio del dispositivo di input che si sono risolti impostando la compliazione a 64 bit.
Ora ho impostato "any CPU" e pare che vada bene lo stesso.
Bisogna vedere quando metterò questo programma su un sistema a 32 bit...

Questa è la schermata:


Per impostare le opzioni si apre dal menu:


che dà questa schermata:


Questo mi valga come promemoria!

Libreria jacSpeech: le mie classi personalizzate di riconoscimento e sintesi vocale.

Bene.
Sono riuscito a creare una jacSpeech.dll con le mie classi di riconoscimento e sintesi vocale, e a linkarla con un qualsiasi codice che istanzi le classi.
Ecco un codice che istanzia le classi jacRecognizer e jacSpeaker.
Public Class Form1
    Dim mioRecognizer As jacRecognizer
    Dim mioSpeaker As jacSpeaker
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        mioRecognizer = New jacRecognizer("C:\users\antonello\desktop\saluti.txt")
        mioSpeaker = New jacSpeaker
        AddHandler mioRecognizer.riconoscimento, AddressOf gestione
    End Sub
    Sub gestione(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs)
        mioSpeaker.parla(e.Result.Text)
    End Sub
End Class
Funziona egregiamente, direi!

lunedì 9 settembre 2013

Codice per scrivere files ad accesso casuale.

Per scrivere files ad accesso casuale (due TextBoxes e un Button sul form...)
Option Strict Off
Structure Record
     Public suono As String
     Public nome As String
End Structure

Public Class Form1
    Dim recNum As Integer

    Dim mioRecord As New Record


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        mioRecord.suono = TextBox1.Text
        mioRecord.nome = TextBox2.Text
        If System.IO.File.Exists("C:\users\antonello\desktop\file") Then
            recNum = CInt((FileLen("C:\users\antonello\desktop\file") / Len(mioRecord)))
        Else
            recNum = 0
        End If

        FileOpen(1, "c:\users\antonello\desktop\file", OpenMode.Random, , , Len(mioRecord))
        FilePut(1, mioRecord, recNum + 1)
        FileClose(1)
    End Sub
End Class

sabato 7 settembre 2013

Cercando di capire che accidenti sia il delegato.

Questo argomento dei delegati l'ho preso proprio in antipatia.
Mi faccio un esempio semplice semplice...

Public Class Form1
    Delegate Sub delegato()

    Dim metodo As delegato

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        metodo = AddressOf fai
        metodo()
        metodo = AddressOf esegui
        metodo()

    End Sub
    Sub esegui()
        MsgBox("eseguo")
    End Sub
    Sub fai()
        MsgBox("faccio")
    End Sub
End Class
Praticamente delegato sarebbe una specie di puntatore che può puntare a tutti i metodi con la stessa firma.

Me ne faccio un altro con una firma un po' più complicata.
Public Class Form1
    Delegate Sub delegato(ByVal stringa As String)
    Dim metodo As delegato
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        metodo = AddressOf fai
        metodo("faccio")
        metodo = AddressOf esegui
        metodo("eseguo")

    End Sub
    Sub esegui(ByVal stringa As String)
        MsgBox(stringa)
    End Sub
    Sub fai(ByVal stringa As String)
        MsgBox(stringa)
    End Sub
End Class
In questo caso la firma è diversa in quanto c'è il parametro stringa.

Ancora sulla cattura di eventi generati da una classe.

Ecco, ora creo una classe che genera un evento allo scoccare di un certo tempo, mediante un timer.
Imports System.Timers
Public Class Classe
    Private tempo As Timer
    Sub New()
        tempo = New Timer(5000)
        AddHandler tempo.Elapsed, AddressOf metodo
        tempo.Enabled = True

    End Sub
    Sub metodo(ByVal source As Object, ByVal e As ElapsedEventArgs)
        MsgBox("tempo")
    End Sub
End Class


Istanzio:
Public Class Form1
    Dim miaClasse As Classe
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        miaClasse = New Classe

    End Sub
End Class
E funziona. Dopo 5 secondi appare la MessageBox.
L'handler dell'evento timer.elapsed definisce già cosa fare.
Ora io voglio che la classe si limiti a registrare l'evento, lasciando al programma chiamante la decisione del cosa fare.

Riscrivo la classe:
Imports System.Timers
Public Class Classe
    Public Event TimerElapsed()
    Private tempo As Timer
    Sub New()
        tempo = New Timer(5000)
        AddHandler tempo.Elapsed, AddressOf metodo
        tempo.Enabled = True

    End Sub
    Sub metodo(ByVal source As Object, ByVal e As ElapsedEventArgs)
        RaiseEvent TimerElapsed()
    End Sub
End Class
e il programma chiamante:
Public Class Form1
    Dim miaClasse As Classe
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        miaClasse = New Classe
        AddHandler miaClasse.TimerElapsed, AddressOf TempoScaduto
    End Sub
    Sub TempoScaduto()
        MsgBox("tempo")
    End Sub
End Class
E funziona ancora.


Cerchiamo di vedere cosa si intende per delegato, per la precisione: non l'ho ancora capito.
AddHandler è seguito da un evento, e AddressOf da un event handler.

Se invece di usare AddHandler io definisco il gestore dell'evento nel programma chiamante mediante la clausola Handles ottengo un errore:
Public Class Form1
    Dim miaClasse As Classe
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        miaClasse = New Classe
        'AddHandler miaClasse.TimerElapsed, AddressOf TempoScaduto
    End Sub
    Sub TempoScaduto() Handles miaClasse.TimerElapsed
        MsgBox("tempo")
    End Sub
End Class
Ho "commentato" AddHandler, e ho aggiunto la clausola Handles al gestore dell'evento.
Ottengo errore:
Errore 1 La clausola Handles richiede una variabile WithEvents definita nel tipo che la contiene o in uno dei suoi tipi di base. C:\Users\Antonello\Documents\Visual Studio 2010\Projects\laboratorio\laboratorio\Form1.vb 7 32 laboratorio
Devo dichiarare Withevents l'istanza della classe, per usare Handles.
Public Class Form1
    Dim WithEvents miaClasse As Classe
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        miaClasse = New Classe
        'AddHandler miaClasse.TimerElapsed, AddressOf TempoScaduto
    End Sub
    Sub TempoScaduto() Handles miaClasse.TimerElapsed
        MsgBox("tempo")
    End Sub
End Class
Ecco. Così funziona.
Quindi:
  • Per usare AddHandler non è necessario dichiarare l'istanza WithEvents.
  • Per usare la clausola Handles è necessario dichiarare l'istanza WithEvents.

Intercettare gli eventi di una classe.

Devo catturare un evento generato da una classe.
Come fare?
Mettiamo in ordine le idee...

Nella classe "mittente" metto questa riga fra le dichiarazioni delle variabili:
Public Event Zum()
(Zum è un nome di fantasia).

...e nel codice dell'evento metto:
   Public Sub evento(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs) Handles motore.SpeechRecognized
       
......

        RaiseEvent Zum()
    End Sub


Nel programma principale dichiaro Withevents l'istanza della classe:
Public Class Form1
    Dim WithEvents mioSpeak As jacSpeak

.....
E nel metodo che voglio evocare in seguito all'evento Zum dell'istanza mioSpeak della classe jacSpeak uso Handles:
    Sub prova() Handles mioSpeak.Zum
        Label1.Text =mioSpeak.risultato
    End Sub
Questo è un primo metodo (che ricordo anche dal VB6), ma non credo proprio che sia l'unico: se l'evento della classe genera un risultato, inoltre, non so come fare, a parte il dichiarare una variabile pubblica della classe che venga modificata dall'evento (in questo caso la variabile Risultato).
Ma non mi sembra una soluzione "estetica"...

Copiare un ArrayList in una matrice (array) e riempire un ArrayList con linee di un file di testo, da trasferire in un array.

Public Class Form1
    Dim insieme As ArrayList
    Dim matrice As String()
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        insieme = New ArrayList
        insieme.Add("gongolo")
        insieme.Add("mammolo")
        insieme.Add("pisolo")
        ReDim matrice(insieme.Count)
        insieme.CopyTo(matrice)
        Debug.Print(matrice(0) & " " & matrice(1) & " " & matrice(2))
    End Sub
End Class
gongolo mammolo pisolo
Bene. Con questo codice ho trascritto il valore di un ArrayList in un array di stringhe.

Adesso il codice che anzichè immettere manualmente gli elementi in un ArrayList li prende da un file.
Creo il file settenani.txt
gongolo
pisolo
mammolo
eolo
dotto
brontolo
cucciolo
E adesso lo "prendo" con l'istruzione FileOpen, trascrivendo ogni linea del file in una variabile stringa e apponendolo all'ArrayList, quindi ridimensiono un array di stringhe alla stessa lunghezza dell'ArrayList e vi trascrivo i valori di questo:
Public Class Form1
    Dim insieme As New ArrayList
    Dim matrice As String()
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim str As String = String.Empty
        Dim n As Integer = 1
        FileOpen(1, "C:\Users\Antonello\Desktop\settenani.txt", OpenMode.Input)
        Do Until EOF(1)
            str = LineInput(1)
            insieme.Add(str)
        Loop
        ReDim matrice(insieme.Count)
        insieme.CopyTo(matrice)
        For Each elemento In matrice
            Debug.Print(elemento)
        Next
    End Sub
End Class
Ecco il risultato nella finestra di debug, dove il programma dice di stampare gli elementi della matrice di stringhe:
gongolo
pisolo
mammolo
eolo
dotto
brontolo
cucciolo
Dunque l'aver preso le linee del file di testo, l'averle messe in un ArrayList e quindi trascritte in un array di stringhe funziona.
Si potrebbe chiedersi perchè non mettere direttamente tutto in un array di stringhe: il problema è che a ogni mutamento di dimensioni di un array questo viene ritrascritto di sana pianta in un'altra locazione della memoria, e l'operazione è poco efficiente.

venerdì 6 settembre 2013

Conversione di un testo in codici ASCII esadecimali.

Il codice per convertire un file di testo nei corrispondenti codici ASCII espressi in notazione esadecimale (lo metto qui come mio promemoria).
Public Class Form1
    Dim b As Byte
    Dim esadecimale As String
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        FileOpen(1, "C:\Users\Antonello\Desktop\dante.txt", OpenMode.Binary)
        Dim n As Integer = 1
        Do Until EOF(1)
            FileGet(1, b, n)
            n += 1
            esadecimale += Hex(b)
        Loop
        FileClose(1)
        Debug.Print(esadecimale)
    End Sub
End Class

Eliminare un elemento di un arraylist se individuato. Metodo Clone...

Costruiamo un codice in cui da un array viene eliminata una voce che vi sia contenuta, se digitata in una textbox.
Public Class Form1
    Dim elenco As New ArrayList
    Dim copia As ArrayList

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        With elenco
            .Add("gongolo")
            .Add("eolo")
            .Add("dotto")
            .Add("mammolo")
            .Add("pisolo")
            .Add("brontolo")
            .Add("cucciolo")
        End With
        copia = DirectCast(elenco.Clone, ArrayList)
        elenca(ListBox1, elenco)
        elenca(ListBox2, copia)
    End Sub
    Sub elenca(ByRef lista As ListBox, ByVal matrice As ArrayList)
        For Each elemento In matrice
            lista.Items.Add(elemento)
        Next
    End Sub

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        If copia.Contains(TextBox1.Text) Then
            copia.Remove(TextBox1.Text)
            ListBox1.Items.Clear()
            ListBox2.Items.Clear()
            elenca(ListBox1, elenco)
            elenca(ListBox2, copia)
        End If

    End Sub
End Class
Ecco. Il metodo Clone() non l'ho capito in pieno. Dovrebbe fare una "copia superficiale" di un array, laddove per copia "superficiale" si intenderebbe solo i riferimenti a eventuali oggetti piuttosto che una copia degli oggetti stessi... Ma se gli elementi non sono oggetti, non ci dovrebbe essere nessuna differenza... Non so. Probabilmente capirò in seguito...

Dichiarazione e definizione di una matrice, ripasso.

La dichiarazione di una matrice, con successiva definizione, sposta la matrice. Praticamente ne crea una nuova. La prova è in questo:
Public Class Form1
    Dim matrice() As Integer
    Dim copia() As Integer

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        copia = matrice
        MsgBox(copia Is matrice)
        matrice = New Integer() {1, 2, 3}
        MsgBox(copia Is matrice)

    End Sub
End Class
Dove la seconda MsgBox restituisce False, mentre la prima restituisce True.
Nel creare spazio per la matrice, precedentemente solo dichiarata ma non definita, il programma sposta la matrice in un nuovo spazio, perchè la matrice-spia chiamata copia(), che ho identificato con la matrice, non risulta più identica ad essa.

Viceversa, se la dichiarazione si accompagna a una definizione, specificando anche il numero di elementi di una matrice, il cambiamento dei valori di questi elementi non sposta la matrice, che rimane identica alla sua matrice-spia copia().
Public Class Form1
    Dim matrice(3) As Integer
    Dim copia(3) As Integer

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        copia = matrice
        MsgBox(copia Is matrice)
        matrice(0) = 1
        matrice(1) = 2
        matrice(2) = 3
        matrice(3) = 4
        MsgBox(copia Is matrice)
        MsgBox(matrice(3))
        MsgBox(copia(3))
    End Sub
End Class
ottenendo True, True, 4, 4. Il secondo True significa che la matrice è rimesta allocata nella stessa area di memoria in quanto le sue dimensioni non sono state toccate.

Ripasso del modo di dichiarare le matrici

Bene.
Voglio un array di stringhe, che vadano spuntate una per una in modo da essere progressivamente eliminate man mano che vengono "nominate".
Vediamo un po' come arrangiare un array...

Ho ancora qualche problema nella comprensione esatta di come si dichiara un array.
Vado su MSDN per una spiegazione ben fatta.

Questo codice mi dà errore:
Imports System.Text
Public Class Form1
    Dim matrice As Integer
    Dim matrice2 As Integer()
    Dim matrice3() As Integer
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ReDim Preserve matrice(2)
    End Sub
End Class
Errore 1 Richiesta una matrice nell'istruzione 'Redim'.
Infatti, nonostante il nome, la variabile matrice non è una matrice.
Sostituendo matrice2 o matrice3 invece il programma si esegue correttamente, segno che si tratta di vere matrici (arrays).
Imports System.Text
Public Class Form1
    Dim matrice As Integer
    Dim matrice2 As Integer()
    Dim matrice3() As Integer
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ReDim Preserve matrice2(2)
    End Sub
End Class
Dunque questi modi di dichiarare matrici di dimensioni zero:
    Dim matrice2 As Integer()
    Dim matrice3() As Integer
sono ambedue validi.
Vediamo invece per la dichiarazione di matrici con un numero definito di voci:
    Dim matrice As Integer
    Dim matrice2 As Integer(3)
    Dim matrice3(3) As Integer
Errore 1 I limiti di matrice non possono trovarsi negli identificatori di tipo. 
Ecco, il numero delle voci non va attaccato all'identificatore del tipo, mentre può esserlo al nome della matrice, come nella terza dichiarazione.

lunedì 2 settembre 2013

Primi passi nel riconoscimento vocale: creazione di grammatiche.

Non mi ricordo con precisione nemmeno io che cosa ho scaricato...

Fammi un po' fare mente locale...

Microsoft Speech Platform - Runtime (Version 11)

Microsoft Speech Platform - Runtime Languages (Version 11)

Microsoft Speech Platform - Software Development Kit (SDK) (Version 10.2)

Bene.
Dopo aver scaricato e installato (almeno in parte) questo po' po' di roba, e creati i dovuti riferimenti, il codice sembra poter creare delle grammatiche elementari:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim motore As New SpeechRecognitionEngine
        Dim costruttore As New GrammarBuilder("why")
        Dim costruttore2 As New GrammarBuilder("yes")
        costruttore.Culture = Globalization.CultureInfo.GetCultureInfo("en-US")
        costruttore2.Culture = Globalization.CultureInfo.GetCultureInfo("en-US")
        Dim grammatica As New Grammar(costruttore)
        Dim grammatica2 As New Grammar(costruttore2)
        motore.LoadGrammar(grammatica)
        motore.LoadGrammar(grammatica2)
        MsgBox(motore.Grammars.Count)
     
    End Sub
End Class
...ottenendo come risultato 2, ossia che le grammatiche caricate nel motore di riconoscimento vocale sono due.
E' già un bel risultato, considerando che ieri sera e stanotte non facevo altro che collezionare errori da questo codice, i quali erano certamente dovuti al fatto che non era ancora stato installato tutto il necessario.