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