Metropoli BBS
VIEWER: frmi_ex3.asm MODE: TEXT (CP437)
; Program to show how to fast copy with REP MOVS, to fast compare with
; REP CMPS, and to execute large loop with LOOPX instruction
; Written by Erwann Corvellec

IDEAL
P386
DOSSEG
JUMPS
MODEL LARGE, PASCAL

INCLUDE "FRMI.INC"
INCLUDE "MYMACROS.MAC"

STACK 200h

═══════════════════════════════════════════════════════════════════════════════

DATASEG
   MEM_TO_ALLOC     EQU 400
   MEM_TO_ALLOC_STR EQU "400"
   LOOPX_CNT        EQU 12345h
   LOOPX_CNT_STR    EQU "12345h"

   TCR              EQU 13, 10
   
   @MKPS ERRMSG_Bad_CPU,             <TCR, "A 386 CPU or greater is needed !", TCR>     
   @MKPS ERRMSG_V86,                 <TCR, "Your CPU is running in V86 MODE ! Please remove QEMM386, 386MAX, EMM386, etc.", TCR>
   @MKPS ERRMSG_XMS_DRIVER_Error     <"Error while initializing XMS driver !", TCR>
   @MKPS ERRMSG_Alloc_Error,         <TCR, TCR, "Error while allocating ", MEM_TO_ALLOC_STR, "KB of memory...", TCR>
   @MKPS ERRMSG_Checking_Error,      <TCR, "Error while checking...", TCR>
   
   @MKPS INFOMSG_Copying,            <TCR, "Fast copy of ", MEM_TO_ALLOC_STR, "KB from DS:0 to allocated zone (EAX)...", TCR, "EAX=">
   @MKPS INFOMSG_Checking,           <TCR, TCR, "Checking now...", TCR>
   @MKPS INFOMSG_Checking_Successful,<"Checking is successful !", TCR>
   @MKPS INFOMSG_Counting,           <TCR, "Counting from ", LOOPX_CNT_STR, " downto 1 using LOOPX. Press a key to stop it...", TCR>

═══════════════════════════════════════════════════════════════════════════════

CODESEG
ASSUME CS:@code, DS:@data

PROC  Disp_PS NEAR USES AX BX CX DX
      ARG PS:NEAR PTR BYTE

   Mov   AH, 40h
   Mov   CH, 0
   Mov   BX, [PS]
   Mov   CL, [BX]
   Inc   BX
   Mov   DX, BX
   Mov   BX, 1
   INT   21h
   RET
ENDP

PROC  Disp_Hex NEAR USES AX BX CX
      ARG Digit:DWORD

   Mov   CX, 8
   
@@Print_Loop:
   Rol   [Digit], 4
   Mov   AL, [BYTE Digit]
   And   AL, 0Fh
   Cmp   AL, 10
   JB    @@Dec_Number

   Add   AL, 'A'-'0'-10

@@Dec_Number:
   Add   AL, '0'
   Mov   BH, 0
   Mov   AH, 0Eh
   INT   10h

   LOOP  @@Print_Loop

   RET
ENDP



───────────────────────────────────────────────────────────────────────────────
STARTUPCODE
   ; Release all memory except the amount currently being used
   ; End of stack is end of non-heap portion of program
   
   Mov   BX, SP
   Shr   BX, 4     ; Convert SP into paragraphs
   Inc   BX
   Mov   AX, SS    ; Calculate size of program using PSP address in ES
   Add   BX, AX
   Mov   AX, ES
   Sub   BX, AX
   Mov   AH, 4Ah   ; Resize memory block with PSP address in ES
   INT   21h       


   CALL  FRMI_START
   JNC   @@In_FRM

   Cmp   AH, CPU_80386
   JAE   @@CPU_Type_OK

   CALL  Disp_PS, OFFSET ERRMSG_Bad_CPU
   @DOS_EXIT 1

@@CPU_Type_OK:
   CALL  Disp_PS, OFFSET ERRMSG_V86
   @DOS_EXIT 1

───────────────────────────────────────────────────────────────────────────────
@@In_FRM:
   CALL  XMS_INIT
   JNC   @@XMS_Init_OK
   
   CALL  Disp_PS, OFFSET ERRMSG_XMS_DRIVER_Error
   @DOS_EXIT  1

───────────────────────────────────────────────────────────────────────────────
@@XMS_Init_OK:
   CALL  GET_RAM, MEM_TO_ALLOC
   JNC   @@Enough_Free       
   
   CALL  Disp_PS, OFFSET ERRMSG_Alloc_Error
   @DOS_EXIT 1

@@Enough_Free:
   CALL  Disp_PS, OFFSET INFOMSG_Copying
   CALL  Disp_Hex, EAX

   Xor   BX, BX
   Mov   ES, BX

   @SEG2LIN ESI, <DS>, <0>
   Mov   EDI, EAX
   Mov   ECX, MEM_TO_ALLOC*1024

   Push  EDI ECX
   
   CLD
   REP MOVS [BYTE ES:EDI], [ES:ESI]    ; This copy ECX bytes ("BYTE") from
                                       ; ES:ESI to ES:EDI
                                       ; Note that in the source pointer
                                       ; (here ES:ESI) the segment can be
                                       ; changed in DS, ES, FS or GS !
                                       ; But that is nonsense in FRM, because
                                       ; ES must be equal to 0 ! 
                                       ; So why would U use another segment ?
   Pop   ECX EDI

   CALL  Disp_PS, OFFSET INFOMSG_Checking

   Mov   ESI, EDI                      ; Just for you to see how CMPS works...
   CLD
   REP CMPS [BYTE ES:EDI], [ES:ESI]    ; Same principle as above...
   
   CALL  FREE_RAM, EAX

   JCXZ  @@Check_OK

   CALL  Disp_PS, OFFSET ERRMSG_Checking_Error ; Can we arrive here ?!?
   JMP   @@Exit

@@Check_OK:
   CALL  Disp_PS, OFFSET INFOMSG_Checking_Successful

───────────────────────────────────────────────────────────────────────────────
   CALL  Disp_PS, OFFSET INFOMSG_Counting

   Mov   ECX, LOOPX_CNT            ; Yes, we will use ECX in LOOP instruction 
   Mov   AX, 0E0Dh
   Mov   BH, 0

@@Loop_X:
   @KB_HIT
   JNZ   @@Exit

   Test  CL, 110000b
   JNZ   @@Dont_Disp

   CALL  Disp_Hex, ECX
   
   INT   10h
   
@@Dont_Disp:
   LOOPX @@Loop_X                  ; Here's the interesting part 

   Mov   AX, 0B800h                ; ES can be used as a standard segment reg
   Mov   ES, AX
   Mov   [DWORD ES:2*80], 0CF4BBF4Fh

───────────────────────────────────────────────────────────────────────────────
@@Exit:
   @DOS_EXIT 0

END
[ RETURN TO DIRECTORY ]