;===========================================================================
;
;    S C R N B L T  -  Graphics Screen Block Transfer for Turbo Pascal
;
;===========================================================================
;
;     by Larry Stone      21-Jul-84
;
; SCRNBLT is written to be called from Turbo Pascal V2.0 using the
; EXTERNAL procedure convention.  It will copy the contents of the
; video graphics display buffer (either low or high resolution) to
; a designated memory location on a virtual "pad" of set dimensions.
;
; To use SCRNBLT, the following lines must be included in your Turbo
; Pascal program.  The type "padtype" demonstrates how a PAD data type
; may be declared. When the PAD type is declared, the dimensions of
; the array must be as shown here.  It should also be specified ABSOLUTE
; with the initial offset 0, so that all of a 64K segment is available
; to it.
;
;  PROCEDURE SCRNBLT( MOVDIR, PADX, PADY : INTEGER; VAR PAD : PADARRAY );
;    EXTERN 'SCRNBLT';
;
;  TYPE
;    padtype = ARRAY [0..159, 0..399] OF BYTE;
;  VAR
;    pad : padtype ABSOLUTE $BA00 : $0000;    (* sample declaration *)
;
;  SCRNBLT(0, x, y, pad);   (* sample call; saves screen on pad(x,y) *)
;
; The parameters PADX and PAXY are specified as absolute coordinates
; on the pad, exactly as screen coordinates would be specified.  The
; X-coordinates correspond to the bits on the pad, not the bytes.
;
; The first parameter, "MOVDIR", determines the direction of the transfer.
; If 0 is specified, the contents of the screen are transferred to the
; designated area of the pad; if a nonzero number is specified,
; the designated area of the pad is transferred to the screen buffer.
;
; SCRNBLT transfers the contents of the screen as a rectangular block,
; 80 bytes wide by 200 bytes high, to a rectangular area of the pad
; at the designated x,y coordinates.  The pad area is not organized
; exactly like the display buffer; odd and even lines are interleaved
; in a more straightforward fashion to allow the offset of an x,y location
; in the pad to be calculated as  (y * padwidth) + x.
;
; To use SCRNBLT with a Turbo Program:
;-------------------------------------
; 1. Assemble this file with MASM.  "A> MASM SCRNBLT;"
; 2. Link it into a .EXE file.      "A> LINK SCRNBLT;"
; 3. Use EXE2BIN to make a .COM file. "A> EXE2BIN SCRNBLT.EXE SCRNBLT.COM"
; 4. Declare as shown in your Turbo Pascal program.
; 5. Ignore any minor diagnostic messages that may be generated when
;    this file is assembled and linked.  EXE2BIN is supplied with the
;    supplemental programs for PC-DOS; see the DOS manual.
;

;
; EQUATES TO DEFINE GRAPHICS CONSTANTS
;
PADWD   EQU     160                     ; WIDTH OF VIRTUAL PAD, BYTES
SCRNWD  EQU     80                      ; WIDTH OF REAL SCREEN
SCRNWD2 EQU     40                      ; HALF OF SCREEN WIDTH
HALFSC  EQU     100                     ; LINES FOR HALF OF SCREEN
EVENSC  EQU     0B800H                  ; ADDRESS OF EVEN HALF OF SCREEN
ODDSC   EQU     02000H                  ; OFFSET OF ODD HALF OF SCREEN

CODE    SEGMENT PUBLIC
        ASSUME  CS:CODE
        PUBLIC  SCRNBLT
;
; EQUATES FOR ARGUMENTS TO SCRNBLT
;
PADOFS  EQU     4[BP]                   ; OFFSET OF PAD ORIGIN
PADSEG  EQU     6[BP]                   ; SEGMENT OF PAD
PADY    EQU     8[BP]                   ; Y-COORD IN PAD
PADX    EQU     10[BP]                  ; X-COORD IN PAD
MOVDIR  EQU     12[BP]                  ; MOVE DIRECTION (0 = TO SCREEN)

SCRNBLT PROC    NEAR
        PUSH    BP
        MOV     BP,SP                   ; CALLING CONVENTION
        PUSH    DS                      ; SAVE DS
;
; COMPUTE THE ADDRESS OF THE ORIGIN OF THE PAD AREA DESIGNATED BY
; PADX, PADY.  LOC = PAD_OFFSET + (PADY * PADWD) + (PADX / 8)
;
        MOV     AX,PADY                 ; PREPARE PADY * PADX
        MOV     BX,PADWD
        MUL     BX                      ; AX = PADY * PADX (DX TRASHED)
        MOV     BX,PADX                 ; ADD IN X COMPONENT:
        MOV     CL,3                    ; (PREPARE TO SHIFT BY 3)
        SHR     BX,CL                   ;  DIV BY 8 SO WE MOVE BY BYTE
        ADD     AX,BX                   ;  ADD IT IN.
        ADD     AX,PADOFS
        CMP     WORD PTR MOVDIR,0       ; IS MOVDIR 0?
        JE      BACKWRD                 ; IF SO, DO BACKWARDS MOVE.
;----------------------------------------------------
; MOVE "FORWARD" : FROM PAD TO REAL SCREEN
;----------------------------------------------------
;  MOVE THE EVEN LINES OF THE SCREEN. THE LOOP IS CONTROLLED BY
;  THE HALFSC VALUE IN DX; A REP MOVSW IS USED TO MOVE THE SCREEN
;  LINES A WORD AT A TIME (PROBABLY NO MORE EFFICIENT ON AN 8088..)
;
        CLD                             ; BE SURE DIR. FLAG IS 0
        MOV     BX,PADSEG               ; SOURCE IS PAD, SO SET DS TO
        MOV     DS,BX                   ;  PAD SEGMENT
        MOV     SI,AX                   ;  AND DI TO OFFSET IN PAD
        MOV     BX,EVENSC               ; DEST IS SCREEN, SET ES TO SCREEN
        MOV     ES,BX                   ;  SEGMENT.
        MOV     DI,0                    ;  AND INDEX OF 0
;
; LOOP TO COPY EVEN LINES
;
        MOV     CX,HALFSC               ; PREPARE LOOP COUNTER
EVEN:   MOV     DX,CX                   ; RECORD LOOP COUNTER
        MOV     CX,SCRNWD2              ; SET UP COUNTER
        REP     MOVSW                   ; DO THE MOVE
        ADD     SI,PADWD+PADWD-SCRNWD   ; MOVE SI TO NEXT SCREEN LINE
        MOV     CX,DX
        LOOP    EVEN
;
; NOW SET UP FOR ODD LINES AND COPY THOSE
;
        MOV     DI,ODDSC                ; SET DI TO ODD SCREEN OFFSET
        MOV     SI,AX
        ADD     SI,PADWD                ; SET SI TO FIRST ODD LINE
;
; COPY ODD LINES
;
        MOV     CX,HALFSC               ; LOAD COUNTER
ODD:    MOV     DX,CX                   ; SAVE COUNTER
        MOV     CX,SCRNWD2              ; SET UP LINE COUNTER
        REP     MOVSW
        ADD     SI,PADWD+PADWD-SCRNWD   ; MOVE SI TO NEXT SCREEN LINE
        MOV     CX,DX                   ; GET LOOP COUNTER
        LOOP    ODD
        JMP     BYE                     ; LEAVE
;----------------------------------------------------
; MOVE "BACKWARD" : FROM REAL SCREEN TO PAD
;----------------------------------------------------
;  THIS IS ALMOST EXACTLY LIKE THE CODE ABOVE THAT MOVES
;  FROM THE VIRTUAL SCREEN TO THE DISPLAY BUFFER
;
BACKWRD:
        CLD                             ; BE SURE DIR. FLAG IS 0
        MOV     BX,PADSEG               ; DEST IS PAD, SO SET ES TO
        MOV     ES,BX                   ;  PAD SEGMENT
        MOV     DI,AX                   ; SET UP SOURCE INDEX
        MOV     BX,EVENSC               ; SET DS TO SCREEN SEGMENT
        MOV     DS,BX                   ;  FOR EVEN LINES OF SCREEN
        MOV     SI,0                    ;  AND SOURCE INDEX
;
; LOOP TO COPY EVEN LINES
;
        MOV     CX,HALFSC               ; PREPARE LOOP COUNTER
BEVEN:  MOV     DX,CX                   ; RECORD LOOP COUNTER
        MOV     CX,SCRNWD2              ; SET UP COUNTER
        REP     MOVSW                   ; DO THE MOVE
        ADD     DI,PADWD+PADWD-SCRNWD   ; MOVE SI TO NEXT SCREEN LINE
        MOV     CX,DX
        LOOP    BEVEN
;
; NOW SET UP FOR ODD LINES AND COPY THOSE
;
        MOV     SI,ODDSC                ; SET DI TO ODD SCREEN OFFSET
        MOV     DI,AX
        ADD     DI,PADWD                ; SET SI TO FIRST ODD LINE
;
; COPY ODD LINES
;
        MOV     CX,HALFSC               ; LOAD COUNTER
BODD:   MOV     DX,CX                   ; SAVE COUNTER
        MOV     CX,SCRNWD2              ; SET UP LINE COUNTER
        REP     MOVSW
        ADD     DI,PADWD+PADWD-SCRNWD   ; MOVE SI TO NEXT SCREEN LINE
        MOV     CX,DX                   ; GET LOOP COUNTER
        LOOP    BODD
;
; DONE.. CLEAN UP THE STACK AND LEAVE
;
BYE:    POP     DS                      ; RESTORE DS
        MOV     SP,BP
        POP     BP                      ; CONVENTIONAL RETURN
        RET     10                      ; TRASH 10 BYTES FOR PARMS

SCRNBLT ENDP
CODE    ENDS
        END
