;--------------------------------------------------;
; file callret.asm                                 ; 
; programma di esempio per CALL e RET              ;
;--------------------------------------------------;
; nasm -f obj callret.asm -l callret.lst           ;
; tlink callret.obj + exelib.obj                   ;
; (oppure link /map callret.obj + exelib.obj)      ;
;--------------------------------------------------;                  

;########### direttive per l'assembler ############

CPU 386                             ; set di istruzioni a 32 bit
%include "exelib.inc"               ; inclusione libreria di I/O

;######### dichiarazione tipi e costanti ##########

%assign STACK_SIZE   0400h          ; 1024 byte per lo stack

;################ segmento dati ###################

SEGMENT     DATASEGM ALIGN=16 PUBLIC USE16 CLASS=DATA

;----- inizio definizione variabili statiche ------

row_counter dw    0600h             ; contatore riga di output
near_addr   dw    0                 ; indirizzo NEAR

strCodesegm db    'CODESEGM = ', 0
stringa1    db    'Chiamata diretta intrasegmento (Rel16)', 0
stringa2    db    'Chiamata indiretta intrasegmento (Reg16)', 0
stringa3    db    'Chiamata indiretta intrasegmento (Mem16)', 0

;------- fine definizione variabili statiche ------

;################ segmento dati 2 #################

SEGMENT     DATASEGM2 ALIGN=16 PUBLIC USE16 CLASS=DATA

;----- inizio definizione variabili statiche ------

far_addr    dd    0                 ; indirizzo FAR

stringa4    db    'Chiamata diretta intersegmento (Ptr16:Ptr16)', 0
stringa5    db    'Chiamata indiretta intersegmento (Mem16:Mem16)', 0

;------- fine definizione variabili statiche ------

;############### segmento codice ##################

SEGMENT     CODESEGM ALIGN=16 PUBLIC USE16 CLASS=CODE

..start:                            ; entry point

   mov      ax, DATASEGM            ; trasferisce DATASEGM
   mov      ds, ax                  ; in DS attraverso AX
   
;------- inizio blocco principale istruzioni ------

   call     far set80x50            ; modo video 80 righe, 50 colonne
   call     far clearScreen         ; pulisce lo schermo

   mov      ax, DATASEGM2           ; trasferisce DATASEGM2
   mov      fs, ax                  ; in FS attraverso AX

   push     ds                      ; copia DS
   pop      es                      ; in ES (per writeString)

; ATTENZIONE! Se si ha la necessita' di modificare ES, bisogna preservarne
; il vecchio contenuto; la procedura writeString presuppone, infatti, che
; ES contenga la componente Seg dell'indirizzo della stringa da visualizzare!

; mostra il valore assegnato dal SO a CODESEGM

   mov      di, strCodesegm         ; di = offset strCodesegm
   mov      dx, 0400h               ; riga 4, colonna 0
   call     far writeString         ; mostra la stringa

   mov      ax, CODESEGM            ; ax = CODESEGM
   mov      dx, 040Bh               ; riga 4, colonna 11
   call     far writeHex16          ; mostra ax

; chiamata diretta intrasegmento di start_maiuscole1 (Rel16)

   mov      bx, stringa1                  ; ds:bx punta a stringa1
   call     start_maiuscole1              ; conversione di stringa1

   mov      dx, [row_counter]             ; dx = coordinate di output
   mov      di, stringa1                  ; es:di punta a stringa1
   call     far writeString               ; stampa la stringa
   add      word [row_counter], 0100h     ; incremento riga

; chiamata indiretta intrasegmento di start_maiuscole1 (Reg16)

   mov      bx, stringa2                  ; ds:bx punta a stringa2
   mov      ax, start_maiuscole1          ; cs:ax punta alla procedura
   call     ax                            ; conversione di stringa2

   mov      dx, [row_counter]             ; dx = coordinate di output
   mov      di, stringa2                  ; es:di punta a stringa2
   call     far writeString               ; stampa la stringa
   add      word [row_counter], 0100h     ; incremento riga

; chiamata indiretta intrasegmento di start_maiuscole1 (Mem16)

   mov      bx, stringa3                  ; ds:bx punta a stringa3
   mov      word [near_addr], start_maiuscole1 ; cs:near_addr punta alla proc.
   call     [near_addr]                   ; conversione di stringa3

   mov      dx, [row_counter]             ; dx = coordinate di output
   mov      di, stringa3                  ; es:di punta a stringa3
   call     far writeString               ; stampa la stringa
   add      word [row_counter], 0100h     ; incremento riga

;--------------------------------------------------

   push     fs                            ; copia FS
   pop      es                            ; in ES (per writeString)

; ATTENZIONE! Se si ha la necessita' di modificare ES, bisogna preservarne
; il vecchio contenuto; la procedura writeString presuppone, infatti, che
; ES contenga la componente Seg dell'indirizzo della stringa da visualizzare!

; chiamata diretta intersegmento di start_maiuscole2 (Ptr16:Ptr16)

   mov      bx, stringa4                  ; fs:bx punta a stringa4
   call     far start_maiuscole2          ; conversione di stringa4

   mov      dx, [row_counter]             ; dx = coordinate di output
   mov      di, stringa4                  ; es:di punta a stringa4
   call     far writeString               ; stampa la stringa
   add      word [row_counter], 0100h     ; incremento riga

; chiamata indiretta intersegmento di start_maiuscole2 (Mem16:Mem16)

   mov      bx, stringa5                  ; fs:bx punta a stringa5
   mov      ax, start_maiuscole2          ; ax = Offset destinazione
   mov      word [fs:far_addr+0], ax      ; salva nella WORD bassa di far_addr
   mov      ax, seg start_maiuscole2      ; ax = Seg destinazione
   mov      word [fs:far_addr+2], ax      ; salva nella WORD alta di far_addr
   call     far [fs:far_addr]             ; conversione di stringa5

   mov      dx, [row_counter]             ; dx = coordinate di output
   mov      di, stringa5                  ; es:di punta a stringa4
   call     far writeString               ; stampa la stringa
   add      word [row_counter], 0100h     ; incremento riga

;-------- fine blocco principale istruzioni -------
   
   mov      ah, 4ch                 ; servizio Terminate Program
   mov      al, 00h                 ; exit code = 0
   int      21h                     ; chiama i servizi DOS

;------------ inizio blocco procedure -------------

;  La procedura start_maiuscole1 converte una stringa in maiuscolo;
;  la stringa deve essere puntata da DS:BX.
;  Prima di effettuare la conversione, start_maiuscole1 visualizza
;  l'indirizzo di ritorno NEAR; tale indirizzo si trova, ovviamente,
;  in cima allo stack e cioe', in SS:SP

start_maiuscole1:                         ; start procedura

   mov      bp, sp                        ; ss:bp = ss:sp
   mov      dx, [row_counter]             ; dx = contatore riga
   mov      ax, [bp]                      ; ax = return Offset
   call     far writeHex16                ; stampa ax
   add      word [row_counter], 0100h     ; incremento riga

strconvert_loop:                          ; WHILE ([ds:bx] != 0)
   mov      al, [bx]                      ; al = codice ASCII da esaminare
   test     al, al                        ; (al == 0) ?
   jz       exit_loop                     ; if (al == 0) jump exit_loop
   cmp      al, 'a'                       ; confronta al con 'a'
   jb       next_char                     ; non e' una minuscola
   cmp      al, 'z'                       ; confronta al con 'z'
   ja       next_char                     ; non e' una minuscola

   sub      al, 32                        ; converte in maiuscolo
   mov      [bx], al                      ; salva nella stringa

next_char:
   inc      bx                            ; prossimo elemento della stringa
   jmp      short strconvert_loop         ; DO

exit_loop:
   ret                                    ; NEAR return = C3h

;-------------- fine blocco procedure -------------

;############## segmento codice 2 #################

SEGMENT     LIBCODE ALIGN=16 PUBLIC USE16 CLASS=CODE

;  La procedura start_maiuscole2 converte una stringa in maiuscolo;
;  la stringa deve essere puntata da FS:BX.
;  Prima di effettuare la conversione, start_maiuscole2 visualizza
;  l'indirizzo di ritorno FAR; tale indirizzo si trova, ovviamente,
;  in cima allo stack e cioe', in SS:SP

start_maiuscole2:                         ; start procedura

   mov      bp, sp                        ; ss:bp = ss:sp
   mov      dx, [row_counter]             ; dx = contatore riga
   mov      ax, [bp]                      ; ax = return Offset
   call     far writeHex16                ; stampa ax
   add      word [row_counter], 0100h     ; incremento riga

   mov      dx, [row_counter]             ; dx = contatore riga
   mov      ax, [bp+2]                    ; ax = return Seg
   call     far writeHex16                ; stampa ax
   add      word [row_counter], 0100h     ; incremento riga

strconvert_loop2:                         ; WHILE ([fs:bx] != 0)
   mov      al, [fs:bx]                   ; al = codice ASCII da esaminare
   test     al, al                        ; (al == 0) ?
   jz       exit_loop2                    ; if (al == 0) jump exit_loop
   cmp      al, 'a'                       ; confronta al con 'a'
   jb       next_char2                    ; non e' una minuscola
   cmp      al, 'z'                       ; confronta al con 'z'
   ja       next_char2                    ; non e' una minuscola

   sub      al, 32                        ; converte in maiuscolo
   mov      [fs:bx], al                   ; salva nella stringa

next_char2:
   inc      bx                            ; prossimo elemento della stringa
   jmp      short strconvert_loop2        ; DO

exit_loop2:
   retf                                   ; FAR return = CBh
   
; al posto di RETF possiamo anche scrivere: db 0CBh

;################# segmento stack #################

SEGMENT     STACKSEGM ALIGN=16 STACK USE16 CLASS=STACK

            resb     STACK_SIZE     ; 1024 byte per lo stack
            
;##################################################