;* --------------------------------------------------------------------------
;* WarpText.asm                   Version 2.0                      Bill Kelly
;*                                                                 07/07/87
;*
;* Copyright 1987 by Bill W. Kelly.
;*
;* Made it possible to use from MODULA-II by
;*   Fridtjof Siebert
;*   Nobileweg 67
;*   7000-Stgt-40
;*
;* Bug Fixed:
;* SetUpFont didn't work corectly with national characters like
;* `äüßå©' etc. because of several bugs like add.w instead of add.l etc.
;*

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

    IFND L
L  =  1

   INCLUDE "df0:exec/types.i"
   INCLUDE "df0:graphics/text.i"
   INCLUDE "df0:graphics/gfx.i"

    STRUCTURE   WarpInfo,0       ;...the <I> denotes internal use.
        APTR    wi_TextFont     ;Pointer to a TextFont structure.
        APTR    wi_BitMap       ;Pointer to a BitMap structure.
        UWORD   wi_WhichPlane   ;Which bitplane to render into.
        UWORD   wi_Left         ;Left edge of 'window' in char loc's.
        UWORD   wi_Top          ;Top edge of 'window' in char loc's.
        UWORD   wi_Width        ;Width of 'window' in char loc's.
        UWORD   wi_Height       ;Height of 'window' in char loc's.
        APTR    wi_WindowTop    ;<I> Address of top of 'window.'
        APTR    wi_CurLine      ;<I> Address of start of current line.
        APTR    wi_LastLine     ;<I> Address of start of last line.
        UWORD   wi_CurX         ;<I> Current X position.
        UWORD   wi_LastX        ;<I> Maximum X position on a line.
        UWORD   wi_BPMod        ;<I> # total possible chars on a line in bp
        UWORD   wi_Modulo       ;<I> Add this to get to next line.
        LABEL   wi_SIZE


;* --------------------------------------------------------------------------
;* The following structure, NewWarpInfo, is used by these three routines:
;*   SetupFont       (Copies font to given 2048 byte array for faster access)
;*   NewWarp         (Emits text at ~30,000 characters per second)
;*   XORCursor       (XOR's a visible cursor to the current xy location)


    STRUCTURE   NewWarpInfo,0
        UWORD   nwi_XLoc        ;Current X position in char. loc's.
        UWORD   nwi_YLoc        ;Current Y position in char. loc's.
        APTR    nwi_BitPlane    ;Address of bitplane to render text into.
        APTR    nwi_FontData    ;Address of 2048 byte array for font data.
        LABEL   nwi_SIZE

  ENDIF


;* ==========================================================================
;*
;*         |      __          ___                   __     __     |
;*         |     /  \   /     /  \     /   /  /|   /  \   /  \    |
;*        \|/   /   /  /     /   /    / / /  /_|  /\__/  /\__/   \|/
;*         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    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    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    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

;*
;* --------------------------------------------------------------------------

;* --------------------------------------------------------------------------
;* 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...)
;*

  dc.l      $12345678             ; this is to be removed later

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

                mulu    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.

;*
;* --------------------------------------------------------------------------

;* --------------------------------------------------------------------------
;* 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.
;*

  dc.l      $23456789             ; this is to be removed later

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

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

;*
;* --------------------------------------------------------------------------

;* --------------------------------------------------------------------------
;* 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????

  dc.l      $3456789A             ; this is to be removed later


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

;*
;* ==========================================================================

;* ==========================================================================
;*
;*        |             ___                        __     __     |
;*        |     /|  /  /     /   /    /   /  /|   /  \   /  \    |
;*       \|/   / | /  /---  / / /    / / /  /_|  /\__/  /\__/   \|/
;*        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.
;*
;* I changed this for MODULA:
;* NewWarpInfo & TextFont has to be given directly to the registers:
;* a1 - NewWarpInfoPtr
;* a2 - TextFontPtr;
;*              -- Fridtjof


  dc.l      $456789AB             ; this is to be removed later

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,1024(a0)       ;upper blank chars, too !!!!
            move.b      #$FE,(a0)+          ;$FE = %11111110
            move.b      #$C6,1024(a0)
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,1024(a0)
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,1024(a0)
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,1024(a0)
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$C6,1024(a0)
            move.b      #$C6,(a0)+          ;$C6 = %11000110
            move.b      #$FE,1024(a0)
            move.b      #$FE,(a0)+          ;$FE = %11111110
            move.b      #$00,1024(a0)
            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.l     #0,d0               ;zero is 1st char in chardata

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

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

            cmp         #95,d0;             ;check upper control codes
            bne         \notupper;          ;Bill, you forgot this !!!
            add.l       #256,a0;            ;that's it.
\notupper:
            move.l      a3,a2               ;point to chardata again

            addq.b      #1,d0               ;add one for next character
            cmp.w       #191,d0             ;need to stop after 223rd char
            ble.s       DoAnother

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

;*
;* --------------------------------------------------------------------------

;* --------------------------------------------------------------------------
;* 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.
;*
;* Direct into Register for MODULA:
;* a0 - NewWarpInfoPtr
;* a1 - Pointer to String
;* d0 - String's length


  dc.l      $56789ABC             ; this is to be removed later

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

            move.l      d0,d3               ;String-count kept in d3
            move.l      a1,a2               ;String-addr kept in a2
            move.l      a0,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

;*
;* --------------------------------------------------------------------------

;* --------------------------------------------------------------------------
;* 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.
;*
;* MODULA Registers:
;* a1 - WarpInfoPtr



  dc.l      $6789ABCD             ; this is to be removed later

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

;*
;* ==========================================================================

            END     ; WarpText ends.

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