;-------------------------------------------------------------------------;
;                                  TIGA                                   ;
;        Copyright (c) 1986-1990  Texas Instruments Incorporated.         ;
;			   All Rights Reserved				  ;
;-------------------------------------------------------------------------;
;   TIGA - Graphics Manager Extension                                     ;
;-------------------------------------------------------------------------;
;                                                                         ;
; zoom_rect function                                                      ;
;                                                                         ;
;   Expand or shrink pixels in source rectangle to fit dimensions of      ;
;   destination rectangle.  This type of function is sometimes referred   ;
;   to as a "stretch blit."                                               ;
;                                                                         ;
;   Horizontal zooming is done by replicating (if expanding) or           ;
;   eliminating (if shrinking) columns of pixels from the source          ;
;   rectangle.  Vertical zooming is done by replicating (if expanding)    ;
;   or eliminating (if shrinking) rows of pixels from the source          ;
;   rectangle.  When shrinking, several pixels from the source rectangle  ;
;   are collapsed into a single pixel in the destination rectangle.  The  ;
;   source pixels collapsed in this manner are combined according to the  ;
;   currently selected pixel processing operation.  For example, the      ;
;   replace operation simply selects a single source pixel to represent   ;
;   all the pixels in the region being collapsed.  A better result can    ;
;   often be obtained using a logical-OR (at one bit per pixel) or MAX    ;
;   (at multiple bits per pixel) operation instead.                       ;
;                                                                         ;
;   The destination rectangle is always on the screen.  The source        ;
;   rectangle is on the screen by default, but can be changed to a        ;
;   linear offscreen buffer by a call to setsrcbm with a nonzero          ;
;   argument.  The source rectangle is specified by the first four        ;
;   arguments:  ws (source width), hs (source height), xs (x coordinate   ;
;   at left side of source rectangle), and ys (y coordinate at top of     ;
;   source rectangle).  If the source array is on the screen,             ;
;   coordinates (xs,ys) are specified either to the screen's current      ;
;   drawing origin.  If the source array is in an offscreen buffer, xs    ;
;   and ys are specified relative to the top left corner of the source    ;
;   array.  The destination rectangle is specified by the second set of   ;
;   four arguments:  wd (destination width), hd (destination height), xd  ;
;   (x coordinate at left side of destination rectangle), and yd (y       ;
;   coordinate at top of destination rectangle).                          ;
;                                                                         ;
;   The last argument sent to the zoom_rect function is the address of a  ;
;   buffer.  The buffer must be large enough to contain one full scan     ;
;   line of the display.  For example, if the screen is 640 pixels wide,  ;
;   and each pixel is 4 bits, the buffer capacity must be at least 640x4  ;
;   = 2560 bits.                                                          ;
;-------------------------------------------------------------------------;
; Usage:  zoom_rect(ws, hs, xs, ys, wd, hd, xd, yd, linebuf);             ;
;                                                                         ;
; Description of stack arguments:                                         ;
;   long ws, hs;     /* source width and height */                        ;
;   long xs, ys;     /* source top left corner coordinates */             ;
;   long wd, hd;     /* destination width and height */                   ;
;   long xd, yd;     /* destination top left corner coordinates */        ;
;   short *linebuf;  /* pointer to scan line buffer */                    ;
;                                                                         ;
; Returned in register A8:  Void (undefined).                             ;
;                                                                         ;
; Registers altered:  A8                                                  ;
;-------------------------------------------------------------------------;
; Revision history:                                                       ;
;   10/29/86...Original version written..................Jerry Van Aken   ;
;   9/03/87....Added 9th input argument, linebuf.........JV               ;
;   9/18/88....Added TIGA dm, globals and dstbm check....Graham Short     ;
;   1/11/89....Added option to have linear srcbm.........Graham Short     ;
;   9/06/89....Fixed window clipping bug.................JV		  ;
;-------------------------------------------------------------------------;
;
;       .title    'zoom rect'
;       .file     'zoom_rect'
;
;
;     DECLARE GLOBAL FUNCTION NAMES
;
        .globl    _zoom_rect
        .globl    _dm_zoom_rect
;
;
;     REFERENCE EXTERNAL GLOBALS
;
        .include  gsptypes.inc
        .include  gspglobs.inc          
        .include  gspreg.inc
;
;  Bitmap structure
BMADDR:   .equ    00h         ;unsigned char *
BMPITCH:  .equ    020h        ;unsigned short
BMXEXT:   .equ    030h        ;unsigned short
BMYEXT:   .equ    040h        ;unsigned short
BMPSIZE:  .equ    050h        ;unsigned short
;
FCOLOR  .set      048120400h          ;lookup color to erase line buffer
;
;     C-PACKET ENTRY POINT
;
_zoom_rect:

* Save A- and B-file registers.
        MMTM      SP,A0,A1,A2,A3,A4,A5,A6,A7,A9,A10,A11,A12,A13
        MMTM      SP,B0,B1,B2,B7,B8,B9,B10,B11,B12,B13,B14
* Pull 8 arguments into register file A.
        SUBI      9*32,A14            ;point to bottom of args
        MMFM      A14,A0,A1,A2,A3,A4,A5,A6,A7,A8
        JR        common_ep
;
;     C-PACKET ENTRY POINT
;
_dm_zoom_rect:
        MOVE      *-A14,A8,1          ;point to data area
* Save A- and B-file registers.
        MMTM      SP,A0,A1,A2,A3,A4,A5,A6,A7,A9,A10,A11,A12,A13
        MMTM      SP,B0,B1,B2,B7,B8,B9,B10,B11,B12,B13,B14
* Pull 8 arguments into register file A.
        SETF      16,1,0
        MOVE      *A8+,A0,0           ; get ws
        MOVE      *A8+,A1,0           ; get hs
        MOVE      *A8+,A2,0           ; get xs
        MOVE      *A8+,A3,0           ; get ys
        MOVE      *A8+,A4,0           ; get wd
        MOVE      *A8+,A5,0           ; get hd
        MOVE      *A8+,A6,0           ; get xd
        MOVE      *A8+,A7,0           ; get yd
        MOVE      *A8,A8,1            ; get linebuf
;
;     TWO ENTRY POINTS JOIN HERE
;
common_ep:

        SETF      16,0,0              ;
        MOVE      A8,B10              ;copy linebuf argument
;
; The 9 arguments are now in the following registers:
;     ws is in A0,  hs is in A1,  xs is in A2,	ys is in A3,
;     wd is in A4,  hd is in A5,  xd is in A6,	yd is in A7,
;     and linebuf is in B10
;
; Check for destination bitmap pointing to the screen.	If not, abort for
; now.	Linear drawing capability will be added to a later revision.
;
        MOVE      @(_env+ENVIRONMENT_DSTBM),A8,1    ; destination bit-map global
        JRNZ      ABORT                     ; if zero, dstbm is set to screen
;
; Check for destination completely to right of or below clipping window.
;
	MOVE	  A7,A8 	      ;get yd
	SLL	  16,A8 	      ;
	MOVX	  A6,A8 	      ;concatenate xd and yd
	MOVE	  @_env+ENVIRONMENT_XYORIGIN,A12,1 ;get yorg::xorg
	ADDXY	  A12,A8	      ;make xd,yd screen-relative
	CPW	  A8,A8 	      ;is (xd,yd) in window?
	JRNV	  INWINDOW	      ;jump if point inside window
	BTST	  6,A8		      ;is xd <= xend ?
	JRNZ	  ABORT 	      ;if not, jump (invisible)
	BTST	  8,A8		      ;is yd <= yend ?
	JRNZ	  ABORT 	      ;if not, jump (invisible)
INWINDOW:
	MOVY	  A12,A13	      ;copy yorg
	SRA	  16,A13	      ;sign-extend yorg
	SEXT	  A12,0 	      ;sign-extend xorg
;
; Check for source bitmap pointing to the screen.
;
        MOVE      @(_env+ENVIRONMENT_SRCBM),A8,1    ; source bit-map global
        JRNZ      LINEAR_SRC                ; if zero, source is set to screen

SCREEN_SRC:
* Convert viewport-relative x coordinates to screen-relative x coordinates.
	ADD	  A12,A6	      ;convert xd
	ADD	  A12,A2	      ;convert xs
        JRNN      XSPOS               ;jump if xs >= 0
* Handle the case of negative starting x coordinate for source array.
        MOVE      A4,A8               ;copy wd
        MPYS      A2,A8               ;xs*wd (negative result)
        DIVS      A0,A8               ;xs*wd/ws
        ADD       A8,A4               ;wd' = wd - |xs*wd/ws|
        SUB       A8,A6               ;xd' = xd + |xs*wd/ws|
        ADD       A2,A0               ;ws' = ws - |xs|
        CLR       A2                  ;xs' = 0 (clip to pos x space)
XSPOS:
* Convert viewport-relative y coordinates to screen-relative y coordinates.
	ADD	  A13,A7	      ;convert yd
	ADD	  A13,A3	      ;convert ys
        JRNN      YSPOS               ;jump if ys >= 0
* Handle the case of negative starting y coordinate for source array.
        MOVE      A5,A8               ;copy hd
        MPYS      A3,A8               ;ys*hd (negative result)
        DIVS      A1,A8               ;ys*hd/hs
        ADD       A8,A5               ;hd' = hd - |ys*hd/hs|
        SUB       A8,A7               ;yd' = yd + |ys*hd/hs|
        ADD       A3,A1               ;hs' = hs - |ys|
        CLR       A3                  ;ys' = 0 (clip to pos y space)
YSPOS:
* Convert source XY coordinates to memory address.
        SLL       16,A3               ;
        MOVY      A3,A2               ;
        MOVE      B3,B1               ;SPTCH = DPTCH
        CVXYL     A2,A2               ;
        JR        SETUP
LINEAR_SRC:
        MOVE      *A8(BMPSIZE),A9,0   ;get PSIZE of source bitmap
        MOVE      @PSIZE,A10,0        ;get screen PSIZE
        CMP       A9,A10              ;equal ?
        JRNE      ABORT               ;if not abort
;
; setup source pitch and address
;
        MOVE      *A8(BMPITCH),A9,0   ;get source pitch 
        MOVE      A9,B1               ;and set SPITCH
	MPYS	  A9,A3 	      ;ys * SPTCH
        LMO       A10,A10             ;lmo of PSIZE
        SUBK      31,A10              ;
        NEG       A10                 ;convert to shift count
        SLL       A10,A2              ;xs * PSIZE...
        ADD       A3,A2               ;...+ ys * SPTCH...
        MOVE      *A8(BMADDR),A9,1    ;
        ADD       A9,A2               ;...+ SADDR
;
; setup destination address
;
	ADD	  A12,A6	      ;convert xd
	ADD	  A13,A7	      ;convert yd
SETUP:
* Make sure transparency is off.
        SETF      1,0,0               ;
        MOVI      CONTROL+5,A13       ;pointer to T bit in CONTROL
        MOVE      *A13,A8,0           ;
        CLR       A9                  ;
        MOVE      A9,*A13,0           ;set T = 0
        SETF      16,0,0              ;
        MOVE      A8,*-SP,1           ;save original T on stack
* If width or height of source or destination is zero or less, all done!
        MOVE      A0,A0               ;test value of ws
        JRLE      DONE                ;done if ws <= 0
        MOVE      A1,A1               ;test value of hs
        JRLE      DONE                ;done if hs <= 0
        MOVE      A4,A4               ;test value of wd
        JRLE      DONE                ;done if wd <= 0
        MOVE      A5,A5               ;test value of hd
        JRLE      DONE                ;done if hd <= 0
* Get pixel size.
        MOVE      @PSIZE,A13,0        ;
* Decide whether to expand or shrink in horizontal dimension.
        CMP       A4,A0               ;is ws < wd ?
        JRGE      HSHRK               ;jump if ws >= wd

*-----------------------------------------------------------------------
* HORIZONTAL EXPAND:  Set up parameters.
*   Destination array is wider than source array.
*-----------------------------------------------------------------------
* Calculate wp = wd / ws and wr = wd % ws, where % is C mod operator.
        CLR       A8                  ;MSBs of dividend are 0s
        MOVE      A4,A9               ;copy wd
        DIVU      A0,A8               ;wp in A8, wr in A9
        MOVE      A9,A9               ;test wr
        JRNZ      HXPD2               ;jump if wr is nonzero
* wr == 0:  All fat destination pixels are of same width, wp = wd / ws.
* Set up subroutine jump address to horizontal expand routine.
        MOVI      horz_expand1,A12    ;
        JR        SAVEHREGS           ;

* wr > 0:  Each fat destination pixel is of width wp or width wp+1.
HXPD2:
        ADD       A9,A9               ;kw1 = 2 * wr
        MOVE      A9,A10              ;dw = 2 * wr - ws
        SUB       A0,A10              ;
        MOVE      A10,A11             ;kw2 = 2 * wr - 2 * ws
        SUB       A0,A11              ;
* Set up subroutine jump address to horizontal expand routine.
        MOVI      horz_expand2,A12    ;
        JR        SAVEHREGS           ;

*-----------------------------------------------------------------------
* HORIZONTAL SHRINK:  Set up parameters.
*   Source array is wider than destination array.
*-----------------------------------------------------------------------
* Calculate wp = ws / wd and wr = ws % wd, where % is C mod operator.
HSHRK:
        CLR       A8                  ;MSBs of dividend are 0s
        MOVE      A0,A9               ;copy ws
        DIVU      A4,A8               ;wp in A8, wr in A9
        ADD       A9,A9               ;kw1 = 2 * wr
        MOVE      A9,A10              ;dw = 2 * wr - wd
        SUB       A4,A10              ;
        MOVE      A10,A11             ;kw2 = 2 * wr - 2 * wd
        SUB       A4,A11              ;
* Preclip source and destination arrays to window, if necessary.
        MOVE      B6,A3               ;check destn array right edge
        ZEXT      A3,0                ;(clear 16 MSBs)
        ADDK      1,A3                ;
        SUB       A6,A3               ;
        SUB       A4,A3               ;
        JRP       RIGHTOK             ;jump if xend - xd - wd >= 0
        ADD       A3,A4               ;clip to right edge of window
RIGHTOK:
        MOVE      B5,A3               ;check dest'n array left edge
        ZEXT      A3,0                ;(clear 16 MSBs)
        SUB       A6,A3               ;
        JRLE      LEFTOK              ;jump if dx = xleft - xd <= 0
        ADD       A3,A6               ;clip to left edge of window
        SUB       A3,A4               ;
        JRLE      DONE                ;done if wd <= 0 (out window)
* Have clipped xd to window; use linear interpolation to clip xs also.
        MOVE      A8,B8               ;get wp * (pixel size) in A14
        CVXYL     B8,B8               ;
        SUB       B4,B8               ;
        MOVE      B8,A14              ;
        MOVE      B8,A12              ;(wp+1)*(pixel size) in A12
        ADD       A13,A12             ;
        MOVE      A10,A10             ;check sign of dw
CLIPXS:
        JRGE      DWPOS               ;jump if dw >= 0
        ADD       A14,A2              ;xs += wp
        ADD       A9,A10              ;dw += kw1
        DSJS      A3,CLIPXS           ;loop dx times
        JR        LEFTOK              ;
DWPOS:
        ADD       A12,A2              ;xs += wp + 1
        ADD       A11,A10             ;dw += kw2
        DSJS      A3,CLIPXS           ;loop dx times
LEFTOK:
        MOVI      horz_shrink,A12     ;horiz. shrink subrtn address

* Save horizontal zoom parameters on stack.  These will be restored from
* the stack prior to each invocation of a horizontal zoom routine.
SAVEHREGS:
        MMTM      SP,A4,A8,A9,A10,A11,A12,A13
        MOVE      A4,B14              ;copy wd

* Decide whether to expand or shrink in vertical dimension.
        CMP       A5,A1               ;is hs < hd ?
        JRGE      VSHRK               ;jump if hs >= hd

*-----------------------------------------------------------------------
* VERTICAL EXPAND:  Set up parameters.
*   Destination array is taller than source array.
*-----------------------------------------------------------------------
* Calculate hp = hd / hs and hr = hd % hs, where % is C mod operator.
        CLR       A8                  ;MSBs of dividend are 0s
        MOVE      A5,A9               ;copy hd
        DIVU      A1,A8               ;hp in A8, hr in A9
        ADD       A9,A9               ;kh1 = 2 * hr
        MOVE      A9,A4               ;dh = 2 * hr - hs
        SUB       A1,A4               ;
        MOVE      A4,A11              ;kh2 = 2 * hr - 2 * hs
        SUB       A1,A11              ;
* Concatenate xs and ys to form destination XY address.
        SLL       16,A7               ;form XY address = yd::xd
        MOVY      A7,A6               ;
* Determine whether off-screen buffer is needed to build source rows.
	BTST	  15,A6 	      ;test sign bit of xd
	JRNZ	  NEEDBUFR	      ;jump if xd < 0
        CPW       A6,A5               ;is top dest'n row in window?
        BTST      7,A5                ;
	JRNZ	  NEEDBUFR	      ;jump if top outside window
        MOVI      CONTROL+10,A13      ;pointer to PPOP code
        SETF      5,0,1               ;PPOP code field size = 5
        MOVE      *A13,A14,1          ;get copy of PPOP code
        SETF      32,0,1              ;restore field 1 to 32 bits
        JRNZ      NEEDBUFR            ;jump if PPOP is not REPLACE
* Top row is in window and PPOP == REPLACE:  Each row can be built up
* on screen in the destination array.  No offscreen buffer is needed.
        MOVI      vert_expand1,B8     ;vert zoom subrtn address
        JR        SETVPARAMS          ;

* Need to build dest'n rows off-screen and then copy them onto screen.
NEEDBUFR:
        MOVI      vert_expand2,B8     ;vert zoom subrtn address
        JR        SETVPARAMS          ;

*-----------------------------------------------------------------------
* VERTICAL SHRINK:  Set up parameters.
*   Destination array is shorter than source array.
*-----------------------------------------------------------------------
* Calculate hp = hs / hd and hr = hs % hd, where % is C mod operator.
VSHRK:
        CLR       A8                  ;MSBs of dividend are 0s
        MOVE      A1,A9               ;copy hs
        DIVU      A5,A8               ;hp in A8, hr in A9
        ADD       A9,A9               ;kh1 = 2 * hr
        MOVE      A9,A4               ;dh = 2 * hr - hd
        SUB       A5,A4               ;
        MOVE      A4,A11              ;kh2 = 2 * hr - 2 * hd
        SUB       A5,A11              ;
* Preclip source and destination arrays to window, if necessary.
        MOVE      B6,A3               ;check dest'n bottom edge
        SRL       16,A3               ;
        ADDK      1,A3                ;
        SUB       A7,A3               ;
        SUB       A5,A3               ;
        JRP       BOTOK               ;jump if yend - yd - hd >= 0
        ADD       A3,A5               ;clip dest'n bottom edge
BOTOK:
        MOVE      B5,A3               ;check dest'n array top edge
        SRL       16,A3               ;
        SUB       A7,A3               ;
        JRLE      TOPOK               ;jump if dy=ystart - yd <= 0
        ADD       A3,A7               ;clip dest'n top edge
        SUB       A3,A5               ;
        JRGT      VISIBLE             ;jump if dest'n rect. visible
* hd <= 0:  Destination rectangle lies completely outside window.
        ADDI      7*32,SP             ;clean off stack
        JR        DONE                ;done!

* Had to clip yd to window; use linear interpolation to adjust ys also.
* For each "skinny" pixel clipped from the height of the destination
* array, clip a "fat" pixel of height hp or hp+1 from the source array.
VISIBLE:
	MOVE	  A8,B9 	      ;get hp * (SPTCH) in A14
        MPYS      B1,B9               ;
        MOVE      B9,A14              ;
        ADD       B1,B9               ;get (hp+1)*(SPTCH) in A12
        MOVE      B9,A12              ;
        MOVE      A4,A4               ;check sign of dh
CLIPYS:
        JRGE      DHPOS               ;jump if dh >= 0
        ADD       A14,A2              ;ys += hp
        ADD       A9,A4               ;dh += kh1
        DSJS      A3,CLIPYS           ;loop dy times
        JR        TOPOK               ;
DHPOS:
        ADD       A12,A2              ;ys += hp + 1
        ADD       A11,A4              ;dh += kh2
        DSJS      A3,CLIPYS           ;loop dy times
TOPOK:
* Concatenate xs and ys to form destination XY address.
        SLL       16,A7               ;form XY address = yd::xd
        MOVY      A7,A6               ;
* Set up hd parameter for vertical shrink routine.
        MOVE      A5,A1               ;move hd
* Go to vertical shrink routine.
        MOVI      vert_shrink,B8      ;vert zoom subrtn address

* Set up parameters for vertical zoom.
SETVPARAMS:
        MOVE      B10,A12             ;copy linebuf pointer
        MOVE      @PSIZE,A13,0        ;
        EXGF      A13,0               ;set field 0 = pixel size
        MOVI      CONTROL+10,A13      ;pointer to PPOP code
        SETF      5,0,1               ;PPOP code field size
        MOVE      *A13,A14,1          ;copy original PPOP code
        MOVI      010000h,B7          ;constant DYDX = +1::0
        MOVE      B7,A3               ;copy constant >00010000
        MOVE      B1,A10              ;copy source pitch
	MOVE	  B14,B1	      ;save dest'n width wd
* Push ppop address (A13), offscreen buffer address (A12), kh2 (A11),
* source pitch (A10), kh1 (A9) and hp (A8) onto system stack.
        MOVE      SP,A7
        MMTM      SP,A1,A4,A5,A8,A9,A10,A11,A12,A13
        JUMP      B8                  ;do vertical zoom

*-----------------------------------------------------------------------
* HORIZONTAL EXPAND:  Inner loop, case 1.
*   Use this form of the horizontal expand if wr == 0, i.e., if width
*   of fat destination pixel is always equal to wp (in A8).
*-----------------------------------------------------------------------

horz_expand1:

        MOVE      *B0+,B9,0           ;oldp = get_pixel(x1++,y1)
        MOVE      A0,A1               ;j = ws
* Count the no. of adjacent pixels in row that are the same color.
* Add each destination fat pixel to width n of fill region.
HXA1:
        CLR       A5                  ;n = 0 (width of fill area)
HXA2:
        ADD       A8,A5               ;n += wp
* Read next source pixel; if it is same color, loop again.
        MOVE      *B0+,B8,0           ;p=get_pixel(x1++,y1)
        CMP       B8,B9               ;p == oldp? (same color?)
        DSJEQ     A1,HXA2             ;loop if p == oldp & --j > 0
* Have reached end of string of same-colored pixels.
* At this point, n = width of new fill region in destination array.
* Replicate pixel "oldp" to fill entire COLOR1 register.
        MOVE      A13,B11             ;copy pixel size
        LMO       B11,B12             ;
        SUBK      26,B12              ;initialize loop count
        JRZ       HXA4                ;  
HXA3:
        MOVE      B9,B10              ;
        SLL       B11,B10             ;
        OR        B10,B9              ;replicate pixels again
        SLL       1,B11               ;double shift count
        DSJS      B12,HXA3            ;
HXA4:                                     
* Fill portion of row n pixels wide with pixel value "oldp".
        MOVE      A7,B2               ;load start address for fill
        ADDXY     A5,A7               ;x2 += n (update dest'n addr)
        ADDXY     A3,A5               ;set DYDX = 1::n
        MOVE      A5,B7               ;
        FILL      XY                  ;all fat pixels of same color
        MOVE      B8,B9               ;oldp = p (next fill color)
        SUBK      1,A1                ;--j
        JRP       HXA1                ;loop while j > 0
* Horizontal expand of pixel row has been completed.
        EXGPC     A12                 ;return

*----------------------------------------------------------------------
* VERTICAL EXPAND:  Inner loop, case 1.
*   Use this version of the vertical expand if the top line of the
*   destination rectangle is below the top of the window, and the pixel
*   processing operation is REPLACE.  Under these conditions, the top
*   line of a row of destination fat pixels can be built up in place
*   on screen rather than in an off screen buffer.
*-----------------------------------------------------------------------

vert_expand1:

* Loop once per each source row.  Each source row gets expanded to m
* destination rows, where m is equal either to hp or to hp+1.
VXA1:
        MOVE      A8,A5               ;m = hp
        MOVE      A4,A4               ;dh < 0 ?
        JRGE      VXA2                ;jump if dh >= 0
        ADD       A9,A4               ;dh += kh1
        JR        VXA3                ;
VXA2:
        ADDK      1,A5                ;++m
        ADD       A11,A4              ;dh += kh2
VXA3:
* Construct top line of row of fat destination pixels.
* Save A-file registers used by vert_expand1 routine.
        MOVE      A7,A8               ;
        MMTM      A8,A1,A4,A5         ;
* Setup A-file registers for horizontal zoom routine.
        MMFM      A7,A4,A8,A9,A10,A11,A12,A13
* Do horizontal zoom.
        MOVE      A2,B0               ;set up source address
        MOVE      A6,A7               ;set up dest'n address
        EXGPC     A12                 ;subroutine call
        MOVX      B1,B7               ;set DYDX = 1::wd
* Restore A-file registers used by vert_expand1 routine.
        MOVE      SP,A7               ;
        MMFM      A7,A1,A4,A5,A8,A9,A10,A11,A12,A13
* Update parameters for next loop of vertical zoom routine.
        MOVE      A6,B8               ;auxptr = dptr
        ADD       A3,A6               ;++yd
* Copy top line of row of fat pixels m-1 more times to finish fat row.
        SUBK      1,A5                ;--m
        JRLE      VXA5                ;jump if m already 0
VXA4:
	CVXYL	  B8,B0 	      ;load source address
        MOVE      A6,B2               ;load destination address
        ADD       A3,A6               ;++yd (update dest'n address)
        PIXBLT    L,XY                ;copy row
        DSJS      A5,VXA4             ;loop m-1 times
VXA5:
        ADD       A10,A2              ;++ys (incr. source address)
        DSJS      A1,VXA1             ;loop (#source rows) times
        JR        CLEANUP             ;all done!

*-----------------------------------------------------------------------
* HORIZONTAL EXPAND:  Inner loop, case 2.
*   Use this form of the horizontal expand if wr > 0, i.e., if width
*   of fat destination pixel varies between wp and wp+1.
*-----------------------------------------------------------------------

horz_expand2:

        MOVE      *B0+,B9,0           ;oldp = get_pixel(x1++,y1)
        MOVE      A0,A1               ;j = ws
* Count the no. of adjacent pixels in row that are the same color.
* Add each destination fat pixel to width n of fill region.
HXB1:
        CLR       A5                  ;n = 0 (width of fill area)
HXB2:
        ADD       A8,A5               ;n += wp
        MOVE      A10,A10             ;dw < 0?
        JRGE      HXB3                ;jump if dw >= 0
        ADD       A9,A10              ;dw += kw1
        JR        HXB4                ;
HXB3:
        ADDK      1,A5                ;++n
        ADD       A11,A10             ;dw += kw2
* Read next source pixel; if it is same color, loop again.
HXB4:
        MOVE      *B0+,B8,0           ;p=get_pixel(x1++,y1)
        CMP       B8,B9               ;p == oldp? (same color?)
        DSJEQ     A1,HXB2             ;loop if p == oldp & --j > 0
* Have reached end of string of same-colored pixels.
* At this point, n = width of new fill region in destination array.
* Replicate pixel "oldp" to fill entire COLOR1 register.
        MOVE      A13,B11             ;copy pixel size
        LMO       B11,B12             ;
        SUBK      26,B12              ;initialize loop count
        JRZ       HXB6                ;
HXB5:
        MOVE      B9,B10              ;
        SLL       B11,B10             ;
        OR        B10,B9              ;replicate pixels again
        SLL       1,B11               ;double shift count
        DSJS      B12,HXB5            ;
HXB6:                                 ;
* Fill portion of row n pixels wide with pixel value "oldp".
        MOVE      A7,B2               ;load start address for fill
        ADDXY     A5,A7               ;x2 += n (update dest'n addr)
        ADDXY     A3,A5               ;set DYDX = 1::n
        MOVE      A5,B7               ;
        FILL      XY                  ;all fat pixels of same color
        MOVE      B8,B9               ;oldp = p (next fill color)
        SUBK      1,A1                ;--j
        JRP       HXB1                ;loop while j > 0
* Horizontal expand of pixel row has been completed.
        EXGPC     A12                 ;return

*----------------------------------------------------------------------
* VERTICAL EXPAND:  Inner loop, case 2.
*   Use this version of the vertical expand if the top line of the
*   destination rectangle is above the top of the window, or if the
*   pixel processing operation is something other than REPLACE.  Under
*   these conditions, the top line of a row of destination fat pixels
*   must be built up in an offscreen buffer and PixBlt'd to the screen.
*-----------------------------------------------------------------------

vert_expand2:

* Loop once per each source row.  Each source row gets expanded to m
* destination rows, where m is equal either to hp or to hp+1.
VXB1:
        MOVE      A8,A5               ;m = hp
        MOVE      A4,A4               ;dh < 0 ?
        JRGE      VXB2                ;jump if dh >= 0
        ADD       A9,A4               ;dh += kh1
        JR        VXB3                ;
VXB2:
        ADDK      1,A5                ;++m
        ADD       A11,A4              ;dh += kh2
VXB3:
* Set up XY offset and window for access to offscreen buffer.
        MOVE      A3,*A13,1           ;set PPOP code = 0 (REPLACE)
        MMTM      SP,B4,B5            ;save offset and window
        MOVE      A12,OFFSET          ;dest'n = offscreen buffer
        CLR       WSTART              ;widen window for buffer
* Clear offscreen buffer to all 0s or all 1s.
        MOVE      A14,B8              ;copy original PPOP code
        MOVI      FCOLOR,B9           ;fill color bits for 22 PPOPs
        SLL       B8,B9               ;left justify fill bit
        SRA       31,B9               ;replicate fill bit 32 times
        MOVX      B1,B7               ;set DYDX = 1::wd
        CLR       DADDR               ;point to start of buffer
        FILL      XY                  ;clear buffer
        MOVE      A14,*A13,1          ;restore original PPOP code
* Before horiz. zoom, save registers used by vert_expand1 routine.
        MOVE      A7,A8               ;
        MMTM      A8,A1,A4,A5         ;
* Setup A-file registers for horizontal zoom routine.
        MMFM      A7,A4,A8,A9,A10,A11,A12,A13
* Use horizontal zoom to construct one row in offscreen buffer.
        MOVE      A2,B0               ;set up source address
        CLR       A7                  ;set up dest'n address
        EXGPC     A12                 ;subroutine call
        MOVE      B4,B8               ;get linear buffer address
        MOVX      B1,B7               ;set DYDX = 1::wd
* Restore registers used by vert_expand1 routine.
        MMFM      SP,B4,B5            ;restore offset and window
        MOVE      SP,A7               ;
        MMFM      A7,A1,A4,A5,A8,A9,A10,A11,A12,A13
* Copy offscreen buffer to m destination rows.
VXB4:
        MOVE      B8,B0               ;load source address
        MOVE      A6,B2               ;load destination address
        ADD       A3,A6               ;++yd (update dest'n address)
        PIXBLT    L,XY                ;copy row
        DSJS      A5,VXB4             ;loop m times
VXB5:
        ADD       A10,A2              ;++ys (incr. source address)
        DSJ       A1,VXB1             ;loop (#source rows) times
        JR        CLEANUP             ;all done!

*-----------------------------------------------------------------------
* HORIZONTAL SHRINK:  Inner loop.
*   Shrink in horizontal direction.  Source and destination arrays
*   should already have been pre-clipped.
*-----------------------------------------------------------------------

horz_shrink:
        MOVE      A7,B2               ;copy destination pointer
        MOVE      A13,B10             ;copy pixel size
        MOVK      1,B11               ;x increment = 1
        MOVE      A4,A1               ;set count j = wd (dst width)
* Count no. n of source pixels to be collapsed into one dest'n pixel.
* No. of iterations = width "wd" of destination rectangle in pixels.
HS1:
        MOVE      A8,A5               ;n = wp
        MOVE      A10,A10             ;dw < 0?
        JRGE      HS2                 ;jump if dw >= 0
        ADD       A9,A10              ;dw += kw1
        JR        HS3                 ;
HS2:
        ADDK      1,A5                ;++n
        ADD       A11,A10             ;dw += kw2
* Collapse n source pixels into a single destination pixel.
HS3:
        PIXT      *B0,B12             ;get pixel from (x1,y1)
        PIXT      B12,*B2.XY          ;move pixel to (x2,y2)
        ADD       B10,B0              ;++x1 (incr. source pointer)
        DSJS      A5,HS3              ;loop until n == 0
        ADDXY     B11,B2              ;x2++ (incr. dest'n pointer)
        DSJS      A1,HS1              ;loop until --j == 0
        EXGPC     A12                 ;return

*----------------------------------------------------------------------
* VERTICAL SHRINK:  Inner loop.
*   This routine shrinks the source rectangle to make it fit in a
*   destination rectangle that is not as tall as the source rectangle.
*-----------------------------------------------------------------------

vert_shrink:

* Loop once per destination row.  Collapse several source rows into one.
VS1:
        MOVE      A3,*A13,1           ;set PPOP = 0 (REPLACE op)
* Count no. m of lines of source rect. to be collapsed into dest'n line.
* Use linear interpolation to decide whether m is equal to hp or hp+1.
        MOVE      A8,A5               ;m = hp
        MOVE      A4,A4               ;dh < 0 ?
        JRGE      VS2                 ;jump if dh >= 0
        ADD       A9,A4               ;dh += kh1
        JR        VS3                 ;
VS2:
        ADDK      1,A5                ;++m
        ADD       A11,A4              ;dh += kh2
VS3:
* Copy first of m rows of "collapsed" region into off screen buffer.
        MOVE      A0,B10              ;DYDX = 1::ws
        MOVX      B10,B7              ;
        MOVE      A2,B0               ;address of next source row
        ADD       A10,A2              ;++ys (incr. source address)
        CLR       B2                  ;load dest'n address
        MMTM      SP,B4,B5            ;save offset, window start
        MOVE      A12,B4              ;get buffer offset
        CLR       B5                  ;open lower window bounds
        PIXBLT    L,XY                ;copy source row to buffer
        MOVE      A14,*A13,1          ;restore original PPOP code
        SUBK      1,A5                ;--m
        JRLE      VS5                 ;jump if m already 0
* Copy remaining m-1 rows of region into same off screen buffer.
VS4:
        MOVE      A2,B0               ;address of next source row
        ADD       A10,A2              ;++ys (incr. source address)
        CLR       B2                  ;load dest'n address
        PIXBLT    L,XY                ;copy source row to buffer
        DSJS      A5,VS4              ;loop until m==0
* Compute linear address of offscreen buffer, which will contain the
* source row to be given to the horizontal zoom routine.
VS5:
        MOVE      B4,B0               ;copy buffer offset address
* Restore XY offset and window start registers.
        MMFM      SP,B4,B5            ;
* Save A-file registers used by vert_shrink routine.
        MOVE      A7,A8               ;
        MMTM      A8,A1,A4            ;
* Setup A-file registers for horizontal zoom routine.
        MMFM      A7,A4,A8,A9,A10,A11,A12,A13
* Do horizontal zoom.
        MOVE      A6,A7               ;set up dest'n address
        EXGPC     A12                 ;subroutine call
* Restore A-file registers used by vert_shrink routine.
        MOVE      SP,A7
        MMFM      A7,A1,A4,A5,A8,A9,A10,A11,A12,A13
* Update parameters for next loop of vertical zoom routine.
        ADD       A3,A6               ;++yd
        DSJ       A1,VS1              ;loop once per dest'n row

*----------------------------------------------------------------------
* Restore registers and return to calling program.
*-----------------------------------------------------------------------
CLEANUP:
        ADDI      16*32,SP            ;clean off system stack
DONE:
        SETF      32,0,1              ;restore default field 1 size
        MOVE      *SP+,A8,1           ;restore original transp. bit
        SETF      1,0,0               ;
        MOVE      A8,@CONTROL+5,0     ;
ABORT:  MMFM      SP,B0,B1,B2,B7,B8,B9,B10,B11,B12,B13,B14
        MMFM      SP,A0,A1,A2,A3,A4,A5,A6,A7,A9,A10,A11,A12,A13
        MOVE      *SP(32),A14,1       ;restore program stack pointer
        RETS      2                   ;
        .end

