+---------------+
 ASSEMBLY XVII 
+---------------+

    Eis  o  arquivo  .ASM com as rotinas para manipulao da memria
expandida:

 +----------------------------------------------------------------+
   IDEAL                                                         
   MODEL LARGE,PASCAL                                            
   LOCALS                                                        
   JUMPS                                                         
                                                                 
   GLOBAL  emmGetVersion : PROC                                  
   GLOBAL  emmGetPageFrameSegment : PROC                         
   GLOBAL  emmGetAvailablePages : PROC                           
   GLOBAL  emmAllocPages : PROC                                  
   GLOBAL  emmFreePages : PROC                                   
   GLOBAL  emmMapPage : PROC                                     
   GLOBAL  emmGetError : PROC                                    
                                                                 
   DATASEG                                                       
                                                                 
   emmVersion  dw  0                                             
   emmError    db  0       ; Nenhum erro ainda... :)             
                                                                 
   CODESEG                                                       
                                                                 
   ; Obtm a verso do EMM.                                      
   ; Devolve no formato 0x0X0Y (onde X  verso e Y reviso).    
   ; Prottipo em C:                                             
   ;   unsigned pascal emmGetVersion(void);                      
   PROC    emmGetVersion                                         
       mov     [emmError],0    ; Inicializa flag de erro...      
       mov     ah,46h                                            
       int     67h             ; Invoca o EMM                    
       or      ah,ah           ; Testa o sucesso da funo...    
       jz      @@no_error                                        
       mov     [emmError],ah   ; Poe erro no flag...             
       mov     ax,-1           ; ... e retorna != 0.             
       jmp     @@done                                            
       mov     ah,al           ; Prepara formato da verso.      
       and     ax,111100001111b ; A funo 46h do EMM devolve    
       mov     [emmVersion],ax  ; no formato BCD... por isso     
   @@done:                      ; precisamos formatar...         
       ret                                                       
   ENDP                                                          
                                                                 
   ; Funo: Obtm o segmento do Page Frame.                     
   ; Prottipo em C:                                             
   ;   unsigned pascal emmGetPageFrameSegment(void);             
   PROC    emmGetPageFrameSegment                                
       mov     ah,41h      ; Usa a funo 41h do EMM             
       int     67h         ; Chama o EMM                         
       mov     ax,bx       ; Poe o segmento em AX                
                           ; Funo 41h coloca o segmento do     
                           ; "Page Frame" em BX.                 
       ret                                                       
   ENDP                                                          
                                                                 
   ; Funo: Obtm o nmero de pginas disponveis na memria.   
   ; Prottipo em C:                                             
   ;   unsigned pascal emmGetAvailablePages(void);               
   ; Obs:                                                        
   ;   No verifica a ocorrencia de erros... modifique se quiser 
   PROC    emmGetAvailablePages                                  
       mov     ah,42h                                            
       int     67h     ; Invoca o EMM.                           
       mov     ax,bx   ; Poe pginas disponiveis em AX.          
       ret                                                       
   ENDP                                                          
                                                                 
   ; Aloca pginas e devolve handle.                             
   ; Prottipo em C:                                             
   ;   int pascal emmGetAvailablePages(unsigned Pages);          
   ; Obs: Devolve -1 se houve erro na alocaao e seta            
   ;      a varivel emmError.                                   
   PROC    emmAllocPages                                         
   ARG     Pages:WORD                                            
       mov     [emmError],0    ; Inicializa flag de erros...     
       mov     bx,[Pages]      ; BX = nmero de pginas a alocar 
       mov     ah,43h                                            
       int     67h             ; Invoca o EMM.                   
       or      ah,ah           ; Verifica erro do EMM.           
       jz      @@no_error                                        
       mov     [emmError],ah   ; Poe erro na varivel emmError   
       mov     dx,-1                                             
   @@no_error:                                                   
       mov     ax,dx           ; retorna cdigo de erro.         
                               ; ou o handle.                    
       ret                                                       
   ENDP                                                          
                                                                 
   ; Libera pginas alocadas.                                    
   ; Prottipo em C:                                             
   ;   void pascal emmFreePages(int handle);                     
   ; Obs: No verifica erros... modifique se quiser...           
   PROC    emmFreePages                                          
   ARG     handle:WORD                                           
       mov     dx,[handle]                                       
       mov     ah,45h                                            
       int     67h                                               
       ret                                                       
   ENDP                                                          
                                                                 
   ; Mapeia uma pgina no Page Frame.                            
   ; Prottipo em C:                                             
   ;   int pascal emmMapPage(int handle,                         
   ;                         unsigned char pfPage,               
   ;                         unsignec PageNbr);                  
   ; Onde: handle  o valor devolvido pela funo de alocao de 
   ;       pginas.                                              
   ;       pfPage  o nmero da pgina do Page Frame (0 at 3).  
   ;       PageNbr   o nmero da pgina a ser colocada no       
   ;       Page Frame (0 at mximo - 1).                        
   ; Devolve -1 se ocorreu erro e seta a varivel emmError.      
   PROC    emmMapPage                                            
   ARG     handle:WORD, pfPage:BYTE, PageNbr:WORD                
       mov     [emmError],0                                      
       mov     ah,44h                                            
       mov     al,[pfPage]                                       
       mov     bx,[PageNbr]                                      
       mov     dx,[handle]                                       
       int     67h                                               
       or      ah,ah                                             
       jz      @@no_error                                        
       mov     [emmError],ah                                     
       mov     ah,-1                                             
   @@no_error:                                                   
       mov     al,ah                                             
       ret                                                       
   ENDP                                                          
                                                                 
   ; Retorna com o erro do EMM.                                  
   ; Prottipo:                                                  
   ;   int pascal emmGetError(void);                             
   PROC    emmGetError                                           
       mov     ax,[emmError]                                     
       ret                                                       
   ENDP                                                          
                                                                 
   END                                                           
 +----------------------------------------------------------------+

    Esta    uma   implementao   simplificada,   mas  para  nossos
propsitos serve muito bem.  Algumas consideraes:  A  alocao  de
memria  via  EMM no  feita da mesma maneira que a funo malloc()
de C ou GetMem() do  TURBO  PASCAL.  No  devolvido nenhum pointer.
Isto se torna bvio a partir do momento que entendemos como funciona
o EMM: Toda a manipulao de bancos de  memria    feita  de  forma
indireta pelo Page Frame.  A funo de alocao deve apenas devolver
um handle para que possamos  manipular as pginas alocadas.  Entenda
esse handle da mesma forma com que os  arquivos  so  manipulados...
Se  quisermos  usar um banco alocado precisamos informar ao EMM qual
dos bancos queremos usar, fazendo  isso  via o handle devolvido pelo
prprio EMM.

    Suponha que queiramos alocar  128kb  da memria expandida para o
nosso programa.  Precisamos alocar 8 pginas  lgicas  (8  *  16k  =
128k).  Chamariamos a funo emmAllocPages() em C da seguinte forma:

 +----------------------------------------------------------------+
   #include <conio.h>                                            
   #include <stdlib.h>                                           
                                                                 
   int emm_handle;                                               
                                                                 
   void f(void)                                                  
   {                                                             
       /* ... */                                                 
       if ((emm_handle = emmAllocPages(8)) == -1) {              
           cprintf("EMM ERROR #%d\r\n", emmGetError());          
           exit(1);                                              
       }                                                         
       /* ... */                                                 
   }                                                             
 +----------------------------------------------------------------+

    Na  funo  emmAllocPages() optei por devolver -1 para indicar o
insucesso da funo...  Voc pode  arrumar um esquema diferente para
chegar isso (por  exemplo,  checando  a  varivel  emmError  aps  a
chamada a funo!).

    Well...  Temos 8 pginas lgicas disponveis.  E agora?...  As 8
pginas esto sempre numeradas de 0 at o mximo - 1.  No nosso caso
teremos as pginas 0 at 7 disponveis ao nosso programa.  Lembre-se
que cada uma tem apenas 16k de tamanho e que podem ser arranjadas de
qq  maneira  q  vc  queira  no  Page Frame.  Vamos usar as 4 pginas
iniciais como  exemplo...  para  isso  precisamos  mapea-las no Page
Frame usando a funo emmMapPage().

 +-----------------------------------------------------------------+
   void f(void)                                                   
   {                                                              
       int i;                                                     
                                                                  
       /* ... */                                                  
       for (i = 0; i < 4; i++)                                    
           emmMapPage(emm_handle,i,i);                            
   }                                                              
 +-----------------------------------------------------------------+

    Depois  deste  pequeno  loop  sabemos  que qualquer alterao no
contedo do  Page  Frame  alterar  as  pginas  que  esto mapeadas
nele...:) Simples n?  S nos resta conhecer o endereo  inicial  do
Page Frame:

 +-----------------------------------------------------------------+
   #include <dos.h>                                               
                                                                  
   void far *PageFrameAddr;                                       
                                                                  
   void f(void)                                                   
   {                                                              
       /* ... */                                                  
       PageFrameAddr = MK_FP(emmGetPageFrameSegment(), 0);        
       /* ... */                                                  
   }                                                              
 +-----------------------------------------------------------------+

    Ao  fim do uso da memria expandida precisamos dealocar o espao
previamente alocado...  C  e  C++  dealocam automaticamente qualquer
espao alocado por malloc(), calloc() e funes afins...   No    o
caso  de  nossas rotinas acima...  ento acostume-se a manter a casa
em ordem e usar  a  funo  emmFree()  quando  no precisar mais das
pginas alocadas.

    Isso  tudo  no funcionar se o EMM no estiver instalado...  No
texto anterior mostrei a rotina  para  determinar a presena do EMM.
E, no mesmo texto, apareceu a rotina emm_majorVer().  Eis  a  rotina
abaixo:

 +----------------------------------------------------------------+
   int emm_majorVer(void)                                        
   { return ((int)emmGetVersion() >> 8); }                       
 +----------------------------------------------------------------+

    See ya l8tr
