view src/tft.asm @ 411:93b270b3503d

BUGFIX: Fix missing ccr setup menu (From 2.02)
author heinrichsweikamp
date Fri, 11 Mar 2016 10:11:07 +0100
parents fec5eec4c8b7
children 9500b2d3e32b
line wrap: on
line source

;=============================================================================
;
;   File tft.asm
;
;   Managing the TFT screen
;
;   Copyright (c) 2011, JD Gascuel, HeinrichsWeikamp, all right reserved.
;=============================================================================
; HISTORY
;  2011-05-24 : [jDG] Cleanups from initial Matthias code.

#include "hwos.inc"
#include "wait.inc"
#include "varargs.inc"
#include "external_flash.inc"
#include "tft_outputs.inc"
#include "eeprom_rs232.inc"

;=============================================================================
; TFT_frame needs to backup coordinates.
        CBLOCK  tmp
            save_top                
            save_height 
            save_left   
            save_width
        	ds_line                 ; Current line (0..239).
        	ds_column               ; Current columnx2 (0..159)
        	ds_pixel:2              ; Current pixel color.
        	ds_count                ; Repetition count.
        ENDC

;=============================================================================
; Basic bit-level macros

RD_H		macro
			bsf tft_rd,0
			endm

RD_L		macro
			bcf tft_rd,0
			endm

RS_H		macro
			bsf tft_rs,0
			endm

RS_L		macro
			bcf tft_rs,0
			endm

NCS_H		macro
			bsf tft_cs,0
			endm

NCS_L		macro
			bcf tft_cs,0
			endm

WR_H		macro
			bsf tft_nwr,0
			endm

WR_L		macro
			bcf tft_nwr,0
			endm

;=============================================================================
; Byte-leve macros
;
Index_out		macro low_b
				movlw low_b
				rcall TFT_CmdWrite
				endm

Parameter_out	macro high_b, low_b
				movlw high_b
				movwf PORTA		; Upper
				movlw low_b
				rcall TFT_DataWrite
				endm


basic   CODE
;
;
;;=============================================================================
;; TFT_write_flash_image
;;
;; Inputs:  FSR2 = EEPROM address / 256
;;          win_left, win_top : imagte CENTER position
;; Outputs: win_height, win_width.
;;          image copyed on screen.
;; Trashed: PROD, hi, lo
;;
;	global	TFT_write_flash_image
;TFT_write_flash_image:
;    ; Get back the full 24bit EEPROM address
;    clrf    ext_flash_address+0
;    movff   FSR2L,ext_flash_address+1
;    movf    FSR2H,W
;    iorlw   0x30
;    movwf   ext_flash_address+2
;
;    ; Read header: width and height
;    global  TFT_write_flash_image_addr
;TFT_write_flash_image_addr:
;	call	ext_flash_read_block_start
;	movff   SSP2BUF,win_width+0
;	movwf	SSP2BUF						; Write to buffer to initiate new read
;	btfss	SSP2STAT, BF                ; Next byte ready ?
;	bra		$-2                         ; NO: wait...
;   	movff   SSP2BUF,win_width+1
;	movwf	SSP2BUF						; Write to buffer to initiate new read
;	btfss	SSP2STAT, BF                ; Next byte ready ?
;	bra		$-2                         ; NO: wait...
;	movff   SSP2BUF,win_height
;	movwf	SSP2BUF						; Write to buffer to initiate new read
;	btfss	SSP2STAT, BF                ; Next byte ready ?
;	bra		$-2                         ; NO: wait...
;	movff   SSP2BUF,WREG                ; drop 4th byte.
;	movwf	SSP2BUF						; Write to buffer to initiate new read
;	btfss	SSP2STAT, BF                ; Next byte ready ?
;	bra		$-2                         ; NO: wait...
;
;    ; Sanity check on header to avoid badly uploaded images.
;    iorwf   WREG                        ; Check height < 256
;    bnz     TFT_write_flash_image_failed
;    movf    win_width+1,W               ; Check width < 512
;    andlw   0xFE
;    bnz     TFT_write_flash_image_failed
;
;    ; Center image on win_top, win_left values
;    bcf     STATUS,C                    ; Clear carry
;    rrcf    win_height,W                ; And get height/2
;    subwf   win_top,F                   ; top -= height/2
;    rrcf    win_width+1,W               ; Get 9th bit into carry
;    rrcf    win_width+0,W               ; Get width/2 (in 0..320 range)
;    bcf     STATUS,C
;    rrcf    WREG,W                      ; Get width/2 in 0..160 range
;    subwf   win_leftx2,F                ; left -= width/2
;
;	rcall	TFT_box_write		    	; Inputs : win_top, win_leftx2, win_height, win_width(in 1..320 range)
;
;    ; Compute number of pixels to move (result on 17 bits !)
;    clrf    TBLPTRU
;    movf    win_width+0,W
;    mulwf   win_height                  ; Result in PRODL:H
;    movf    win_width+1,W
;    bz      TFT_write_flash_image_1     ; width > 8bits ?
;    movf    win_height,W                ; YES: add extra
;    addwf   PRODH,F
;    rlcf    TBLPTRU                     ; And carry into upper register.
;TFT_write_flash_image_1:
;    incf    PRODH,F                     ; Pre-condition nested loops
;    incf    TBLPTRU,F
;
;    ; Write pixels
;	Index_out 0x22						; Frame Memory Data Write start
;	RS_H								; Data
;
;TFT_write_flash_image_loop:
;	btfss	SSP2STAT, BF				; Buffer full?
;	bra		$-2                         ; NO: wait...
;	movff	SSP2BUF,PORTH			    ; Read lo
;	movwf	SSP2BUF						; Write to buffer to initiate new read
;
;	btfss	SSP2STAT, BF				; Buffer full?
;	bra		$-2                         ; NO: wait...
;	movff	SSP2BUF,PORTA               ; And read hi
;	movwf	SSP2BUF						; Write to buffer to initiate new read
;	WR_L
;	WR_H								; Write 1 Pixel
;
;	decfsz	PRODL,F
;	bra		TFT_write_flash_image_loop
;	decfsz	PRODH,F
;	bra		TFT_write_flash_image_loop
;	decfsz	TBLPTRU,F
;	bra		TFT_write_flash_image_loop
;
;	btfss	SSP2STAT, BF				; Buffer full?
;	bra		$-2	                        ; No, wait
;	movf	SSP2BUF,W                   ; Read dummy byte
;
;	bsf		flash_ncs					; CS=1
;	movlw	0x00                        ; NOP, to stop window mode
;	bra     TFT_CmdWrite				; This routine "returns"
;
;	;---- Draw a 4x4 red square in place of missing images...
;TFT_write_flash_image_failed:
;    movlw   -1
;    addwf   win_leftx2,F
;    movlw   -2
;    addwf   win_top,F
;    movlw   2
;    movwf   win_width+0
;    clrf    win_width+1
;    movlw   4
;    movwf   win_height
;    movlw   color_red
;    rcall   TFT_set_color
;    goto    TFT_box
;
;;=============================================================================
;

    global  TFT_CmdWrite
TFT_CmdWrite:
	RS_L				; Command
	clrf	PORTA		; Upper
	movwf	PORTH		; Lower
	WR_L
	WR_H				; Tick
	return;

    global  TFT_DataWrite
TFT_DataWrite:
	RS_H				; Data
	movwf 	PORTH		; Lower
	WR_L
	WR_H				; Tick
	return

;=============================================================================
;
    global  TFT_ClearScreen
TFT_ClearScreen:
	Index_out 0x50				; Window Horizontal Start Address
	Parameter_out 0x00, 0x00	; 0-239
	Index_out 0x51				; Window Horizontal End Address
	Parameter_out 0x00, 0xEF	; 0-239
	Index_out 0x52				; Window Vertical Start Address
	Parameter_out 0x00, 0x00	; 0-319
	Index_out 0x53				; Window Vertical End Address
	Parameter_out 0x01, 0x3F	; 0-319
	Index_out 0x20				; Frame Memory Horizontal Address
	Parameter_out 0x00, 0x00	; 0-239
	Index_out 0x21				; Frame Memory Vertical Address
	Parameter_out 0x01, 0x3F	; 0-319

	Index_out 0x22				; Frame Memory Data Write start

	RD_H						; Not Read
	RS_H						; Data
	NCS_L						; Not CS
	clrf	PORTH				; Data Lower

	movlw	d'10'
	movwf	tft_temp3
TFT_ClearScreen2:
	movlw	d'30'
	movwf	tft_temp2
TFT_ClearScreen3:
	clrf	tft_temp1				; 30*10*256=76800 Pixels -> Clear complete 240*320
TFT_ClearScreen4:
	WR_L
	WR_H							; Tick
	decfsz	tft_temp1,F
	bra		TFT_ClearScreen4
	decfsz	tft_temp2,F
	bra		TFT_ClearScreen3
	decfsz	tft_temp3,F
	bra		TFT_ClearScreen2
	return

;=============================================================================
; 
    global  TFT_DisplayOff
TFT_DisplayOff:
	clrf	CCPR1L				; PWM OFF
	clrf	PORTA
    clrf	PORTH
	RD_L	; LOW
	nop
	RS_L	; LOW
	bcf		tft_nwr
	nop
	bcf		tft_cs
	nop
	bcf		tft_nreset
	WAITMS	d'1'
	bsf		tft_power				; inverted...
	bcf		lightsen_power  		; power-down light sensor
	return

; -----------------------------
; TFT boot
; -----------------------------
    global  TFT_boot
TFT_boot:
	clrf	PORTA
	clrf	PORTH
	RD_L	; LOW
	bcf		tft_nwr
	nop
	bcf		tft_cs
	nop
	bcf		tft_nreset
	WAITMS	d'1'
	bcf		tft_power				; inverted...
	WAITMS	d'1'

	RD_H	; Keep high
	WR_H	;
	NCS_L	; Not CS

	WAITMS	d'2'
	bsf		tft_nreset
	WAITMS	d'150'
	bsf		lightsen_power  		; Supply power to light sensor

; Data Transfer Synchronization
	Parameter_out 0x00, 0x00
	Parameter_out 0x00, 0x00

    Index_out 0x00
    rcall   TFT_CmdRead_PROD           ; Get ID into PRODL:PRODH
    ; 5:197 -> display0
    ;37:147 -> display1
    movlw   .5
    cpfseq  PRODL           ; display0?
    bra     TFT_boot_1      ; No
    movlw   .197
    cpfseq  PRODH           ; display0?
    bra     TFT_boot_1      ; No

; Init through config table...
    movlw   LOW     display0_config_table
    movwf   TBLPTRL
    movlw   HIGH    display0_config_table
    movwf   TBLPTRH
    movlw   UPPER   display0_config_table
    movwf   TBLPTRU
    bcf     screen_type
    bra     TFT_boot_com

TFT_boot_1:
; Init through config table...
    movlw   0x74
    movwf   TBLPTRL
    movlw   0xF7
    movwf   TBLPTRH
    movlw   0x01
    movwf   TBLPTRU
    bsf     screen_type

TFT_boot_com:
    rcall   display0_init_loop

    Index_out 0x03
    btfsc   flip_screen             ; 180° rotation ?
    bra     TFT_boot2               ; Yes

    btfss   screen_type             ; display1?
    bra     TFT_boot1a              ; no
    Parameter_out 0x10, 0x00        ; display1
    bra     TFT_boot3
TFT_boot1a:
    Parameter_out 0x50, 0x20        ; display0
    bra     TFT_boot3
TFT_boot2:
    btfss   screen_type             ; display1?
    bra     TFT_boot2a              ; no
    Parameter_out 0x10, 0x30        ; display1
    bra     TFT_boot3
TFT_boot2a:
    Parameter_out 0x50, 0x10        ; display0
TFT_boot3:
	Index_out 0x22
	rcall	TFT_ClearScreen
	Index_out 0x07
	Parameter_out 0x01, 0x33
	return

display0_config_table:
    ; Reg, Dat0, Dat1 or 0xFF,0x00,0x00 for end
    db  0xA4,0x00,0x01,0xFF,.002,0x00
    db  0x09,0x00,0x01,0x92,0x04,0x00
    db  0x93,0x04,0x02,0x94,0x00,0x02
    db  0x07,0x00,0x00,0x10,0x04,0x30
    db  0x11,0x02,0x37,0x12,0x11,0x8D
    db  0x13,0x11,0x00,0x01,0x01,0x00
    db  0x02,0x02,0x00,0x03,0x50,0x20
    db  0x0A,0x00,0x08,0x0D,0x00,0x00
    db  0x0E,0x00,0x30,0xFF,.151,0x00
    db  0x12,0x11,0xBD,0x20,0x00,0x00
    db  0x21,0x00,0x00,0x30,0x06,0x02
    db  0x31,0x56,0x0D,0x32,0x05,0x07
    db  0x33,0x06,0x09,0x34,0x00,0x00
    db  0x35,0x09,0x06,0x36,0x57,0x05
    db  0x37,0x0D,0x06,0x38,0x02,0x06
    db  0x39,0x00,0x00,0xFF,0x00,0x00

display0_init_loop:
    TBLRD*+
    movlw   0xFF
    cpfseq  TABLAT
    bra     display0_config_write    ; Write Config pair to Display
    ; Delay ms or quit (return)
    TBLRD*+
    tstfsz  TABLAT                  ; End of config?
    bra     $+4                     ; No
    return                          ; Done.
    movf    TABLAT,W
    call    WAITMSX                 ; Wait WREG milliseconds
    TBLRD*+                         ; Dummy read (Third byte of delay command)
    bra     display0_init_loop      ; Loop

display0_config_write:              ; With command in WREG
    movf    TABLAT,W
    rcall   TFT_CmdWrite            ; Write command
    TBLRD*+                         ; Get config0
    movff   TABLAT,PORTA
    TBLRD*+                         ; Get config1
    movf    TABLAT,W
    rcall   TFT_DataWrite           ; Write config
    bra     display0_init_loop      ; Loop


;=============================================================================
; Smooth lighting-up of the display:
;
; Trashes: WREG, PRODL
; Typical usage:
;    clrf    CCPR1L            ; Backlight off
;   [draw splash screen]
;   call     TFT_DisplayFadeIn
;
        global  TFT_Display_FadeIn
TFT_Display_FadeIn:
    	movlw	CCP1CON_VALUE			; See hwos.inc
        movwf	CCP1CON
		bsf		tft_is_dimming	; TFT is dimming, ignore ambient sensor!
        clrf    CCPR1L          ; Backlight off - to be sure
		movff	max_CCPR1L,PRODL
TFT_Display_FadeIn_0:
        incf    CCPR1L,F        ; Duty cycle
        WAITMS  d'2'
        decfsz  PRODL,F
        bra     TFT_Display_FadeIn_0 
		bcf		tft_is_dimming	; dimming done.
        return

;=============================================================================
; Smooth lighting-off of the display:
; Trashes: WREG, PRODL
        global  TFT_Display_FadeOut
TFT_Display_FadeOut:
		movff	max_CCPR1L,PRODL
		bsf		tft_is_dimming	; TFT is dimming, ignore ambient sensor!
TFT_Display_FadeOut_0:
        movff   PRODL,CCPR1L    ; Duty cycle
        WAITMS  d'1'
        decfsz  PRODL,F
        bra     TFT_Display_FadeOut_0 
        clrf    CCPR1L
        return

;=============================================================================

        global  box_std_block, box_black_block, box_color_block

box_std_block:                          ; Use white color
        setf    WREG
        bra     box_common
box_black_block:                        ; Use black color
        clrf    WREG
box_common:
box_color_block:
        rcall   TFT_set_color
        VARARGS_BEGIN
            VARARGS_GET8    win_top
            VARARGS_GET8    win_height
            VARARGS_GET8    win_leftx2
            VARARGS_GET8    win_width
        VARARGS_END
        bra    TFT_box

;-----------------------------------------------------------------------------

        global  box_frame_std, box_frame_common, box_frame_color, box_frame_color16

box_frame_std:
        setf    WREG
        rcall   TFT_set_color

box_frame_common:
        VARARGS_BEGIN
            VARARGS_GET8    win_top
            VARARGS_GET8    win_height
            VARARGS_GET8    win_leftx2
            VARARGS_GET8    win_width
        VARARGS_END
        bra    TFT_frame

box_frame_color:
      	rcall	TFT_set_color
box_frame_color16:
		bra		box_frame_common

;=============================================================================
; Init for half_pixel_write
; Set column register on TFT device, and current color.
; Inputs: win_leftx2
; Outputs: win_color:2
; Trashed: WREG, PROD
        global init_pixel_write
init_pixel_write:
        movff   win_leftx2,WREG
        mullw   2
        rcall   pixel_write_col320      ; Start Address Vertical (.0 - .319)
        setf    WREG
        bra     TFT_set_color

;-----------------------------------------------------------------------------
; Writes two half-pixels at position (win_top,win_leftx2)
; Inputs: win_leftx2, win_top, win_color:2
; Trashed: WREG, PROD
    global  pixel_write
pixel_write:
        movff   win_leftx2,WREG
        mullw   2						; win_leftx2 x 2 -> PRODH:PRODL
        rcall   pixel_write_col320      ; Start Address Vertical (.0 - .319)
        rcall   half_pixel_write        ; Write this half-one.

        movff   win_leftx2,WREG         ; Address of next one
        mullw   2
        infsnz  PRODL                   ; +1
        incf    PRODH
    	rcall   pixel_write_col320
    	bra     half_pixel_write        ; Note: Cmd 0x20 is mandatory, because
    	                                ; of the autoincrement going vertical

	global	pixel_write_col320
pixel_write_col320:
        btfsc   screen_type             ; display1?
        bra     pixel_write_col320_d1   ; Yes
        ; Display0
    	btfss  flip_screen              ; 180° rotation?
        bra    pixel_write_noflip_H     ; No
        bra    pixel_write_flip_H       ; Yes
pixel_write_col320_d1:                  ; Display1
    	btfsc  flip_screen              ; 180° rotation?
        bra    pixel_write_noflip_H     ; Yes for d1
pixel_write_flip_H: ; Flip d0
        movf    PRODL,W                 ; 16bits 319 - PROD --> PROD
        sublw   LOW(.319)               ; 319-W --> W
        movwf   PRODL
        movf    PRODH,W
        btfss   STATUS,C                ; Borrow = /CARRY
        incf    WREG
        sublw   HIGH(.319)
        movwf   PRODH

pixel_write_noflip_H:
		Index_out 0x21					; Frame Memory Vertical Address
		bra     TFT_DataWrite_PROD      ; and return...

;-----------------------------------------------------------------------------
; Writes one half-pixel at position (win_top,win_leftx2).
; Inputs: win_leftx2, win_top, win_color:2
; Trashed: WREG, PROD
        global  half_pixel_write
half_pixel_write:
    	movff  	win_top,WREG            ; d'0' ... d'239'
    ; Variant with Y position in WREG.
half_pixel_write_1:
    	btfss   flip_screen             ; 180° rotation?
    	sublw   .239                    ; 239-Y --> Y
        mullw   1                       ; Copy row to PRODL (PRODH=0)
		Index_out 0x20                  ; Frame Memory Horizontal Address
		rcall   TFT_DataWrite_PROD

		Index_out 0x22                  ; Frame Memory Data Write start
		RS_H				; Data
		movff	win_color1,PORTA		; Upper
		movff	win_color2,PORTH		; Lower
		WR_L
		WR_H				; Tick
    	return

;-----------------------------------------------------------------------------
; Writes a vertical line of half-pixel at position (win_top,win_leftx2,win_height).
; Inputs: win_leftx2, win_top, win_height, win_color:2
; Trashed: WREG, PROD, TABLAT, TBLPTRL
	global	half_vertical_line
half_vertical_line:
        clrf    TABLAT                  ; Loop index.

half_vertical_line_loop:
        movff   win_leftx2,WREG         ; Init X position.
        mullw   2
        movf    TABLAT,W                ; Get loop index
        andlw   1                       ; Just low bit
        xorwf   PRODL,F                 ; And use it to jitter current X position
        rcall   pixel_write_col320      ; Start Address Vertical (.0 - .319)

        movff   win_height,WREG         ; Index reached height (Bank0 read) ?
        xorwf   TABLAT,W
        btfsc   STATUS,Z                ; Equals ?
        return                          ; Yes: done.
        movff   win_top,WREG            ; Y = top + index (Bank0 read)
        addwf   TABLAT,W
        rcall   half_pixel_write_1
        incf    TABLAT,F                ; index++
        bra     half_vertical_line_loop

;-----------------------------------------------------------------------------
; Writes a horizontal line of half-pixel at position (win_top,win_leftx2,win_width).
; Inputs: win_leftx2, win_top, win_width, win_color:2
; Trashed: WREG, PROD, TABLAT, TBLPTRL
	global	half_horizontal_line
half_horizontal_line:
        clrf    TABLAT                  ; Loop index.

half_horizontal_line_loop:
        movff   win_leftx2,WREG         ; Init X position.
        mullw   2
        rcall   pixel_write_col320      ; Start Address Vertical (.0 - .319)
        movff   win_width,WREG          ; Index reached height (Bank0 read) ?
        xorwf   TABLAT,W
        btfsc   STATUS,Z                ; Equals ?
        return                          ; Yes: done.
        movff   win_top,WREG            ; Y = top + index (Bank0 read)
        addwf   TABLAT,W
        rcall   half_pixel_write_1
        incf    TABLAT,F                ; index++
        bra     half_horizontal_line_loop


;-----------------------------------------------------------------------------
; TFT Data Cmd via W
;
    global  TFT_DataWrite_PROD
TFT_DataWrite_PROD:
;	RD_H				; Keep high
	RS_H				; Data
	movff	PRODH,PORTA	; Move high byte to PORTA
    movff	PRODL,PORTH	; Move low byte to PORTH
	WR_L
	WR_H                ; Tick
	return

TFT_DataRead_PROD:
    Index_out 0x22                  ; Frame Memory Data Read start
TFT_CmdRead_PROD:
    setf    TRISA                   ; PortA as input.
    setf    TRISH                   ; PortH as input.
	RS_H				; Data
;	WR_H                ; Not write
	RD_L                ; Read!
    nop
    nop
    nop
	RD_H				; Tick
    nop
    nop
    nop
	RD_L                ; Read!
    nop
 ;   nop
 ;   nop
    movff   PORTA,PRODH
    movff   PORTH,PRODL
	RD_H				; Tick
    nop
    clrf    TRISA                   ; PortA as output
    clrf    TRISH                   ; PortH as output
	return

;=============================================================================
; Output TFT Window Address commands.
; Inputs : win_top, win_leftx2, win_height, win_width.
; Output : PortA/PortH commands.
; Trashed: PROD
;
        global  TFT_box_write
TFT_box_write:
		movff	win_leftx2,WREG         ; Compute left = 2*leftx2 --> PROD
		mullw	2

        global  TFT_box_write_16bit_win_left
TFT_box_write_16bit_win_left:           ; With column in PRODL:PRODH
        btfsc   screen_type             ; display1?
        bra     TFT_box_write_16bit_win_left_d1 ; Yes
        ; Display0
    	btfsc  flip_screen              ; 180° rotation?
    	bra    DISP_box_flip_H          ; Yes
        bra     TFT_box_write_16bit_win_left_com    ; No
TFT_box_write_16bit_win_left_d1:        ; Display1
    	btfss  flip_screen              ; 180° rotation?
    	bra    DISP_box_flip_H          ; No for d1
        ; Yes for d1
TFT_box_write_16bit_win_left_com:
        ;---- Normal horizontal window ---------------------------------------
		Index_out 0x52				; Window Vertical Start Address
		rcall   TFT_DataWrite_PROD          ; Output left
		Index_out 0x21				; Frame Memory Vertical Address
		rcall   TFT_DataWrite_PROD			; Output left

		movff	win_width+0,WREG	    ; right = left + width - 1
		addwf	PRODL,F
		movff	win_width+1,WREG
		addwfc	PRODH,F
		decf	PRODL,F			    ; decrement result
		btfss   STATUS,C
		decf	PRODH,F

		Index_out 0x53				; Window Vertical End Address
		rcall   TFT_DataWrite_PROD
		bra     DISP_box_noflip_H

        ;---- Flipped horizontal window --------------------------------------
DISP_box_flip_H:
        movf    PRODL,W                 ; 16bits 319 - PROD --> PROD
        sublw   LOW(.319)               ; 319-W --> W
        movwf   PRODL
        movf    PRODH,W
        btfss   STATUS,C                ; Borrow = /CARRY
        incf    WREG
        sublw   HIGH(.319)
        movwf   PRODH

  		Index_out 0x53				; Window Vertical Start Address
		rcall   TFT_DataWrite_PROD  ; Output left
		Index_out 0x21				; Frame Memory Vertical Address
		rcall   TFT_DataWrite_PROD	; Output left

        movff   win_width+0,WREG        ; 16bits PROD - width --> PROD
        subwf   PRODL,F                 ; PRODL - WREG --> PRODL
        movff   win_width+1,WREG
        subwfb  PRODH,F
        infsnz  PRODL                   ; PROD+1 --> PROD
        incf    PRODH

		Index_out 0x52				; Window Vertical End Address
		rcall   TFT_DataWrite_PROD

DISP_box_noflip_H:
    	btfss   flip_screen             ; 180° rotation ?
    	bra     TFT_box_noflip_V        ; No.

   ;---- Flipped vertical window -----------------------------------------
		movff	win_top,PRODH           ; top --> PRODH (first byte)
		movff   win_height,WREG
		addwf   PRODH,W
        decf	WREG
		movwf	PRODL                   ; top+height-1 --> PRODL (second byte)

		Index_out 0x50				; Window Horizontal Start Address
		movf	PRODH,W
		rcall	TFT_DataWrite		; Lower (and tick)

		Index_out 0x51				; Window Horizontal End Address
		movf	PRODL,W
		rcall	TFT_DataWrite		; Lower (and tick)

		Index_out 0x20				; Frame Memory Horizontal Address
		movf	PRODH,W
		rcall	TFT_DataWrite		; Lower (and tick)
		return


TFT_box_noflip_V:
        ;---- Normal vertical window ----------------------------------------
		movff   win_top,PRODL
		movff   win_height,WREG
		addwf   PRODL,W
		sublw   .240                 ; 240 - top - height
		movwf   PRODH                ; First byte

		movf	PRODL,W
		sublw   .239                ; 239-top
		movwf   PRODL               ; --> second byte.

		Index_out 0x50				; Window Horizontal Start Address
		movf	PRODH,W
		rcall	TFT_DataWrite		; Lower (and tick)

		Index_out 0x51				; Window Horizontal End Address
		movf	PRODL,W
		rcall	TFT_DataWrite		; Lower (and tick)

		Index_out 0x20				; Frame Memory Horizontal Address
		movf	PRODL,W
		rcall	TFT_DataWrite		; Lower (and tick)
		return


;=============================================================================
; TFT_frame : draw a frame around current box with current color.
; Inputs:  win_top, win_leftx2, win_height, win_width, win_color1, win_color2
; Outputs: (none)
; Trashed: WREG, PROD, aa_start:2, aa_end:2
    global  TFT_frame
TFT_frame:
    movff   win_top,save_top            ; Backup everything.
    movff   win_height,save_height
    movff   win_leftx2,save_left
    movff   win_width,save_width

    ;---- TOP line -----------------------------------------------------------
    movlw   1                           ; row ~ height=1
    movff   WREG,win_height
    rcall   TFT_box

    ;---- BOTTOM line --------------------------------------------------------
    movff   save_top,PRODL               ; Get back top,
    movff   save_height,WREG             ; and height
    addwf   PRODL,W                      ; top+height
    decf    WREG                         ; top+height-1
    movff   WREG,win_top                 ; top+height-1 --> top
    rcall   TFT_box                        

    ;---- LEFT column --------------------------------------------------------
    movff   save_top,win_top             ; Restore top/height.
    movff   save_height,win_height
    movlw   1                               ; column ~ width=1
    movff   WREG,win_width
    rcall   TFT_box

    ;---- RIGHT column -------------------------------------------------------
    movff   save_left,WREG
    movff   save_width,PRODL
    addwf   PRODL,W
    decf    WREG
    movff   WREG,win_leftx2
    rcall     TFT_box
    
    ;---- Restore everything -------------------------------------------------
    movff   save_left,win_leftx2
    movff   save_width,win_width
    return

;=============================================================================
; TFT_box : fills current box with current color.
; Inputs:  win_top, win_leftx2, win_height, win_width, win_color1, win_color2
; Outputs: (none)
; Trashed: WREG, PROD
    global  TFT_box

TFT_box:
    ;---- Define Window ------------------------------------------------------
	movf	win_width,W
	bcf     STATUS,C
	rlcf    WREG
	movwf   win_width+0
	movlw   0
	rlcf    WREG
	movwf   win_width+1
	rcall   TFT_box_write               ; Setup box

    global  TFT_box_16bit_win_left
TFT_box_16bit_win_left:
    rrcf    win_width+1,W               ; width /= 2
    rrcf    win_width+0,W
    movwf   win_width

    ;---- Fill Window --------------------------------------------------------
	Index_out 0x22						; Frame Memory Data Write start

	clrf	PRODH                       ; Column counter.
	RS_H				; Data

TFT_box2:                              ; Loop height times
	movff	win_height,PRODL

TFT_box3:                              ; loop width times
	movff	win_color1,PORTA			; Upper
	movff	win_color2,PORTH			; Lower
	WR_L
	WR_H				; Tick
;
;	movff	win_color1,PORTA			; Upper
;	movff	win_color2,PORTH			; Lower
	WR_L
	WR_H				; Tick

	decfsz	PRODL,F                     ; row loop finished ?
	bra		TFT_box3                   ; No: continue.

    incf    PRODH,F                     ; column count ++

    movff   win_bargraph,WREG           ; current column == bargraph ?
    cpfseq  PRODH
    bra     TFT_box4                   ; No: just loop.

    clrf    WREG                        ; Yes: switch to black
    movff   WREG,win_color1
    movff   WREG,win_color2
TFT_box4:

    movff   win_width+0,WREG            ; compare ?
    xorwf   PRODH,W
    bnz     TFT_box2                    ; Loop not finished.

	movlw	0x00                        ; NOP, to stop window mode
	rcall   TFT_CmdWrite

	setf    WREG                        ; Reset bargraph mode...
	movff   WREG,win_bargraph

	return

;=============================================================================
;Converts 8Bit RGB b'RRRGGGBB' into 16Bit RGB b'RRRRRGGGGGGBBBBB'
    global  TFT_set_color

TFT_set_color:
	movwf	tft_temp1				; Get 8Bit RGB b'RRRGGGBB'
	movwf   tft_temp2               ; Copy

	; Mask Bit 7,6,5,4,3,2
	movlw	b'00000011'
	andwf	tft_temp2,F

	movlw	b'00000000'
	dcfsnz	tft_temp2,F
	movlw	b'01010000'
	dcfsnz	tft_temp2,F
	movlw	b'10100000'
	dcfsnz	tft_temp2,F
	movlw	b'11111000'
	movwf	tft_temp3				; Blue done.

	movff	tft_temp1,	tft_temp2	; Copy
	; Mask Bit 7,6,5,1,0
	movlw	b'00011100'
	andwf	tft_temp2,F
	rrncf	tft_temp2,F
	rrncf	tft_temp2,F

	movlw	b'00000000'
	dcfsnz	tft_temp2,F
	movlw	b'00000100'
	dcfsnz	tft_temp2,F
	movlw	b'00001000'
	dcfsnz	tft_temp2,F
	movlw	b'00001100'
	dcfsnz	tft_temp2,F
	movlw	b'00010000'
	dcfsnz	tft_temp2,F
	movlw	b'00010100'
	dcfsnz	tft_temp2,F
	movlw	b'00100000'
	dcfsnz	tft_temp2,F
	movlw	b'00111111'
	movwf	tft_temp4			

	rrcf	tft_temp4,F
	rrcf	tft_temp3,F

	rrcf	tft_temp4,F
	rrcf	tft_temp3,F

	rrcf	tft_temp4,F
	rrcf	tft_temp3,F		; tft_temp3 (b'GGGBBBBB') done.

	movff	tft_temp1,	tft_temp2	; Copy
	clrf	tft_temp1

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F		; Green done.

	; Mask Bit 4,3,2,1,0
	movlw	b'11100000'
	andwf	tft_temp2,F

	rrncf	tft_temp2,F
	rrncf	tft_temp2,F
	rrncf	tft_temp2,F
	rrncf	tft_temp2,F
	rrncf	tft_temp2,F

	movlw	b'00000000'
	dcfsnz	tft_temp2,F
	movlw	b'00000100'
	dcfsnz	tft_temp2,F
	movlw	b'00001000'
	dcfsnz	tft_temp2,F
	movlw	b'00001100'
	dcfsnz	tft_temp2,F
	movlw	b'00010000'
	dcfsnz	tft_temp2,F
	movlw	b'00010100'
	dcfsnz	tft_temp2,F
	movlw	b'00100000'
	dcfsnz	tft_temp2,F
	movlw	b'00111111'
	movwf	tft_temp4			

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F	

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F

	rrcf	tft_temp4,F
	rrcf	tft_temp1,F		; Red done.

	movff	tft_temp1,win_color1
	movff	tft_temp3,win_color2	; Set Bank0 Color registers...
	return

;=============================================================================
; Dump screen contents to the UART

    global  TFT_dump_screen
TFT_dump_screen:
    bsf         no_sensor_int
	movlw	    'l'
	movwf	    TXREG                   ; Send command echo.
    call		rs232_wait_tx           ; wait for UART
    ;---- Send DISPLAY box command for the full screen window -------------------
	Index_out 0x50				; Window Horizontal Start Address
	Parameter_out 0x00, 0x00	; 0-239
	Index_out 0x51				; Window Horizontal End Address
	Parameter_out 0x00, 0xEF	; 0-239
	Index_out 0x52				; Window Vertical Start Address
	Parameter_out 0x00, 0x00	; 0-319
	Index_out 0x53				; Window Vertical End Address
	Parameter_out 0x01, 0x3F	; 0-319

    clrf        ds_column
    rcall       dump_screen_pixel_reset
dump_screen_1:
    btg         LEDr                 ; LED activity toggle
    ; Dump even column
	movlw	    .240                 ; 240 lines, once.
	movwf	    ds_line
dump_screen_2:
	Index_out   0x20				; Frame Memory Horizontal Address
    movff       ds_line,WREG        ; d'0' ... d'239'
   	mullw       1                   ; Copy row to PRODH:L
	rcall       TFT_DataWrite_PROD

    movff       ds_column,WREG     ; Init X position.
    mullw       2
    rcall       pixel_write_col320  ; Start Address Vertical (.0 - .319)

    rcall       TFT_DataRead_PROD      ; read pixel
    rcall       dump_screen_pixel

    decfsz	    ds_line,F
    bra		    dump_screen_2
    rcall       dump_screen_pixel_flush

    ; Dump odd column
	movlw	    .240                    ; 240 lines, twice.
	movwf	    ds_line
dump_screen_3:
	Index_out   0x20				; Frame Memory Horizontal Address
    movff   	ds_line,WREG        ; d'0' ... d'239'
   	mullw       1                   ; Copy row to PRODH:L
	rcall       TFT_DataWrite_PROD

    movff       ds_column,WREG     ; Init X position.
    mullw       2
    movlw       .1
    addwf       PRODL,F
    movlw       0
    addwfc      PRODH,F             ; +1
    rcall       pixel_write_col320  ; Start Address Vertical (.0 - .319)

    rcall       TFT_DataRead_PROD      ; read pixel
    rcall       dump_screen_pixel

    decfsz	    ds_line,F
    bra		    dump_screen_3
    rcall       dump_screen_pixel_flush

    incf        ds_column,F
    movlw       .160
    cpfseq      ds_column
    bra		    dump_screen_1

    bcf         no_sensor_int
    clrf        RCREG1              ; Clear receive buffer
	bcf         RCSTA1,CREN         ; Clear receiver status
	bsf         RCSTA1,CREN
    bsf         enable_screen_dumps ; =1: Ignore vin_usb, wait for "l" command (Screen dump)
    return


;=============================================================================
; Pixel compression
;
; Input: PRODH:L = pixel.
; Output: Compressed stream on output.
; Compressed format:
;       0ccccccc    : BLACK pixel, repeated ccccccc+1 times (1..128).
;       11cccccc    : WHITE pixel, repeated cccccc+1 times (1..64).
;       10cccccc HIGH LOW : color pixel (H:L) repeated ccccc+1 times (1..64).
;
dump_screen_pixel:
    movf        PRODH,W                 ; Compare pixel-high
    xorwf       ds_pixel+1,W
    bnz         dump_screen_pixel_1     ; Different -> dump.

    movf        PRODL,W                 ; Compare pixel-low
    xorwf       ds_pixel+0,W
    bnz         dump_screen_pixel_1     ; Different -> dump.

    incf        ds_count,F              ; Same color: just increment.
    return

dump_screen_pixel_1:                    ; Send (pixel,count) tuple
    movf        ds_count,W              ; Is count zero ?
    bz          dump_screen_pixel_2     ; Yes: skip sending.

    movf        ds_pixel+1,W            ; This is a BLACK pixel ?
    iorwf       ds_pixel+0,W    
    bz          dump_screen_pix_black   ; YES.

    movf        ds_pixel+1,W            ; This is a white pixel ?
    andwf       ds_pixel+0,W
    incf        WREG
    bz          dump_screen_pix_white   ; YES.

    ; No: write the pixel itself...
    movlw       .64                     ; Max color pixel on a single byte.
    cpfsgt      ds_count                ; Skip if count > 64
    movf        ds_count,W              ; W <- min(64,count)
    subwf       ds_count,F              ; ds_count <- ds_count-W
    decf        WREG                    ; Save as 0..63
    iorlw       b'10000000'             ; MARK as a color pixel.

    movwf       TXREG
    call		rs232_wait_tx           ; wait for UART
    movff       ds_pixel+1,TXREG
    call		rs232_wait_tx           ; wait for UART
    movff       ds_pixel+0,TXREG
    call		rs232_wait_tx           ; wait for UART
    bra         dump_screen_pixel_1

dump_screen_pixel_2:
    movff       PRODH,ds_pixel+1        ; Save new pixel color
    movff       PRODL,ds_pixel+0
    movlw       1
    movwf       ds_count                ; And set count=1.
    return

dump_screen_pix_black:
    movlw       .128                    ; Max black pixel on a single byte.
    cpfsgt      ds_count                ; Skip if count > 128
    movf        ds_count,W              ; W <- min(128,count)
    subwf       ds_count,F              ; ds_count <- ds_count-W
    decf        WREG                    ; Save as 0..127
dump_screen_pix_3:
    movwf       TXREG
    call        rs232_wait_tx
    bra         dump_screen_pixel_1     ; More to dump ?

dump_screen_pix_white:
    movlw       .64                     ; Max white pixel on a single byte.
    cpfsgt      ds_count                ; Skip if count > 64
    movf        ds_count,W              ; W <- min(64,count)
    subwf       ds_count,F              ; ds_count <- ds_count-W
    decf        WREG                    ; Save as 0..63
    iorlw       b'11000000'             ; MARK as a compressed white.
    bra         dump_screen_pix_3

dump_screen_pixel_flush:
    clrf        PRODH
    clrf        PRODL
    rcall       dump_screen_pixel_1     ; Send it
dump_screen_pixel_reset:
    clrf        ds_count                ; But clear count.
    return

    end