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

SlowCursorSpeed * 16
FastCursorSpeed * 8
OnFlashTime * 48
OffFlashTime * 16

;       InitCursor - initialise cursor shape and address

InitCursor ROUT
        Push    R14
        LDR     R6, [WsPtr, #CursorFlags]
        TST     R6, #TeletextMode               ; if teletext mode
        MOVNE   R0, #&72                        ; then start at 9 (slow flash)
        MOVEQ   R0, #&67                        ; else start at 7 (slow flash)
        BL      ProgReg10AndCopy

        MOV     R0, #1                          ; set to flash immediately
        STR     R0, [WsPtr, #CursorCounter]     ; AFTER setting CursorSpeed
                                                ; in case VSYNC happens
        LDR     R1, [WsPtr, #VduSprite]
        TEQ     R1, #0                          ; if outputting to sprite
        MOVNE   R0, #&20                        ; then turn cursor off
        BLNE    ProgReg10AndCopy

        LDR     R0, [WsPtr, #RowMult]           ; 8 or 10 or 16 or 20
        Pull    R14

; and drop thru to ...

SetCursorBottom
        LDR     R1, [WsPtr, #LineLength]
        MUL     R2, R1, R0
        STR     R2, [WsPtr, #CursorEndOffset]
        MOV     PC, R14

SetCursorTop
        LDR     R1, [WsPtr, #LineLength]
        MUL     R2, R1, R0
        STR     R2, [WsPtr, #CursorStartOffset]
        MOV     PC, R14

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

; Cursors are split, so remove output cursor

DoOutputCursor ROUT
        Push    R14
        LDR     R1, [WsPtr, #LineLength]
        MOV     R1, R1, LSL #3                  ; and end after 8 rows
        LDR     R0, [WsPtr, #ModeFlags]
        TST     R0, #ModeFlag_DoubleVertical    ; if double vertical
        ADDNE   R1, R1, R1                      ; end after 16 rows
        MOV     R0, #0                          ; start on top line
        BL      EORCursor
        LDR     R2, [WsPtr, #InputCursorAddr]   ; flashing cursor is at input
        Pull    R14
        B       PreWC10

; *****************************************************************************
;
;       PreWrchCursor - Remove cursors prior to Wrch
;
; out:  R6 = new CursorFlags; R5,R7 preserved; others corrupted
;

PreWrchCursor

; Need to disable IRQs here to stop Vsync modifying CursorFlags in between
; us reading it and writing it

        MRS     R3, CPSR
        ORR     R1, R3, #I32_bit
        MSR     CPSR_c, R1

        LDR     R6, [WsPtr, #CursorFlags]
        MOVS    R0, R6, LSL #(32-InWrchBitPosn) ; CS => was already off
        ORRCC   R6, R6, #InWrchBit              ; protect against vsyncs
        STRCC   R6, [WsPtr, #CursorFlags]

        LDR     R0, [WsPtr, #CursorStack]
        MOV     R0, R0, RRX                     ; shift down + put current
                                                ; state in top bit
        STR     R0, [WsPtr, #CursorStack]

        MSR     CPSR_c, R3                      ; restore old I bit

        MOV     R2,R14                          ; n.b. calling remove even if
        LDR     R14, [WsPtr, #SWP_Under]        ; soft pointer not currently
        TEQ     R14, #0                         ; visible. This sets the lock,
        BLNE    RemovePointer                   ; preventing it from being
        MOV     R14,R2                          ; turned on while we're busy.

        MOVCS   PC, R14                         ; already off, so exit

        LDR     R2, [WsPtr, #CursorAddr]        ; point to output
        TST     R6, #CursorsSplit
        BNE     DoOutputCursor
PreWC10
        TST     R6, #ActualState
        MOVEQ   PC, R14                         ; flash cursor is off anyway
        BIC     R6, R6, #ActualState
        STR     R6, [WsPtr, #CursorFlags]

; and drop thru to EORFlashCursor

; *****************************************************************************
;
;       EORCursor - Exclusive-OR cursor with screen
;
; in:   R0 = Start offset from top of cursor
;       R1 = End+1 offset
;       R2 = Screen address of appropriate cursor
;       R6 = CursorFlags
;
; out:  R5-R7 preserved; R0-R4, R8-R11 corrupted
;

EORFlashCursor
        ASSERT  CursorEndOffset-CursorStartOffset=4
        ADD     R0, WsPtr, #CursorStartOffset
        LDMIA   R0, {R0,R1}
EORCursor
        CMP     R0, R1
        MOVCS   PC, R14                 ; top >= bottom, so nowt

        ADD     R1, R1, R2

        LDR     R3, [WsPtr, #LineLength]

        ASSERT  CursorNbit = CursorFill +4
        ADD     R4, WsPtr, #CursorFill
        LDMIA   R4, {R4, PC}            ; load CursorFill and CursorNbit

Cursor1bit
        LDRB    R8, [R0, R2]!
        EOR     R8, R8, R4
        STRB    R8, [R0], R3
        TEQ     R0, R1
        MOVEQ   PC, R14
Cursor1loop
        LDRB    R8, [R0]
        EOR     R8, R8, R4
        STRB    R8, [R0], R3
        TEQ     R0, R1
        BNE     Cursor1loop
        MOV     PC, R14

Cursor2bit
        ADD     R0, R0, R2
Cursor2loop
        LDRB    R8, [R0, #1]
        EOR     R8, R8, R4
        STRB    R8, [R0, #1]
        LDRB    R8, [R0]
        EOR     R8, R8, R4
        STRB    R8, [R0], R3
        TEQ     R0, R1
        BNE     Cursor2loop
        MOV     PC, R14

CursorTeletext
        Push    "R0, R1, R3, R5, R14"
        LDR     R14, [WsPtr, #ScreenSize]
        ADD     R0, R0, R14, LSR #1             ; go to other screen
        ADD     R1, R1, R14, LSR #1
        LDR     R5, [WsPtr, #Log2BPC]
        ADRL    R14, CursorNbitTab
      [ HiResTTX
        ADD     R5, R5, #1
      ]
        LDR     R5, [R14, R5, LSL #2]
        ADD     R5, R5, R14
      [ NoARMv5
        MOV     R14, PC
        MOV     PC, R5
      |
        BLX     R5
      ]
        MOV     R8, R5
        Pull    "R0, R1, R3, R5, R14"
        MOV     PC, R8

Cursor4bit
        LDR     R8, [R0, R2]!
        EOR     R8, R8, R4
        STR     R8, [R0], R3
        TEQ     R0, R1
        MOVEQ   PC, R14
Cursor4loop
        LDR     R8, [R0]
        EOR     R8, R8, R4
        STR     R8, [R0], R3
        TEQ     R0, R1
        BNE     Cursor4loop
        MOV     PC, R14

Cursor8bit
        ADD     R0, R0, R2
Cursor8loop
        LDMIA   R0, {R8,R9}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        STMIA   R0, {R8,R9}
        ADD     R0, R0, R3
        TEQ     R0, R1
        BNE     Cursor8loop
        MOV     PC, R14

Cursor16bit
        ADD     R0, R0, R2
Cursor16loop
        LDMIA   R0, {R8-R11}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        EOR     R10, R10, R4
        EOR     R11, R11, R4
        STMIA   R0, {R8-R11}
        ADD     R0, R0, R3
        TEQ     R0, R1
        BNE     Cursor16loop
        MOV     PC, R14

Cursor32bit
        ADD     R0, R0, R2
        SUB     R3, R3, #32
Cursor32loop
        LDMIA   R0, {R8-R11}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        EOR     R10, R10, R4
        EOR     R11, R11, R4
        STMIA   R0!, {R8-R11}
        LDMIA   R0, {R8-R11}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        EOR     R10, R10, R4
        EOR     R11, R11, R4
        STMIA   R0!, {R8-R11}
        ADD     R0, R0, R3
        TEQ     R0, R1
        BNE     Cursor32loop
        MOV     PC, R14

Cursor64bit
        ADD     R0, R0, R2
        SUB     R3, R3, #64
Cursor64loop
        LDMIA   R0, {R8-R11}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        EOR     R10, R10, R4
        EOR     R11, R11, R4
        STMIA   R0!, {R8-R11}
        LDMIA   R0, {R8-R11}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        EOR     R10, R10, R4
        EOR     R11, R11, R4
        STMIA   R0!, {R8-R11}
        LDMIA   R0, {R8-R11}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        EOR     R10, R10, R4
        EOR     R11, R11, R4
        STMIA   R0!, {R8-R11}
        LDMIA   R0, {R8-R11}
        EOR     R8, R8, R4
        EOR     R9, R9, R4
        EOR     R10, R10, R4
        EOR     R11, R11, R4
        STMIA   R0!, {R8-R11}
        ADD     R0, R0, R3
        TEQ     R0, R1
        BNE     Cursor64loop
        MOV     PC, R14

; *****************************************************************************
;
;       PostWrchCursor - Put back cursors after Wrch
;
; out:  R6 = new CursorFlags, R5,R7 preserved, all others undefined
;

PostWrchCursor ROUT

        LDR     R6,[WsPtr, #SWP_Restore]
        TEQ     R6,#0
        BEQ     %FT10

        MOV     R6,R14
        BL      RestorePointer
        MOV     R14,R6
10

        LDR     R6, [WsPtr, #CursorFlags]
        LDR     R0, [WsPtr, #CursorStack]
        MOVS    R0, R0, LSL #1
        STR     R0, [WsPtr, #CursorStack]
        MOVCS   PC, R14                         ; we're still off, so do nowt

        Push    R14
        [       ForceMark
        LDR     R0, [WsPtr, #CursorCounter]
        TEQ     R0, #0                          ; are we flashing
        LDRNE   R0, [WsPtr, #CursorSpeed]       ; force to start of mark state
        STRNE   R0, [WsPtr, #CursorCounter]
        MOVNE   R0, #ActualState
        STRNE   R0, [WsPtr, #CursorDesiredState]
        ]

        [       RePlot
        LDR     R1, [WsPtr, #CursorDesiredState]
        EOR     R1, R1, R6              ; EOR of desired and actual
        ANDS    R1, R1, #ActualState    ; just get that bit
        BEQ     PWC10                   ; same, then go home

        EOR     R6, R1, R6              ; EOR actual bit

        TST     R6, #CursorsSplit
        LDRNE   R2, [WsPtr, #InputCursorAddr]
        LDREQ   R2, [WsPtr, #CursorAddr]
        BL      EORFlashCursor
PWC10
        ]

        BIC     R6, R6, #InWrchBit              ; coming out of wrch now
        TST     R6, #CursorsSplit
        STREQ   R6, [WsPtr, #CursorFlags]
        Pull    PC, EQ                          ; return if no output cursor

        LDR     R2, [WsPtr, #CursorAddr]
        LDR     R1, [WsPtr, #LineLength]
        MOV     R1, R1, LSL #3                  ; and end after 8 rows
        LDR     R0, [WsPtr, #ModeFlags]
        TST     R0, #ModeFlag_DoubleVertical    ; if double vertical
        ADDNE   R1, R1, R1                      ; end after 16 rows
        MOV     R0, #0                          ; start on top line
        BL      EORCursor
        STR     R6, [WsPtr, #CursorFlags]       ; only clear it now ?
        Pull    PC


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

VsyncCall ROUT
        Push    "R0-R11,R14"

        BL      PollPointer
        BL      UpdatePointer

        LDR     R6, [WsPtr, #CursorFlags]
        TST     R6, #TeletextMode
        BLNE    TeletextFlashTest               ; if TTX, do other stuff
VsyncReturn
        LDR     R1, [WsPtr, #CursorDesiredState]
        LDR     R0, [WsPtr, #CursorCounter]
        SUBS    R0, R0, #1
        LDREQ   R0, [WsPtr, #CursorSpeed]       ; if is zero, reload
        EOREQ   R1, R1, #ActualState            ; and toggle desired state
        STREQ   R1, [WsPtr, #CursorDesiredState]

        STRCS   R0, [WsPtr, #CursorCounter]     ; store back unless was frozen

        TST     R6, #InWrchBit
        Pull    "R0-R11,PC", NE         ; in wrch, so don't touch screen
                                        ; or modify CursorFlags

        EOR     R1, R1, R6              ; EOR of desired and actual
        ANDS    R1, R1, #ActualState    ; just get that bit
        Pull    "R0-R11,PC", EQ         ; same, then go home

        EOR     R6, R1, R6              ; EOR actual bit
        STR     R6, [WsPtr, #CursorFlags]

        TST     R6, #CursorsSplit
        LDRNE   R2, [WsPtr, #InputCursorAddr]
        LDREQ   R2, [WsPtr, #CursorAddr]
        BL      EORFlashCursor

        Pull    "R0-R11,PC"

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

TeletextFlashTest ROUT
        LDR     R3, [WsPtr, #TeletextCount]
        SUBS    R3, R3, #1
        BNE     %FT20                           ; count not expired

        LDR     R1, [WsPtr, #TeletextOffset]
        LDR     R0, [WsPtr, #ScreenSize]
        EORS    R1, R1, R0, LSR #1              ; switch to other flash bank
        STR     R1, [WsPtr, #TeletextOffset]
        MOVEQ   R3, #OnFlashTime
        MOVNE   R3, #OffFlashTime
        LDR     R0, [WsPtr, #DisplayStart]
        STR     R3, [WsPtr, #TeletextCount]
        B       SetVinit                        ; tail-call to VsyncReturn on exit
20
        STR     R3, [WsPtr, #TeletextCount]
        B       VsyncReturn

; *****************************************************************************
;
;       Vdu23_0_10 - Program cursor start, flash/steady, on/off
;

Vdu23_0_10
        LDRB    R0, [WsPtr, #QQ+2]      ; get parameter
ProgReg10AndCopy
        STR     R0, [WsPtr, #Reg10Copy]

; and drop thru to ...

ProgReg10
        AND     R1, R0, #&60
        CMP     R1, #&40
        BCS     IsFlashing
        MOV     R2, #0
        STR     R2, [WsPtr, #CursorCounter]       ; freeze the flashing
        TST     R1, #&20
        MOVEQ   R2, #ActualState                ; steady cursor
        STR     R2, [WsPtr, #CursorDesiredState]
        B       PR1010

IsFlashing
        TST     R1, #&20
        MOVEQ   R2, #FastCursorSpeed
        MOVNE   R2, #SlowCursorSpeed
        STR     R2, [WsPtr, #CursorSpeed]
        LDR     R2, [WsPtr, #CursorCounter]
        TEQ     R2, #0                          ; was flashing frozen ?
                                                ; if not, don't perturb flash
        MOVEQ   R2, #1                          ; set to flash immediately
        STREQ   R2, [WsPtr, #CursorCounter]

PR1010
        AND     R0, R0, #&1F                    ; get start position bits
        TST     R6, #TeletextMode               ; if teletext mode
        MOVNE   R0, R0, LSR #1                  ; then divide by 2
        LDR     R1, [WsPtr, #ModeFlags]
        TST     R1, #ModeFlag_DoubleVertical    ; if double vertical
        MOVNE   R0, R0, LSL #1                  ; then double cursor value

        LDR     R1, [WsPtr, #RowMult]
        CMP     R0, R1                          ; is it > rowmult ?
        MOVHI   R0, R1                          ; set to rowmult if so
        B       SetCursorTop


; *****************************************************************************
;
;       Vdu23_0_11 - Program cursor end
;

Vdu23_0_11
        LDRB    R0, [WsPtr, #QQ+2]      ; get parameter
        TST     R6, #TeletextMode       ; if teletext
        MOVNE   R0, R0, LSR #1          ; then divide by 2

        ADD     R0, R0, #1              ; get end line +1

        LDR     R1, [WsPtr, #ModeFlags]
        TST     R1, #ModeFlag_DoubleVertical ; if double vertical
        MOVNE   R0, R0, LSL #1               ; then double cursor value

        LDR     R1, [WsPtr, #RowMult]
        CMP     R0, R1
        MOVHI   R0, R1                  ; if > rowmult, set to rowmult
        B       SetCursorBottom

; *****************************************************************************
;
;       Vdu23_1 - Program cursor
;

Vdu23_1
        LDR     R0, [WsPtr, #CursorFlags]
        TST     R0, #Vdu5Bit
        MOVNE   PC, R14                 ; none of this nonsense in VDU5 mode

        LDRB    R1, [WsPtr, #QQ+1]      ; get parameter

CursorOnOff
        CMP     R1, #1
        MOVCC   R0, #&20                ; 0 -> just turn off
        LDRCS   R0, [WsPtr, #Reg10Copy] ; 1,2,3 -> read old state
        BLS     ProgReg10               ; 0,1 -> program hardware
        TEQ     R1, #2
        BICEQ   R0, R0, #&60            ; 2 -> steady
        ORRNE   R0, R0, #&60            ; 3 -> slow flashing
        STR     R0, [WsPtr, #Reg10Copy] ; save in copy
        B       ProgReg10

; *****************************************************************************
;
;       DoCursorEdit
;
; in:   R0 = &87 => COPY
;            &88 => cursor left
;            &89 => cursor right
;            &8A => cursor down
;            &8B => cursor up
;
; out:  C=0 => doing COPY and valid character
;       C=1    otherwise
;


DoCursorEdit
        LDR     R6, [WsPtr, #CursorFlags]
        TEQ     R0, #&87
        BNE     IsCursorMove

; COPY character under cursor

        Push    R14
        TST     R6, #CursorsSplit
        BEQ     BadCopyExit             ; cursors not split so don't copy

        TST     R6, #Vdu5Bit
        BNE     BadCopyExit             ; can't copy in VDU5 mode

        BL      ReadCharacter

        TEQ     R0, #0
        BEQ     BadCopyExit

        BL      VDUBE                   ; is a cursor movement valid ?
        BNE     DoCE10

        Push    R0
        BL      PreWrchCursor
        BL      InputCursorHT
        BL      PostWrchCursor
        Pull    R0

DoCE10
        CLC
        Pull    PC

BadCopyExit
        BL      BEL                     ; make bell sound
        SEC
        Pull    PC

; *****************************************************************************
;
;       VDUBE - Check if valid to move cursor
;
; out:  R0 preserved
;       R1 = 0 => OK, R1<>0 => OK
;       R6 = CursorFlags
;       Z => OK, NZ => not OK
;       C = 1

VDUBE
        LDR     R6, [WsPtr, #CursorFlags]
        LDROSB  R1, VDUqueueItems               ; zero if not buffering
        TEQ     R1, #0

        ANDEQS  R1, R6, #Vdu5Bit                ; zero if not in VDU 5 mode

; insert check for vdu disabled here

        CMP     R1, #0                          ; set Z on R1, C:=1
        MOV     PC, R14

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

IsCursorMove
        Push    R14
        BL      VDUBE
        Pull    PC, NE

        Push    R0
        BL      PreWrchCursor                   ; remove both cursors
        Pull    R0
        BL      ICM10
        BL      PostWrchCursor

        SEC
        Pull    PC

ICM10
        TST     R6, #CursorsSplit
        BNE     AlreadySplit

        Push    R0
        LDR     R0, [WsPtr, #Reg10Copy]
        AND     R0, R0, #&DF                    ; use double flash rate
        BSR     ProgReg10

        LDR     R0, [WsPtr, #CursorX]           ; copy parameters
        STR     R0, [WsPtr, #InputCursorX]
        LDR     R0, [WsPtr, #CursorY]
        STR     R0, [WsPtr, #InputCursorY]
        LDR     R0, [WsPtr, #CursorAddr]
        STR     R0, [WsPtr, #InputCursorAddr]

        ORR     R6, R6, #CursorsSplit
        STR     R6, [WsPtr, #CursorFlags]

        Pull    R0

AlreadySplit
        CMP     R0, #&89
        BCC     InputCursorLeft         ; &88
        BEQ     InputCursorRight        ; &89
        CMP     R0, #&8B
        BCC     InputCursorDown         ; &8A

; and drop thru to ...

InputCursorUp
        LDR     R1, [WsPtr, #InputCursorY]
        LDR     R2, [WsPtr, #InputCursorAddr]
        LDR     R3, [WsPtr, #RowLength]
        LDR     R4, [WsPtr, #TWTRow]

        SUB     R1, R1, #1
        SUB     R2, R2, R3
        CMP     R1, R4
        LDRLT   R1, [WsPtr, #TWBRow]
        STR     R1, [WsPtr, #InputCursorY]      ; need signed comparison
        STRGE   R2, [WsPtr, #InputCursorAddr]   ; in case Y went -ve
        MOVGE   PC, R14
        B       AddressInputCursor

InputCursorDown
        LDR     R1, [WsPtr, #InputCursorY]
        LDR     R2, [WsPtr, #InputCursorAddr]
        LDR     R3, [WsPtr, #RowLength]
        LDR     R4, [WsPtr, #TWBRow]

        ADD     R1, R1, #1
        ADD     R2, R2, R3
        CMP     R1, R4
        LDRHI   R1, [WsPtr, #TWTRow]
        STR     R1, [WsPtr, #InputCursorY]
        STRLS   R2, [WsPtr, #InputCursorAddr]
        MOVLS   PC, R14

; and drop thru to ...

AddressInputCursor
        Push    R14
        LDR     R0, [WsPtr, #InputCursorX]
        LDR     R1, [WsPtr, #InputCursorY]
        BL      AddressR0R1
        STR     R2, [WsPtr, #InputCursorAddr]
        Pull    PC

AddressCursors
        LDR     R6, [WsPtr, #CursorFlags]
        TST     R6, #CursorsSplit
        BEQ     AddressCursor
        BSR     AddressInputCursor
        B       AddressCursor


InputCursorLeft
        LDR     R0, [WsPtr, #InputCursorX]
        LDR     R2, [WsPtr, #InputCursorAddr]
        LDR     R3, [WsPtr, #CharWidth]
        LDR     R4, [WsPtr, #TWLCol]

        SUB     R0, R0, #1
        SUB     R2, R2, R3
        CMP     R0, R4
        LDRLT   R0, [WsPtr, #TWRCol]
        STR     R0, [WsPtr, #InputCursorX]
        STRGE   R2, [WsPtr, #InputCursorAddr]   ; I do mean GE !
        MOVGE   PC, R14

        BSR     AddressInputCursor
        B       InputCursorUp

InputCursorRight
        LDR     R0, [WsPtr, #InputCursorX]
        LDR     R2, [WsPtr, #InputCursorAddr]
        LDR     R3, [WsPtr, #CharWidth]
        LDR     R4, [WsPtr, #TWRCol]

        ADD     R0, R0, #1
        ADD     R2, R2, R3
        CMP     R0, R4
        LDRHI   R0, [WsPtr, #TWLCol]
        STR     R0, [WsPtr, #InputCursorX]
        STRLS   R2, [WsPtr, #InputCursorAddr]
        MOVLS   PC, R14

        BSR     AddressInputCursor
        B       InputCursorDown

; *****************************************************************************
;
;       InputCursorHT - move input cursor "right" after copying
;

InputCursorHT
        Push    R14
        LDR     R6, [WsPtr, #CursorFlags]
        BL      InputCursorMove
        BCC     ICHTExit

        BL      InputCursorB0
        BL      AddressInputCursor
        EOR     R6, R6, #8
        BL      InputCursorMove
        BLCS    InputCursorB0
ICHTExit
        Pull    R14
        B       AddressInputCursor

        LTORG

        END