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

; *****************************************************************************
;
;       DefineChar - define soft character
;
; in:   R0 = char to be defined
;

DefineChar
        ADD     R1, WsPtr, #(Font-32*8)
        ADD     R1, R1, R0, LSL #3              ; point to char definition

        ADD     R2, WsPtr, #QQ+1
        LDMIA   R2, {R2, R3}                    ; load 8 bytes
        STMIA   R1, {R2, R3}                    ; store 8 bytes
Nop
        MOV     PC, R14

; *****************************************************************************
;
;       VDU 23,7,m,d,z| - Scroll window directly
;
;       m=0     Scroll text window
;       m=1     Scroll entire screen
;
;       d=0     Scroll right
;       d=1     Scroll left
;       d=2     Scroll down
;       d=3     Scroll up
;       d=4     Scroll "right"
;       d=5     Scroll "left"
;       d=6     Scroll "down"
;       d=7     Scroll "up"
;
;       z=0     Scroll by 1 character cell
;       z=1     Scroll by 1 char vertically, 1 byte (whatever that is) horiz.
;

Vdu23_7 ROUT
        LDRB    R0, [WsPtr, #QQ+1]      ; R0 := m
        LDRB    R1, [WsPtr, #QQ+2]      ; R1 := d
        LDRB    R2, [WsPtr, #QQ+3]      ; R2 := z
Scroll012
        LDR     R6, [WsPtr, #CursorFlags]
        AND     R1, R1, #7              ; ignore higher bits
        CMP     R1, #4
        BCC     %FT10
        LDR     R4, ScrollDirWord
        CMP     R1, #6
        MOVCS   R4, R4, ROR #16
        AND     R3, R6, #&0E
        MOV     R3, R3, LSL #1
        MOV     R4, R4, LSR R3
        MOVS    R1, R1, LSR #1
        AND     R1, R4, #&0F
        EORCS   R1, R1, #1
10
        Push    R14
        TST     R6, #ClipBoxEnableBit   ; if calculating clip box
        BLNE    ClipScroll              ; then add text window or whole screen
        CMP     R0, #1                  ; C=1 => whole screen
        ADC     R1, R1, R1              ; put m into bit 0
        TEQ     R2, #0
        ORRNE   R1, R1, #&08            ; put z into bit 3
        ADR     R0, ScrollTab
        LDR     R2, [R0, R1, LSL #2]
        ADR     R14, ScrollRetn
        ADD     PC, R0, R2              ; carry on entry is m

ScrollRetn
        Pull    R14

; insert code here to re-address input cursor as well

        B       AddressCursors          ; re-address cursor positions

ScrollDirWord
        &       &33221010

ScrollTab
        &       ScrollRightChar-ScrollTab ; window right char
        &       ScrollRightChar-ScrollTab ; screen right char
        &       ScrollLeftChar-ScrollTab ; window left char
        &       ScrollLeftChar-ScrollTab ; screen left char
        &       ScrollDown-ScrollTab    ; window down char
        &       HardScrollDown-ScrollTab ; screen down char
        &       ScrollUp-ScrollTab      ; window up char
        &       HardScrollUp-ScrollTab  ; screen up char
        &       ScrollRightByte-ScrollTab ; window right byte
        &       ScrollRightByte-ScrollTab ; screen right byte
        &       ScrollLeftByte-ScrollTab ; window left byte
        &       ScrollLeftByte-ScrollTab ; screen left byte
        &       ScrollDown-ScrollTab    ; window down byte
        &       HardScrollDown-ScrollTab ; screen down byte
        &       ScrollUp-ScrollTab      ; window up byte
        &       HardScrollUp-ScrollTab  ; screen up byte


; *****************************************************************************
;
;       VDU 23,17,c,t| - Set tint
;
;       c=0     Set text foreground tint (default &FF)
;       c=1     Set text background tint (default &00)
;       c=2     Set graphics foreground tint (default &FF)
;       c=3     Set graphics background tint (default 0)
;       c=4     t=0 => BBC compatible ECFs, t=1 => native ECFs (default 0)
;       c=5     Swap foreground and background text colours/tints
;       c=6     VDU 23,17,6,x;y; - Set ECF origin to (x,y)
;       c=7     VDU 23,17,7,flags,x;y;0,0 - Set character size(s)
;       c=8-255 Reserved
;
;       t=tint  Only bits 6,7 relevant with this hardware

Vdu23_17
        LDRB    R0, [WsPtr, #QQ+1]              ; get c
        CMP     R0, #7
        MOVHI   R0, #17                         ; if unknown (>7), reload R0
        BHI     UnknownVdu23                    ; and call UKVDU23 vector
        BEQ     SetCharSizes

        CMP     R0, #6
        BEQ     SetECFOrigin

        CMP     R0, #4
        BHI     SwapColours                     ; 5 => swap colours

        LDRB    R1, [WsPtr, #QQ+2]              ; tint or BBC parm
        STREQ   R1, [WsPtr, #BBCcompatibleECFs] ; VDU 23,17,4,t...
        MOVEQ   PC, R14

        ADD     R2, WsPtr, #TFTint
        LDR     R3, [R2, R0, LSL #2]            ; get old tint
        STR     R1, [R2, R0, LSL #2]

        CMP     R0, #2                          ; graphics tint ?
        BCS     SetColour                       ; then update masks

        EOR     R1, R1, R3
        TST     R1, #&C0                        ; are top two bits different ?
        MOVEQ   PC, R14                         ; no need to redo table

        ; amg: use R1 here instead of 'bpp' which is R0. I need R0 in a moment.
        LDR     R1, [WsPtr, #BitsPerPix]
        CMP     R1, #8                          ; are we in 8 bits-per-pixel or more?
        MOVCC   PC, R14                         ; no, then exit

        Push    "lr"

        ; amg: only do foreground or background as appropriate
        CMP     R0,#1
        BEQ     %FT05
        BL      CompileTextFg
        B       %FT10
05
        BL      CompileTextBg                   ; update TextFgColour/TextBgColour
10
        Pull    "lr"

SetColoursDirty
        LDR     R6, [WsPtr, #CursorFlags]
        ORR     R6, R6, #TEUpdate               ; yes, then set colours dirty
        STR     R6, [WsPtr, #CursorFlags]
        MOV     PC, R14

; *****************************************************************************

SwapColours ROUT
        LDR     R1, [WsPtr, #TForeCol]
        LDR     R2, [WsPtr, #TBackCol]
        STR     R1, [WsPtr, #TBackCol]
        STR     R2, [WsPtr, #TForeCol]

        LDR     R1, [WsPtr, #TextFgColour]
        LDR     R2, [WsPtr, #TextBgColour]
        STR     R1, [WsPtr, #TextBgColour]
        STR     R2, [WsPtr, #TextFgColour]

        LDR     R3, [WsPtr, #BitsPerPix]
        CMP     R3, #8                          ; C=1 <=> 256 colour mode
        BCC     %FT10                           ; [not 256 colour mode]

        LDR     R1, [WsPtr, #TFTint]
        LDR     R2, [WsPtr, #TBTint]
        STR     R1, [WsPtr, #TBTint]
        STR     R2, [WsPtr, #TFTint]
10
        B       SetColoursDirty                 ; don't bother optimising the case
                                                ; when both colours are the same

; *****************************************************************************
;
;       SetECFOrigin - Set the origin of the ECF pattern
;
;       Called by VDU 23,17,6,x;y;0,0,0
;
; in:   QQ+2, QQ+3 = xlo, xhi
;       QQ+4, QQ+5 = ylo, yhi
;

SetECFOrigin ROUT

; *****Change made by DJS
; Original code was:
;        LDRB    R0, [WsPtr, #QQ+3]      ; xhi
;        LDRB    R1, [WsPtr, #QQ+2]      ; xlo
;        PackXtnd R0, R0, R1             ; pack 2 bytes and sign extend
;        LDRB    R1, [WsPtr, #QQ+5]      ; yhi
;        LDRB    R2, [WsPtr, #QQ+4]      ; ylo
;        PackXtnd R1, R1, R2             ; pack 2 bytes and sign extend

        LoadCoordPair R0, R1, WsPtr, QQ+2

; *****End of change made by DJS

SetECFOriginCommon
        Push    R14
        ADD     R8, WsPtr, #GCsX        ; save ECursor away, cos EIG changes it
        LDMIA   R8, {R6, R7}            ; and we don't want it to!

        MOV     R2, #&FF                ; convert external-to-internal
        BL      EIG                     ; as absolute coordinates

; now R0 = internal X; R1 = internal Y

        LDR     R3, [WsPtr, #NPix]
        LDR     R4, [WsPtr, #Log2BPC]

        AND     R0, R0, R3              ; X MOD (number of pixels per word)

; *****Change made by DJS
; Original code was:
;        MOVS    R0, R0, LSL R4          ; number of bits to rotate ECF left by
;        MOVEQ   R0, #32                 ; if zero then make 32
;        RSB     R0, R0, #32             ; so we make it zero again!
; I don't think anything cares whether this value is 0 or 32 in this case - it
; is only used as Rm in a couple of MOV Rn, Rn, ROR Rm instructions. So change
; to:
        MOV     R0, R0, LSL R4          ; number of bits to rotate ECF left by
        RSB     R0, R0, #32
; *****End of change made by DJS

        STR     R0, [WsPtr, #ECFShift]  ; number of bits to rotate right by

        LDR     R3, [WsPtr, #YWindLimit]
; *****Change made by DJS
; Need to increase R3 by one to produce correct alignment of the ECF patterns,
; as YWindLimit is an inclusive bound, not an exclusive one.
        ADD     R3, R3, #1
; *****End of change made by DJS
        SUB     R1, R3, R1              ; invert Y
        AND     R1, R1, #7              ; modulo 8
        STR     R1, [WsPtr, #ECFYOffset]

        STMIA   R8, {R6, R7}

        BL      SetColour               ; update colours now

        Pull    PC

; *****************************************************************************
;
;       SWISetECFOrigin - Entry point for SWI OS_SetECFOrigin
;
; in:   R0 = x-coordinate (external coords)
;       R1 = y-coordinate (-------""------)
;

SWISetECFOrigin ROUT
        Push    "R0-R9, R14"
        VDWS    WsPtr
        BL      SetECFOriginCommon
        Pull    "R0-R9, R14"
        ExitSWIHandler

; *****************************************************************************
;
;       Scroll left and right
;
; in:   C=0 => scroll window
;       C=1 => scroll screen

ScrollLeftChar
        MOV     R9, #0
        B       ScrollLeft

ScrollLeftByte
        LDR     R9, [WsPtr, #Log2BPP]
        LDR     R10, [WsPtr, #ModeFlags]
        TST     R10, #Flag_BBCGapMode           ; if in BBC gap mode
        SUBNE   R9, R9, #1                      ; then 1 BBC byte = 2 ARM bytes

ScrollLeft
        LDR     R10, [WsPtr, #Log2BPC]
        SUB     R9, R10, R9

        MOV     R10, #1
        MOV     R9, R10, LSL R9                 ; R9 = bytes to move by

        ADC     R0, R0, R0                      ; bit0(R0) set => whole screen
        TST     R6, #TeletextMode               ; if teletext, then do special
        BNE     TTXScrollLeft

        Push    "R0, R14"                       ; save link and carry
        MOVS    R0, R0, LSR #1                  ; C=0/1 => window/screen
        BL      GetScrWindInfo
        BL      ScrollLeft2
        Pull    R0
        MOVS    R0, R0, LSR #1

; now clear right hand column

        ADDCC   R0, WsPtr, #TWBRow              ; C=0 => window
        LDMCCIA R0, {R1-R3}                     ; R1 = TWBRow; R2 = TWRCol
        MOVCC   R0, R2                          ; R3 = TWTRow; R0 = TWRCol

        ADDCS   R0, WsPtr, #ScrRCol             ; C=1 => screen
        LDMCSIA R0, {R0, R1}                    ; R0 = ScrRCol; R1 = ScrBRow
        MOVCS   R2, R0                          ; R2 = ScrRCol; R3 = 0
        MOVCS   R3, #0

;       R9 = width of column to clear (in bytes)

        BL      GetBoxInfo              ; get info for right hand char column
                                        ; R2 -> top left of right hand char col
                                        ; R5 := number of bytes for one char
        ADD     R2, R2, R5              ; R2 -> off right of top line
        MOV     R5, R9                  ; R5 := real number of bytes to do
        SUB     R2, R2, R5              ; R2 -> top left of column to clear
        Pull    R14
        B       ClearThisBox

; *****************************************************************************

GetScrWindInfo
        BCC     GetWindowInfo
GetScreenInfo
        MOV     R0, #0
        LDR     R1, [WsPtr, #ScrBRow]
        LDR     R2, [WsPtr, #ScrRCol]
        MOV     R3, #0
        B       GetBoxInfo

; *****************************************************************************
;
;       ScrollLeft2 - Scroll area of screen left (don't clear right column)
;
; in:   R2 -> screen(left,top)
;       R5 = width of box in bytes
;       R6 = number of scan lines in box
;       R7 = linelength
;       R9 = number of bytes to scroll by
;

ScrollLeft2 ROUT
        SUBS    R5, R5, R9                      ; number of bytes to scroll
        MOVEQ   PC, R14                         ; none to scroll

        MOV     R10, R9, LSL #3                 ; number of bits moving by
        CMP     R10, #32
        MOVCS   R10, #32                        ; only do 32 at a time
        RSB     R11, R10, #32                   ; 32-number of bits moving by

ScrollLineLeft
        MOV     R0, R2                          ; to addr (left)
        ADD     R1, R0, R9                      ; from addr (right)
        ADD     R3, R0, R5                      ; end+1 to addr
10
        TST     R0, #3                          ; if not on word boundary
        LDRNEB  R4, [R1], #1                    ; then copy a byte from R1
        STRNEB  R4, [R0], #1                    ; to R0
        CMPNE   R0, R3                          ; if not on bdy and not at end
        BNE     %BT10                           ; then loop

20
        ADD     R1, R1, #3                      ; round R1 up to next word
        BIC     R1, R1, #3

        TEQ     R11, #0                         ; if no shifts, use fast code
        BEQ     %FT60

        SUB     R3, R3, #3                      ; fudge factors anonymous
        CMP     R0, R3                          ; if can't do any words
        BCS     %FT40                           ; then skip
        LDR     R8, [R0]                        ; get word to be put into R4
30
        MOV     R4, R8, LSR R10                 ; get 2nd bits of old word
        LDR     R8, [R1], #4                    ; load new word
        ORR     R4, R4, R8, LSL R11             ; merge with 1st bits of new
        STR     R4, [R0], #4                    ; and store
        CMP     R0, R3                          ; if more words to do then loop
        BCC     %BT30
40
        ADD     R3, R3, #3                      ; put it back again

;       finish off the odd bytes
;       R0 -> next dest byte

        ADD     R1, R0, R9                      ; R1 -> next source byte
        CMP     R0, R3                          ; if not at end
50
        LDRCCB  R4, [R1], #1                    ; then copy a byte from R1
        STRCCB  R4, [R0], #1                    ; to R0
        CMPCC   R0, R3                          ; check for end
        BCC     %BT50                           ; and loop if not

        ADD     R2, R2, R7                      ; goto next line down
        SUBS    R6, R6, #1                      ; one less line to do
        BNE     ScrollLineLeft
        MOV     PC, R14

; fast code for when R10=32, R11=0

60
        SUB     R3, R3, #7                      ; must be able to do 8 bytes
        CMP     R0, R3
70
        LDMCCIA R1!, {R4, R8}                   ; if OK then copy 2 words
        STMCCIA R0!, {R4, R8}
        CMPCC   R0, R3                          ; check for end
        BCC     %BT70
        ADD     R3, R3, #4                      ; must be able to do 4 bytes
        CMP     R0, R3
80
        LDRCC   R4, [R1], #4                    ; if OK then copy a word
        STRCC   R4, [R0], #4
        CMPCC   R0, R3                          ; check for end
        BCC     %BT80
        B       %BT40                           ; do odd bytes at end

; *****************************************************************************

ScrollRightChar
        MOV     R9, #0
        B       ScrollRight

ScrollRightByte
        LDR     R9, [WsPtr, #Log2BPP]
        LDR     R10, [WsPtr, #ModeFlags]
        TST     R10, #Flag_BBCGapMode           ; if in BBC gap mode
        SUBNE   R9, R9, #1                      ; then 1 BBC byte = 2 ARM bytes

ScrollRight
        LDR     R10, [WsPtr, #Log2BPC]
        SUB     R9, R10, R9

        MOV     R10, #1
        MOV     R9, R10, LSL R9                 ; R9 = bytes to move by

        ADC     R0, R0, R0                      ; bit0(R0) set => whole screen
        TST     R6, #TeletextMode               ; if teletext, then do special
        BNE     TTXScrollRight

        Push    "R0, R14"                       ; save link and carry
        MOVS    R0, R0, LSR #1                  ; C=0/1 => window/screen
        BL      GetScrWindInfo
        BL      ScrollRight2
        Pull    R0
        MOVS    R0, R0, LSR #1

; now clear left column

        ADDCC   R0, WsPtr, #TWLCol              ; C=0 => window
        LDMCCIA R0, {R0-R3}                     ; R0 = TWLCol; R1 = TWBRow
        MOVCC   R2, R0                          ; R2 = TWLCol; R3 = TWTRow

        MOVCS   R0, #0
        LDRCS   R1, [WsPtr, #ScrBRow]
        MOVCS   R2, R0
        MOVCS   R3, #0

;       R9 = width of column to clear (in bytes)

        BL      GetBoxInfo              ; get info for right hand char column
                                        ; R2 -> top left of left hand char col
                                        ; R5 := number of bytes for one char
        MOV     R5, R9                  ; R5 := real number of bytes to do
        Pull    R14
        B       ClearThisBox

; *****************************************************************************
;
;       ScrollRight2 - Scroll area of screen right (don't clear left column)
;
; in:   R2 -> screen(left,top)
;       R5 = width of box in bytes
;       R6 = number of scan lines in box
;       R7 = linelength
;       R9 = number of bytes to scroll by
;

ScrollRight2 ROUT
        ADD     R2, R2, R5                      ; R2 -> off top right
        SUB     R2, R2, #1                      ; R2 -> top right

        SUBS    R5, R5, R9                      ; number of bytes to scroll
        MOVEQ   PC, R14                         ; none to scroll

        MOV     R10, R9, LSL #3                 ; number of bits moving by
        CMP     R10, #32
        MOVCS   R10, #32                        ; only do 32 at a time
        RSB     R11, R10, #32                   ; 32-number of bits moving by

ScrollLineRight
        MOV     R0, R2                          ; to addr (right)
        SUB     R1, R0, R9                      ; from addr (left)
        SUB     R3, R0, R5                      ; end-1 to addr

        MOV     R8, #(3 :SHL: 30)
10
        CMP     R8, R0, LSL #30                 ; EQ if (R0 AND 3)=3
        LDRNEB  R4, [R1], #-1                   ; if not then copy a byte
        STRNEB  R4, [R0], #-1
        CMPNE   R0, R3                          ; if not aligned and not end
        BNE     %BT10                           ; then loop

        SUB     R1, R1, #3                      ; round R1 down to next word
        BIC     R1, R1, #3

        SUB     R0, R0, #3                      ; make R0 -> word boundary
        TEQ     R11, #0                         ; if no shifts, use fast code
        BEQ     %FT60

        CMP     R0, R3                          ; if no words
        BLS     %FT40                           ; then skip
        LDR     R8, [R0]                        ; get word to be put into R4
30
        MOV     R4, R8, LSL R10
        LDR     R8, [R1], #-4
        ORR     R4, R4, R8, LSR R11
        STR     R4, [R0], #-4
        CMP     R0, R3                          ; if R3 < R1 then no more words
        BHI     %BT30
40
        ADD     R0, R0, #3                      ; put it back again

;       finish off the odd bytes
;       R0 -> next dest byte

        SUB     R1, R0, R9                      ; R1 -> next source byte
        CMP     R0, R3                          ; check for end
50
        LDRHIB  R4, [R1], #-1                   ; if not end then copy byte
        STRHIB  R4, [R0], #-1
        CMPHI   R0, R3                          ; check for end
        BHI     %BT50

        ADD     R2, R2, R7                      ; move to next row
        SUBS    R6, R6, #1                      ; one less row to do
        BNE     ScrollLineRight
        MOV     PC, R14

; fast code for when R10=32, R11=0

60
        ADD     R3, R3, #4                      ; need to do at least 8 bytes
        CMP     R0, R3
70
        LDMHIDA R1!, {R4, R8}                   ; if OK then copy 2 words
        STMHIDA R0!, {R4, R8}
        CMPHI   R0, R3                          ; check for end
        BHI     %BT70

        SUB     R3, R3, #4                      ; put it back
        CMP     R0, R3
80
        LDRHI   R4, [R1], #-4                   ; if OK then copy 1 word
        STRHI   R4, [R0], #-4
        CMPHI   R0, R3                          ; check for end
        BHI     %BT80
        B       %BT40

; *****************************************************************************
;
;       VDU 23,16,x,y| - Change cursor flags
;
;       new := (old AND y) EOR x

Vdu23_16
        LDRB    R0, [WsPtr, #CursorFlags]       ; just affect bottom byte
        LDRB    R1, [WsPtr, #QQ+1]              ; x
        LDRB    R2, [WsPtr, #QQ+2]              ; y
        AND     R0, R0, R2
        EOR     R0, R0, R1
        STRB    R0, [WsPtr, #CursorFlags]

        TST     R0, #1
        BEQ     RCRLF                           ; leaving 81 column mode
                                                ; so release CRLF
        MOV     PC, R14

; *****************************************************************************
;
;       Move cursor to R0 chars from boundary
;       and check for being legal
;
; out:  C=1 => was within window
;       C=0 => outside window

        ASSERT  CursorY = CursorX +4

CursorBdyCheck
        Push    R14
        BL      CursorBdy
        ADD     R9, WsPtr, #CursorX
        LDMIA   R9, {R9, R10}
        CMP     R4, R9
        CMPCS   R3, R10
        Pull    PC

; *****************************************************************************
;
;       Move 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
;
; out:  R7, R8, R11 undefined
;       R0, R1, R6, R9, R10, R12, R13 preserved
;       R2-R5 are window coords (left,down,right,up)
;
        ASSERT  TWBRow = TWLCol + 4
        ASSERT  TWRCol = TWLCol + 8
        ASSERT  TWTRow = TWLCol + 12

InputCursorB0
        MOV     R0, #0
InputCursorBdy
        ADD     R11, WsPtr, #InputCursorX
        B       CBDY05

CursorB0
        MOV     R0, #0
CursorBdy
        ADD     R11, WsPtr, #CursorX
CBDY05
        ADD     R7, WsPtr, #TWLCol      ; point to window coords
        LDMIA   R7, {R2-R5}

        ADR     R7, CBDYTab
DoJumpTable
        AND     R8, R6, #&0E
        LDR     R8, [R7, R8, LSL #1]
        ADD     PC, R7, R8

CBDYTab
        &       CursorLBdy-CBDYTab
        &       CursorRBdy-CBDYTab
        &       CursorLBdy-CBDYTab
        &       CursorRBdy-CBDYTab
        &       CursorUBdy-CBDYTab
        &       CursorUBdy-CBDYTab
        &       CursorDBdy-CBDYTab
        &       CursorDBdy-CBDYTab

; Move cursor to left boundary + R0 characters in

CursorLBdy
        ADD     R7, R0, R2
CBDY10
        STR     R7, [R11, #CursorX-CursorX]
        MOV     PC, R14

; Move cursor to right boundary + R0 characters in

CursorRBdy
        SUB     R7, R4, R0
        B       CBDY10

; Move cursor to up boundary + R0 characters in

CursorUBdy
        ADD     R7, R0, R5
CBDY20
        STR     R7, [R11, #CursorY-CursorX]
        MOV     PC, R14

; Move cursor to down boundary + R0 characters in

CursorDBdy
        SUB     R7, R3, R0
        B       CBDY20

; *****************************************************************************
;
;       CursorMove - Move cursor in direction corresponding to R6 bits 1-3
;       (0,4 = right; 2,6 = left; 8,10 = down; 12,14 = up)
;       R6 is preserved

        ASSERT  InputCursorY-InputCursorX = CursorY-CursorX
        ASSERT  InputCursorAddr-InputCursorX = CursorAddr-CursorX

InputCursorMove
        ADD     R11, WsPtr, #InputCursorX
        B       CurM10

CursorMove
        ADD     R11, WsPtr, #CursorX
CurM10
        ADR     R7, CMVTab
        B       DoJumpTable

CMVTab
        &       MoveRight-CMVTab
        &       MoveLeft-CMVTab
        &       MoveRight-CMVTab
        &       MoveLeft-CMVTab
        &       MoveDown-CMVTab
        &       MoveDown-CMVTab
        &       MoveUp-CMVTab
        &       MoveUp-CMVTab

; Move cursor right if possible - on exit C=1 iff at RHS

MoveRight
        LDR     R0, [R11, #CursorX-CursorX]
        LDR     R4, [WsPtr, #TWRCol]
        CMP     R0, R4
        MOVCS   PC, R14
        LDR     R2, [R11, #CursorAddr-CursorX]
      [ HiResTTX
        LDR     R3, [WsPtr, #CharWidth]
      |
        LDR     R3, [WsPtr, #BytesPerChar]
      ]
        ADD     R0, R0, #1
        ADD     R2, R2, R3
        STR     R0, [R11, #CursorX-CursorX]
        STR     R2, [R11, #CursorAddr-CursorX]
        MOV     PC, R14

; Move cursor left if possible - on exit C=1 iff at LHS

MoveLeft
        LDR     R0, [R11, #CursorX-CursorX]
        LDR     R4, [WsPtr, #TWLCol]
        CMP     R4, R0
        MOVCS   PC, R14
        LDR     R2, [R11, #CursorAddr-CursorX]
      [ HiResTTX
        LDR     R3, [WsPtr, #CharWidth]
      |
        LDR     R3, [WsPtr, #BytesPerChar]
      ]
        SUB     R0, R0, #1
        SUB     R2, R2, R3
        STR     R0, [R11, #CursorX-CursorX]
        STR     R2, [R11, #CursorAddr-CursorX]
        MOV     PC, R14

; Move cursor down if possible - on exit C=1 iff at bottom

MoveDown
        LDR     R1, [R11, #CursorY-CursorX]
        LDR     R4, [WsPtr, #TWBRow]
        CMP     R1, R4
        MOVCS   PC, R14
        LDR     R2, [R11, #CursorAddr-CursorX]
        LDR     R3, [WsPtr, #RowLength]
        ADD     R1, R1, #1
        ADD     R2, R2, R3
        STR     R1, [R11, #CursorY-CursorX]
        STR     R2, [R11, #CursorAddr-CursorX]
        MOV     PC, R14

; Move cursor up if possible - on exit C=1 iff at top

MoveUp
        LDR     R1, [R11, #CursorY-CursorX]
        LDR     R4, [WsPtr, #TWTRow]
        CMP     R4, R1
        MOVCS   PC, R14
        LDR     R2, [R11, #CursorAddr-CursorX]
        LDR     R3, [WsPtr, #RowLength]
        SUB     R1, R1, #1
        SUB     R2, R2, R3
        STR     R1, [R11, #CursorY-CursorX]
        STR     R2, [R11, #CursorAddr-CursorX]
        MOV     PC, R14

; *****************************************************************************
;
;       Special HT - used when abnormal cursor direction selected
;                    or if C81Bit set
;
; in:   R6 = CursorFlags
;

SpecialHT
        TST     R6, #Vdu5Bit
        BNE     Vdu5HT
        Push    "R6,R14"
        BL      RCRLFR6
        Pull    R6
        BL      CursorMove              ; try to move in appropriate direction
        Pull    PC, CC                  ; if successful, finish
        BL      CursorB0                ; else move cursor to -ve X boundary
        BL      AddressCursor           ; re-address cursor
        Pull    R14
        B       VduLF                   ; and do a line-feed

; *****************************************************************************
;
;       CHT - version of HT used after printing character
;       will set pending CRLF if necessary
;
; in:   R6 = CursorFlags

CHT
        TST     R6, R6, ROR #10 ; test for Vdu5 or &1E or C81Bit, latter not
                                ; possible as we just printed a char
        BNE     SpecialCHT

        LDR     R0, [WsPtr, #CursorX]
        LDR     R2, [WsPtr, #CursorAddr]
      [ HiResTTX
        LDR     R3, [WsPtr, #CharWidth]
      |
        LDR     R3, [WsPtr, #BytesPerChar]
      ]
        LDR     R4, [WsPtr, #TWRCol]

        ADD     R0, R0, #1
        ADD     R2, R2, R3
        CMP     R0, R4
        STRLS   R0, [WsPtr, #CursorX]
        STRLS   R2, [WsPtr, #CursorAddr]
        MOVLS   PC, R14

        TST     R6, #1                          ; are we in "81-column" mode
        BNE     CHT10                           ; yes, then just set C81Bit

        BSR     PageTest

        LDR     R0, [WsPtr, #TWLCol]
        LDR     R1, [WsPtr, #CursorY]
        LDR     R4, [WsPtr, #TWBRow]

        ADD     R1, R1, #1
        CMP     R1, R4
        BLS     CursorR0R1                       ; not on bottom line

        STR     R0, [WsPtr, #CursorX]
        BSR     ScrollUp
        B       AddressCursor                    ; re-address cursor position

CHT10
        ORR     R6, R6, #C81Bit
        STR     R6, [WsPtr, #CursorFlags]
        MOV     PC, R14

SpecialCHT
        TST     R6, #Vdu5Bit
        BNE     Vdu5HT
        BSR     CursorMove              ; try to move in appropriate direction
        MOVCC   PC, R14                 ; if successful, finish

        TST     R6, #1
        BNE     CHT10

        Push    R14
        BL      CursorB0                ; else move cursor to -ve X boundary
        BL      AddressCursor           ; re-address cursor
        Pull    R14
        B       VduLF                   ; and do a line-feed

; *****************************************************************************
;
;       Special BS - used when abnormal cursor direction selected
;                    or Vdu5 mode
;                    or if C81Bit was set
;
; in :  R6 = CursorFlags
;

SpecialBS
        TST     R6, #Vdu5Bit            ; VDU 5 mode ?
        BNE     Vdu5BS                  ; then branch

        TST     R6, #C81Bit             ; did we come here cos of C81Bit
        BICNE   R6, R6, #C81Bit         ; if so then clear the bit
        STRNE   R6, [WsPtr, #CursorFlags] ; store it back
        MOVNE   PC, R14                 ; and leave cursor where it is

        EOR     R6, R6, #6              ; move "left"
        Push    R14
        BL      CursorMove
        Pull    PC, CC                  ; if successful
        BL      CursorB0                ; move to +ve X boundary
        BL      AddressCursor           ; re-address cursor
        Pull    R14
        B       VT                      ; and do reverse line-feed

; *****************************************************************************
;
;       Special LF - used when abnormal cursor direction selected
;
; in :  R6 = CursorFlags
;

SpecialLF
        EOR     R6, R6, #8              ; move "down"
        Push    R14
        BL      CursorMove
        Pull    PC, CC                  ; if successful
        BL      CanScroll               ; see if we can scroll
        Pull    PC, CS                  ; if failed, exit with all done
        Pull    R14
        MOV     R0, #0                  ; scroll window
        MOV     R1, #7                  ; "up"
        MOV     R2, #0                  ; by 1 char
        B       Scroll012

; *****************************************************************************
;
;       Special VT - used when abnormal cursor direction selected
;
; in :  R6 = CursorFlags
;

SpecialVT
        EOR     R6, R6, #14             ; move "up"
        Push    R14
        BL      CursorMove
        Pull    PC, CC                  ; if successful
        BL      CanScroll               ; see if we can scroll
        Pull    PC, CS                  ; if failed, exit with all done
        Pull    R14
        MOV     R0, #0                  ; scroll window
        MOV     R1, #6                  ; "down"
        MOV     R2, #0                  ; by 1 char
        B       Scroll012

; *****************************************************************************
;
;       CanScroll - check whether we can scroll in direction R6
;
; in:   R6 = CursorFlags

CanScroll ROUT
        Push    R14
        TST     R6, #&10            ; do we scroll or move to other edge
        BNE     CantScroll

        TST     R6, #CursorsSplit
        BEQ     %FT10                   ; cursors not split, so input cursor
                                        ; not relevant
        EOR     R6, R6, #6
        BL      InputCursorMove         ; if cursor fails to move, it's OK
                                        ; where it is
        BL      AddressInputCursor      ; readdress it anyway, just in case
        EOR     R6, R6, #6              ; put R6 back
10
        CLC
        Pull    PC

CantScroll
        BL      CursorB0                ; move to opposite edge
        BL      AddressCursor
        SEC
        Pull    PC

; *****************************************************************************
;
;       Release any pending CRLF
;

RCRLF
        LDR     R6, [WsPtr, #CursorFlags]

; Extra entry point when R6 already loaded

RCRLFR6
        TST     R6, #C81Bit
        MOVEQ   PC, R14                 ; no pending CRLF
        BSR     CR10                    ; (clears C81Bit)
        B       VduLF

; *****************************************************************************
;
;       VDU 23,8,t1,t2,x1,y1,x2,y2| - Clear block of text
;
;       t=0     Top left of window
;       t=1     Top of cursor column
;       t=2     Off top right of window
;
;       t=4     Left end of cursor line
;       t=5     Cursor position
;       t=6     Off right of cursor line
;
;       t=8     Bottom left of window
;       t=9     Bottom of cursor column
;       t=10    Off bottom right of window
;

Vdu23_8
        MOV     R0, #0                  ; top left of window
        STRB    R0, [WsPtr, #CBWS+0]
        STRB    R0, [WsPtr, #CBWS+1]
        BSR     CP81                    ; cursor position
        STRB    R0, [WsPtr, #CBWS+2]
        STRB    R1, [WsPtr, #CBWS+3]
        BSR     WBotRig                 ; bottom right of window
        ADD     R0, R0, #1              ; off bottom right of window
        STRB    R0, [WsPtr, #CBWS+4]
        STRB    R1, [WsPtr, #CBWS+5]

        ADD     R3, WsPtr, #CBWS
        ADD     R2, R3, #QQ+2-CBWS
        LDRB    R0, [WsPtr, #QQ+1]      ; t1
        BSR     CLBL50
        LDRB    R0, [WsPtr, #QQ+2]      ; t2
        BSR     CLBL50

; Check end is after start

        LDRB    R2, [WsPtr, #CBStart]   ; start X
        LDRB    R3, [WsPtr, #CBStart+1] ; start Y
        LDRB    R4, [WsPtr, #CBEnd]     ; end X
        LDRB    R5, [WsPtr, #CBEnd+1]   ; end Y

        CMP     R5, R3
        CMPEQ   R4, R2
        MOVLS   PC, R14                 ; end <= start so do nothing

        LDR     R0, [WsPtr, #CursorX]   ; save cursor position
        LDR     R1, [WsPtr, #CursorY]
        Push    "R0,R1"

        MOV     R0, R3                  ; First row to clear
        MOV     R1, R2                  ; Start position for first row
        LDRB    R7, [WsPtr, #CBWS+4]    ; End position for all but last row

        ADD     R6, WsPtr, #TWLCol
        LDMIA   R6, {R8-R11}

        LDR     R6, [WsPtr, #CursorFlags]
CLBL20
        TEQ     R0, R5
        BEQ     CLBL40
        BSR     RowClear
        ADD     R0, R0, #1
        MOV     R1, #0                  ; start position for subsequent rows
        B       CLBL20

CLBL40
        MOV     R7, R4                  ; end position for last row
        BSR     RowClear

        Pull    "R0,R1"
        STR     R0, [WsPtr, #CursorX]
        STR     R1, [WsPtr, #CursorY]
        B       AddressCursor

CLBL50
        AND     R1, R0, #3
        MOV     R1, R1, LSL #1
        LDRB    R6, [WsPtr, #CBWS+4]
        BSR     CLBL60
        MOV     R1, R0, LSR #1
        ORR     R1, R1, #1
        LDRB    R6, [WsPtr, #CBWS+5]
CLBL60
        LDRB    R4, [R3, R1]            ; get base coordinate to use
        LDRB    R5, [R2, #1]!           ; get x or y offset

        MOV     R5, R5, LSL #24
        ADDS    R4, R4, R5, ASR #24     ; add sign extended offset to base
        MOVMI   R4, #0                  ; if <0 then make =0
        CMP     R4, R6
        MOVHI   R4, R6                  ; is this stringent enough ?

        STRB    R4, [R2, #CBStart-(QQ+3)]
        MOV     PC, R14

; *****************************************************************************
;
;       RowClear - Clear a "row" {R1 <= X < R7, Y = R0}

RowClear
        Push    "R0-R11,R14"
        SUB     R7, R7, #1
        CMP     R1, R7
        BHI     RowCL10         ; left > right, so ignore

        TST     R6, #8

        MOVEQ   R2, R1          ; left
        MOVEQ   R3, R0          ; bottom
        MOVEQ   R4, R7          ; right
        MOVEQ   R5, R0          ; top

        MOVNE   R2, R0          ; left
        MOVNE   R3, R7          ; bottom
        MOVNE   R4, R0          ; right
        MOVNE   R5, R1          ; top

        TST     R6, #2

        ADDEQ   R0, R8, R2
        ADDEQ   R2, R8, R4

        SUBNE   R0, R10, R4
        SUBNE   R2, R10, R2

        TST     R6, #4

        ADDEQ   R1, R11, R3
        ADDEQ   R3, R11, R5

        SUBNE   R1, R9, R5
        SUBNE   R3, R9, R3

        BL      ClearBox

RowCL10
        Pull    "R0-R11,PC"

        [ :LNOT: AssemblingArthur

; *****************************************************************************
;
;       DoVduExternal - External entry point for
;       a) reading input cursor position
;       b) reading output cursor position
;       c) reading character at cursor position and screen mode
;       d) resetting all or part of font
;       e) reading font definitions
;       f) reading VDU status
;       g) reading VDU variables
;       h) reading palette (OSWORD &0B)
;       i) setting palette (OSWORD &0C)
;       j) various mouse/pointer stuff
;       k) set top of screen address
;       l) set VDU driver bank
;       m) set display bank
;
; in:   R0 = 0 => read input cursor position
;       R0 = 1 => read output cursor position
;       R0 = 2 => OSBYTE &87 - R1 := char at cursor, R2 := screen mode
;       R0 = 3 => reset font (R1*32 = start char, R2 = number of pages)
;       R0 = 4 => OSWORD 10 (R1 -> control block)
;       R0 = 5 => read VDU status
;       R0 = 6 => read VDU variables number R1, R1+1
;       R0 = 7 => read palette
;       R0 = 8 => set palette
;       R0 = 9 => mouse/pointer control
;       R0 = 10 => set top of screen address
;       R0 = 11 => set VDU driver bank
;       R0 = 12 => set display bank
;
; out:  a,b; R1 = X, R2 = Y
;       c; R1=char, R2=screen mode
;       f; R1=VDU status
;       g; R1=variable(n), R2=variable(n+1)
;       R0, R3 destroyed
;       R4-R11 preserved
;

DoVduExternal   ROUT
        CMP     R0, #ExtEntries
        MOVCS   PC, R14
        LDR     R3, [PC, R0, LSL #2]
        ADD     PC, PC, R3

ExtTable OutputExternals

        ]

; *****************************************************************************

DoReadPOSVPOSI ROUT
        MOV     R0, #0
        B       %FT05
DoReadPOSVPOSO
        MOV     R0, #1
05
        Push    "R4-R11, R14"
        LDR     R6, [WsPtr, #CursorFlags]
        TST     R6, #CursorsSplit               ; if cursors are split
        TEQNE   R0, #1                          ; and reading input cursor
        BNE     %FT10                           ; then use input cursor
        BL      CP81                            ; else read output cursor
        B       %FT20
10
        BL      CP79
20
        MOV     R2, R1
        MOV     R1, R0
        Pull    "R4-R11, PC"

; *****************************************************************************
;
;       DoSetScreenStart - Set top of screen address (display + drivers)
;
; in:   R1 -> control block
;       [R1, #0] = bit mask;    bit 0 set => change drivers address
;                               bit 1 set => change display address
;
;       [R1, #1..4] = byte offset from lowest screen address
;
; out:  -
;

DoSetScreenStart ROUT
        Push    R14
        LDRB    R3, [R1, #0]                    ; R3 = bitmask
      [ NoARMv6 :LOR: NoUnaligned
        LDRB    R0, [R1, #1]
        LDRB    R2, [R1, #2]
        ORR     R0, R0, R2, LSL #8
        LDRB    R2, [R1, #3]
        ORR     R0, R0, R2, LSL #16
        LDRB    R2, [R1, #4]
        ORR     R0, R0, R2, LSL #24             ; R0 is now the offset
      |
        ; Use unaligned load from ARMv6
        LDR     R0, [R1, #1]
      ]
        LDR     R2, [WsPtr, #TotalScreenSize]
        CMP     R0, R2
        BCS     %FT10                           ; offset too large
        Push    "R0,R2,R3,R5-R10"

        MOV     R0, #1
        STRB    R0, [WsPtr, #ScreenMemoryClaimed]
                                                ; indicate memory not available
        BL      PreWrchCursor
        Pull    "R0,R2,R3,R5-R10"
        LDR     R14, [WsPtr, #ScreenEndAddr]
        ADD     R0, R0, R14
        SUB     R0, R0, R2                      ; make into a LOGRAM address

        TST     R3, #1
        BEQ     %FT05
        Push    R0
        BL      NewScreenStart
        Pull    R0
05
        TST     R3, #2
        BLNE    SetVinit               ; program vinit and set DisplayStart

        Push    "R5-R10"                        ; save registers
        BL      PostWrchCursor
        Pull    "R5-R10"
10
        Pull    PC

; *****************************************************************************
;
;       NewScreenStart - Update ScreenStart and readdress cursors
;
; in:   R0 = new screen start
;
; out:  R0, R2 corrupted; all others preserved
;

NewScreenStart ROUT
        Push    R14

        LDR     R2, [WsPtr, #DisplayScreenStart]
        BL      SetDisplayScreenStart
        LDR     R14, [WsPtr, #VduSprite]        ; test if outputting to sprite
        TEQ     R14, #0                         ; Z => outputting to screen
        MOVNE   PC, R14                 ; if outputting to sprite, don't update
                                        ; Screenstart or cursor addresses

        STR     R0, [WsPtr, #ScreenStart]
        SUB     R0, R0, R2                      ; R0 := new-old
AdjustCursorVars
        LDR     R14, [WsPtr, #CursorAddr]
        ADD     R14, R14, R0
        STR     R14, [WsPtr, #CursorAddr]
        LDR     R14, [WsPtr, #InputCursorAddr]
        ADD     R14, R14, R0
        STR     R14, [WsPtr, #InputCursorAddr]
        Pull    PC

; *****************************************************************************
;
;       DoSetDriverBank - Set VDU driver's screen bank (OSBYTE &70)
;
; in:   R1 = bank number (1..n) (NOTE: starts at 1)
;

DoSetDriverBank ROUT
        Push    R14
        MOVS    R2, R1
        BNE     %FT10
        LDR     R1, [WsPtr, #VduStatus]
        TST     R1, #Shadowing
        MOVEQ   R2, #1
        MOVNE   R2, #2
10
        LDR     R0, =ZeroPage
        LDRB    R1, [R0, #OsbyteVars + :INDEX: MemDriver] ; old value

        Push    "R1,R4-R10"             ; save registers
        BL      ConvertBankToAddress    ; R3 := start address of NEXT bank
        LDR     R14, [WsPtr, #ScreenEndAddr]
        CMP     R3, R14                 ; if after end, can't do it
        Pull    "R1,R4-R10,PC", HI      ; so exit

        STRB    R2, [WsPtr, #ScreenMemoryClaimed]
                                        ; indicate memory not available (R2<>0)
        STRB    R2, [R0, #OsbyteVars + :INDEX: MemDriver] ; store new value

        LDR     R0, [WsPtr, #DriverBankAddr] ; R0:=old default bank start
        LDR     R2, [WsPtr, #DisplayScreenStart] ; R2:=old current screen start
        SUB     R0, R2, R0              ; R0 := (current-default)
                                        ; in range 1-TotalSize..TotalSize-1

        LDR     R2, [WsPtr, #ScreenSize]
        SUB     R3, R3, R2              ; default start of THIS bank
        STR     R3, [WsPtr, #DriverBankAddr] ; R3 := new default bank start

        ADD     R0, R0, R3              ; new current start (not bound checked)

        LDR     R2, [WsPtr, #TotalScreenSize]

        RSBS    R0, R0, R14
        ADDLS   R0, R0, R2
        CMP     R0, R2
        SUBHI   R0, R0, R2              ; now in bounds (but inverted)
        RSB     R0, R0, R14

        Push    R0
        BL      PreWrchCursor
        Pull    R0

        BL      NewScreenStart

        BL      PostWrchCursor
        Pull    "R1,R4-R10,PC"

; *****************************************************************************
;
;       DoSetDisplayBank - Set displayed screen bank (OSBYTE &71)
;
; in:   R1 = bank number (1..n) (NOTE: starts at 1)
;

DoSetDisplayBank ROUT
        Push    R14
        MOVS    R2, R1
        BNE     %FT10
        LDR     R1, [WsPtr, #VduStatus]
        TST     R1, #Shadowing
        MOVEQ   R2, #1
        MOVNE   R2, #2
10
        LDR     R0, =ZeroPage
        LDRB    R1, [R0, #OsbyteVars + :INDEX: MemDisplay] ; old value
        Push    "R1,R4-R5"              ; save registers
        BL      ConvertBankToAddress    ; R3 := start address of NEXT bank
        LDR     R14, [WsPtr, #ScreenEndAddr]
        CMP     R3, R14                 ; if after end, can't do it
        Pull    "R1,R4-R5,PC", HI       ; so exit

        STRB    R2, [WsPtr, #ScreenMemoryClaimed]
                                        ; indicate memory not available (R2<>0)
        STRB    R2, [R0, #OsbyteVars + :INDEX: MemDisplay] ; store new value

        LDR     R0, [WsPtr, #DisplayBankAddr] ; R0 := old default bank start
        LDR     R2, [WsPtr, #DisplayStart]    ; R2 := old current display start
        SUB     R0, R2, R0              ; R0 := (current-default)
                                        ; in range 1-TotalSize..TotalSize-1

        LDR     R2, [WsPtr, #ScreenSize]
        SUB     R3, R3, R2              ; default start of THIS bank
        STR     R3, [WsPtr, #DisplayBankAddr] ; R3 := new default bank start

        ADD     R0, R0, R3              ; new current start (not bound checked)

        LDR     R2, [WsPtr, #TotalScreenSize]

        RSBS    R0, R0, R14
        ADDLS   R0, R0, R2
        CMP     R0, R2
        SUBHI   R0, R0, R2              ; now in bounds (but inverted)
        RSB     R0, R0, R14

        BL      SetVinit                ; program vinit and set DisplayStart
        Pull    "R1,R4-R5,PC"

; *****************************************************************************
;
;       CP81 - Read output cursor position,
;       taking C81Bit and CursorFlags into account
;
; out:  R0=X, R1=Y

CP81
        Push    R14
        BL      CP80
        LDR     R6, [WsPtr, #CursorFlags]
        TST     R6, #C81Bit
        ADDNE   R0, R0, #1                      ; in hidden column, so inc X
        Pull    PC

; *****************************************************************************
;
;       CP79 - Read input cursor position,
;       taking CursorFlags into account
;
; out:  R0=X, R1=Y

CP79
        MOV     R1, #InputCursorX-TWLCol
        B       CP80R1

; *****************************************************************************
;
;       WBotRig - Read coordinates of "bottom right" in user coords
;
; out:  R0=X, R1=Y

WBotRig
        ADD     R5, WsPtr, #TWLCol
        LDR     R6, [WsPtr, #CursorFlags]
        LDR     R0, [R5, #TWLCol-TWLCol]            ; get TWLCol
        LDR     R1, [R5, #TWRCol-TWLCol]            ; get TWRCol
        SUB     R4, R1, R0
        MOV     R2, #0
        MOV     R1, #0
        B       CP8010

; *****************************************************************************
;
;       CP80 - Read cursor position, taking CursorFlags into account

CP80
        MOV     R1, #CursorX-TWLCol
CP80R1
        ADD     R5, WsPtr, #TWLCol
        LDR     R6, [WsPtr, #CursorFlags]
        MOV     R2, #2
        MOV     R0, #0
        BSR     CP8020
        MOV     R4, R2
        MOV     R2, #4
CP8010
        ADD     R1, R1, #4
        MOV     R0, #TWTRow-TWLCol
        BSR     CP8020
        MOV     R0, R2
        MOV     R1, R2
        TST     R6, #8
        MOVEQ   R0, R4
        MOVNE   R1, R4

        MOV     PC, R14

CP8020
        TST     R2, R6
        EORNE   R0, R0, #8
        LDR     R2, [R5, R0]
        LDR     R3, [R5, R1]
        SUBNE   R2, R2, R3
        SUBEQ   R2, R3, R2
        MOV     PC, R14

; *****************************************************************************
;
;       Vdu23_0 - Program "6845"
;

Vdu23_0
        LDRB    R1, [WsPtr, #QQ+1]      ; get register number
        AND     R2, R1, #31             ; address register is 5 bits
        CMP     R2, #8
        MOVCC   PC, R14
        BEQ     Vdu23_0_8
        CMP     R2, #10
        BEQ     Vdu23_0_10
        CMP     R2, #12
        BCC     Vdu23_0_11

        [ DoVdu23_0_12
        BEQ     Vdu23_0_12
        CMP     R2, #13
        BEQ     Vdu23_0_13
        ]
        B       UnknownVdu23


; *****************************************************************************
;
;       Vdu23_0_8 - Program interlace
;
; in:   R1 = unmasked register number (bits 5-7 may be set)

Vdu23_0_8 ROUT
        LDRB    R0, [WsPtr, #QQ+2]      ; value to program into interlace
        TST     R0, #&80                ; if negative, don't EOR with *TV
        TSTEQ   R1, #&E0                ; if not register 8, don't EOR
        BNE     %FT10                   ; don't EOR

        LDROSB  R1, TVInterlace
        TST     R1, #1
        EORNE   R0, R0, #1              ; toggle if *TV n,1 and number +ve
10
 [ UseGraphicsV
        Push    "R14"
        MOV     R4, #GraphicsV_SetInterlace
        BL      CallGraphicsV
        Pull    "R14"
 |
        Push    "R0-R3, R9, R12, LR"
        mjsAddressHAL
        mjsCallHAL    HAL_Video_SetInterlace
        Pull    "R0-R3, R9, R12, LR"
 ]

        MOV     PC, R14

        [ DoVdu23_0_12

; *****************************************************************************
;
;       Vdu23_0_12 - Program "hi" byte of start of screen
;

Vdu23_0_12
        MOV     R2, #11                 ; starting bit number
V23012_10
        LDRB    R0, [WsPtr, #QQ+2]      ; get parameter
        LDR     R1, [WsPtr, #VinitCopy]
        MOV     R1, R1, ROR R2          ; move changing bits to bottom
        BIC     R1, R1, #&FF            ; clear old bits out
        ORR     R1, R1, R0              ; put in new bits
        RSB     R2, R2, #32             ; fudge cos we ain't got ROL
        MOV     R0, R1, ROR R2          ; put back to correct position
        B       SetVinitPhys

; *****************************************************************************
;
;       Vdu23_0_13 - Program "lo" byte of start of screen
;

Vdu23_0_13
        MOV     R2, #3                  ; starting bit number
        B       V23012_10

        ]

; *****************************************************************************
;
;       Vdu23_9  - Equivalent of FX 9
;       Vdu23_10 - Equivalent of FX 10
;
; in:   R0 = 9 or 10
;

Vdu23_9
Vdu23_10
        Push    R14                             ; for the SWI
        LDRB    R1, [WsPtr, #QQ+1]              ; get X parameter
        SWI     XOS_Byte
        Pull    PC, VC                          ; no error, then return

        Pull    R14                             ; get stack back
        B       VduBadExit                      ; and tell the world

; *****************************************************************************
;
;       DoReadVDUStatus - Read VDU status
;
; out:  R1 = status
;       bit 0 set =>    printer enabled by VDU 2
;       bit 1 set =>    N/A
;       bit 2 set =>    in page mode
;       bit 3 set =>    text window in force
;       bit 4 set =>    in a shadow mode
;       bit 5 set =>    in VDU 5 mode
;       bit 6 set =>    cursor editing in progress
;       bit 7 set =>    VDU disabled (by VDU 21)
;
;       R2 corrupted
;

DoReadVDUStatus
        LDR     R1, [WsPtr, #VduStatus]         ; Vdu2, Window, Shadow bits

        LDR     R2, [WsPtr, #CursorFlags]

        TST     R2, #PageMode
        ORRNE   R1, R1, #&04

        TST     R2, #Vdu5Bit
        ORRNE   R1, R1, #&20

        TST     R2, #CursorsSplit
        ORRNE   R1, R1, #&40

        TST     R2, #VduDisabled
        ORRNE   R1, R1, #&80

        MOV     PC, R14

; *****************************************************************************
;
;       DoReadVDUVariable - Read BBC-style VDU variables
;
; in:   R1 = variable to read (only &00..&0F are supported)
;
; out:  R1 = value of that variable
;       R2 = value of next variable
;

DoReadVDUVariable
        CMP     R1, #(BBCVduVarsEnd-BBCVduVars)
        MOVCS   PC, R14                 ; don't support this variable
        ADR     R3, BBCVduVars          ; point to lookup table
        LDRB    R0, [R3, R1]!           ; get index for first variable
        LDRB    R1, [WsPtr, R0]         ; get first variable
        LDRB    R0, [R3, #1]            ; get index for second variable
        LDRB    R2, [WsPtr, R0]         ; get second variable
        MOV     PC, R14

BBCVduVars
        EQUB    GWLCol
        EQUB    GWLCol+1
        EQUB    GWBRow
        EQUB    GWBRow+1
        EQUB    GWRCol
        EQUB    GWRCol+1
        EQUB    GWTRow
        EQUB    GWTRow+1
        EQUB    TWLCol
        EQUB    TWBRow
        EQUB    TWRCol
        EQUB    TWTRow
        EQUB    OrgX
        EQUB    OrgX+1
        EQUB    OrgY
        EQUB    OrgY+1
BBCVduVarsEnd

; *****************************************************************************
;
;       SetCharSizes - Set char sizes for VDU 4 or VDU 5 text
;
; in:   QQ+2 = flags:   bit 0 set => set VDU 4 size
;                       bit 1 set => set VDU 5 size
;                       bit 2 set => set VDU 5 spacing
;       QQ+3,4  x size in pixels
;       QQ+5,6  y size in pixels
;

        ASSERT  YEigFactor = XEigFactor +4

SetCharSizes ROUT
        LDRB    R0, [WsPtr, #QQ+3]
        LDRB    R2, [WsPtr, #QQ+4]
        ORR     R0, R0, R2, LSL #8      ; R0 = x size

        LDRB    R1, [WsPtr, #QQ+5]
        LDRB    R2, [WsPtr, #QQ+6]
        ORR     R1, R1, R2, LSL #8      ; R1 = y size

        LDRB    R2, [WsPtr, #QQ+2]

        ADD     R3, WsPtr, #GCharSizes
        TST     R2, #2                  ; if modifying VDU 5 size
        STMNEIA R3, {R0, R1}

        ADD     R3, R3, #8
        TST     R2, #4                  ; if modifying VDU 5 spacing
        STMNEIA R3, {R0, R1}

        MOV     PC, R14

        END