Having only had a few hours Sunday night to work on the last part of the code I wasn’t expecting to actually reach my goal of a pixel on the screen. In the end though, I managed to get a square block in the center of the screen synced well enough to show flicker free on both my TV and Digital TV card in the PC.

The actual TV picture is a nice vibrant yellow, the screen capture doesn’t do it justice looking washed out. That reminds me, the colour burst wasn’t that hard after all, just a case of making sure the colour is set in the upper 4 bits and the luma in the lower 4.

Big Pixel

Big Pixel

Adding in the VSync and overscan code wasn’t that difficult, the hard part was counting all the clock cycles to make sure they met the timing requirements. In fact the most trouble was with the comparison loop where I check whether the scanline is within the block and needs to be drawn in colour. This results in 3 possible code paths all of which had to have exactly the same number of cycles before reaching the final DELAY(174-3) instruction. A little fudging goes a long way though :)

The theme for this GID was “Extreme Forces”, well the forces exerted on my TV by all the badly formed signals I sent throughout Saturday were pretty extreme, does that count?

The source code is zipped up and available for download. It works fine on my TV but that doesn’t mean the code is correct so I’d love to hear back from any of the more experienced XGS users if you see any flaws. The final code weighed in at roughly 374 bytes. After looking at a few other peoples delay macros I can see a saving of several bytes there alone, so I’m sure this code can be optimised significantly, maybe another day.

Full Code Dump: Be aware the formatting is a little messed up

; //////////////////////////////////////////////////////////////////////
; GID j - Pixel in a day entry
; Gary Preston 8th April 2006
; //////////////////////////////////////////////////////////////////////
include "Setup52.inc"
include "macros.inc"
 
; //////////////////////////////////////////////////////////////////////
; Consts
; //////////////////////////////////////////////////////////////////////
;
BLACK_LEVEL EQU 6
WHITE_LEVEL EQU 15
 
; Colors
VID_SYNC    EQU %11110000                      ; No color + 0v
VID_BLACK   EQU (%11110000 + BLACK_LEVEL)      ; 0.25-0.3V
VID_WHITE   EQU (%11110000 + WHITE_LEVEL)      ; 1.0V
TEMP_COLOUR EQU 011111                         ; yellow
 
; Color Burst signal
VID_COLORBURST_OFF EQU (%11110000 + BLACK_LEVEL)
VID_COLORBURST_ON EQU  (000000 + BLACK_LEVEL)
 
; //////////////////////////////////////////////////////////////////////
; Variables
; //////////////////////////////////////////////////////////////////////
 
;----------------------------------------------------------------------
; $00 - 09 - registers (05-09 = ports RA-RE)
; 0A - 0F - globals
; Banks (00-0F, 10-1F, 20-2F ... F0-FF) bank 0 via semi-direct only
 
;----------------------------------------------------------------------
; GLOBALS (Not available using semi-direct!)
ORG $0A        ; skip past global registers and banks 1-F
counter    DS   1  ; counter required by delay macro
 
;----------------------------------------------------------------------
; defines for TV Signal generation using Bank 1
ORG $10
BANK_VIDEO EQU $
 
pixels     DS 1
scanline   DS 1
 
; //////////////////////////////////////////////////////////////////////
; Program start
; //////////////////////////////////////////////////////////////////////
main
    ; Initialise ports for video (RE port tied to video hardware)
    mov RE, #000000      ; clear port
    mov !RE, #000000     ; set all pins to output
 
    ; --------------------------------------------------------------
    ; Video Kernel (Colour upper nibble, Luma lower nibble)
    ; VID_CSEL3 (b7) - VID_CSEL0 (b4)
    ; VID_ISEL3 (b3) - VID_ISEL0 (b0)
    ; Tied to port RE0-7
    ; PAL 5Mhz
    ;   312 scan lines
    ;   pixel every 181.81ns approx 292 pixels per line (53.1us / 181.81ns)
 
    ; set bank
    _bank   BANK_VIDEO
 
vidloop  
 
    ; we have 312 lines potentially with pal but will use only 192 active
    mov scanline, #206                      ; 2
:scanline_loop
    ;--------------------------------------------------------------
    ; Horizontal signal component 274 active lines
    ; blanking voltage 0.3V for 1.5us (front porch)
    mov RE, #VID_BLACK                      ; 2
    DELAY( 120-2 )
 
    ; horizontal sync 0V delay 4.7us
    mov RE, #VID_SYNC                       ; 2
    DELAY( 376-2 )
 
    ; pre burst breezeway 0.6us voltage 0.3V
    mov RE, #VID_BLACK                      ; 2
    DELAY( 48-2 )
 
    ; color burst 9-10cycles 0.3V for 2.5us (skipping data just delay for now)
    mov RE, #VID_COLORBURST_ON              ; 2
    DELAY( 200-2 )                          ; 200-2
   
    ; post burst blanking 0.3V 1.6us
    mov RE, #VID_BLACK                      ; 2
    mov pixels, #255                        ; 2   
    DELAY( 128-4 )
 
    ; All the 3 branches following MUST have the same execution time, thus
  ; the extra jumps that don't go anywhere
    ; black for 141 pixels (2037 cycles) and color for 12 (174 cycles)
    mov RE, #VID_BLACK                      ; 2
 
    ; color for 12 pixels (174 cycles)   
    cjb scanline, #97, :skipcolor1          ; 4/6
    cja scanline, #109, :skipcolor2         ; 4/6
    DELAY(2037-10)
    mov RE, #TEMP_COLOUR                    ; 2 setup pixel colour 
    jmp :color                              ; 3
 
:skipcolor2
    DELAY(2037-12)
    nop 
    nop
    jmp :color                              ; 3
:skipcolor1
    DELAY(2037-6)
    nop 
    nop
    jmp :color                              ; 3
:color
    DELAY( 174-5 )
 
    ; black for 141 pixels (2037 cycles)                           
    mov RE, #VID_BLACK                      ; 2 setup pixel colour 
    DELAY(2037-6)                           ; inc following jump
 
    djnz scanline, :scanline_loop           ; 2/4 (extra 2 from 4 is unaccounted for!)
   
  ; END of Horizontal Component (total scanline = 64us)
  ; we're 2 clock cycles over!
 
; ---------------------------------------------------------------------------------------------
; OVERSCAN BOTTOM
; --------------------------------------------------------------------------------------------- 
    mov scanline, #50   ; 2
:overscan_bottom
    ;--------------------------------------------------------------
    ; Horizontal signal component 274 active lines
    ; blanking voltage 0.3V for 1.5us (front porch)
    mov RE, #VID_BLACK                      ; 2
    DELAY( 120-2 )
 
    ; horizontal sync 0V delay 4.7us
    mov RE, #VID_SYNC                       ; 2
    DELAY( 376-2 )
 
    ; pre burst breezeway 0.6us voltage 0.3V
    mov RE, #VID_BLACK                      ; 2
    DELAY( 48-2 )
 
    ; color burst 9-10cycles 0.3V for 2.5us (skipping data just delay for now)
    mov RE, #VID_COLORBURST_ON              ; 2
    DELAY( 200-2 )                          ; 200-2
   
    ; post burst blanking 0.3V 1.6us
    mov RE, #VID_BLACK                      ; 2
    mov pixels, #255                        ; 2   
    DELAY( 128-4 )
 
    ; draw full scanline (53.1us = 4248 cycles)
    mov RE, #VID_BLACK    ; 2
    DELAY( 2124-2 )  ; 4248 is one full scanline, delay function would overflow with that amount
    DELAY( 2124-4 )  ; Really need to build a better delay function that supports longer delays
    djnz scanline, :overscan_bottom ; 2/4
    ; full scanlein = 64us
 
; ---------------------------------------------------------------------------------------------
; VSYNC LINES
; ---------------------------------------------------------------------------------------------
    mov scanline, #5                        ; 2
:vsync
    ; 4 sync lines
    mov RE, #VID_SYNC                       ; 2
    DELAY( 2560-2 )  ; 4248 is one full scanline, delay function would overflow with that amount
    DELAY( 2560-4 )  ; Really need to build a better delay function that supports longer delays
    djnz scanline, :vsync                   ; 2/4
 
; ---------------------------------------------------------------------------------------------
; OVERSCAN TOP
; --------------------------------------------------------------------------------------------- 
    mov scanline, #50                       ; 2
:overscan_top
    ;--------------------------------------------------------------
    ; Horizontal signal component 274 active lines
    ; blanking voltage 0.3V for 1.5us (front porch)
    mov RE, #VID_BLACK                      ; 2
    DELAY( 120-2 )
 
    ; horizontal sync 0V delay 4.7us
    mov RE, #VID_SYNC                       ; 2
    DELAY( 376-2 )
 
    ; pre burst breezeway 0.6us voltage 0.3V
    mov RE, #VID_BLACK                      ; 2
    DELAY( 48-2 )
 
    ; color burst 9-10cycles 0.3V for 2.5us (skipping data just delay for now)
    mov RE, #VID_COLORBURST_ON              ; 2
    DELAY( 200-2 )                          ; 200-2
   
    ; post burst blanking 0.3V 1.6us
    mov RE, #VID_BLACK                      ; 2
    mov pixels, #255                        ; 2   
    DELAY( 128-4 )
 
    ; draw full scanline (53.1us = 4248 cycles)
    mov RE, #VID_BLACK                      ; 2
    DELAY( 2124-2 )  ; 4248 is one full scanline, delay function would overflow with that amount
    DELAY( 2124-4 )  ; Really need to build a better delay function that supports longer delays
    djnz scanline, :overscan_top            ; 2/4
 
    jmp     vidloop                         ; 3