;MonitorTypes to support
;
;0 - TV
;1 - Multiscan
;2 - Hi-res monochrome
;3 - VGA
;4 - Super-VGA
;5 - LCD (laptop only, for refernce only)
;6 - ?
;7 - Monitor description file

#set VIDC1_reference_clock = 24000	;stock clock speed of RO3.1 VIDC clock
#set VIDC1_addr            = &3400000	;base address of VIDC1

#set RPC_reference_clock   = 24000	;RiscPC VIDC20 clock rate
#set A7000_reference_clock = 32000	;A7000 VIDC20 clock rate
#set RCLK                  = 0	;0 = VCLK, 1 = HCLK, 2 = RCLK

#set IOMD_FSIZE            = &90	;FSIZE IOMD register
#set IOMD_ID0              = &94	;ID0 low IOMD register
#set IOMD_ID1              = &98	;ID0 high IOMD register

#set VIDC20_VPAR           = &10
#set VIDC20_BCR            = &40
#set VIDC20_CPLC1          = &50
#set VIDC20_HCR            = &80
#set VIDC20_HSWR           = &81
#set VIDC20_HBSR           = &82
#set VIDC20_HDSR           = &83
#set VIDC20_HDER           = &84
#set VIDC20_HBER           = &85
#set VIDC20_HCSR           = &86
#set VIDC20_HIR            = &87
#set VIDC20_VCR            = &90
#set VIDC20_VSWR           = &91
#set VIDC20_VBSR           = &92
#set VIDC20_VDSR           = &93
#set VIDC20_VDER           = &94
#set VIDC20_VBER           = &95
#set VIDC20_VCSR           = &96
#set VIDC20_VCER           = &97
#set VIDC20_SIR            = &A0
#set VIDC20_SFR            = &B0
#set VIDC20_SCR            = &B1
#set VIDC20_ER             = &C0
#set VIDC20_FSR            = &D0
#set VIDC20_CR             = &E0
#set VIDC20_DCTL           = &F0

#set DCR_SnA               = 1 << 12
#set DCR_Hdis              = 1 << 13
#set DCR_Bus31_0           = %01 << 16
#set DCR_Bus63_0           = %11 << 16

;Limits of VCO, /CK is used to go lower.  These values must use 8 or less bits
#set VCO_max         = 99328	;109568		;max VCO frequency in KHz
#set VCO_min         = 23808	;55040		;min VCO frequency in KHz

#set fpShift         = 14			;no. bits for floating point
#set fpFraction      = &FF << (fpShift - 8)	;significant bits of fraction


ALIGN   32, 4
.VIDC_adjustment_table			;these values are subtracted from
					;the VIDC1 translated values

					;VIDC TRM values
					;1bpp
  DCB 8 - 2				;HCR  correct from (N-2)/2 to (N-8)
  DCB 8 - 2				;HSWR correct from (N-2)/2 to (N-8)
  DCB 12 - 1   -1			;HBSR correct from (N-1)/2 to (N-12)
  DCB (18 - 19 -1) AND &FF		;HDSR correct from (N-19)/2 to (N-18)
  DCB (18 - 19 -1) AND &FF		;HDER correct from (N-19)/2 to (N-18)
  DCB 12 - 1   -1			;HBER correct from (N-1)/2 to (N-12)
#if compile_GPU == 1
  DCB 17 - 6   -1 - 6			;HCSR correct from (N-6) to (N-17)
#endif
#if compile_IOMD == 1
  DCB 17 - 6   -1			;HCSR correct from (N-6) to (N-17)
#endif
  DCB 0					;VCxR adjustment value

					;2bpp
  DCB 8 - 2				;HCR  correct from (N-2)/2 to (N-8)
  DCB 8 - 2				;HSWR correct from (N-2)/2 to (N-8)
  DCB 12 - 1  -1			;HBSR correct from (N-1)/2 to (N-12)
  DCB 18 - 11 -1			;HDSR correct from (N-11)/2 to (N-18)
  DCB 18 - 11 -1			;HDER correct from (N-11)/2 to (N-18)
  DCB 12 - 1  -1			;HBER correct from (N-1)/2 to (N-12)
#if compile_GPU == 1
  DCB 17 - 6 - 6			;HCSR correct from (N-6) to (N-17)
#endif
#if compile_IOMD == 1
  DCB 17 - 6				;HCSR correct from (N-6) to (N-17)
#endif
  DCB 0					;VCxR adjustment value

					;4bpp
  DCB 8 - 2				;HCR  correct from (N-2)/2 to (N-8)
  DCB 8 - 2				;HSWR correct from (N-2)/2 to (N-8)
  DCB 12 - 1 -1				;HBSR correct from (N-1)/2 to (N-12)
  DCB 18 - 7 -1				;HDSR correct from (N-7)/2 to (N-18)
  DCB 18 - 7 -1				;HDER correct from (N-7)/2 to (N-18)
  DCB 12 - 1 -1				;HBER correct from (N-1)/2 to (N-12)
#if compile_GPU == 1
  DCB 17 - 6 - 6			;HCSR correct from (N-6) to (N-17)
#endif
#if compile_IOMD == 1
  DCB 17 - 6				;HCSR correct from (N-6) to (N-17)
#endif
  DCB 8					;VCxR adjustment value

					;8bpp
  DCB 8 - 2				;HCR  correct from (N-2)/2 to (N-8)
  DCB 8 - 2				;HSWR correct from (N-2)/2 to (N-8)
  DCB 12 - 1 -1				;HBSR correct from (N-1)/2 to (N-12)
  DCB 18 - 5 -1				;HDSR correct from (N-5)/2 to (N-18)
  DCB 18 - 5 -1				;HDER correct from (N-5)/2 to (N-18)
  DCB 12 - 1 -1				;HBER correct from (N-1)/2 to (N-12)
#if compile_GPU == 1
  DCB 17 - 6 - 6			;HCSR correct from (N-6) to (N-17)
#endif
#if compile_IOMD == 1
  DCB 17 - 6				;HCSR correct from (N-6) to (N-17)
#endif
  DCB 0					;VCxR adjustment value



;VIDC1_abort
;----------
;An attempt was made to write to the VIDC1 memory space
;
;Entry:
; DAV_r0 - memory block, containing R0, R1 ... R14, PC
; R1 - aborting address
;
;Exit:
; R0-R12 corrupt

ALIGN   32, 4
.VIDC1_abort				;decode instruction and translate to
					;VIDC20
 ADR     R12, VARS
 ADR     R6, DAV_r0			;R6 = register stack
 #if compile_IOMD == 1
   LDR     R7, [R12, #VIDC20_addr - VARS]	;R7 = VIDC20 address
 #endif

 LDR     R0, DAV_abort			;the faulting address
 LDR     R0, [R0, #0]			;load the faulting instruction in R0
                 ;%100PUSWL		;STM instruction
 AND     R1, R0, #%11100001 << 20
 TEQ     R1,     #%10000000 << 20	;STMxx Rx, {Rn...Rn} ?
 BNE     _not_STM			;NO

					;R0 bits 15...0 contain registers
					;..written
 MOV     R5, R0, LSL #17		;reduce R5 to registers being written
 MOVS    R5, R5, LSR #18		;stripping off PC and leaving R0 in CS
 ._STM_loop
   LDRCS   R0, [R6]			;C set if register used
   BLCS    translate_VIDC1_to_VIDC20	;|+   perform the translation
   ADD     R6, R6, #4			;go to next register
   MOVS    R5, R5, LSR #1		;are there any more registers?
 BNE     _STM_loop			;loop until we've handled all registers
 BCS     _STM_loop			;catch the last register in C

B       AbortI_skip			;exit back to user code


 ._not_STM
                 ;%01IPUBWL		;STR instruction
 AND     R1, R0, #%11000001 << 20
 TEQ     R1,     #%01000000 << 20	;STR Rd, [Rn ...] ?
 BNE     AbortI_skip			;NO

					;R0 contains instruction to decode
 AND     R1, R0, #%1111 << 12		;R0 = R2 << 12

 LDR     R0, [R6, R1, LSR #12 - 2]	;R0 = value written to VIDC
 ADR     R14, AbortI_skip		;return address
B       translate_VIDC1_to_VIDC20	;perform the translation




;translate_VIDC1_to_VIDC20
;-------------------------
;translate a write to VIDC1 to the equivalant write to VIDC20
;
;Entry:
;R12 = pointer to VARS
; R7 = VIDC20 address (IOMD) if compiled for VIDC20, unused otherwise
; R0 = value being written to VIDC
;
; The following registers are free to use: R0-R4, R8-R12

.translate_VIDC1_to_VIDC20
 MOV     R1, R0, LSR #24		;R1 = VIDC command
 BIC     R0, R0, #&FF << 24		;R0 = VIDC value

 CMP     R1, #&40			;is it a palette command?
 BLO     _VIDC_palette			;YES

 CMP     R1, #&50			;is it border/cursor palette?
 BLO     _VIDC_border_cursor_palette	;YES

 CMP     R1, #&60			;check for reserved registers
 MOVLO   PC, R14			;and exit

 CMP     R1, #&80			;is it a stereo command?
 BLO     _VIDC_stereo			;YES

 CMP     R1, #&A0			;is it a horizontal command?
 BLO     _VIDC_horizontal		;YES

 CMP     R1, #&C0			;is it a vertical command?
 BLO     _VIDC_vertical			;YES
 BEQ     _VIDC_sound_frequency		;is it sound frequency?

 TEQ     R1, #&E0			;is it a control command?
 BEQ     _VIDC_control

MOV     PC, R14				;unknown command, exit




;translate palette commands
;--------------------------
;VIDC1-10
;VIDC20-17

._VIDC_palette
 AND     R1, R1, #%111100
 LDRB    R2, [R12, #current_mode_bitdepth - VARS]	;get current bitdepth
 TEQ     R2, #%11			;256 colour mode?
 BNE     _not_256_colour

 BIC     R0, R0, #%100011001000		;clear tint bits

 TST     R1, #%000100
 ORRNE   R0, R0, #%000000001000		;red L4
 TST     R1, #%001000
 ORRNE   R0, R0, #%000001000000		;green L5
 TST     R1, #%010000
 ORRNE   R0, R0, #%000010000000		;green L6
 TST     R1, #%100000
 ORRNE   R0, R0, #%100000000000		;blue L7

 ._not_256_colour
 BIC     R0, R0, #1 << 12		;clear supremacy bit
 AND     R8, R0, #%000000001111		;red
 AND     R3, R0, #%000011110000		;green
 ORR     R8, R8, R3, LSL #4

 AND     R3, R0, #%111100000000		;blue
 ORR     R8, R8, R3, LSL #8		;_B_G_R

#if compile_GPU == 1
 ORR     R0, R8, R8, LSL #4		;duplicate to higher bits
 MOV     R2, R14
 ADR     R8, GPU_palette
 BL      correct_gamma
 STR     R0, [R8, R1]			;store the palette entry
MOV     PC, R2
#endif
#if compile_IOMD == 1
 ORR     R8, R8, R8, LSL #4		;duplicate to higher bits
 MOV     R1, R1, LSR #2			;bits 2-5 hold the palette entry
 ORR     R1, R1, #VIDC20_VPAR << 24	;add VIDC20 command
 STMIA   R7, {R1, R8}			;write to VIDC20
MOV     PC, R14
#endif




;translate border and cursor colour commands
;-------------------------------------------
;VIDC1-11
;VIDC20-18

._VIDC_border_cursor_palette
 ANDS    R1, R1, #%1100			;mask logical colour
 #if compile_GPU == 1
   MOVEQ   PC, R14			;exit if border colour
 #endif

 AND     R2, R0, #%000000001111		;red

 AND     R3, R0, #%000011110000		;green
 ORR     R2, R2, R3, LSL #4

 AND     R3, R0, #%111100000000		;blue
 ORR     R2, R2, R3, LSL #8

 #if compile_GPU == 1
   ORR     R0, R2, R2, LSL #4		;duplicate to higher bits
   MOV     R2, R14
   ADR     R8, Cursor_palette
   BL      correct_gamma
   STR     R0, [R8, R1]			;store palette entry
  MOV     PC, R2
 #endif
 #if compile_IOMD == 1
   ORR     R2, R2, R2, LSL #4		;duplicate to higher bits
   MOV     R1, R1, LSL #26		;shift to bits 28-29
   ORR     R1, R1, #VIDC20_BCR << 24	;R1 = VIDC20 command
   ORR     R1, R1, R2			;add into VIDC20 command
   STR     R1, [R7]			;write to VIDC20
  MOV     PC, R14
 #endif




;translate stereo commands
;-------------------------
;VIDC1-12
;VIDC20-27

._VIDC_stereo
#if compile_IOMD == 1
 SUB     R1, R1, #&60			;reduce to 0+
 MOV     R1, R1, LSR #2			;divide by 4
 ADD     R1, R1, #VIDC20_SIR		;R1 = VIDC20 command

 AND     R0, R0, #%111			;mask down data
 ORR     R1, R0, R1, LSL #24

 STR     R1, [R7]			;write to VIDC20
#endif
MOV     PC, R14




;translate horizontal commands
;-----------------------------
;VIDC1-13
;VIDC20-18

._VIDC_horizontal
 SUB     R1, R1, #&80			;reduce to 0+
 MOV     R1, R1, LSR #2			;divide by 4

 MOV     R0, R0, LSR #13		;R0=value * 2
 TEQ     R1, #VIDC20_HIR - VIDC20_HCR	;HIR?
 BEQ     _VIDC_horizontal_P1		; |+ no adjustment required

 LDRB    R2, [R12, #current_mode_bitdepth - VARS]	;get current bitdepth
 ADR     R4, VIDC_adjustment_table	;correction table
 ADD     R4, R4, R2, LSL #3		;move to correct bpp values
 LDRB    R3, [R4, R1]			;R3 = correction value
 MOV     R3, R3, LSL #24		;sign extend value
 SUBS    R0, R0, R3, ASR #24		;R0=(N-x) converted from (N-x)/2
 MOVMI   R0, #0				;check it's not gone negative

 ._VIDC_horizontal_P1
 ADD     R1, R1, #VIDC20_HCR		;R1 = VIDC20 command

 #if update_Cursor_softcopy == 1 AND compile_RO == 400
   TEQ     R1, #VIDC20_HDSR		;HDSR...need to update
   MOVEQ   R2, #&1000			;..CursorFudgeFactor
   STREQ   R0, [R2, #&D4]		;..in page zero
 #endif
 #if update_Cursor_softcopy == 1 AND compile_RO > 400
   MOV     R4, R14
   TEQ     R1, #VIDC20_HDSR		;HDSR...need to update
   BLEQ    check_JIT_and_taskID
   BLEQ    task_page_zero_R2
   TEMP    R14
   STREQ   R0, [R2, #page_zero_CursorFudgeFactor]	;..emulate Page Zero access
   LOCK    R14
   MOV     R14, R4
 #endif

 ADD     R2, R12, #(vidc20_regs - (VIDC20_HCR << 2)) - VARS

 TEQ     R1, #VIDC20_HCSR		;HCSR
 BICNE   R0, R0, #%1			;everything else clears at least bit 0
 TEQ     R1, #VIDC20_HCR
 BICEQ   R0, R0, #%11			;HCR also clears bit 1

 LDR     R3, [R2, R1, LSL #2]
 TEQ     R0, R3				;did the value change?
 MOVEQ   PC, R14			;NO, exit
 STR     R0, [R2, R1, LSL #2]		;store a copy of the VIDC20 register

 #if compile_IOMD == 1
   ORR     R0, R0, R1, LSL #24		;add in the command
   STR     R0, [R7]			;write to VIDC20

   #if update_VIDC_softcopy == 1 AND compile_RO == 400
     TEQ     R1, #VIDC20_HSWR
     MOVEQ   R4, #&1400
     STREQ   R0, [R4, #&74]		;update HSWRSoftCopy
   #endif
 #endif
 #if update_VIDC_softcopy == 1 AND compile_GPU == 1
   MOV     R4, R14
   TEQ     R1, #VIDC20_HSWR		;HSWR...need to update
   BLEQ    check_JIT_and_taskID
   BLEQ    task_page_zero_R2
   TEMP    R14
   STREQ   R0, [R2, #page_zero_HSWRSoftCopy]	;..emulate Page Zero access
   LOCK    R14
   MOV     R14, R4
 #endif
 #if compile_GPU == 1 AND use_overlay == 1
   TEMP    R4
   CMP     R1, #VIDC20_HSWR
   CMPNE   R1, #VIDC20_HCSR
   STRLO   R13, HAL_vidc_list_dirty	;update GPU on next VSync
   LOCK    R4
 #endif

 TEQ     R1, #VIDC20_HDSR		;HDSR?
 TEQNE   R1, #VIDC20_HDER		;HDER?
 MOVNE   PC, R14			;NO, exit

 LDR     R3, [R2, #VIDC20_HDSR << 2]	;HDSR
 LDR     R4, [R2, #VIDC20_HDER << 2]	;HDER
 SUBS    R1, R4, R3
 MOVMI   PC, R14			;exit if negative

 TEMP    R0
 #if JIT_debug == 1 AND compile_IOMD == 1
   STR     R1, [R12, #VIDC20_width - VARS]
 #endif
 #if compile_GPU == 1
   LDR     R2, [R12, #VIDC20_width - VARS]
   TEQ     R1, R2			;has the value changed?
   STRNE   R1, [R12, #VIDC20_width - VARS]	;YES, update
   STRNE   R13, HAL_vidc_list_dirty	; |+ and update GPU on next VSync
 #endif
 LOCK    R0

 #if compile_IOMD == 1			;calculate DCTL
   LDRB    R4, [R12, #current_mode_bitdepth - VARS]	;get current bitdepth to correct values
   RSB     R4, R4, #5			;reverse and divide by 8

   LDR     R0, [R12, #vram_available - VARS]
   CMP     R0, #2048 * 1024		;check VRAM available
   ADDEQ   R4, R4, #1			;divide by 2 for dual port VRAM

   MOV     R2, #1
   MOV     R2, R2, LSL R4
   SUB     R2, R2, #1			;R2=bitmask for Hdis check
;ADD     R3, R12, #vidc20_regs - VARS - (VIDC20_HCR << 2)
;LDR     R3, [R3, #VIDC20_HDSR << 2]
;TST     R3, R2
;MOV     R1, R1, LSR R4
;ADDNE   R1, R1, #1
;MOV R1, #DCR_Hdis
   TST     R1, R2			;can we divide without losing pixels?
   MOVNE   R1, #DCR_Hdis		;NO, turn off Hdis
   MOVEQ   R1, R1, LSR R4		;YES, shift down

   CMP     R0, #2048 * 1024		;calculate BUS[1:0] and SnA
   ORRLT   R1, R1, #DCR_Bus31_0 + DCR_SnA	;DRAM or 1mb VRAM, 32bit Sync
   ORREQ   R1, R1, #DCR_Bus63_0 + DCR_SnA	;2mb VRAM, 64bit Sync
;   TEQ     R0, #0
;   BICEQ   R1, R1, #%11 << 18		;not required as no VRAM
;ORR R1, R1, #(%01<<18)+(%01<<16) + DCR_SnA

   ORR     R1, R1, #VIDC20_DCTL << 24	;add in the command
   STR     R1, [R7]			;write to VIDC20
 #endif
MOV     PC, R14




;translate vertical commands
;---------------------------
;VIDC1-17
;VIDC20-22

._VIDC_vertical
 SUB     R1, R1, #&A0			;reduce to 0+
 MOV     R1, R1, LSR #2			;divide by 4
 ADD     R1, R1, #VIDC20_VCR		;R1 = VIDC20 command

 MOV     R0, R0, LSR #14		;shift value to correct bits
 SUBS    R0, R0, #2 - 1			;VCR correct value from (N-1) to (N-2)
					;NOTE VIDC20 TRM is wrong stating (N-1)
 MOVMI   R0, #0				;went negative, set to 0

 #if compile_IOMD == 1
   MOV     R3, R0
   CMP     R1, #VIDC20_VBSR		;VBSR or above?
   LDRHSB  R2, [R12, #TV_offset - VARS]		;YES, adjust for *TV
   SUBHSS  R0, R0, R2			; |
   MOVMI   R0, R3			; |+ went negative, use original value
 #endif

 #if update_Cursor_softcopy == 1 AND compile_RO == 400
   TEQ     R1, #VIDC20_VDSR		;VDSR...need to update mouse
   MOVEQ   R2, #&1500			;..VertAdjust
   STREQ   R0, [R2, #&88]		;..in page zero
 #endif
 #if update_Cursor_softcopy == 1 AND compile_RO > 400
   MOV     R4, R14
   TEQ     R1, #VIDC20_VDSR		;VDSR...need to update
   BLEQ    check_JIT_and_taskID
   BLEQ    task_page_zero_R2
   TEMP    R14
   STREQ   R0, [R2, #page_zero_VertAdjust]	;..emulate Page Zero access
   LOCK    R14
   MOV     R14, R4
 #endif

; @@@ remove extra line in Pesky Muskrats and Boogie Buggy
; TEQ     R1, #VIDC20_VDER		;reduce VDER by 1
; TEQNE   R1, #VIDC20_VBER
; SUBEQ   R0, R0, #1			;..to fix screen height
; @@@

 ADD     R2, R12, #(vidc20_regs - (VIDC20_HCR << 2)) - VARS

 TEQ     R1, #VIDC20_VCSR		;VCSR?
 TEQNE   R1, #VIDC20_VCER		;or VCER?
 BNE     _VIDC_vertical_P1		;NO, correction not required

 LDRB    R3, [R12, #current_mode_bitdepth - VARS]	;get current bitdepth
 ADR     R4, VIDC_adjustment_table	;correction table
 ADD     R4, R4, R3, LSL #3		;move to correct bpp values
 LDRB    R4, [R4, #7]
 SUB     R0, R0, R4			;adjust VCxR for this bitdepth
 B       _VIDC_vertical_P2		;and skip change check

 ._VIDC_vertical_P1
 LDR     R3, [R2, R1, LSL #2]
 TEQ     R0, R3				;did the value change?
 MOVEQ   PC, R14			;NO, exit

 ._VIDC_vertical_P2
 STR     R0, [R2, R1, LSL #2]		;store a copy of the VIDC20 register

 #if compile_GPU == 1 AND use_overlay == 1
   TEMP    R4
   CMP     R1, #VIDC20_VSWR
   CMPNE   R1, #VIDC20_VCSR
   STRLO   R13, HAL_vidc_list_dirty	;update GPU on next VSync
   LOCK    R4
 #endif

 #if compile_GPU == 1
   TEQ     R1, #VIDC20_VCSR		;VCSR?
   TEQNE   R1, #VIDC20_VCER		;or VCER?
   BNE     _skip_GPU_cursor
   LDR     R4, [R2, #VIDC20_VCSR << 2]
   LDR     R3, [R2, #VIDC20_VCER << 2]
   SUBS    R3, R3, R4			;is there a cursor?
   MOVGT   R3, #1			;YES
   MOVLE   R3, #0			;NO
   STR     R3, [R12, #Cursor_state - VARS]
   ._skip_GPU_cursor
 #endif

 #if compile_IOMD == 1
   ORR     R0, R0, R1, LSL #24		;put command in and
   STR     R0, [R7]			;..write to VIDC20

   #if update_VIDC_softcopy == 1 AND compile_RO == 400
     TEQ     R1, #VIDC20_VSWR
     MOVEQ   R4, #&1400
     STREQ   R0, [R4, #&78]		;update OS softcopy
   #endif
 #endif

 TEQ     R1, #VIDC20_VDSR
 TEQNE   R1, #VIDC20_VDER
 MOVNE   PC, R14

 LDR     R0, [R2, #VIDC20_VDSR << 2]
 LDR     R1, [R2, #VIDC20_VDER << 2]
 SUBS    R1, R1, R0			;R1 = display height
 MOVMI   PC, R14

 TEMP    R0
 #if JIT_debug == 1 AND compile_IOMD == 1
   STR     R1, [R12, #VIDC20_height - VARS]
 #endif
 #if compile_GPU == 1
   LDR     R2, [R12, #VIDC20_height - VARS]
   TEQ     R1, R2			;has the value changed?
   STRNE   R1, [R12, #VIDC20_height - VARS]	;YES, update
   STRNE   R13, HAL_vidc_list_dirty	; |+ and update GPU on next VSync
 #endif
 LOCK    R0

 #if compile_IOMD == 1
   LDR     R4, [R2, #VIDC20_VCR << 2]
   CMP     R4, #0
   MOVMI   PC, R14			;exit if negative
   SUBS    R4, R4, R1			;R4 = flyback rasters
   SUBPLS  R4, R4, #15			;use a window to fix jurking
   BIC     R4, R4, #%1111		;..during vertical scrolling
   CMP     R4, #16
   MOVLT   R4, #16			;minimum of 16
   CMP     R4, #255			;if over 255
   MOVGT   R4, #255			;set to 255

   LDRB    R1, [R12, #IOMD_FSIZE_value - VARS]
   TEQ     R4, R1				;did the value change?
   LDRNE   R2, [R12, #IOMD_addr - VARS]		;YES
   STRNEB  R4, [R2, #IOMD_FSIZE]		; |  update IOMD FSIZE register
   STRNEB  R4, [R12, #IOMD_FSIZE_value - VARS]	; |+ and our copy
 #endif
MOV   PC, R14




;translate sound frequency command
;---------------------------------
;Tracks the sound period
;
;VIDC1-19
;VIDC20-27

._VIDC_sound_frequency
 #if Managed_ChannelHandler == 1
   AND     R0, R0, #&FF			;lower 8 bits = nS
   ADD     R2, R0, #1			;correct from (N-1) to N

   ADR     R1, hypervised_sound_configure
   LDR     R0, [R1, #sound_config_struct.VIDC1_period]
   TEQ     R0, #0			;have we cached a previous value?
   LDREQ   R0, [R1, #sound_config_struct.period]	;NO
   STREQ   R0, [R1, #sound_config_struct.VIDC1_period]	;cache it

   STR     R2, [R1, #sound_config_struct.period]	;and update current
 #endif
MOV     PC, R14




;translate control register command
;----------------------------------
;Sets both the Frequency Synthesizer Register and the Control Register
;
;VIDC1-20
;VIDC20-25 / 29

._VIDC_control
 ORR     R1, R0, #&E0 << 24
 TEMP    R3
 STR     R1, [R12, #VIDC1_CR - VARS]	;store new CR
 LOCK    R3
#if compile_GPU == 1
 MOV     PC, R14
#endif
#if compile_IOMD == 1
 MOV     R8, R14			;preserve R14

 BL      set_HV_sync			;set VIDC20 HSync / VSync

 LDR     R1, [R12, #VIDC1_clock - VARS]		;VIDC1 clock rate
 MOV     R1, R1, LSL #1			;shift so we can do /1.5
 ANDS    R3, R0, #%11			;pixel rate divisor
 MOVEQ   R4, #6				;8Mhz  (/3)
 MOVNE   R4, #4				;12MHz (/2)
 CMP     R3, #%10
 MOVEQ   R4, #3				;16MHz (/1.5)
 MOVHI   R4, #2				;24MHz (/1)

 TEMP    R2				;R3=R1 DIV R4
 oDIV    R3, R1, R4			;divide clock to get required pixel rate
 LOCK    R2
 LDR     R2, [R12, #VIDC20_clock - VARS]	;reference CLK on RPC

 AND     R4, R0, #%1100			;translate Bits per Pixel
 MOV     R4, R4, LSR #2
 STRB    R4, [R12, #current_mode_bitdepth - VARS]	;keep track of bitdepth

 BL      calc_VMOD_RMOD			;set VMOD, RMOD and FIFO
MOV     PC, R8



;calc_VMOD_RMOD
;--------------
;calculates VMOD, RMOD and CLK/n for a given MHz
;
;Input:
; R2 - reference clock in KHz
; R3 - pixel rate required in KHz
; current_mode_bitdepth - bits/pixel
;
;Exit:
; R2 - CK/n << 2 | FIFO << 8
; R3 - VMOD << 8 | RMOD
; R7 = VIDC20 address
; R1, R4, R9 - R11 corrupt

.calc_VMOD_RMOD
 MOV     R4, R3				;R4 = target MHz
 MOV     R3, R3, LSL #fpShift		;shift for accuracy
 TEMP    R7				;R9=R3 DIV R2
 oDIV    R9, R3, R2			;R9 = target/reference clock
 LOCK    R7
 CMP     R2, R4, LSR #1			;is the remainder > 0.5?
 ADDHI   R9, R9, #1			;YES, correct
 MOV     R10, #&4000 << 16		;check when delta below this value
 MOV     R2, R9				;R2 = divisor to add on each loop
 MOV     R7, #2				;R7 = RMOD
 ._L1
   MOV     R11, R4			;R11 (VCO) = target VCO
   ADD     R2, R2, R9			;R2 += divisor
   MOV     R1, R2			;R1 (VMOD << fpShift)

   CMP     R11, #VCO_min		;is VCO below min allowed?
   BLO     _try_CK_2			;YES, try next CK/?
   CMP     R11, #VCO_max		;is VCO above max allowed?
   BHI     _no_more_VCO			;YES, skip remaining CK/?'s
   CMP     R1, #64 << fpShift		;is VMOD > 64?
   BHS     _no_more_VCO			;YES, skip remaining CK/?'s

   ANDS    R3, R1, #fpFraction		;R3 = delta between VCO and target
   BEQ     _good_VCO1			;if 0, its an exact match
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(1 - 1) << 18			; |+  store CK/?

   ._try_CK_2
   ADD     R11, R11, R4			;R11 (VCO) = target VCO * 2
   ADD     R1, R1, R2			;R1 (VMOD << fpShift) = R2 * 2

   CMP     R11, #VCO_min		;is VCO below min allowed?
   BLO     _try_CK_3			;YES, try next CK/?
   CMP     R11, #VCO_max		;is VCO above max allowed?
   BHI     _bad_VCO			;YES, skip remaining CK/?'s
   CMP     R1, #64 << fpShift		;is VMOD > 64?
   BHS     _try_CK_3			;YES, skip remaining CK/?'s

   ANDS    R3, R1, #fpFraction		;R3 = delta between VCO and target
   BEQ     _good_VCO2			;if 0, its an exact match
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(2 - 1) << 18			; |+  store CK/?

   ._try_CK_3				;repeat for CK/3 to CK/8
   ADD     R11, R11, R4
   ADD     R1, R1, R2

   CMP     R11, #VCO_min
   BLO     _try_CK_4
   CMP     R11, #VCO_max
   BHI     _bad_VCO
   CMP     R1, #64 << fpShift
   BHS     _try_CK_4

   ANDS    R3, R1, #fpFraction
   BEQ     _good_VCO3
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(3 - 1) << 18			; |+  store CK/?

   ._try_CK_4
   ADD     R11, R11, R4
   ADD     R1, R1, R2

   CMP     R11, #VCO_min
   BLO     _try_CK_5
   CMP     R11, #VCO_max
   BHI     _bad_VCO
   CMP     R1, #64 << fpShift
   BHS     _try_CK_5

   ANDS    R3, R1, #fpFraction
   BEQ     _good_VCO4
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(4 - 1) << 18			; |+  store CK/?

   ._try_CK_5
   ADD     R11, R11, R4
   ADD     R1, R1, R2

   CMP     R11, #VCO_min
   BLO     _try_CK_6
   CMP     R11, #VCO_max
   BHI     _bad_VCO
   CMP     R1, #64 << fpShift
   BHS     _try_CK_6

   ANDS    R3, R1, #fpFraction
   BEQ     _good_VCO5
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(5 - 1) << 18			; |+  store CK/?

   ._try_CK_6
   ADD     R11, R11, R4
   ADD     R1, R1, R2

   CMP     R11, #VCO_min
   BLO     _try_CK_7
   CMP     R11, #VCO_max
   BHI     _bad_VCO
   CMP     R1, #64 << fpShift
   BHS     _try_CK_7

   ANDS    R3, R1, #fpFraction
   BEQ     _good_VCO6
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(6 - 1) << 18			; |+  store CK/?

   ._try_CK_7
   ADD     R11, R11, R4
   ADD     R1, R1, R2

   CMP     R11, #VCO_min
   BLO     _try_CK_8
   CMP     R11, #VCO_max
   BHI     _bad_VCO
   CMP     R1, #64 << fpShift
   BHS     _try_CK_8

   ANDS    R3, R1, #fpFraction
   BEQ     _good_VCO7
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(7 - 1) << 18			; |+  store CK/?

   ._try_CK_8
   ADD     R11, R11, R4
   ADD     R1, R1, R2

   CMP     R11, #VCO_min
   BLO     _bad_VCO
   CMP     R11, #VCO_max
   BHI     _bad_VCO
   CMP     R1, #64 << fpShift
   BHS     _bad_VCO

   ANDS    R3, R1, #fpFraction
   BEQ     _good_VCO8
   CMP     R3, R10, LSR #16 + (fpShift - 16)		;is delta better?
   ORRLO   R10, R7, R3, LSL #16 + (fpShift - 16)	;YES, store RMOD and delta
   ANDLO   R3, R1, #&FF << fpShift			; |
   ORRLO   R10, R10, R3, LSR #8 + (fpShift - 16)	; |   store VMOD
   ORRLO   R10, R10, #(8 - 1) << 18			; |+  store CK/?

   ._bad_VCO
   ADD     R7, R7, #1			;RMOD += 1
   TEQ     R7, #64			;is RMOD 64?
 BNE     _L1				;YES, no more to check

 ._no_more_VCO
 MOV     R2, R10, LSR #16
 EOR     R3, R10, R2, LSL #16		;R3 = VMOD/RMOD in correct bits for VIDC20
 AND     R2, R2, #%11100		;R2 = CK/? in correct bits for VIDC20
 B       _calc_FIFO


 ._good_VCO1
 MOV     R2, #(1 - 1) << 2		;(VMOD/RMOD * VCO) is exact match
 B       _good_VCO
 ._good_VCO2
 MOV     R2, #(2 - 1) << 2		;(VMOD/RMOD * VCO) / 2 is exact match
 B       _good_VCO
 ._good_VCO3
 MOV     R2, #(3 - 1) << 2		;(VMOD/RMOD * VCO) / 3 is exact match
 B       _good_VCO
 ._good_VCO4
 MOV     R2, #(4 - 1) << 2		;(VMOD/RMOD * VCO) / 4 is exact match
 B       _good_VCO
 ._good_VCO5
 MOV     R2, #(5 - 1) << 2		;(VMOD/RMOD * VCO) / 5 is exact match
 B       _good_VCO
 ._good_VCO6
 MOV     R2, #(6 - 1) << 2		;(VMOD/RMOD * VCO) / 6 is exact match
 B       _good_VCO
 ._good_VCO7
 MOV     R2, #(7 - 1) << 2		;(VMOD/RMOD * VCO) / 7 is exact match
 B       _good_VCO
 ._good_VCO8
 MOV     R2, #(8 - 1) << 2		;(VMOD/RMOD * VCO) / 8 is exact match

 ._good_VCO
 AND     R3, R1, #&FF << fpShift
 ORR     R3, R7, R3, LSR #fpShift - 8	;R3 = V/RMOD in correct bits for VIDC20


 ;Input:
 ; R2 - CK/n
 ; R3 - VMOD / RMOD
 ; R4 - pixelrate
 ; current_mode_bitdepth - bits/pixel

 ._calc_FIFO
 LDRB    R7, [R12, #current_mode_bitdepth - VARS]	;R7 = bits/pixel
 MOV     R11, R4, LSL R7		;R11 = peak memory bandwidth

 ADR     R9, _FIFOLoadTable		;FIFO calculation table

 LDR     R10, [R9, #4*4]
 CMP     R11, R10
 ORRHI   R2, R2, #%100 << 8		;FIFO 4
 ADDHI   R9, R9, #4*4

 LDR     R10, [R9, #2*4]
 CMP     R11, R10
 ORRHI   R2, R2, #%010 << 8		;FIFO 2 or 6
 ADDHI   R9, R9, #2*4

 LDR     R10, [R9, #1*4]
 CMP     R11, R10
 ORRHI   R2, R2, #%001 << 8		;FIFO 1, 3 or 7

 ORR     R2, R2, R7, LSL #5		;add in bits/pixel
 ORR     R2, R2, #(VIDC20_CR << 24) + RCLK	;insert VIDC command (conreg)

 SUB     R3, R3, #&101			;R3 = VMOD - 1 / RMOD - 1
 ORR     R3, R3, #VIDC20_FSR << 24	;insert VIDC command (fsynreg)

 LDR     R7, [R12, #VIDC20_addr - VARS]	;R7 = VIDC20 address
 STMIA   R7, {R2, R3}			;write to VIDC20

 #if update_VIDC_softcopy == 1 AND compile_RO == 400
   MOV     R4, #&1500
   STR     R2, [R4, #&84]		;update VIDCControlCopy
   MOV     R4, #&1300
   STR     R3, [R4, #&D4]		;update VIDC20_FSynRegCopy
   STR     R2, [R4, #&D8]		;update VIDC20_ConRegCopy
 #endif

MOV     PC, R14


._FIFOLoadTable
 DCD     0
 DCD     0
 DCD     0
 DCD     0
 DCD     0
 DCD     60000 << 3
 DCD     75000 << 3
 DCD     90000 << 3




;set_HV_sync
;-----------
;Sets VIDC20 HSync / VSync
;
;entry:
; R0 - VIDC1 CR
; R7 - VIDC20 address
;
;exit:
; R0 - preserved
; R1 - corrupt

.set_HV_sync
 MOV     R1, #(VIDC20_ER << 24) + 3	;VIDC20 command & ereg[1:0]=3
 ORR     R1, R1, #1 << 12		;set DAC's on

 TST     R0, #1 << 12			;check polarity VIDC1-20, VIDC20-24
 ORRNE   R1, R1, #1 << 18		;set nVSYNC
 TST     R0, #1 << 11
 ORRNE   R1, R1, #1 << 16		;set nHSYNC

 STR     R1, [R7]

 #if update_VIDC_softcopy == 1 AND compile_RO == 400
   MOV     R4, #&1300
   STR     R1, [R4, #&D0]		;update VIDC20_ERCopy
 #endif

MOV     PC, R14
#endif



;Entry:
;R0 - colour as &xxBBGGRR
;R12 - pointer to VARS

.correct_gamma
 STMFD   R13!, {R1-R2, R14}
 LDR     R1, [R12, #emulate_RISCOS - VARS]
 CMP     R1, #&350 << 8
 LDMHSFD R13!, {R1-R2, PC}

 ADR     R1, _gamma_table
 AND     R2, R0, #&F0			;red
 LDRB    R2, [R1, R2, LSR #4]
 BIC     R0, R0, #&FF
 ORR     R0, R0, R2
 ADD     R1, R1, #16
 AND     R2, R0, #&F000			;green
 LDRB    R2, [R1, R2, LSR #12]
 BIC     R0, R0, #&FF00
 ORR     R0, R0, R2, LSL #8
 ADD     R1, R1, #16
 AND     R2, R0, #&F00000		;blue
 LDRB    R2, [R1, R2, LSR #20]
 BIC     R0, R0, #&FF0000
 ORR     R0, R0, R2, LSL #16
LDMFD   R13!, {R1-R2, PC}

._gamma_table
DCB 0,0,0,0,21,43,64,85,106,128,149,170,191,213,234,255		;linear
DCB 0,0,0,0,21,43,64,85,106,128,149,170,191,213,234,255
DCB 0,0,0,0,21,43,64,85,106,128,149,170,191,213,234,255

;DCB 0,0,0,0,64,94,118,139,157,174,189,204,218,231,243,255		;R 1.8
;DCB 0,0,0,0,69,99,123,143,161,177,192,206,219,232,244,255		;G 1.9
;DCB 0,0,0,0,59,89,113,134,152,170,186,201,215,229,243,255		;B 1.7
;;DCB 0,0,0,0,49,77,101,123,142,161,178,195,211,226,241,255		;B 1.5
