;*************************************************************************** ;* TV_TIME.ASM ;*************************************************************************** .DEVICE AT90S1200 ; 16.00 Mhz ;*************************************************************************** ;* a few, very useful macros ;*************************************************************************** .LISTMAC .MACRO ADDI subi @0, -@1 .ENDMACRO ;skip next instruction if not equal to zero .MACRO skipne ;.SET _skipne = PC+2 brne PC+2 ;_skipne .ENDMACRO ;skip next instruction if carry set .MACRO skipcs ;.SET _skipcs = PC+2 brcs PC+2 ;_skipcs .ENDMACRO ;skip next instruction if carry clear .MACRO skipcc ;.SET _skipcc = PC+2 brcc PC+2 ;_skipcc .ENDMACRO ;skip next instruction if equal to zero .MACRO skipeq ;.SET _skipeq = PC+2 breq PC+2 ;_skipeq .ENDMACRO ;skip next instruction if lower .MACRO skiplo ;.SET _skiplo = PC+2 brlo PC+2 ;_skiplo .ENDMACRO ;skip next instruction if half carry set .MACRO skiphs ;.SET _skiphs = PC+2 brhs PC+2 ;_skiphs .ENDMACRO ;skip next instruction if half carry clear .MACRO skiphc ;.SET _skiphc= PC+2 brhc PC+2 ;_skiphc .ENDMACRO ;skip next instruction if T set .MACRO skipts ;.SET _skipts = PC+2 brts PC+2 ;_skipts .ENDMACRO ;skip next instruction if T clear .MACRO skiptc ;.SET _skiptc = PC+2 brtc PC+2 ;_skiptc .ENDMACRO ;wait two cycles but waste only one instruction .MACRO doublenop ;.SET _doublenop = PC+1 rjmp PC+1 ;_doublenop .ENDMACRO ;include AT90S1200 registers definitions .include "1200def.inc" ;include font definitions ;fonts are stored in a EEPROM table, edit fonts.inc to modify it as you wish .include "fonts.inc" ;*************************************************************************** ;* VARIABLE ASSIGNEMENTS ;*************************************************************************** .CSEG .def SaveStatus = r0 ;status buffer during interrupts .def XX= r1 .def PrintAB = r2 ;print buffer for BCD charachters, .def PrintCD = r3 ;the sequence is A B C D E F .def PrintEF = r4 ; .def ct50hz = r5 ; .def charLow = r12 ;Car 3 timer, BCD .def charMid = r13 .def charHigh = r14 .def PixelMask = r15 .def RowsModFour = r18 ;raster rows counter, split in two for .def RowsDivFour = r19 ;convenience of use. Counts up to 312 .def Arg1 = r20 ;Temp variable used by interrupt .def Arg2 = r21 ; " " " " .def Arg3 = r22 ; " " " " .def Arg4 = r23 ; " " " " .def Main1 = r24 ;Temp variable used by main program .def Main2 = r25 ; " " " " " .def Main3 = r26 ; " " " " " .def Main4 = r27 ; " " " " " .def MiscFlags = r28 ;Assorted flags ;bit 0,1: position (0-3) of interrupt ;identifies wich of the four line position ;we are on (3=end of line, 0=sync pulse) when ;an interrupt occurs ;used by timer interrupt routine ;flags bit definition .equ Position1Bit = 0 .equ Position2Bit = 1 .equ POSITIONMASK = 3 .def ContaMillesimi= r29 ;nibble basso: numero di righe ;nibble alto: numero millesimi modulo 8 ;quando il nibble basso è zero scatta un nuovo millesimo .def CurrHeight = r30 ;character height (running counter) ;*************************************************************************** ;* Constants ;*************************************************************************** .equ DisplayRow = 150/4 ;counter value for display start .equ StartRetrace = (312/4)-1;counter value at wich retrace starts .equ StopRetrace = (312/4) ;maximum counter value .equ STARTBLOCKED = 255 ;time value signalling indefinite ;delay due to jump start ;*************************************************************************** ;* Port Pin Assignements ;*************************************************************************** ;port D bit definitions (OUTPUTS) .equ CSyncBit = 0 ;set video output to composite sync level when low .equ VideoBit = 1 ;set video level to white when high, black when low ;port B bit definitions (INPUTS) ;*************************************************************************** ;* VECTORS ;*************************************************************************** rjmp RESET ;Reset Handle rjmp RESET ;Ext. interrupt request 0 rjmp TIMER ;Timer ;*************************************************************************** ;* ;* MAIN PROGRAM ;* ;*************************************************************************** ;* INITAILIZATION ;*************************************************************************** RESET: clt ;clear T bit flag ;VERY important: ;clear T flag, here used to ;verify if we were sleeping ldi Arg1, 0b01111111 ;set port D bits to outputs out DDRD, Arg1 ldi Arg1, 0b00000001 ;preset output state out PortD, Arg1 ldi Arg1, 0b00000000 ;set port B to inputs out DDRB, Arg1 ldi Arg1, 0b11111111 ;turn on pullups on inputs out PortB, Arg1 ldi Arg1, 1 out TCCR0, Arg1 ;dont'use timer prescaler ldi Arg1, 32 out MCUCR, Arg1 ;enable sleep idle mode ldi Arg1, 2 out TIMSK, Arg1 ;enable timer interrupt ldi CurrHeight, 8 rcall ClearCounters ldi MiscFlags, 1 clr PixelMask clr Arg1 ;and clear interrupt variables clr Arg2 clr Arg3 clr Arg4 sei ;at last, allow interrupts ;*************************************************************************** ;* MAIN LOOP ;*************************************************************************** FOREVER: rjmp FOREVER CLEARCOUNTERS: ldi Main1, 0x45 mov charLow, Main1 ldi Main1, 0x23 mov charMid, Main1 ldi Main1, 0x01 mov charHigh, Main1 ret ;**************************************************************************** ;* ;* Timer Interrupt: occurs four times in each video line. ;* The Position records which of four interrupt stages we are on. ;* At the third stage, we go in sleep mode, in order to have constant ;* servicing time for the fourth, crucial interrupt were we will output ;* the sync waveform. ;* This is the very heart of sync generation. ;* ;**************************************************************************** TIMER: ;if we were sleeping, then this is a synchronizing cycle: just return brtc TIMER_NOSLEEP ;if T cleared, do timer routine reti ;otherwise return (sync sleep) TIMER_NOSLEEP: in SaveStatus, SREG ;if not at line start,simply decrement position counter and return ;otherwise do the real thing... dec MiscFlags ;Position(bits 0 and 1) range from 3 down to 0 mov Arg1, MiscFlags andi Arg1, POSITIONMASK breq WAITNEWLINE ;if Position = 0 then wait (sleeping) for new line TIMER_EXIT: out SREG, SaveStatus ;otherwise do nothing reti WAITNEWLINE: set ;set T flag to tell we are sleeping sei ;re-enable interrupts, otherwise we sleep forever! sleep ;wait sleeping the next timer interrupt ;(during sleep we have constant interrupt recovery time) cli ;disable further interrupts NEWLINE: ;when awaken, we will restart here ori MiscFlags, 3 ;reset position index in Arg1, PortD ;read previous CSync level ldi Arg2, 0b00000001 ;load mask for toggle csync bit TOGGLESYNC: eor Arg1, Arg2 ;toggle CSync output out PortD, Arg1 ;now we are in the horizontal sync pulse HOUSEKEEPING: ;once in horizontal sync pulse space, we have ;enough time to do some housekeeping routines... inc RowsModFour ;increment row counters cpi RowsModFour, 4 ;if RowsModFour == 4 skipne inc RowsDivFour ;then increment RowsDivFour andi RowsModFour, 0b00000011 ;clear anyway bit 2 CHECKLASTLINE: ;when at last line, the sync pulse is stretched to half line cpi RowsDivFour, StopRetrace ;if at last line skiplo rjmp LASTLINE ;do a long pulse WAITSYNCEND: ;else wait sync pulse end in Arg1, TCNT0 ;note that now we are perfectly in cpi Arg1, 64 ;sync with timer. 64 = 4 uS brlo WAITSYNCEND ldi Arg2, 0b00000001 ;prepare mask for csync toggle VERTICALRETRACE: ldi Arg1, StartRetrace cpi RowsModFour, 0 ;if at start of vertical retrace pulse cpc RowsDivFour, Arg1 brne HSYNCEND ;then leave CSync low ldi Arg2, 0 HSYNCEND: in Arg1, PortD ; eor Arg1, Arg2 ;toggle CSync output out PortD, Arg1 ;now we are in the color burst space MILLISECONDS: inc ContaMillesimi ;increment row counter: if low nibble=0, 1 mS elapsed tup2: clt ;restore T to low level used by interrupt TIMEUPDATE: ;update millisecond status/counter andi ContaMillesimi, 0b01111111 cpi ContaMillesimi, 0x00 ;if also high nibble is 0, 1, 2 skipne ;then count 15 rows instead of 16 inc ContaMillesimi cpi ContaMillesimi, 0x10 skipne inc ContaMillesimi cpi ContaMillesimi, 0x20 skipne inc ContaMillesimi WAITVISIBLEPORTION: in Arg1, TCNT0 ;wait for start of visible area cpi Arg1, 240 ;increasing this moves display right brlo WAITVISIBLEPORTION rjmp BUILDSCREEN ;go to display building table LASTLINE: ;this is a good time to make slow (1/50 sec.) housekeepings, ;or to do things that are made once every screen picture mov arg4,ct50hz inc arg4 mov ct50hz,arg4 cpi arg4,100 brne not1 ldi arg1, 0x06 mov charHigh, arg1 ldi arg1, 0x66 mov charMid, arg1 mov charLow, arg1 rjmp noend not1: cpi arg4,120 brne not2 ldi arg1, 0x61 mov charHigh, arg1 rjmp noend not2: cpi arg4,140 brne not3 ldi arg1, 0x26 mov charMid, arg1 ldi arg1, 0x66 mov charHigh, arg1 rjmp noend not3: cpi arg4,160 brne not4 ldi arg1, 0x63 mov charMid, arg1 rjmp noend not4: cpi arg4,180 brne not5 ldi arg1, 0x46 mov charLow, arg1 ldi arg1, 0x66 mov charMid, arg1 rjmp noend not5: cpi arg4,200 brne not6 ldi arg1, 0x65 mov charLow, arg1 rjmp noend not6: cpi arg4,220 brne noend ldi arg1, 0x45 mov charLow, arg1 ldi arg1, 0x23 mov charMid, arg1 ldi arg1, 0x01 mov charHigh, arg1 rjmp noend noend: clr RowsDivFour ;reset row counters clr RowsModFour WAITHALFLINE: in Arg1, TIFR ;wait for two timer overflows sbrs Arg1, TOV0 ;(each overflow is a quarter line) rjmp WAITHALFLINE ldi Arg1, EXP2(TOV0) ;reset timer overflow bit out TIFR, Arg1 SECONDQUARTER: in Arg1, TIFR ;wait second overflow sbrs Arg1, TOV0 ; rjmp SECONDQUARTER ldi Arg1, EXP2(TOV0) ;reset timer overflow bit out TIFR, Arg1 andi MiscFlags, 0b11111101 ori MiscFlags, 0b00000001 ;and align position counter (1) sbi PortD, CSyncBit ;and end composite sync rjmp TIMER_EXIT ;**************************************************************************** ;* SCREEN MAKEUP JUMP STRUCTURE ;* This compare and jump table determines the appearence of the display ;* at each step, the current line number is examinated to decide ;* which display routine to use ;**************************************************************************** BUILDSCREEN: ;it's time to decide what to display, depending on current line cpi RowsDivFour, 256/4 ;from line 256 on, blank display brlo _A1 rjmp VOIDLINE _A1: cpi RowsDivFour, 192/4 ;64 lines brlo _A2 mov PrintEF, charLow mov PrintCD, charmid mov PrintAB, charHigh rjmp DISPLAYDIGITS _A2: cpi RowsDivFour, 150/4 ;64 lines brlo _A3 rjmp VOIDLINE _A3: cpi RowsDivFour, 100/4 ;64 lines brlo _A4 _A4: VOIDLINE: clr PixelMask ;reset pixel mask rjmp TIMER_EXIT dolines: sbi PortD, VideoBit ;turn pixel on or... doublenop cbi PortD, VideoBit ;...turn pixel off ret ;**************************************************************************** ;* ON SCREEN DISPLAY ROUTINES ;**************************************************************************** DISPLAYDIGITS: tst CurrHeight ;that prints a ROW of pixels breq _7 ;according to the buffer PrintAB-CD nop ;and the height specified in CharHeight rjmp _8 _7: ldi CurrHeight, 8 lsl PixelMask _8: skipne inc PixelMask mov Arg3, PrintAB ;load single charachters in Arg3 swap Arg3 ;and call the display charachter rcall DISPLAYCHAR ;routine: character A mov Arg3, PrintAB ;then charachter B rcall DISPLAYCHAR mov Arg3, PrintCD ;then character C swap Arg3 rcall DISPLAYCHAR mov Arg3, PrintCD ;then caharacter D rcall DISPLAYCHAR mov Arg3, PrintEF ;then character E swap Arg3 rcall DISPLAYCHAR mov Arg3, PrintEF ;then caharacter F rcall DISPLAYCHAR dec CurrHeight ;decrease the height counter rjmp DISPLAYLINERESTART ;end line ;**************************************************************************** ;* DISPLAYCHAR: prints on screen the row set by PixelMask of the character Arg3 ;* Character matrix is 5 x 7 dots, and is stored by column in eeprom ;* Arg3= character to display ;* PixelMask = mask for vertical pixel selection ;* Destroys Arg1, 2, 3 ;**************************************************************************** DISPLAYCHAR: andi Arg3, 0b00001111 ;discard high nibble mov Arg1, Arg3 ; lsl Arg1 ;multiply character by 5 lsl Arg1 ;to get offset in font table add Arg3, Arg1 ;now Arg3 is the table offset ldi Arg2, 5 ;Arg2 = column number DISPLAYCOLUMN: out EEAR, Arg3 ;set eeprom address register sbi EECR, 0 ;send read command in Arg1, EEDR ;read font table in EEPROM and Arg1, PixelMask ;mask unwanted pixels skipne ;according to table sbi PortD, VideoBit ;turn pixel on or... skipeq cbi PortD, VideoBit ;...turn pixel off inc Arg3 ;next column in table dec Arg2 ;if not last column brne DISPLAYCOLUMN ;then display next one ;otherwise this is the doublenop ;last pixel, waste time... doublenop doublenop doublenop doublenop cbi PortD, VideoBit ;turn pixel off ret ;else exit ;**************************************************************************** ;* Official line exit point for all display routines ;**************************************************************************** DISPLAYLINERESTART: ldi Arg1, EXP2(TOV0) ;clear timer overflow bit out TIFR, Arg1 ; rjmp WAITNEWLINE