| CODE |
; **************************************************************************** ; **************************************************************************** ; ; File : Video.Asm ; ; Description : 4Mhz 3-Resistor SCART Video Code ; ; Author : Paul Robson, based on the original 16Mhz version ; by Alberto Riccibitti ; ; Date : 2nd September 1999 ; ; Notes : This is a PAL Version ; ; **************************************************************************** ; **************************************************************************** .def Tmp1 = r30 ; Temp variables used by interrupt .def Tmp2 = r31 ; (preserved by the interrupt routine) .equ CSyncBit = 4 ; Composite Sync Level pin .equ VideoBit = 7 ; Video Level pin .equ CSyncPort = PORTB ; Ports on which these pins are placed .equ VideoPort = PORTD .equ StartRetrace = (312/4)-1; counter value at wich retrace starts .equ StopRetrace = (312/4) ; maximum counter value .equ FirstLine = 48 ; first visible line .equ LastLine = 48+128 ; line AFTER last line. ; **************************************************************************** ; ; Set up the MCU to run the video. Needs an SEI to make it go :) ; ; **************************************************************************** VideoEnable: ldi Tmp1, $FF ; Set port B & D bits to outputs out DDRD, Tmp1 out DDRB, Tmp1 sbi CSyncPort,CSyncBit ; Set sync on, video off cbi VideoPort,VideoBit ldi Tmp1, 1 ; Don't use timer prescaler out TCCR0, Tmp1 ; (e.g. IRQ at Clock/256) ldi Tmp1, 32 ; Enable sleep idle mode out MCUCR, Tmp1 ldi Tmp1, 2 ; Enable timer interrupt out TIMSK, Tmp1 ret ;**************************************************************************** ; ; The video timer routine, called once every 256 cycles. ; 4,000,000 / 256 = 15,625 Hz (Line Sync Frequency) ; ; The MCU is available from lines 0-60 and 192-312 (180 lines) ; the interrupt routine takes about 35 cycles, so the % hit to ; the MCU is (180/312) * (1-(35/256)) i.e. about 50%. ; ; In each frame for execution there are 180 * 220 cycles ; i.e. about 40,000 cycles available, and in that time we ; need to do 17-18 CHIP8 instructions, about 2,000 each :) ; ; The idea and original code was written by Alberto Riccibitti, ; for a 16Mhz 1200. This is fundamentally identical except it ; has been slowed down to 4Mhz, and (of course) the start-of-sync ; code is completely changed, as is the video generation bit ; (because I don't wanna draw a voltmeter). Thanks Alberto ! ; ; If you want a pixel display this can be 64 x however much RAM ; you can spare. ; ;**************************************************************************** VideoTimerRoutine: in SaveStatus,SREG ; save Status push Tmp1 ; save work registers push Tmp2 rjmp Newline ; do a new line Timer_Exit: cbi VideoPort,VideoBit ; make sure video is off pop Tmp2 ; restore registers pop Tmp1 out SREG, SaveStatus ; restore status and exit reti Newline: in Tmp1, CSyncPort ; Read previous CSync level ldi Tmp2, 1 << CSyncBit ; Load mask for toggle csync bit eor Tmp1, Tmp2 ; Toggle CSync output out CSyncPort, Tmp1 ; the horizontal sync pulse beginneth ; for it lasteth 16 cycles, counting.. inc RowsModFour ; Increment row counters [1] cpi RowsModFour, 4 ; If RowsModFour == 4 [2] brne _1 ; [3] inc RowsDivFour ; Then increment RowsDivFour [4] _1: andi RowsModFour, 3 ; (range 0..3) [5] ; ; At the last line the sync pulse is stretched to half a line ; cpi RowsDivFour, StopRetrace; If at last line [6] brsh EndLine ; Sync pulse stops at 1/2 line [7] nop ; To make it a 4us pulse [8] ldi Tmp2, 1 << CSyncBit ; Get mask for csync toggle [9] ldi Tmp1, StartRetrace ; Check if start of vertical [10] cpi RowsModFour, 0 ; retrace pulse [11] cpc RowsDivFour, Tmp1 ; [12] brne HSyncEnd ; if so leave CSync low [13] ldi Tmp2, 0 ; [14] HSyncEnd: in Tmp1, CSyncPort ; [15] eor Tmp1, Tmp2 ; Toggle CSync output [16] out CSyncPort, Tmp1 ; In the color burst space ; actually got a bit of spare time cpi RowsDivFour, LastLine/4; Exit now if not on the displayable brsh Timer_Exit ; part of the screen cpi RowsDivFour, FirstLine/4-1 brlo Timer_Exit breq DoScreen rjmp BuildScreen ; Go to display building section ; **************************************************************************** ; Called 4 lines before the display start. If it is selected, it puts the ; MCU in a "sleep loop" until the display generation has finished, so ; that multi cycle instructions don't make the screen wobble. ; **************************************************************************** DoScreen: or RowsModFour,RowsModFour; check actual first line brne Timer_Exit ; if not, it is blank. push SaveStatus ; save the status register sei ; have a little loop and a reentrant ; interrupt DoLine: sleep ; wait for interrupt cpi RowsDivFour,LastLine/4 ; wait for the screen to end brlo DoLine ; while <= last line breq DoLine cli ; int off again pop SaveStatus ; restore status register rjmp Timer_Exit ; and continue as if nothing happened ; **************************************************************************** ; The last (e.g. 312th line), extend the pulse for half a line ; **************************************************************************** EndLine: ; this is a good time to make slow (1/50 sec.) housekeepings, ; or to do things that are made once every screen picture (frame !) clr RowsDivFour ; Reset row counters clr RowsModFour WaitMidline: ; Wait for Half-Line position in Tmp1, TCNT0 cpi Tmp1, 128 brlo WaitMidline sbi CSyncPort, CSyncBit ; and end composite sync rjmp Timer_Exit ; **************************************************************************** ; Output the video data to the video port. Assumes port pin is #7 ; **************************************************************************** .MACRO Bit8 out VideoPort,@0 ; yes I know its crap. But this way add @0,@0 ; I can shift bits out every 2 cycles out VideoPort,@0 ; and that gives me enough time to add @0,@0 ; get 64 bit horizontal resolution. out VideoPort,@0 ; Any other method will reduce the add @0,@0 ; resolution *OR* make the pixels out VideoPort,@0 ; uneven. add @0,@0 out VideoPort,@0 add @0,@0 out VideoPort,@0 add @0,@0 out VideoPort,@0 add @0,@0 out VideoPort,@0 add @0,@0 .ENDMACRO ; **************************************************************************** ; Preamble (in Colour Burst) ; **************************************************************************** BuildScreen: push XL ; A part of the screen actually push XH ; with graphics on it. push r1 ; We save all the relevant registers push r2 ; on the stack and set up for a push r3 ; bit stream to the VideoBit pin push r4 ; We need all these registers because push r5 ; the ADD x,x;OUT PORT x sequence takes push r6 ; 2 cycles; no room for a LD r,X+ push r7 push r8 mov XL,RowsDivFour ; XL is SL/4 -> (SL+128)/4 subi XL,FirstLine/4 ; XL in range 0..31 add XL,XL ; Make XL 0..248 (lower 3 bits zero) add XL,XL ; which means it points to the start add XL,XL ; of a line. ldi XH,$01 ; XHXL points to Video RAM ld r1,X+ ; Load display data into r1-r8 ld r2,X+ ; Boring typing, folks. ld r3,X+ ld r4,X+ ld r5,X+ ld r6,X+ ld r7,X+ ld r8,X+ ; **************************************************************************** ; Wait for horizontal position, then generate video ; **************************************************************************** WaitVisiblePortion: in Tmp1, TCNT0 ; wait for start of visible area cpi Tmp1, 74 ; decreasing this moves display left brlo WaitVisiblePortion ; not much spare time.... Bit8 r1 ; Bang em out.... 128 words down Bit8 r2 ; the pan. Gimme that 8Mhz Crystal. Bit8 r3 Bit8 r4 ; Actually its the fault of b****y Bit8 r5 ; Maplins in Norwich who didn't stock Bit8 r6 ; it. Mind you, they looked baffled Bit8 r7 ; when I asked for a 90S1200... wassat ? Bit8 r8 ; ignorant or what ? out VideoPort,r8 ; This forces the video to zero, if ; it is '1' in HSYNC we get a mess ; I know this from bitter experience ; **************************************************************************** ; Post video generation ; **************************************************************************** pop r8 ; yet more boring typin' but there's pop r7 ; no alternative in reality. pop r6 pop r5 pop r4 pop r3 pop r2 pop r1 pop XH pop XL rjmp Timer_Exit ; go do something else. |