VGAPIC32

vga.S

9:e6fa4e9b4e4f
2017-05-14 Paul Boddie Introduced interrupt and exception initialisation fixes plus a revised state machine for signal generation and use of two output compare units for the sync pulses. This does generate a signal but refinement is needed. Moved framebuffer initialisation to a point where it does not cause an exception for some undiagnosed reason.
     1 /*     2  * Generate a VGA signal using a PIC32 microcontroller.     3  *     4  * Copyright (C) 2017 Paul Boddie <paul@boddie.org.uk>     5  *     6  * This program is free software: you can redistribute it and/or modify     7  * it under the terms of the GNU General Public License as published by     8  * the Free Software Foundation, either version 3 of the License, or     9  * (at your option) any later version.    10  *    11  * This program is distributed in the hope that it will be useful,    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    14  * GNU General Public License for more details.    15  *    16  * You should have received a copy of the GNU General Public License    17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.    18  */    19     20 #include "mips.h"    21 #include "pic32.h"    22     23 #define LINE_LENGTH	160	/* pixels */    24     25 #define HFREQ_LIMIT	1254	/* 40MHz cycles */    26 #define LINE_LIMIT	960	/* 40MHz cycles (3 per byte/pixel) */    27 #define HSYNC_LIMIT	96	/* 40MHz cycles (3 per byte/pixel) */    28     29 #define HSYNC_START	LINE_LIMIT    30 #define HSYNC_END	(HSYNC_START + HSYNC_LIMIT)    31     32 #define VISIBLE_START	15	/* horizontal lines, back porch end */    33 #define VFP_START	527	/* horizontal lines, front porch start */    34 #define VSYNC_START	529	/* horizontal lines, front porch end */    35 #define VSYNC_END	531	/* horizontal lines, back porch start */    36     37 #define SCREEN_SIZE	(40 * 1024)    38     39 /* Disable JTAG functionality on pins. */    40     41 .section .devcfg0, "a"    42 .word 0xfffffffb	/* DEVCFG0<2> = JTAGEN = 0 */    43     44 /*    45 Set the oscillator to be the FRC oscillator with PLL, with peripheral clock    46 divided by 1, and FRCDIV+PLL selected.    47     48 The system clock and peripheral clock are therefore the same.    49     50 The watchdog timer (FWDTEN) is also disabled.    51     52 The secondary oscillator pin (FSOSCEN) is disabled to avoid pin conflicts with    53 RPB4.    54 */    55     56 .section .devcfg1, "a"    57 .word 0xff7fcfd9	/* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0;    58 			DEVCFG1<5> = FSOSCEN = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */    59     60 /*    61 Set the FRC oscillator PLL function with an input division of 4, an output    62 division of 2, a multiplication of 20, yielding a multiplication of 2.5.    63     64 The FRC is apparently at 16MHz and this produces a system clock of 40MHz.    65 */    66     67 .section .devcfg2, "a"    68 .word 0xfff9ffdb	/* DEVCFG2<18:16> = FPLLODIV<2:0> = 001;    69 			DEVCFG2<6:4> = FPLLMUL<2:0> = 101;    70 			DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */    71     72 .text    73 .globl _start    74     75 _start:    76 	/*    77 	Configure RAM.    78 	See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization    79 	*/    80     81 	la $v0, BMXCON    82 	li $v1, (1 << 6)	/* BMXCON<6> = BMXWSDRM = 0 */    83 	sw $v1, CLR($v0)    84     85 	/* Enable caching. */    86     87 	li $v0, CONFIG_CM_CACHABLE_NONCOHERENT    88 	mtc0 $v0, CP0_CONFIG    89 	nop    90     91 	/* Get the RAM size. */    92     93 	la $v0, BMXDRMSZ    94 	lw $v0, 0($v0)    95     96 	/* Initialise the stack pointer. */    97     98 	li $v1, KSEG0_BASE    99 	addu $sp, $v0, $v1	/* sp = KSEG0_BASE + RAM size */   100    101 	/* Initialise framebuffer. */   102    103 	jal init_framebuffer   104 	nop   105    106 	/* Initialise the globals pointer. */   107    108 	lui $gp, %hi(_GLOBAL_OFFSET_TABLE_)   109 	ori $gp, $gp, %lo(_GLOBAL_OFFSET_TABLE_)   110    111 	/* Set pins for output. */   112    113 	jal init_pins   114 	nop   115    116 	jal init_oc_pins   117 	nop   118    119 	/* Initialise the status register. */   120    121 	jal init_interrupts   122 	nop   123    124 	/* Initialise timer. */   125    126 	jal init_timer2   127 	nop   128    129 	/* Initialise PMP. */   130    131 	jal init_pmp   132 	nop   133    134 	/* Initialise DMA. */   135    136 	jal init_dma   137 	nop   138    139 	/* Initialise OC3 and OC2. */   140    141 	jal init_oc   142 	nop   143    144 	/* Enable interrupts and loop. */   145    146 	jal enable_interrupts   147 	nop   148    149 	jal handle_error_level   150 	nop   151    152 	/* Set initial sync conditions. */   153    154 	la $t0, PORTB   155 	li $t1, (1 << 5)	/* PORTB<5> = RB5 */   156 	sw $t1, SET($t0)   157    158 	la $t0, PORTB   159 	li $t1, (1 << 10)	/* PORTB<10> = RB10 */   160 	sw $t1, SET($t0)   161    162 	/* Main program. */   163    164 	li $a1, 5000000		/* counter = 5000000 */   165    166 	/* Initialise the display state. */   167    168 	li $s0, 0		/* line counter */   169 	la $s1, vbp_active	/* current event */   170 	move $s2, $zero		/* line address */   171    172 	/* Monitoring loop. */   173 loop:   174 	addiu $a1, $a1, -1	/* counter -= 1 */   175 	bnez $a1, loop		/* until counter == 0 */   176 	nop   177    178 	li $a1, 5000000		/* counter = 5000000 */   179    180 	la $t0, PORTB   181 	li $t1, (1 << 11)	/* PORTB<11> = RB11 */   182 	sw $t1, INV($t0)   183    184 _next:   185 	j loop   186 	nop   187    188    189    190 init_pins:   191 	/* DEVCFG0<2> needs setting to 0 before the program is run. */   192    193 	la $v0, CFGCON   194 	li $v1, (1 << 3)	/* CFGCON<3> = JTAGEN = 0 */   195 	sw $v1, CLR($v0)   196    197 init_outputs:   198 	/* Remove analogue features from pins. */   199    200 	la $v0, ANSELA   201 	sw $zero, 0($v0)	/* ANSELA = 0 */   202 	la $v0, ANSELB   203 	sw $zero, 0($v0)	/* ANSELB = 0 */   204    205 	la $v0, TRISA   206 	sw $zero, 0($v0)   207 	la $v0, TRISB   208 	sw $zero, 0($v0)   209    210 	la $v0, PORTA   211 	sw $zero, 0($v0)   212 	la $v0, PORTB   213 	sw $zero, 0($v0)   214    215 	jr $ra   216 	nop   217    218    219    220 /* Initialisation routines. */   221    222 init_timer2:   223    224 	/* Initialise Timer2 interrupt. */   225    226 	la $v0, T2CON   227 	sw $zero, 0($v0)	/* T2CON = 0 */   228 	nop   229    230 	la $v0, TMR2   231 	sw $zero, 0($v0)	/* TMR2 = 0 */   232    233 	la $v0, PR2   234 	li $v1, HFREQ_LIMIT   235 	sw $v1, 0($v0)		/* PR2 = HFREQ_LIMIT */   236    237 	/* Initialise Timer2 interrupt. */   238    239 	la $v0, IFS0   240 	li $v1, (1 << 9)   241 	sw $v1, CLR($v0)	/* T2IF = 0 */   242    243 	la $v0, IPC2   244 	li $v1, 0b11111   245 	sw $v1, CLR($v0)	/* T2IP, T2IS = 0 */   246    247 	la $v0, IPC2   248 	li $v1, 0b11111   249 	sw $v1, SET($v0)	/* T2IP = 7; T2IS = 3 */   250    251 	la $v0, IEC0   252 	li $v1, (1 << 9)   253 	sw $v1, SET($v0)	/* T2IE = 1 */   254    255 	/* Start timer. */   256    257 	la $v0, T2CON   258 	li $v1, (1 << 15)   259 	sw $v1, SET($v0)	/* ON = 1 */   260    261 	jr $ra   262 	nop   263    264    265    266 /*   267 Output compare initialisation.   268    269 Timer2 will be used to trigger two events using OC3: one initiating the hsync   270 pulse, and one terminating the pulse. The pulse should appear after the line   271 data has been transferred using DMA, but this is achieved by just choosing   272 suitable start and end values.   273    274 Using OC2, Timer 2 triggers a level shifting event and OC2 is reconfigured to   275 reverse the level at a later point.   276 */   277    278 init_oc:   279 	/* Disable OC3 interrupts. */   280    281 	la $v0, IEC0   282 	li $v1, (1 << 17)	/* IEC0<17> = OC3IE = 0 */   283 	sw $v1, CLR($v0)   284    285 	la $v0, IFS0   286 	li $v1, (1 << 17)	/* IFS0<17> = OC3IF = 0 */   287 	sw $v1, CLR($v0)   288    289 	/* Initialise OC3. */   290    291 	la $v0, OC3CON   292 	li $v1, 0b101		/* OC3CON<2:0> = OCM<2:0> = 101 (dual compare, continuous pulse) */   293 	sw $v1, 0($v0)   294    295 	/* Pulse start and end. */   296    297 	la $v0, OC3R   298 	li $v1, HSYNC_END	/* HSYNC_START for positive polarity */   299 	sw $v1, 0($v0)   300    301 	la $v0, OC3RS   302 	li $v1, HSYNC_START	/* HSYNC_END for positive polarity */   303 	sw $v1, 0($v0)   304    305 	/* OC3 is enabled. */   306    307 	la $v0, OC3CON   308 	li $v1, (1 << 15)   309 	sw $v1, SET($v0)   310    311 	/* Disable OC2 interrupts. */   312    313 	la $v0, IEC0   314 	li $v1, (1 << 12)	/* IEC0<12> = OC2IE = 0 */   315 	sw $v1, CLR($v0)   316    317 	la $v0, IFS0   318 	li $v1, (1 << 12)	/* IFS0<12> = OC2IF = 0 */   319 	sw $v1, CLR($v0)   320    321 	/* Initialise OC2. */   322    323 	la $v0, OC2CON   324 	li $v1, 0b010		/* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */   325 	sw $v1, 0($v0)   326    327 	/* Set pulse position. */   328    329 	la $v0, OC2R   330 	sw $zero, 0($v0)   331    332 	/* Enable OC2 later. */   333    334 	jr $ra   335 	nop   336    337    338 init_oc_pins:   339 	/* Unlock the configuration register bits. */   340    341 	la $v0, SYSKEY   342 	sw $zero, 0($v0)   343 	li $v1, 0xAA996655   344 	sw $v1, 0($v0)   345 	li $v1, 0x556699AA   346 	sw $v1, 0($v0)   347    348 	la $v0, CFGCON   349 	lw $t8, 0($v0)   350 	li $v1, (1 << 13)	/* IOLOCK = 0 */   351 	sw $v1, CLR($v0)   352    353 	/* Map OC3 to RPB10. */   354    355 	la $v0, RPB10R   356 	li $v1, 0b0101		/* RPB10R<3:0> = 0101 (OC3) */   357 	sw $v1, 0($v0)   358    359 	/* Map OC2 to RPB5. */   360    361 	la $v0, RPB5R   362 	li $v1, 0b0101		/* RPB5R<3:0> = 0101 (OC2) */   363 	sw $v1, 0($v0)   364    365 	la $v0, CFGCON   366 	sw $t8, 0($v0)   367    368 	/* Lock the oscillator control register again. */   369    370 	la $v0, SYSKEY   371 	li $v1, 0x33333333   372 	sw $v1, 0($v0)   373    374 	jr $ra   375 	nop   376    377    378    379 /* Parallel Master Port initialisation. */   380    381 init_pmp:   382 	/* Disable PMP interrupts. */   383    384 	la $v0, IEC1   385 	li $v1, (1 << 16)	/* IEC1<16> = PMPIE = 0 */   386 	sw $v1, CLR($v0)   387    388 	/* Initialise PMP.   389    390 	PMCON<12:11> = ADDRMUX<1:0> = 0; demultiplexed address and data   391 	PMCON<9> = PTWREN<0> = 0; no write pin   392 	PMCON<8> = PTRDEN<0> = 0; no read pin   393 	PMCON<7:6> = CSF<1:0> = 0; no chip select pins   394 	*/   395    396 	la $v0, PMCON   397 	sw $zero, 0($v0)   398    399 	/*   400 	PMMODE<14:13> = IRQM<1:0> = 1; interrupt after every read/write   401 	PMMODE<12:11> = INCM<1:0> = 0; no increment on every read/write   402 	PMMODE<10> = MODE16<0> = 0; 8-bit transfers   403 	PMMODE<9:8> = MODE<1:0> = 10; master mode 2   404 	PMMODE<5:2> = WAITM<3:0> = 00; single cycle read/write, no chip select wait cycles   405 	*/   406    407 	la $v0, PMMODE   408 	li $v1, 0x2200   409 	sw $v1, 0($v0)   410    411 	/* Free non-essential pins for general I/O. */   412    413 	la $v0, PMAEN   414 	sw $zero, 0($v0)   415    416 	la $v0, PMADDR   417 	sw $zero, 0($v0)   418    419 	la $v0, IFS1   420 	li $v1, (3 << 16)	/* IFS1<17:16> = PMPEIF, PMPIF = 0 */   421 	sw $v1, CLR($v0)   422    423 	/* Start PMP mode. */   424    425 	la $v0, PMCON   426 	li $v1, (1 << 15)	/* PMCON<15> = ON = 1 */   427 	sw $v1, SET($v0)   428    429 	jr $ra   430 	nop   431    432    433    434 /*   435 Direct Memory Access initialisation.   436    437 Write 160 pixels to the PMP for the line data. This is initiated by a timer   438 interrupt. Upon completion of the transfer, a DMA interrupt initiates the   439 address update routine, changing the source address of the DMA channel.   440 */   441    442 init_dma:   443 	/* Disable DMA interrupts. */   444    445 	la $v0, IEC1   446 	li $v1, (1 << 28)	/* IEC1<28> = DMA0IE = 0 */   447 	sw $v1, CLR($v0)   448    449 	/* Clear DMA interrupt flags. */   450    451 	la $v0, IFS1   452 	li $v1, (1 << 28)	/* IFS1<28> = DMA0IF = 0 */   453 	sw $v1, CLR($v0)   454    455 	/* Enable DMA. */   456    457 	la $v0, DMACON   458 	li $v1, (1 << 15)   459 	sw $v1, SET($v0)   460    461 	/*   462 	Initialise a line channel.   463 	The line channel will be channel 0 (x = 0).   464    465 	Once the hsync channel has completed a transfer, the line channel   466 	transfer is initiated.   467    468 	Specify a priority of 3:   469 	DCHxCON<1:0> = CHPRI<1:0> = 3   470    471 	Auto-enable the channels:   472 	DCHxCON<4> = CHAEN = 1   473 	*/   474    475 	la $v0, DCH0CON   476 	li $v1, 0b10011   477 	sw $v1, 0($v0)   478    479 	/*   480 	Initiate channel transfers when the initiating interrupt condition   481 	occurs:   482 	DCHxECON<15:8> = CHSIRQ<7:0> = timer 2 interrupt   483 	*/   484    485 	la $v0, DCH0ECON   486 	li $v1, (9 << 8) | (1 << 4)   487 	sw $v1, 0($v0)   488    489 	/*   490 	The line channel has a cell size of 160 bytes:   491 	DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LENGTH   492 	*/   493    494 	la $v0, DCH0CSIZ   495 	li $v1, LINE_LENGTH   496 	sw $v1, 0($v0)   497    498 	/*   499 	Each source has a size identical to the cell size:   500 	DCHxSSIZ<15:0> = CHSSIZ<15:0> = n   501 	*/   502    503 	la $v0, DCH0SSIZ   504 	li $v1, LINE_LENGTH   505 	sw $v1, 0($v0)   506    507 	/*   508 	The source address is the physical address of the line data:   509 	DCHxSSA = line data physical address   510 	*/   511    512 	la $v0, DCH0SSA   513 	sw $zero, 0($v0)   514    515 	/*   516 	Each destination has a size of 1 byte:   517 	DCHxDSIZ<15:0> = CHDSIZ<15:0> = 1   518 	*/   519    520 	la $v0, DCH0DSIZ   521 	li $v1, 1   522 	sw $v1, 0($v0)   523    524 	/*   525 	For the line channel, the destination address is the physical address of   526 	PMDIN: DCHxDSA = physical(PMDIN)   527 	*/   528    529 	la $v0, DCH0DSA   530 	li $v1, PMDIN   531 	li $t8, KSEG1_BASE   532 	subu $v1, $v1, $t8   533 	sw $v1, 0($v0)   534    535 	/*   536 	Use the block transfer completion interrupt to indicate when the source   537 	address can be updated.   538 	*/   539    540 	la $v0, DCH0INT   541 	li $v1, (1 << 19)	/* CHBCIE = 1 */   542 	sw $v1, 0($v0)   543    544 	/* Enable interrupt for address updating. */   545    546 	la $v0, IPC10   547 	li $v1, 0b11111		/* DMA0IP, DMA0IS = 0 */   548 	sw $v1, CLR($v0)   549    550 	la $v0, IPC10   551 	li $v1, 0b11111		/* DMA0IP = 7, DMA0IS = 3 */   552 	sw $v1, SET($v0)   553    554 	la $v0, IEC1   555 	li $v1, (1 << 28)	/* IEC1<28> = DMA0IE = 1 */   556 	sw $v1, SET($v0)   557    558 	/* Enable channels. */   559    560 	la $v0, DCH0CON   561 	li $v1, 0b10000000   562 	sw $v1, SET($v0)   563    564 	jr $ra   565 	nop   566    567    568    569 /* Framebuffer initialisation. */   570    571 init_framebuffer:   572 	li $v0, KSEG0_BASE   573 	li $t8, SCREEN_SIZE   574 	li $v1, 0xff031ce0   575    576 _init_fb_loop:   577 	sw $v1, 0($v0)   578 	addiu $v0, $v0, 4   579 	addiu $t8, $t8, -4   580 	bnez $t8, _init_fb_loop   581 	nop   582    583 	jr $ra   584 	nop   585    586    587    588 /* Utilities. */   589    590 handle_error_level:   591 	mfc0 $t3, CP0_STATUS   592 	li $t4, ~(STATUS_ERL | STATUS_EXL)   593 	and $t3, $t3, $t4   594 	mtc0 $t3, CP0_STATUS   595 	jr $ra   596 	nop   597    598 enable_interrupts:   599 	mfc0 $t3, CP0_STATUS   600 	li $t4, ~STATUS_IRQ	/* Clear interrupt priority bits. */   601 	and $t3, $t3, $t4   602 	li $t4, ~STATUS_BEV	/* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */   603 	and $t3, $t3, $t4   604 	ori $t3, $t3, STATUS_IE   605 	mtc0 $t3, CP0_STATUS   606 	jr $ra   607 	nop   608    609 init_interrupts:   610 	mfc0 $t3, CP0_DEBUG   611 	li $t4, ~DEBUG_DM   612 	and $t3, $t3, $t4   613 	mtc0 $t3, CP0_DEBUG   614    615 	mfc0 $t3, CP0_STATUS   616 	li $t4, STATUS_BEV   617 	or $t3, $t3, $t4   618 	mtc0 $t3, CP0_STATUS   619    620 	la $t3, exception_handler   621 	mtc0 $t3, CP0_EBASE	/* EBASE = exception_handler */   622    623 	li $t3, 0x20		/* Must be non-zero or the CPU gets upset */   624 	mtc0 $t3, CP0_INTCTL   625    626 	li $t3, CAUSE_IV	/* IV = 1 (use EBASE+0x200 for interrupts) */   627 	mtc0 $t3, CP0_CAUSE   628    629 	/* Multiple vector...   630 	la $t3, INTCON   631 	li $t4, (1 << 12)   632 	sw $t4, SET($t3) */   633    634 	jr $ra   635 	nop   636    637    638    639 /* Exception servicing. */   640    641 .section .flash, "a"   642    643 /* Exception servicing. */   644    645 exception_handler:   646 	li $t8, 2500000   647 exc_loop:   648 	addiu $t8, $t8, -1   649 	bnez $t8, exc_loop   650 	nop   651 	la $v0, PORTB   652 	li $v1, (1 << 11)	/* PORTB<11> = RB11 */   653 	sw $v1, INV($v0)   654 	j exception_handler   655 	nop   656    657    658    659 /* Interrupt servicing. */   660    661 .org 0x200   662    663 interrupt_handler:   664    665 	/* Check for a timer interrupt condition. */   666    667 	la $v0, IFS0   668 	lw $v1, 0($v0)   669 	andi $v1, $v1, (1 << 9)	/* T2IF */   670 	beqz $v1, irq_dma   671 	nop   672    673 	/* Increment the line counter. */   674    675 	addiu $s0, $s0, 1   676    677 	/* Jump to the event handler. */   678    679 	jalr $s1   680 	nop   681    682 irq_clear_timer:   683    684 	/* Clear the timer interrupt condition. */   685    686 	la $v0, IFS0   687 	li $v1, (1 << 9)	/* IFS0<9> = T2IF = 0 */   688 	sw $v1, CLR($v0)   689    690 irq_dma:   691    692 	/* Check for a DMA interrupt condition. */   693    694 	la $v0, IFS1   695 	lw $v1, 0($v0)   696 	li $t8, (1 << 28)	/* DMA0IF */   697 	and $v1, $v1, $t8   698 	beqz $v1, irq_exit   699 	nop   700    701 	/* Test the block transfer completion interrupt flag. */   702    703 	la $v0, DCH0INT   704 	lw $v1, 0($v0)   705 	andi $v1, $v1, (1 << 3)	/* CHBCIF */   706 	beqz $v1, irq_clear_dma   707 	nop   708    709 	/* Clear the block transfer completion interrupt flag. */   710    711 	li $v1, (1 << 3)	/* CHBCIF = 0 */   712 	sw $v1, CLR($v0)   713    714 	/*   715 	Update the line data address if the line counter (referring to the   716 	next line) is even.   717 	*/   718    719 	andi $t8, $s0, 1   720 	bnez $t8, irq_clear_dma   721 	nop   722    723 	/* Reference the next line and update the DMA source address. */   724    725 	addiu $s2, $s2, LINE_LENGTH   726    727 	/* Test for wraparound. */   728    729 	li $t8, SCREEN_SIZE   730 	sltu $t8, $s2, $t8   731 	bnez $t8, irq_dma_update   732 	nop   733    734 	/* Reset the source address. */   735    736 	move $s2, $zero   737    738 irq_dma_update:   739    740 	la $v0, DCH0SSA   741 	sw $s2, 0($v0)   742    743 irq_clear_dma:   744    745 	/* Clear the DMA interrupt condition. */   746    747 	la $v0, IFS1   748 	li $v1, (1 << 28)	/* IFS1<28> = DMA0IF = 0 */   749 	sw $v1, CLR($v0)   750    751 irq_exit:   752 	eret   753 	nop   754    755    756    757 /* Event routines. */   758    759 /* The vertical back porch. */   760    761 vbp_active:   762 	/* Test for visible region. */   763    764 	sltiu $v0, $s0, VISIBLE_START   765 	bnez $v0, _vbp_active_ret   766 	nop   767    768 	/* Start the visible region. */   769    770 	la $s1, visible_active   771    772 _vbp_active_ret:   773 	jr $ra   774 	nop   775    776    777    778 /* The visible region. */   779    780 visible_active:   781 	/* Test for front porch. */   782    783 	sltiu $v0, $s0, VFP_START   784 	bnez $v0, _visible_active_ret   785 	nop   786    787 	/* Start the front porch region. */   788    789 	la $s1, vfp_active   790    791 _visible_active_ret:   792 	jr $ra   793 	nop   794    795    796    797 /* Within the vertical front porch. */   798    799 vfp_active:   800 	/* Test for vsync. */   801    802 	sltiu $v0, $s0, VSYNC_START   803 	bnez $v0, _vfp_active_ret   804 	nop   805    806 	/* Start the vsync. */   807    808 	la $s1, vsync_active   809    810 	/* Bring vsync low when the next line starts. */   811    812 	la $v0, OC2CON   813 	li $v1, 0b010 | (1 << 15)	/* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */   814 	sw $v1, 0($v0)   815    816 _vfp_active_ret:   817 	jr $ra   818 	nop   819    820    821    822 /* The vsync period. */   823    824 vsync_active:   825 	/* Test for front porch. */   826    827 	sltiu $v0, $s0, VSYNC_END   828 	bnez $v0, _vsync_active_ret   829 	nop   830    831 	/* Start the back porch. */   832    833 	move $s0, $zero   834 	la $s1, vbp_active   835    836 	/* Bring vsync high when the next line starts. */   837    838 	la $v0, OC2CON   839 	li $v1, 0b001 | (1 << 15)	/* OC2CON<2:0> = OCM<2:0> = 001 (single compare, output driven high) */   840 	sw $v1, 0($v0)   841    842 _vsync_active_ret:   843 	jr $ra   844 	nop