; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; > $.Source.Vdu5

; *****************************************************************************
;
;       Print a character in VDU 5 mode
;

; *****This file has been extensively modified by DJS

; Alter these at your peril !

cxl     RN      0
scr     RN      0

ecfptr  RN      1
cword0  RN      1

cyb     RN      2
cyt2    RN      2
scrend  RN      2
cword1  RN      2

cxr     RN      3
cyb2    RN      3
ormask  RN      3

cxl2    RN      4
eormask RN      4

cyb3    RN      5
charmsk RN      5
cword2  RN      5

cyt     RN      6
llength RN      6
chartmp RN      6

sizex   RN      7
charptr RN      7

sizey   RN      8
invcharshf RN   8

charbyte RN     9

cxr2    RN      10
charshf RN      10

lgBPC   RN      11

Vdu5Wrch
        Push    R14
        BL      Vdu5DoChar
        Pull    R14
        B       PostCharMove

; Vdu23_18
;        LDR     R0, [WsPtr, #QQ+1]
Vdu5DoChar
        ADD     ecfptr, WsPtr, #FgEcfOraEor  ;Base address of OR/EOR pairs
Vdu5DoChar10
        Push    "R14"
        MOV     charbyte, R0                    ;Put character out of harm's
                                                ;  way
        ASSERT  cxl < cyt
        ASSERT  GCsIY = GCsIX +4
        ADD     cxl, WsPtr, #GCsIX
        LDMIA   cxl, {cxl, cyt}                 ; cxl=where left pixel would be
                                                ; cyt=where top pixel would be

        ASSERT  sizex < sizey
        ADD     sizex, WsPtr, #GCharSizes
        LDMIA   sizex, {sizex, sizey}
        CMP     sizey, #16                      ; if SizeY=8 or SizeY=16
        TEQNE   sizey, #8                       ;   (leaving CS <=> SizeY=16)
        TEQEQ   sizex, #8                       ; and SizeX=8, then do it fast
        BNE     SlowVdu5                        ; else do by scaled char

        TEQ     charbyte, #127                  ; Which font do we use?
        ADDNE   charptr, WsPtr, #(Font-32*8)    ; Soft if character not DELETE
        ADREQL  charptr, HardFont-32*8          ; Hard if character is DELETE
        ADD     charptr, charptr, charbyte, LSL #3      ;Point at char def

        MOVCS   charptr, charptr, ASL #1        ; Double address if SizeY = 16
        SUBCS   charptr, charptr, #1            ;   (tricky code, originally
                                                ;   designed by TMD)

        SUB     cyb, cyt, sizey
        ADD     cyb, cyb, #1                    ; where bottom pixel would be
        ADD     cxr, cxl, #7                    ; where right pixel would be

        ASSERT  GWBRow = GWLCol+4
        ASSERT  GWRCol = GWLCol+8
        ASSERT  GWTRow = GWLCol+12
        ASSERT  cyb3 > cxl2
        ASSERT  cxr2 > cyb3
        ASSERT  R14 > cxr2
        ADD     cxl2, WsPtr, #GWLCol
        LDMIA   cxl2, {cxl2, cyb3, cxr2, R14}

        Greatest cxl2, cxl2, cxl                ; cxl2 := windowed left
                                                ;   (2 instructions)
        Least   cxr2, cxr2, cxr                 ; cxr2 := windowed right
                                                ;   (2 instructions)
        Greatest cyb2, cyb3, cyb                ; cyb2 := windowed bottom
                                                ;   (3 instructions)
        Least   cyt2, R14, cyt                  ; cyt2 := windowed top
                                                ;   (3 instructions)

        SUBS    cyb2, cyt2, cyb2                ; top >= bottom ?
        CMPGE   cxr2, cxl2                      ; and right >= left ?
        Pull    "PC", LT                        ; Exit if not. Otherwise cyb2
                                                ;   has become clipped height

        LDR     R14, [WsPtr, #CursorFlags]      ; update changed box, if wanted
        TST     R14, #ClipBoxEnableBit
        BLNE    ClipVdu5

        ADD     charptr, charptr, cyt           ; adjust charptr to point at
        SUB     charptr, charptr, cyt2          ;   first used row of character

        MOV     charmsk, #&FF                   ; Produce mask to do left/right
        SUB     R14, cxl2, cxl                  ;   clipping
        MOV     charmsk, charmsk, LSR R14
        SUB     R14, cxr2, cxl2
        ADD     R14, R14, #1
        BIC     charmsk, charmsk, charmsk, LSR R14

        TEQ     sizey, #16                      ; If double height, signal this
        ORREQ   charmsk, charmsk, #1:SHL:31     ;   by making charmsk negative

        ASSERT  llength > scr
        ASSERT  LineLength = YWindLimit+4
        LDR     lgBPC, [WsPtr, #Log2BPC]        ; Address point at (cxl, cyt2),
        MOV     charshf, cxl, LSL lgBPC         ;   putting address in scr, bit
        ADD     scr, WsPtr, #YWindLimit         ;   offset in charshf and
        LDMIA   scr, {scr, llength}             ;   adjusting ecfptr
        SUB     scr, scr, cyt2
        AND     R14, scr, #7
        ADD     ecfptr, ecfptr, R14, LSL #3
        LDR     R14, [WsPtr, #ScreenStart]
        MLA     scr, llength, scr, R14
        MOV     R14, charshf, ASR #5
        ADD     scr, scr, R14, LSL #2
        AND     charshf, charshf, #31

        MLA     scrend, llength, cyb2, scr      ;Calculate terminating address

        RSB     invcharshf, charshf, #32        ;And inverse shift

Vdu5NextLine
        ASSERT  eormask > ormask
        LDMIA   ecfptr!, {ormask, eormask}      ;Pick up ecf info and advance
        TST     ecfptr, #63                     ;  ecf pointer, wrapping if
        SUBEQ   ecfptr, ecfptr, #64             ;  necessary

        ASSERT  HardFont < &40000000 :LOR: HardFont >= &C0000000
        ASSERT  WsPtr < &40000000 :LOR: WsPtr >= &C0000000
        TEQ     charmsk, #0                     ; Double height? (MI=yes)
        LDRMIB  charbyte, [charptr, -charptr, ASR #1] ; Use ASR, as font will be at
        LDRPLB  charbyte, [charptr]             ; top or bottom of memory (zero page,
        ADD     charptr, charptr, #1            ; system heap or ROM)

        ANDS    charbyte, charbyte, charmsk     ;Clip left & right; skip loop
        BEQ     Vdu5TryNextLine                 ;  body if nothing to plot

        Push    "ecfptr,scrend,charmsk,llength,charptr"

        LDR     R14, [WsPtr, #TextExpandArea]   ;Address correct expansion

        ADD     charptr, charbyte, #&100        ;  table entry, assuming that
        ADD     charptr, R14, charptr, LSL lgBPC        ;  this is not mode 10 or greater

        CMP     lgBPC, #1                       ;Pick up this entry:
        LDRLSB  cword0, [charptr, #0]           ;  1 byte if lgBPC = 0
        LDREQB  R14, [charptr, #1]              ;  2 bytes if lgBPC = 1
        ORREQ   cword0, cword0, R14, LSL #8
        MOV     cword1, #0
        BLS     Vdu5DoOneWord
        CMP     lgBPC, #3
        LDRLO   cword0, [charptr, #0]           ;  1 word if lgBPC = 2
        BLO     Vdu5DoOneWord
        BHI     Vdu5Mode10                      ;  4 words if lgBPC = 4
        LDMIA   charptr, {cword0, cword1}       ;  2 words if lgBPC = 3
        MOVS    cword2, cword1, LSR invcharshf

        MACRO
$l      DoVdu5Word $c, $reg, $off
$l
        LDR$c   chartmp, [scr, #$off]
        AND$c   R14, ormask, $reg
        ORR$c   chartmp, chartmp, R14
        AND$c   R14, eormask, $reg
        EOR$c   chartmp, chartmp, R14
        STR$c   chartmp, [scr, #$off]
        MEND

Vdu5DoTwoWords
        DoVdu5Word NE, cword2, 8
        MOV     cword1, cword1, LSL charshf
Vdu5DoOneWord
        ORRS    cword1, cword1, cword0, LSR invcharshf
        DoVdu5Word NE, cword1, 4
        MOVS    cword0, cword0, LSL charshf
        DoVdu5Word NE, cword0, 0

Vdu5LinePainted
        Pull    "ecfptr,scrend,charmsk,llength,charptr"

Vdu5TryNextLine
        TEQ     scr, scrend
        ADDNE   scr, scr, llength
        BNE     Vdu5NextLine

        Pull    "PC"

; This is mode 10 (or similar) - we must do bit expansion on the fly

Vdu5Mode10
        CMP     lgBPC, #5                       ; is this a 32 bit per pixel mode, if so then ignore the
        BEQ     Vdu5Mode32                      ; existing code and use a newer function

        ADRL    charptr, C16BTab
        AND     R14, charbyte, #&0F
        ADD     R14, charptr, R14, LSL #3
        LDMIA   R14, {cword0, cword1}

        MOVS    cword2, cword1, LSR invcharshf
        DoVdu5Word NE, cword2, 16
        MOV     cword1, cword1, LSL charshf
        ORRS    cword1, cword1, cword0, LSR invcharshf
        DoVdu5Word NE, cword1, 12
        MOVS    cword2, cword0, LSL charshf

        AND     R14, charbyte, #&F0
        ADD     R14, charptr, R14, LSR #1
        LDMIA   R14, {cword0, cword1}
        ORRS    cword2, cword2, cword1, LSR invcharshf
        B       Vdu5DoTwoWords

; Expand the character data out to 32 bit per pixel (mode 48 or similar)

        MACRO
$l      PixMunge32 $reg, $mask, $offset
$l
        TST     $reg, #$mask
        LDRNE   chartmp, [scr, #$offset]
        ORRNE   chartmp, chartmp, ormask
        EORNE   chartmp, chartmp, eormask
        STRNE   chartmp, [scr, #$offset]
        MEND

        MACRO
$l      PixSolid32 $reg, $mask, $offset
$l
        TST     $reg, #$mask
        STRNE   chartmp, [scr, #$offset]
        MEND

; Perform bit expansion on the fly, this is a word based operation, no need to
; extract sub-pixels from the ORR / EOR masks, simply perform the bit test and
; then write the character data to the screen!

Vdu5Mode32

 [ AvoidScreenReads
        CMP     ormask, #&FFFFFFFF
        BNE     Vdu5Mode32_NotSolid

        MVN     chartmp, eormask
        PixSolid32 charbyte, 1<<7, 0
        PixSolid32 charbyte, 1<<6, 4
        PixSolid32 charbyte, 1<<5, 8
        PixSolid32 charbyte, 1<<4, 12
        PixSolid32 charbyte, 1<<3, 16
        PixSolid32 charbyte, 1<<2, 20
        PixSolid32 charbyte, 1<<1, 24
        PixSolid32 charbyte, 1<<0, 28

        B       Vdu5LinePainted

Vdu5Mode32_NotSolid
 ]
        PixMunge32 charbyte, 1<<7, 0
        PixMunge32 charbyte, 1<<6, 4
        PixMunge32 charbyte, 1<<5, 8
        PixMunge32 charbyte, 1<<4, 12
        PixMunge32 charbyte, 1<<3, 16
        PixMunge32 charbyte, 1<<2, 20
        PixMunge32 charbyte, 1<<1, 24
        PixMunge32 charbyte, 1<<0, 28

        B       Vdu5LinePainted                 ; flow down and try the next line


; *****************************************************************************
;
;       Slow VDU 5 - Print char by scaled method (in SprExtend)
;
; in:   R1 (ecfptr)   = pointer to ecf pattern
;       R7 (sizex)    = SizeX
;       R8 (sizey)    = SizeY
;       R9 (charbyte) = character to plot
;
;       Stack:  Return address
;

        ASSERT  ecfptr   = R1
        ASSERT  sizex    = R7
        ASSERT  sizey    = R8
        ASSERT  charbyte = R9

SlowVdu5 ROUT

        MOV     R10, R1                         ; R10 := ecfptr

; now save current GCOL on stack if necessary

        ADD     R11, WsPtr, #FgEcfOraEor
        TEQ     R10, R11                        ; if going to use this one
        BEQ     %FT20                           ; then skip

        MOV     R0, #64                         ; 64 bytes
10
        LDMIA   R11, {R3-R6}                    ; copy old GCOL into stack
        Push    "R3-R6"                         ;   frame, reversing order of
                                                ;   16 byte chunks
        LDMIA   R10!, {R3-R6}                   ; copy new colour
        STMIA   R11!, {R3-R6}                   ; into GCOL
        SUBS    R0, R0, #16
        BNE     %BT10

20
        MOV     R4, #8
        MOV     R5, #8
        Push    "R4,R5"
        Push    "R7,R8"
        MOV     R6, R13                         ; R6 -> scaling block

        SUB     R1, R8, #1                      ; SizeY-1
        LDR     R5, [WsPtr, #YEigFactor]
        ADD     R3, WsPtr, #GCsX
        LDMIA   R3, {R3, R4}                    ; R3 = ext X; R4 = ext Y (top)
        SUB     R4, R4, R1, LSL R5              ; R4 = ext Y (bottom)
        MOV     R1, R9                          ; R1 = char
        MOV     R0, #SpriteReason_PaintCharScaled
        SWI     XOS_SpriteOp

        ADD     R13, R13, #4*4                  ; junk scaling block

        TEQ     R10, R11                        ; if we didn't copy GCOLs
        Pull    "PC", EQ                        ; then return, else copy back

        MOV     R0, #64
30
        Pull    "R3-R6"                         ; Reverse order of 16 byte
        STMDB   R11!, {R3-R6}                   ; chunks during copy again
        SUBS    R0, R0, #16
        BNE     %BT30
        Pull    PC


; *****************************************************************************
;
;       VDU 5 - Start printing text at graphics cursor

ENQ
        GraphicsMode R0
        MOVNE   PC, R14

        MOV     R1, #0
        BSR     CursorOnOff                     ; turn cursor off without
                                                ; saving to copy

        LDR     R6, [WsPtr, #CursorFlags]
        ORR     R6, R6, #Vdu5Bit
        B       R6toCursorFlags

; *****************************************************************************
;
;       VDU 4 - Return to printing text at text cursor

EOT
        GraphicsMode R0
        MOVNE   PC,LR

        MOV     R1, #1
        BSR     CursorOnOff                     ; restore cursor from copy

        LDR     R6, [WsPtr, #CursorFlags]
        BIC     R6, R6, #Vdu5Bit
        B       R6toCursorFlags

; *****************************************************************************
;
;       Vdu5HT - Move cursor "right" when in VDU 5 mode
;
; in:   R6 = CursorFlags

Vdu5HT
        Push    R14
        BL      GCursorMove             ; try to move in +ve X direction
        BCC     EndVdu5HT               ; if successful, finish
        BL      GCursorB0               ; else move cursor to -ve X boundary
Vdu5HT10
        EOR     R6, R6, #8              ; change to +ve Y direction
        BL      GCursorMove             ; try to move in that direction
        BLCS    GCursorB0               ; if unsuccessful, move to -ve Y bdy
EndVdu5HT
        Pull    R14
        B       IEG

; *****************************************************************************
;
;       Vdu5BS - Move cursor "left" when in VDU 5 mode
;
; in:   R6 = CursorFlags

Vdu5BS
        EOR     R6, R6, #6              ; go to -ve X direction
        B       Vdu5HT                  ; dead easy, huh ?

; *****************************************************************************
;
;       Vdu5LF - Move cursor "down" when in VDU 5 mode
;
; in:   R6 = CursorFlags

Vdu5LF
        Push    R14
        B       Vdu5HT10

; *****************************************************************************
;
;       Vdu5VT - Move cursor "up" when in VDU 5 mode
;
; in:   R6 = CursorFlags

Vdu5VT
        EOR     R6, R6, #6              ; go to -ve Y direction (eventually)
        B       Vdu5LF

; *****************************************************************************
;
;       Vdu5CR - Carriage return in VDU 5 mode
;
; in:   R6 = CursorFlags

Vdu5CR
        BSR     GCursorB0
        B       IEG

; *****************************************************************************
;
;       Vdu5FF - Clear screen in VDU 5 mode
;
; in:   R6 = CursorFlags

Vdu5FF
        BSR     Home
        B       CLG

; *****************************************************************************
;
;       Vdu5TAB - TAB(X,Y) in VDU 5 mode
;
; in:   R0 = X, R1 = Y, R6 = CursorFlags

Vdu5TAB
        BSR     GCursorBdy
        EOR     R6, R6, #8
        MOV     R0, R1
        BSR     GCursorBdy
        B       IEG

; *****************************************************************************
;
;       Vdu5Delete - Delete in VDU 5 mode (already done backspace)
;
; in:   R6 = CursorFlags

Vdu5Delete
        ADD     ecfptr, WsPtr, #BgEcfStore ; STORE background colour/ecf
        MOV     R0, #127                   ; uses hard font for this char
        B       Vdu5DoChar10

; *****************************************************************************
;
;       GCursorMove - Move graphics cursor in direction specified by R6
;       (0,4 = right; 2,6 = left; 8,10 = down; 12,14 = up)
;       R6 is preserved

GCursorMove
        ADD     R0, WsPtr, #GCsIX
        LDMIA   R0, {R0, R1}                    ; R0 = GCsIX; R1 = GCsIY
        ADD     R2, WsPtr, #GCharSpacing+8      ; +8 needed to address it
        LDMDB   R2, {R2, R3}                    ; R2=GCharSpaceX;R3=GCharSpaceY
        WINDow  R0, R1, R8,R9,R10,R11           ; return LT if outside window
        ADR     R7, GCMVTab
        B       DoJumpTable

GCMVTab
        &       GMoveRight-GCMVTab
        &       GMoveLeft-GCMVTab
        &       GMoveRight-GCMVTab
        &       GMoveLeft-GCMVTab
        &       GMoveDown-GCMVTab
        &       GMoveDown-GCMVTab
        &       GMoveUp-GCMVTab
        &       GMoveUp-GCMVTab

; Move graphics cursor right if possible - on exit C=0 iff OK to move

GMoveRight
        ADD     R0, R0, R2              ; add on GCharSpaceX
GMove10
        STR     R0, [WsPtr, #GCsIX]
GMove15
        BLT     GMoveOK                 ; if we were outside already
        TST     R6, #&40                ; or we are in nowrap mode
        BNE     GMoveOK                 ; then we were OK to move
        WINDow  R0, R1, R8,R9,R10,R11
        BGE     GMoveOK                 ; if inside now, then OK
        SEC
        MOV     PC, R14

GMoveOK
        CLC
        MOV     PC, R14

; Move graphics cursor left if possible - on exit C=0 iff OK to move

GMoveLeft
        SUB     R0, R0, R2              ; subtract off GCharSpaceX
        B       GMove10

; Move graphics cursor down if possible - on exit C=0 iff OK to move

GMoveDown
        SUB     R1, R1, R3              ; subtract off GCharSpaceY
GMove20
        STR     R1, [WsPtr, #GCsIY]
        B       GMove15

; Move graphics cursor up if possible - on exit C=0 iff OK to move

GMoveUp
        ADD     R1, R1, R3              ; add on GCharSpaceY
        B       GMove20

; *****************************************************************************
;
;       Move graphics cursor to boundary indicated by value of R6 in bits 1-3
;       (0 or 4 = left, 2 or 6 = right, 8 or 10 = up, 12 or 14 = down)
;       + R0 character positions in
;

GCursorB0
        MOV     R0, #0
GCursorBdy
        ADR     R7, GCBDYTab
        B       DoJumpTable

GCBDYTab
        &       GCursorLBdy-GCBDYTab
        &       GCursorRBdy-GCBDYTab
        &       GCursorLBdy-GCBDYTab
        &       GCursorRBdy-GCBDYTab
        &       GCursorUBdy-GCBDYTab
        &       GCursorUBdy-GCBDYTab
        &       GCursorDBdy-GCBDYTab
        &       GCursorDBdy-GCBDYTab

; Move graphics cursor to left boundary + R0 characters in

GCursorLBdy
        LDR     R7, [WsPtr, #GWLCol]
        LDR     R2, [WsPtr, #GCharSpaceX]
        MLA     R0, R2, R0, R7                  ; GCsIX := GWLCol + (X*SpaceX)
        STR     R0, [WsPtr, #GCsIX]
        MOV     PC, R14

; Move graphics cursor to right boundary + R0 characters in
; (adjust for character size)

GCursorRBdy
        LDR     R7, [WsPtr, #GWRCol]
        LDR     R2, [WsPtr, #GCharSizeX]
        SUB     R7, R2, R7                      ; R7 = SizeX-GWRCol
        LDR     R2, [WsPtr, #GCharSpaceX]
        MLA     R0, R2, R0, R7                  ; R0 = SizeX-GWRCol+X*SpaceX
        RSB     R0, R0, #1                      ; GWRCol-X*SpaceX-(SizeX-1)
        STR     R0, [WsPtr, #GCsIX]
        MOV     PC, R14

; Move graphics cursor to up boundary + R0 characters in

GCursorUBdy
        LDR     R7, [WsPtr, #GWTRow]
        LDR     R2, [WsPtr, #GCharSpaceY]
        RSB     R2, R2, #0                      ; -SizeY
        MLA     R0, R2, R0, R7                  ; GWTRow-Y*SizeY
        STR     R0, [WsPtr, #GCsIY]
        MOV     PC, R14

; Move graphics cursor to down boundary + R0 characters in
; (adjust for character size)

GCursorDBdy
        LDR     R7, [WsPtr, #GWBRow]
        LDR     R2, [WsPtr, #GCharSpaceY]
        MLA     R0, R2, R0, R7                  ; GWBRow+Y*SpaceY
        LDR     R2, [WsPtr, #GCharSizeY]
        ADD     R0, R0, R2
        SUB     R0, R0, #1                      ; GWBRow+Y*SpaceY+(SizeY-1)
        STR     R0, [WsPtr, #GCsIY]
        MOV     PC, R14

; *****************************************************************************
;
;       ClipVdu5 - Add a VDU 5 char to clip box
;
; in:   cxl2 = left
;       cyb2 = top-bottom
;       cxr2 = right
;       cyt2 = top
;
; out:  All registers preserved
;

        ASSERT  (cxl2=R4) :LAND: (cyb2=R3) :LAND: (cxr2=R10) :LAND: (cyt2=R2)

ClipVdu5 ROUT
        Push    "R0-R3, R4, R10, R14"
        MOV     R0, cxl2
        SUB     R1, cyt2, cyb2       ; top-(top-bottom) = bottom
        MOV     R3, cyt2
        MOV     R2, cxr2
        BL      MergeClipBox
        Pull    "R0-R3, R4, R10, PC"

        END