How to create or tweak a video mode: Difference between revisions

From Uzebox Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 13: Line 13:


==Checking the Current Timing==
==Checking the Current Timing==
Let's see the current timing. Start AVRStudio and insure a simulator is defined by going into menu Debug->Select Platform and Device... and choose AVR Simulator (you can choose AVR Simulator 2, but it is much slower because it models more closely the chip). Open project Tutorial, and import source file /kernel/videoMode1/videoMode1.s in the workspace. Perform a build and set a breakpoint on the line defined below. Then hit Run/Start.
Let's see the current timing. Start AVRStudio and insure a simulator is defined by going into menu Debug->Select Platform and Device... and choose AVR Simulator (you can choose AVR Simulator 2, but it is much slower because it models more closely the chip). Open project Tutorial, and import source file /kernel/videoMode1/videoMode1.s in the workspace. Perform a build and set a breakpoint on the line defined below. Then hit Run/Start and the F5.
[[File:Videomode1 trace1.png]]
[[File:Videomode1 trace1.png]]
The programs run for a while, then stops at the breakpoint. Hit F11 to step into the function call. In the top-Right window, select TIMER_COUNTER_1 from the list. In the window just underneath select TCNT1, this is the TIMER 1 current count value.
[[File:Videomode1 trace2.png]]
Look at the function's comment: TCNT1 should be equal to 0x68 on the cbi, 0xf0 on the sbi. Now check the current TCNT1 value on the right panel: 0x68...bingo. When the cursor is on the hsync_pulse function first instruction (the CBI), TCNT1 must always be 0x68, whatever the video mode. hsync_pulse is a shared function called by all video modes. It will generate the HSYNC pulse and also mix and/or update the sound port. You don't have to care about the SBI, since this will be handled by the sync code later.
So that's it! Hit F5 again to continue execution. This will run one iteration of our line render loop, the "render_tile_line" function and then come back to the breakpoint. Insure TCNT1 is fine. Run at least 8 time to be sure a full tile height is covered and validate timing is still ok. Then remove the breakpoint, locate label "text_frame_end:" and put a breakpoint on the "rcall hsync_pulse" following. Hit run and validate again timing is ok. If all is fine, you're done, timing will be rock steady on your TV.


==Implementing the Change==
==Implementing the Change==

Revision as of 01:38, 17 October 2012

Introduction

This tutorial describe the approach that was use to develop video modes and, more specifically, how to adjust delay loops so the video timing is correct.

As a concrete example, we will use video mode 1 that we will tweak to support 8 pixels wide tiles (in addition to the current 6 pixels ones). The most important thing when making or tweaking a video mode is to be sure each scan line is always exactly 1820 cycles. Two approach are possible to count cycles, manual and with an emulator. Manual is tedious and error prone. Moreover, if you are off by any cycles amount, you won't see the result in uzem. The best approach is to use Atmel's own AVR simulator. The simulator displays a global cycles counter and also all I/Os and, most importantly, the current values of all timers.

A video frame is made of a series or scanlines. Those scanlines are then it turn made of a horizontal synchronization pulse (also called HSYNC) and image data. A full picture is made of 524 of those lines. The kernel uses TIMER1, a 16-bit counter set to count to 1820, rollover and then generate an interrupt. The video rendering process is initiated by that interrupt. The kernel then does some setup and then delegates to the video mode that will actually render a full frame in one shot. It's important to note that during frame rendering, interrupts are disabled but the TIMER1 counter continues to count.

Video mode 1 works according to the following process:

Videomode1 process.png

The one thing thing that will be critical is to insure that those "Generate H-Sync pulse" function calls are always executed at the same cycle in regard to TIMER1. That's where the simulator will come handy and help us insure our timing is spot on.

Checking the Current Timing

Let's see the current timing. Start AVRStudio and insure a simulator is defined by going into menu Debug->Select Platform and Device... and choose AVR Simulator (you can choose AVR Simulator 2, but it is much slower because it models more closely the chip). Open project Tutorial, and import source file /kernel/videoMode1/videoMode1.s in the workspace. Perform a build and set a breakpoint on the line defined below. Then hit Run/Start and the F5. Videomode1 trace1.png

The programs run for a while, then stops at the breakpoint. Hit F11 to step into the function call. In the top-Right window, select TIMER_COUNTER_1 from the list. In the window just underneath select TCNT1, this is the TIMER 1 current count value. Videomode1 trace2.png Look at the function's comment: TCNT1 should be equal to 0x68 on the cbi, 0xf0 on the sbi. Now check the current TCNT1 value on the right panel: 0x68...bingo. When the cursor is on the hsync_pulse function first instruction (the CBI), TCNT1 must always be 0x68, whatever the video mode. hsync_pulse is a shared function called by all video modes. It will generate the HSYNC pulse and also mix and/or update the sound port. You don't have to care about the SBI, since this will be handled by the sync code later.

So that's it! Hit F5 again to continue execution. This will run one iteration of our line render loop, the "render_tile_line" function and then come back to the breakpoint. Insure TCNT1 is fine. Run at least 8 time to be sure a full tile height is covered and validate timing is still ok. Then remove the breakpoint, locate label "text_frame_end:" and put a breakpoint on the "rcall hsync_pulse" following. Hit run and validate again timing is ok. If all is fine, you're done, timing will be rock steady on your TV.


Implementing the Change

To implement our 8 pixel wide option, we need to modified the "render_tile_line" function:

;*************************************************
; Renders a line within the current tile row.
; Draws 40 tiles wide @ 6 clocks per pixel
;
; r22     = Y offset in tile row (0-7)
; r23 	  = tile width in bytes
; Y       = VRAM adress to draw from (must not be modified)
; 
; cycles  = 1495
;*************************************************
render_tile_line:

	movw XL,YL			;copy current VRAM pointer to X
	
	;////////////////////////////////////////////
	;Compute the adress of the first tile to draw
	;////////////////////////////////////////////
	ld	r20,X+			;load absolute tile adress from VRAM (LSB)
	ld	r21,X+			;load absolute tile adress from VRAM (MSB)
	mul r22,r23			;compute Y offset in current tile row
	movw r24,r0			;store result in r24:r25 for use in inner loop
	add r20,r24			;add Y offset to tile address
	adc r21,r25 		;add Y offset to tile address
	movw ZL,r20 		;copy to Z, the only register that can read from flash

	ldi r18,SCREEN_TILES_H ;load the number of horizontal tiles to draw

mode1_loop:	
	lpm r16,Z+			;get pixel 0 from flash
	out VIDEO_PORT,r16	;and output it to the video DAC
	
	ld	r20,X+			;load next tile adress from VRAM (LSB)

	lpm r16,Z+			;get pixel 1 from flash
	out VIDEO_PORT,r16	;and output it to the video DAC

	ld	r21,X+			;load next tile adress from VRAM (MSB)

	lpm r16,Z+			;get pixel 2 from flash
	out VIDEO_PORT,r16	;and output it to the video DAC

	rjmp .				;2 cycles delay

	lpm r16,Z+			;get pixel 3 from flash
	out VIDEO_PORT,r16	;and output it to the video DAC

	add r20,r24			;add Y offset to tile address
	adc r21,r25 		;add Y offset to tile address

	lpm r16,Z+			;get pixel 4 from flash
	out VIDEO_PORT,r16	;and output it to the video DAC

	lpm r16,Z+			;get pixel 5 from flash
	movw ZL,r20			;load the next tile's adress in Z
	dec r18				;decrement horizontal tiles to draw

	out VIDEO_PORT,r16	;and output it to the video DAC
	brne mode1_loop		
	
	rjmp .				;2 cycles delay
	nop					;1 cycle delay
	clr r16				;set last pixel to zero (black)
	out VIDEO_PORT,r16

	ret



First we will need to tweak the file constants definition file /kernel/videoMode1/videoMode1.def.h to support 6 or 8 pixels tile wide. From now on, we can use -DTILE_WIDTH=8 in our makefile to tell the video mode we want 8 pixels wide tiles.

...
#ifndef TILE_WIDTH
	#define TILE_WIDTH 6
#endif

#if TILE_WIDTH == 6
	#define VRAM_TILES_H 40 
	#define SCREEN_TILES_H 40
#elif TILE_WIDTH == 8
	#define VRAM_TILES_H 30 
	#define SCREEN_TILES_H 30	
#else
	#error Invalid value defined in the makefile for TILE_WIDTH. Supported values are 6 or 8.
#endif
...