* --------------------------------------------------------------------------
* WarpText.asm                   Version 2.0                      Bill Kelly
*                                                                 07/07/87
*
* Copyright 1987 by Bill W. Kelly.
*

    XDEF InitWarpInfo
    XDEF GotoXY
    XDEF GetXY
    XDEF WarpText
    XDEF SetupFont
    XDEF NewWarp
    XDEF XORCursor

            NOLIST  ; I don't want to see all of this junque...

               INCLUDE "graphics/text.i"
               INCLUDE "graphics/gfx.i"
               INCLUDE "graphics/WarpText.i"

            LIST    ; Turn listing back on...

            CODE    ; WarpText begins...

* ==========================================================================
*
*         |      __          ___                   __     __     |
*         |     /  \   /     /  \     /   /  /|   /  \   /  \    |
*        \|/   /   /  /     /   /    / / /  /_|  /\__/  /\__/   \|/
*         V    \__/  /___  /___/     \/\/  /  | /   \  /         V
*
* --------------------------------------------------------------------------
* InitWarpInfo:
* -------------
*
* INITWARPINFO NEEDS:
*
* a0 - Pointer to an instance of the WarpInfo structure, with the following
*      fields initialized:
*
*   wi_TextFont:    Pointer to an open font.  (I.e. what OpenFont() returns
*                   to you, other than NULL.)
*   wi_BitMap:      Pointer to an initialized, 'working' BitMap structure.
*                   (E.g. Open a window.  Get the pointer to the screen
*                   out of wd_WScreen.  From there the BitMap structure is
*                   at sc_BitMap, which is an offset, not a pointer, into
*                   the Screen structure.)
*   wi_WhichPlane:  Which bitplane you want the routine to draw into.
*                   Numbering begins at zero.  On the Workbench screen
*                   (or any two-bitplane screen) the possible numbers for
*                   wi_WhichPlane are 0 and 1.
*   wi_Left:        Left edge of the 'window' (in character locations)
*                   you want the routine to write into.
*   wi_Top:         Top edge of the 'window' in character locations.
*   wi_Width:       Width of 'window' (also on character locations).
*   wi_Height:      Height of 'window' -- guess what? Character locations.
*
* REGISTER USAGE FOR INITWARPINFO:
*
* d0 - Left, Top, Width, Height, etc.
* d1 - wi_Modulo
* d2 - Scratch
* d3 - Y size of font
* d4 - Scratch
*
* a0 - Pointer to WarpInfo structure with above fields initialized.
* a1 - Addr of top of bitplane, wi_WindowTop, etc.
* a2 - Scratch
*

InitWarpInfo:   movem.l d0-d4/a1-a2,-(sp)

                moveq   #0,d4                   ; Clear scratch.
                move.w  d4,wi_CurX(a0)          ; Store it in WarpInfo.

                move.w  wi_Width(a0),wi_LastX(a0)  ; Max X position.
                sub.w   #1,wi_LastX(a0)         ; make it 0-relative.

                move.l  wi_BitMap(a0),a1        ; Get some stuff from bmap.

                moveq   #0,d2
                move.w  bm_BytesPerRow(a1),d2   ; #bytes per row in d2
                move.w  d2,wi_BPMod(a0)         ; #bytes per row->wi_BPMod

                move.w  wi_WhichPlane(a0),d4    ; Get bitplane#.
                asl.l   #2,d4                   ; Multiply plane# by four.
                move.l  bm_Planes(a1,d4.l),a1   ; Get address of bitplane.

                moveq   #0,d3
                move.l  wi_TextFont(a0),a2
                move.w  tf_YSize(a2),d3         ; Get YSize of font.

                moveq   #0,d1
                move.l  d2,d1                   ; Copy bytesperrow to d1.
                mulu.w  d3,d1                   ; YSize*BytesPerRow.
                move.w  d1,wi_Modulo(a0)        ; Store it in WarpInfo.

                moveq   #0,d4
                move.l  d1,d4                   ; Copy wi_modulo to scratch.
                mulu.w  wi_Top(a0),d4           ; Mul by Y offset.
                add.w   wi_Left(a0),d4          ; Add X offset.

                move.l  a1,a2                   ; BPlane addr to scratch.
                add.l   d4,a2                   ; Add offset to btpln addr.
                move.l  a2,wi_WindowTop(a0)     ; Stick in WarpInfo.
                move.l  a2,wi_CurLine(a0)       ; Stick in WarpInfo.

                moveq   #0,d2
                move.w  wi_Top(a0),d2           ; Copy Y top to scratch.
                add.w   wi_Height(a0),d2        ; Add Y height to Y top.
                move.l  d1,d4                   ; Copy wi_modulo to scratch.
                mulu.w  d2,d4                   ; Mul by Y offset.
                add.w   wi_Left(a0),d4          ; Add X offset.

                move.l  a1,a2                   ; BPlane addr to scratch.
                add.l   d4,a2                   ; Add offset to btpln addr.
                move.l  a2,wi_LastLine(a0)      ; Stick in WarpInfo.

                movem.l (sp)+,d0-d4/a1-a2

                rts
*
* --------------------------------------------------------------------------

* --------------------------------------------------------------------------
* GotoXY:
* -------
*
* GOTOXY NEEDS:
*
* d0 - New X position.          \ All regs. but d1 preserved,
* d1 - New Y position.
*
* a0 - Pointer to initialized WarpInfo structure.
*
* NOTE: These positions are given in character locations, like everything
*       else...  The positions are relative to the 'window' you have
*       defined using InitWarpInfo.  Position 0,0 is the character in the
*       top left corner of the window.
*       This routine does no error checking.  (You're supposed to know
*       how big your window is...)
*

GotoXY:         move.w  d0,wi_CurX(a0)      ; Set new X position.

                mulu.w  wi_Modulo(a0),d1    ; Make Y an offset into window
                add.l   wi_WindowTop(a0),d1 ; Add this offset to the window
                move.l  d1,wi_CurLine(a0)   ; to make it the new Y position.

                rts
*
* --------------------------------------------------------------------------

* --------------------------------------------------------------------------
* GetXY:
* ------
*
* GETXY NEEDS:
*
* a0 - Pointer to an initialized WarpInfo structure.
*
* It returns the current X position in d0 and the current Y
* position in d1.
*

GetXY:          move.l  wi_CurLine(a0),d1   ; Get current Y addr
                sub.l   wi_WindowTop(a0),d1 ; Sub window to get Y offset.
                divu.w  wi_Modulo(a0),d1    ; Should not be a remainder.

                moveq   #0,d0
                move.w  wi_CurX(a0),d0      ; Get current X position.

                rts
*
* --------------------------------------------------------------------------

* --------------------------------------------------------------------------
* WarpText:
* ---------
*
* WARPTEXT NEEDS:
*
* d0 - Number of characters to type. (count)
*
* a0 - Pointer to an initialized WarpInfo structure.  (See InitWarpInfo)
* a1 - Address of string of characters to type.
*
* REGISTER USAGE FOR WARPTEXT:  (All regs preserved but d0 which will be 0)
*
* d0 - Number of characters to type (count)
* d1 - Character to be emitted
* d2 - LoChar
* d3 - HiChar
* d4 - Current X position
* d5 - Scratch
* d7 - Bitplane modulo: add to get to next raster line in bitplane.
*
* a0 - Pointer to WarpInfo structure
* a1 - Address of string of characters to type
* a2 - Pointer to TextFont structure, address of tf_CharData
* a3 - Addr of Current line.
* a4 - Scratch
* a5 - Scratch
*
* STACK USAGE FOR WARPTEXT:
*
*  0(sp) - Last line
*  4(sp) - Top line
*  8(sp) - tf_YSize
* 12(sp) - tf_Modulo: add it to get to next line in font data
* 16(sp) - wi_Modulo: add it to get to next line in bitplane 'window'
* 20(sp) - Maximum (last) possible X position on a line
*

sp_LastLine:    equ     0   ; Offsets into stack to get at this data.
sp_TopLine:     equ     4
sp_YSize:       equ     8
sp_tf_Mod:      equ     12
sp_wi_Mod:      equ     16
sp_LastX:       equ     20

NoChar:         equ     256 ; I thought this was where the "empty-box"
                            ; character was.  It isn't.  Anyone know where
                            ; it is?  Or am I supposed to generate it
                            ; myself????

WarpText:   movem.l d1-d5/d7/a2-a5,-(sp)

            moveq   #0,d4
            move.w  wi_CurX(a0),d4      ; Get current X position.

            moveq   #0,d5
            move.w  wi_LastX(a0),d5     ; Get maximum X position.
            move.l  d5,-(sp)            ; Stick it on stack.  (1st item)

            move.w  wi_Modulo(a0),d5    ; Get wi_Modulo.
            move.l  d5,-(sp)            ; Stick it on stack.  (2nd item)

            move.l  wi_TextFont(a0),a2  ; Use TextFont to get some stuff:

              moveq   #0,d2
              move.b  tf_LoChar(a2),d2    ; Get tf_LoChar.

              moveq   #0,d3
              move.b  tf_HiChar(a2),d3    ; Get tf_HiChar.

              move.w  tf_Modulo(a2),d5    ; Get tf_Modulo.
              move.l  d5,-(sp)            ; Stick it on stack.  (3rd item)

              move.w  tf_YSize(a2),d5     ; Get tf_YSize
              move.l  d5,-(sp)            ; Stick it on stack.  (4th item)

              move.l  tf_CharData(a2),a2  ; Replace textfont w/ tf_Chardata.

            move.l  wi_WindowTop(a0),a4 ; Get addr of wi_WindowTop.
            move.l  a4,-(sp)            ; Stick it on stack.  (5th item)

            move.l  wi_LastLine(a0),a4  ; Get addr of wi_LastLine.
            move.l  a4,-(sp)            ; Stick it on stack.  (6th item)

            move.l  wi_CurLine(a0),a3   ; Get addr of wi_CurLine.

            moveq   #0,d7
            move.w  wi_BPMod(a0),d7     ; Get wi_BPMod.

            subq    #1,d0               ; Take one from count.
            moveq   #0,d1               ; Clear d1 because using .b size.
            subq    #1,d4               ; Take one from Current X.

DoNextChar:   addq    #1,d4               ; Add 1 to Current X.
DoNextSinAdd: move.b  (a1)+,d1            ; Move char to emit to d1.
              cmpi.b  #32,d1              ; Is it a space?
               beq     Blank
              cmp.b   d2,d1               ; Compare with LoChar.
               blt     BoffoChar           ; May be a LF, FF, CR, etc.
              cmp.b   d3,d1               ; Compare with HiChar.
               blt     DoNoChar

              move.l  a2,a4               ; Copy tf_CharData to scratch.
              sub.l   d2,d1               ; Sub LoChar from char.
              adda.l  d1,a4               ; Add char to tf_CharData.

DoChar:       move.l  a3,a5               ; Copy current line to scratch.
              adda.l  d4,a5               ; Add XPos to current line.

              move.l  sp_YSize(sp),d5     ; YSize is loop count.
              subq.l  #1,d5
MoveChar:       move.b  (a4),(a5)           ; Move line of char to bitplane.
                adda.l  sp_tf_Mod(sp),a4    ; Add tf_Modulo to tf_Chardata.
                adda.l  d7,a5               ; Add wi_BPMod to bitplane.
                 dbra    d5,MoveChar

Blank:        cmp.l   sp_LastX(sp),d4     ; Compare max X and Current X...
               blt     GotoDoNext          ; Do next if current < max.

DoLF:         moveq   #0,d4               ; Xpos is zero now.

DoCR:         cmpa.l  (sp),a3             ; CMP sp_LastLine with current.
               beq     GotoNSA             ; Wrap on line if equ.

              adda.l  sp_wi_Mod(sp),a3    ; Point at next line.
               bra     GotoNSA             ; Do another character.

DoNoChar:   move.l  a2,a4               ; Copy tf_Chardata to scratch.
            add.l   #NoChar,a4          ; Point at empty-box char.
             bra     DoChar              ; Do the empty-box char.

BoffoChar:  cmpi.l  #10,d1              ; Is is a linefeed?
             beq     DoLF
            cmpi.l  #12,d1              ; Is it a formfeed?
             beq     DoFF
            cmpi.l  #13,d1              ; Is it a CR?
             beq     DoCR
             bra     DoNoChar           ; Fine. Put up the box.

DoFF:       moveq   #0,d4               ; Make X pos zero.  |  It doesn't
            move.l  sp_TopLine(sp),a3   ; Point at top      |  CLS yet.
             bra     GotoNSA

GotoDoNext: dbra    d0,DoNextChar       ; Keep looping?
             bra     WindUp              ; Loop is done. Clean up & bail.

GotoNSA:    dbra    d0,DoNextSinAdd     ; Do NextChar without adding.

WindUp:     addq.l  #8,sp               ; Get rid of stuff kept on stack.
            addq.l  #8,sp
            addq.l  #8,sp

            move.w  d4,wi_CurX(a0)      ; Store current X for next time.
            move.l  a3,wi_CurLine(a0)   ; Store current Y for next time.

            movem.l (sp)+,d1-d5/d7/a2-a5

            rts
*
* ==========================================================================

* ==========================================================================
*
*        |             ___                        __     __     |
*        |     /|  /  /     /   /    /   /  /|   /  \   /  \    |
*       \|/   / | /  /---  / / /    / / /  /_|  /\__/  /\__/   \|/
*        V   /  |/  /___   \/\/     \/\/  /  | /   \  /         V
*
* --------------------------------------------------------------------------
* SetupFont:
* ----------
* Call with NewWarpInfo and TextFont on the stack.
* (TextFont should be on top)
*
* You must allocate a 2048 byte buffer (does not have to reside in CHIP RAM)
* and store its address into the nwi_FontData field of the NewWarpInfo
* structure prior to calling SetupFont.
* SetupFont will take the font data from the pointer to the FontData
* structure you supply on the stack and rearrange it into the 2048 byte
* array.  It is not a smart routine -- it is set up to unpack only the
* Topaz 8 font, or any 8x8 font with the same number of characters as topaz
* and the same modulo.  If you want to use an 8x8 font that has different
* number of characters defined and a different modulo, you will have to
* modify the SetupFont code.
*

SetupFont:  movem.l     d0/a0-a3,-(sp)      ;Save used regs

            move.l      24(sp),a2           ;FontData kept in a2
            move.l      28(sp),a1           ;NewWarpInfo kept in a1
            move.l      nwi_FontData(a1),a0 ;array to rearrange font into

            move.l      #31,d0              ;number of blank chars.
TheUnCola:  move.b      #$FE,(a0)+          ;$FE = %11111110
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$FE,(a0)+          ;$FE = %11111110
            move.b      #$00,(a0)+          ;$00 = %00000000
             dbf         d0,TheUnCola       ;Get the picture? :-)

            move.l      tf_CharData(a2),a2  ;get addr of the font data
            move.l      a2,a3               ;save for later loops
            moveq       #0,d0               ;zero is 1st char in chardata

DoAnother:  add.w       d0,a2               ;add char offset to chardata

            move.b      (a2),(a0)+          ;begin copying char into array
            add.w       #192,a2             ;add tf_modulo for next byte
            move.b      (a2),(a0)+
            add.w       #192,a2
            move.b      (a2),(a0)+
            add.w       #192,a2
            move.b      (a2),(a0)+
            add.w       #192,a2
            move.b      (a2),(a0)+
            add.w       #192,a2
            move.b      (a2),(a0)+
            add.w       #192,a2
            move.b      (a2),(a0)+
            add.w       #192,a2
            move.b      (a2),(a0)+          ;finished copying this character

            move.l      a3,a2               ;point to chardata again
            addq.b      #1,d0               ;add one for next character
            cmp.w       #223,d0             ;need to stop after 223rd char
             ble.s       DoAnother

            movem.l     (sp)+,d0/a0-a3      ;restore used regs

            rts
*
* --------------------------------------------------------------------------

* --------------------------------------------------------------------------
* NewWarp:
* --------
* Call with NewWarpInfo, String-address, and String-count on the stack.
* (String-count should be on top)
*
* You must have called SetupFont AND have filled in the nwi_BitPlane
* field in the NewWarpInfo structure before you call NewWarp.
* It would be a good idea, also, to make sure you've set up nwi_XLoc
* and nwi_YLoc to the correct places before you go writing text.
* They are character-position relative, rather than pixel relative.
* Setting nwi_XLoc and nwi_XLoc to 8,8 for example, would be like telling
* the Graphics library Text() routine to write text at 80,80.
* Another note: Unlike Text(), NewWarp writes directly into a bitplane
* and does not do clipping at the edges of rastports.  This makes
* NewWarp very fast, but less flexible.  I have found it easiest to
* use the WarpText routines in SUPER_BITMAP windows.
*

NewWarp:    movem.l     d0-d3/a0-a4,-(sp)   ;save used registers

            move.l      40(sp),d3           ;String-count kept in d3
            move.l      44(sp),a2           ;String-addr kept in a2
            move.l      48(sp),a4           ;NewWarpInfo kept in a4

            move.w      nwi_XLoc(a4),d0     ;move to d0 before incrementing

            add.w       d3,nwi_XLoc(a4)     ;increment XLoc like Text()
            subq.w      #1,d3               ;adjust count for dbf

            move.w      nwi_YLoc(a4),d1
            move.l      nwi_BitPlane(a4),a0
            move.l      nwi_FontData(a4),a1
            move.l      a1,a3               ;safe keeping of FontData

            asl.w       #7,d1               ;This multiplies YLoc
            move.w      d1,d2               ;times 640 in 26 cycles
            asl.w       #2,d1               ;instead of the 74 cycles
            add.w       d2,d1               ;a MULU would take.

            add.w       d0,d1               ;Add XLOC to expanded YLOC.
            add.w       d1,a0               ;Add LOC offset to bitplane.

OnceAgain:  moveq       #0,d0
            move.b      (a2)+,d0            ;Put character in d0.
            asl.w       #3,d0               ;Make offset into FontData
            add.w       d0,a1               ;Add offset to fontData address.

            move.b      (a1)+,(a0)          ;Start moving the font data
            add.w       #80,a0              ;into the bitmap...
            move.b      (a1)+,(a0)
            add.w       #80,a0
            move.b      (a1)+,(a0)
            add.w       #80,a0
            move.b      (a1)+,(a0)
            add.w       #80,a0
            move.b      (a1)+,(a0)
            add.w       #80,a0
            move.b      (a1)+,(a0)
            add.w       #80,a0
            move.b      (a1)+,(a0)
            add.w       #80,a0
            move.b      (a1)+,(a0)          ;Finished moving data.

            sub.w       #559,a0             ;restore a0 to next char loc
            move.l      a3,a1               ;restore a1 to FontData

             dbf         d3,OnceAgain

            movem.l     (sp)+,d0-d3/a0-a4   ;restore used registers

            rts
*
* --------------------------------------------------------------------------

* --------------------------------------------------------------------------
* XORCursor:
* ----------
* Call with NewWarpInfo on the stack.
*
* You must have called SetupFont AND have filled in the nwi_BitPlane
* field in the NewWarpInfo structure before you call XORCursor.
* XORCursor XOR's a console-device-like cursor to the current xy location.
* To get the cursor to be a different color than the Text you've been
* emitting with NewWarp you have to change the bitplane being pointed to
* by nwi_BitPlane.  I thought of adding another APTR to the end of
* the NewWarpInfo structure called nwi_CursorPlane that you could set
* to point to a different bitplane which would only be used only
* by XORCursor.  I haven't yet, as I want to get this stuff out.
* If you would like to, feel free to do so.
*

XORCursor:  movem.l     d0-d1/a0-a1,-(sp)   ;Save used registers

            move.l      20(sp),a1           ;NewWarpInfo kept in a1
            move.w      nwi_YLoc(a1),d0
            move.l      nwi_BitPlane(a1),a0

            asl.w       #7,d0               ;This multiplies YLoc
            move.w      d0,d1               ;times 640 in 26 cycles
            asl.w       #2,d0               ;instead of the 74 cycles
            add.w       d1,d0               ;a MULU would take.

            add.w       nwi_XLoc(a1),d0     ;Add XLOC to expanded YLOC.
            add.w       d0,a0               ;Add LOC offset to bitplane.

            eor.b       #$FF,(a0)           ;XOR the cursor...
            add.w       #80,a0
            eor.b       #$FF,(a0)
            add.w       #80,a0
            eor.b       #$FF,(a0)
            add.w       #80,a0
            eor.b       #$FF,(a0)
            add.w       #80,a0
            eor.b       #$FF,(a0)
            add.w       #80,a0
            eor.b       #$FF,(a0)
            add.w       #80,a0
            eor.b       #$FF,(a0)
            add.w       #80,a0
            eor.b       #$FF,(a0)

            movem.l     (sp)+,d0-d1/a0-a1   ;restore used registers

            rts
*
* ==========================================================================

            END     ; WarpText ends.

* --------------------- *
* End of "WarpText.asm" *
* --------------------- *
