;
****************************************************************************
;
****************************************************************************
;
;       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.


