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 #include "vga.h" 23 24 /* Disable JTAG functionality on pins. */ 25 26 .section .devcfg0, "a" 27 .word 0xfffffffb /* DEVCFG0<2> = JTAGEN = 0 */ 28 29 /* 30 Set the oscillator to be the FRC oscillator with PLL, with peripheral clock 31 divided by 2, and FRCDIV+PLL selected. 32 33 The watchdog timer (FWDTEN) is also disabled. 34 35 The secondary oscillator pin (FSOSCEN) is disabled to avoid pin conflicts with 36 RPB4. 37 */ 38 39 .section .devcfg1, "a" 40 .word 0xff7fdfd9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<1:0> = 1; 41 DEVCFG1<5> = FSOSCEN = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */ 42 43 /* 44 Set the FRC oscillator PLL function with an input division of 4, an output 45 division of 2, a multiplication of 24, yielding a multiplication of 3. 46 47 The FRC is apparently at 16MHz and this produces a system clock of 48MHz. 48 */ 49 50 .section .devcfg2, "a" 51 .word 0xfff9fffb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 001; 52 DEVCFG2<6:4> = FPLLMUL<2:0> = 111; 53 DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */ 54 55 .text 56 .globl _start 57 .extern init_framebuffer 58 .extern init_framebuffer_with_pattern 59 .extern screendata 60 .extern fontdata 61 .extern blit_string 62 .extern message 63 64 _start: 65 /* 66 Configure RAM. 67 See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization 68 */ 69 70 la $v0, BMXCON 71 lw $v1, 0($v0) 72 li $t8, ~(1 << 6) /* BMXCON<6> = BMXWSDRM = 0 */ 73 and $v1, $v1, $t8 74 li $t8, ~0b111 /* BMXCON<2:0> = BMXARB<2:0> = 0 */ 75 ori $t8, $t8, 0b010 /* BMXCON<2:0> = BMXARB<2:0> = 2 */ 76 and $v1, $v1, $t8 77 sw $v1, 0($v0) 78 79 /* Enable caching. */ 80 81 mfc0 $v1, CP0_CONFIG 82 li $t8, ~CONFIG_K0 83 and $v1, $v1, $t8 84 ori $v1, $v1, CONFIG_K0_CACHABLE_NONCOHERENT 85 mtc0 $v1, CP0_CONFIG 86 nop 87 88 /* Get the RAM size. */ 89 90 la $v0, BMXDRMSZ 91 lw $t0, 0($v0) 92 93 /* Initialise the stack pointer. */ 94 95 li $v1, KSEG0_BASE 96 addu $sp, $t0, $v1 /* sp = KSEG0_BASE + RAM size */ 97 98 /* Initialise the globals pointer. */ 99 100 lui $gp, %hi(_GLOBAL_OFFSET_TABLE_) 101 ori $gp, $gp, %lo(_GLOBAL_OFFSET_TABLE_) 102 103 /* Set pins for output. */ 104 105 jal init_pins 106 nop 107 108 la $t0, PORTA 109 li $t1, (1 << 3) /* PORTA<3> = RA3 */ 110 sw $t1, CLR($t0) 111 112 jal init_oc_pins 113 nop 114 115 /* Initialise the status register. */ 116 117 jal init_interrupts 118 nop 119 120 /* Initialise framebuffer. */ 121 122 la $a0, screendata 123 jal init_framebuffer 124 nop 125 126 sync 127 128 /* Initialise timer. */ 129 130 jal init_timer2 131 nop 132 133 /* Initialise DMA. */ 134 135 jal init_dma 136 nop 137 138 /* Initialise OC1 and OC2. */ 139 140 jal init_oc 141 nop 142 143 /* Initialise the display state. */ 144 145 li $s0, 0 /* line counter */ 146 la $s1, vbp_active /* current event */ 147 li $s2, SCREEN_BASE /* line address */ 148 li $s3, SCREEN_BASE /* screen address */ 149 150 /* Save the state for retrieval in the interrupt handler. */ 151 152 li $k0, IRQ_STACK_LIMIT 153 sw $s0, -44($k0) 154 sw $s1, -48($k0) 155 sw $s2, -52($k0) 156 sw $s3, -56($k0) 157 158 /* Enable interrupts and loop. */ 159 160 jal enable_interrupts 161 nop 162 163 jal handle_error_level 164 nop 165 166 /* Main program. */ 167 168 li $a1, (3 << 24) /* counter ~= 50000000 */ 169 li $a2, 0xffffff /* test counter at every 1/4 of range */ 170 move $t2, $zero /* picture to show */ 171 172 /* Monitoring loop. */ 173 loop: 174 addiu $a1, $a1, -1 /* counter -= 1 */ 175 and $t1, $a2, $a1 176 bnez $t1, loop 177 nop 178 179 la $t0, PORTA 180 li $t1, (1 << 3) /* PORTA<3> = RA3 */ 181 sw $t1, INV($t0) 182 183 bnez $a1, loop /* until counter == 0 */ 184 nop 185 186 bnez $t2, _picture1 187 nop 188 189 /* Show picture 0. */ 190 191 la $a0, screendata 192 jal init_framebuffer 193 nop 194 195 la $a0, message0 196 li $a1, SCREEN_BASE_KSEG0 197 jal blit_string 198 nop 199 200 li $t2, 1 201 j _next 202 nop 203 204 _picture1: 205 /* Show picture 1. */ 206 207 jal init_framebuffer_with_pattern 208 nop 209 210 la $a0, message1 211 li $a1, SCREEN_BASE_KSEG0 212 jal blit_string 213 nop 214 215 move $t2, $zero 216 217 _next: 218 li $a1, (3 << 24) /* counter ~= 50000000 */ 219 li $a2, 0xffffff /* test counter at every 1/4 of range */ 220 j loop 221 nop 222 223 224 225 init_pins: 226 /* DEVCFG0<2> needs setting to 0 before the program is run. */ 227 228 la $v0, CFGCON 229 li $v1, (1 << 3) /* CFGCON<3> = JTAGEN = 0 */ 230 sw $v1, CLR($v0) 231 232 init_outputs: 233 /* Remove analogue features from pins. */ 234 235 la $v0, ANSELA 236 sw $zero, 0($v0) /* ANSELA = 0 */ 237 la $v0, ANSELB 238 sw $zero, 0($v0) /* ANSELB = 0 */ 239 240 la $v0, TRISA 241 sw $zero, 0($v0) 242 la $v0, TRISB 243 sw $zero, 0($v0) 244 245 la $v0, PORTA 246 sw $zero, 0($v0) 247 la $v0, PORTB 248 sw $zero, 0($v0) 249 250 jr $ra 251 nop 252 253 254 255 /* Initialisation routines. */ 256 257 init_timer2: 258 259 /* Initialise Timer2 interrupt. */ 260 261 la $v0, T2CON 262 sw $zero, 0($v0) /* T2CON = 0 */ 263 nop 264 265 la $v0, TMR2 266 sw $zero, 0($v0) /* TMR2 = 0 */ 267 268 la $v0, PR2 269 li $v1, HFREQ_LIMIT 270 sw $v1, 0($v0) /* PR2 = HFREQ_LIMIT */ 271 272 /* Initialise Timer2 interrupt. */ 273 274 la $v0, IFS0 275 li $v1, (1 << 9) 276 sw $v1, CLR($v0) /* T2IF = 0 */ 277 278 la $v0, IPC2 279 li $v1, 0b11111 280 sw $v1, CLR($v0) /* T2IP, T2IS = 0 */ 281 282 la $v0, IPC2 283 li $v1, 0b11111 284 sw $v1, SET($v0) /* T2IP = 7; T2IS = 3 */ 285 286 la $v0, IEC0 287 li $v1, (1 << 9) 288 sw $v1, SET($v0) /* T2IE = 1 */ 289 290 /* Start timer. */ 291 292 la $v0, T2CON 293 li $v1, (1 << 15) 294 sw $v1, SET($v0) /* ON = 1 */ 295 296 jr $ra 297 nop 298 299 300 301 /* 302 Output compare initialisation. 303 304 Timer2 will be used to trigger two events using OC1: one initiating the hsync 305 pulse, and one terminating the pulse. The pulse should appear after the line 306 data has been transferred using DMA, but this is achieved by just choosing 307 suitable start and end values. 308 309 Using OC2, Timer 2 triggers a level shifting event and OC2 is reconfigured to 310 reverse the level at a later point. 311 */ 312 313 init_oc: 314 /* Disable OC1 interrupts. */ 315 316 la $v0, IEC0 317 li $v1, (1 << 7) /* IEC0<7> = OC1IE = 0 */ 318 sw $v1, CLR($v0) 319 320 la $v0, IFS0 321 li $v1, (1 << 7) /* IFS0<7> = OC1IF = 0 */ 322 sw $v1, CLR($v0) 323 324 /* Initialise OC1. */ 325 326 la $v0, OC1CON 327 li $v1, 0b101 /* OC1CON<2:0> = OCM<2:0> = 101 (dual compare, continuous pulse) */ 328 sw $v1, 0($v0) 329 330 /* Pulse start and end. */ 331 332 la $v0, OC1R 333 li $v1, HSYNC_END /* HSYNC_START for positive polarity */ 334 sw $v1, 0($v0) 335 336 la $v0, OC1RS 337 li $v1, HSYNC_START /* HSYNC_END for positive polarity */ 338 sw $v1, 0($v0) 339 340 /* OC1 is enabled. */ 341 342 la $v0, OC1CON 343 li $v1, (1 << 15) 344 sw $v1, SET($v0) 345 346 /* Disable OC2 interrupts. */ 347 348 la $v0, IEC0 349 li $v1, (1 << 12) /* IEC0<12> = OC2IE = 0 */ 350 sw $v1, CLR($v0) 351 352 la $v0, IFS0 353 li $v1, (1 << 12) /* IFS0<12> = OC2IF = 0 */ 354 sw $v1, CLR($v0) 355 356 /* Initialise OC2. */ 357 358 la $v0, OC2CON 359 li $v1, 0b010 /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */ 360 sw $v1, 0($v0) 361 362 /* Set pulse position. */ 363 364 la $v0, OC2R 365 sw $zero, 0($v0) 366 367 /* Enable OC2 later. */ 368 369 jr $ra 370 nop 371 372 373 init_oc_pins: 374 /* Unlock the configuration register bits. */ 375 376 la $v0, SYSKEY 377 sw $zero, 0($v0) 378 li $v1, 0xAA996655 379 sw $v1, 0($v0) 380 li $v1, 0x556699AA 381 sw $v1, 0($v0) 382 383 la $v0, CFGCON 384 lw $t8, 0($v0) 385 li $v1, (1 << 13) /* IOLOCK = 0 */ 386 sw $v1, CLR($v0) 387 388 /* Map OC1 to RPA0. */ 389 390 la $v0, RPA0R 391 li $v1, 0b0101 /* RPA0R<3:0> = 0101 (OC1) */ 392 sw $v1, 0($v0) 393 394 /* Map OC2 to RPA1. */ 395 396 la $v0, RPA1R 397 li $v1, 0b0101 /* RPA1R<3:0> = 0101 (OC2) */ 398 sw $v1, 0($v0) 399 400 la $v0, CFGCON 401 sw $t8, 0($v0) 402 403 /* Lock the oscillator control register again. */ 404 405 la $v0, SYSKEY 406 li $v1, 0x33333333 407 sw $v1, 0($v0) 408 409 jr $ra 410 nop 411 412 413 414 /* 415 Direct Memory Access initialisation. 416 417 Write 160 pixels to PORTB for the line data. This is initiated by a timer 418 interrupt. Upon completion of the transfer, a DMA interrupt initiates the 419 address update routine, changing the source address of the DMA channel. 420 */ 421 422 init_dma: 423 /* Disable DMA interrupts. */ 424 425 la $v0, IEC1 426 li $v1, (3 << 28) /* IEC1<29:28> = DMA1IE, DMA0IE = 0 */ 427 sw $v1, CLR($v0) 428 429 /* Clear DMA interrupt flags. */ 430 431 la $v0, IFS1 432 li $v1, (3 << 28) /* IFS1<29:28> = DMA1IF, DMA0IF = 0 */ 433 sw $v1, CLR($v0) 434 435 /* Enable DMA. */ 436 437 la $v0, DMACON 438 li $v1, (1 << 15) 439 sw $v1, SET($v0) 440 441 /* 442 Initialise a line channel. 443 The line channel will be channel 0 (x = 0). 444 445 Specify a priority of 3: 446 DCHxCON<1:0> = CHPRI<1:0> = 3 447 448 Auto-enable the channel: 449 DCHxCON<4> = CHAEN = 1 450 */ 451 452 la $v0, DCH0CON 453 li $v1, 0b10011 454 sw $v1, 0($v0) 455 456 /* 457 Initialise a level reset channel. 458 The reset channel will be channel 1 (x = 1). 459 460 Specify a priority of 3: 461 DCHxCON<1:0> = CHPRI<1:0> = 3 462 463 Chain the channel to channel 0: 464 DCHxCON<5> = CHCHN = 1 465 466 Allow the channel to receive events when disabled: 467 DCHxCON<6> = CHAED = 1 468 */ 469 470 la $v0, DCH1CON 471 li $v1, 0b1100011 472 sw $v1, 0($v0) 473 474 /* 475 Initiate channel transfers when the initiating interrupt condition 476 occurs: 477 DCHxECON<15:8> = CHSIRQ<7:0> = timer 2 interrupt 478 DCHxECON<4> = SIRQEN = 1 479 480 For now, however, prevent initiation by not setting SIRQEN. 481 */ 482 483 la $v0, DCH0ECON 484 li $v1, (9 << 8) 485 sw $v1, 0($v0) 486 487 /* 488 Initiate reset channel transfer when channel 0 is finished: 489 DCHxECON<15:8> = CHSIRQ<7:0> = channel 0 interrupt 490 DCHxECON<4> = SIRQEN = 1 491 */ 492 493 la $v0, DCH1ECON 494 li $v1, (60 << 8) | (1 << 4) 495 sw $v1, 0($v0) 496 497 /* 498 The line channel has a cell size of the number bytes in a line: 499 DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LENGTH 500 */ 501 502 la $v0, DCH0CSIZ 503 li $v1, LINE_LENGTH 504 sw $v1, 0($v0) 505 506 /* 507 The reset channel has a cell size of a single zero byte: 508 DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LENGTH 509 */ 510 511 la $v0, DCH1CSIZ 512 li $v1, 1 513 sw $v1, 0($v0) 514 515 /* 516 The source has a size identical to the cell size: 517 DCHxSSIZ<15:0> = CHSSIZ<15:0> = LINE_LENGTH 518 */ 519 520 la $v0, DCH0SSIZ 521 li $v1, LINE_LENGTH 522 sw $v1, 0($v0) 523 524 la $v0, DCH1SSIZ 525 li $v1, 1 526 sw $v1, 0($v0) 527 528 /* 529 The source address is the physical address of the line data: 530 DCHxSSA = physical(line data address) 531 */ 532 533 la $v0, DCH0SSA 534 li $v1, SCREEN_BASE 535 sw $v1, 0($v0) 536 537 /* 538 For the reset channel, a single byte of zero is transferred: 539 DCHxSSA = physical(zero data address) 540 */ 541 542 la $v0, DCH1SSA 543 la $v1, zerodata 544 li $t8, KSEG0_BASE 545 subu $v1, $v1, $t8 546 sw $v1, 0($v0) 547 548 /* 549 The destination has a size of 1 byte: 550 DCHxDSIZ<15:0> = CHDSIZ<15:0> = 1 551 */ 552 553 la $v0, DCH0DSIZ 554 li $v1, 1 555 sw $v1, 0($v0) 556 557 la $v0, DCH1DSIZ 558 sw $v1, 0($v0) 559 560 /* 561 The destination address is the physical address of PORTB: 562 DCHxDSA = physical(PORTB) 563 */ 564 565 la $v0, DCH0DSA 566 li $v1, PORTB 567 li $t8, KSEG1_BASE 568 subu $v1, $v1, $t8 569 sw $v1, 0($v0) 570 571 la $v0, DCH1DSA 572 sw $v1, 0($v0) 573 574 /* 575 Use the block transfer completion interrupt to indicate when the source 576 address can be updated. 577 */ 578 579 la $v0, DCH0INT 580 li $v1, (1 << 19) /* CHBCIE = 1 */ 581 sw $v1, 0($v0) 582 583 /* Enable interrupt for address updating. */ 584 585 la $v0, IPC10 586 li $v1, 0b11111 /* DMA0IP, DMA0IS = 0 */ 587 sw $v1, CLR($v0) 588 589 la $v0, IPC10 590 li $v1, 0b11111 /* DMA0IP = 7, DMA0IS = 3 */ 591 sw $v1, SET($v0) 592 593 la $v0, IEC1 594 li $v1, (1 << 28) /* IEC1<28> = DMA0IE = 1 */ 595 sw $v1, SET($v0) 596 597 /* Enable line channel. */ 598 599 la $v0, DCH0CON 600 li $v1, 0b10000000 601 sw $v1, SET($v0) 602 603 jr $ra 604 nop 605 606 zerodata: 607 .word 0 608 609 610 611 /* Utilities. */ 612 613 handle_error_level: 614 mfc0 $t3, CP0_STATUS 615 li $t4, ~(STATUS_ERL | STATUS_EXL) 616 and $t3, $t3, $t4 617 mtc0 $t3, CP0_STATUS 618 jr $ra 619 nop 620 621 enable_interrupts: 622 mfc0 $t3, CP0_STATUS 623 li $t4, ~STATUS_IRQ /* Clear interrupt priority bits. */ 624 and $t3, $t3, $t4 625 li $t4, ~STATUS_BEV /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */ 626 and $t3, $t3, $t4 627 ori $t3, $t3, STATUS_IE 628 mtc0 $t3, CP0_STATUS 629 jr $ra 630 nop 631 632 init_interrupts: 633 mfc0 $t3, CP0_DEBUG 634 li $t4, ~DEBUG_DM 635 and $t3, $t3, $t4 636 mtc0 $t3, CP0_DEBUG 637 638 mfc0 $t3, CP0_STATUS 639 li $t4, STATUS_BEV /* BEV = 1 or EBASE cannot be set */ 640 or $t3, $t3, $t4 641 mtc0 $t3, CP0_STATUS 642 643 la $t3, exception_handler 644 mtc0 $t3, CP0_EBASE /* EBASE = exception_handler */ 645 646 li $t3, 0x20 /* Must be non-zero or the CPU gets upset */ 647 mtc0 $t3, CP0_INTCTL 648 649 li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */ 650 mtc0 $t3, CP0_CAUSE 651 652 jr $ra 653 nop 654 655 656 657 /* Exception servicing. */ 658 659 .section .flash, "a" 660 661 /* TLB error servicing. */ 662 663 tlb_handler: 664 j exception_handler 665 nop 666 667 668 669 /* General exception servicing. */ 670 671 .org 0x180 672 673 exception_handler: 674 j exc_handler 675 nop 676 677 678 /* Interrupt servicing. */ 679 680 .org 0x200 681 682 interrupt_handler: 683 684 /* Store affected registers. */ 685 686 li $k0, IRQ_STACK_LIMIT 687 sw $v0, -4($k0) 688 sw $v1, -8($k0) 689 sw $s0, -12($k0) 690 sw $s1, -16($k0) 691 sw $s2, -20($k0) 692 sw $s3, -24($k0) 693 sw $t8, -28($k0) 694 sw $ra, -32($k0) 695 sw $sp, -36($k0) 696 697 /* Load state. */ 698 699 lw $s0, -44($k0) 700 lw $s1, -48($k0) 701 lw $s2, -52($k0) 702 lw $s3, -56($k0) 703 704 li $sp, IRQ_STACK_TOP 705 706 /* Check for a timer interrupt condition. */ 707 708 la $v0, IFS0 709 lw $v1, 0($v0) 710 andi $v1, $v1, (1 << 9) /* T2IF */ 711 beqz $v1, irq_dma 712 nop 713 714 /* Increment the line counter. */ 715 716 addiu $s0, $s0, 1 717 718 /* Jump to the event handler. */ 719 720 jalr $s1 721 nop 722 723 irq_clear_timer: 724 725 /* Clear the timer interrupt condition. */ 726 727 la $v0, IFS0 728 li $v1, (1 << 9) /* IFS0<9> = T2IF = 0 */ 729 sw $v1, CLR($v0) 730 731 irq_dma: 732 733 /* Check for a DMA interrupt condition. */ 734 735 la $v0, IFS1 736 lw $v1, 0($v0) 737 li $t8, (1 << 28) /* DMA0IF */ 738 and $v1, $v1, $t8 739 beqz $v1, irq_exit 740 nop 741 742 /* Test the block transfer completion interrupt flag. */ 743 744 la $v0, DCH0INT 745 lw $v1, 0($v0) 746 andi $v1, $v1, (1 << 3) /* CHBCIF */ 747 beqz $v1, irq_clear_dma 748 nop 749 750 /* Clear the block transfer completion interrupt flag. */ 751 752 li $v1, (1 << 3) /* CHBCIF = 0 */ 753 sw $v1, CLR($v0) 754 755 /* 756 Update the line data address if the line counter (referring to the 757 next line) is even. 758 */ 759 760 andi $t8, $s0, 1 761 bnez $t8, irq_clear_dma 762 nop 763 764 /* Reference the next line and update the DMA source address. */ 765 766 addiu $s2, $s2, LINE_LENGTH 767 768 /* Test for wraparound. */ 769 770 li $t8, (SCREEN_BASE + SCREEN_SIZE) 771 sltu $t8, $s2, $t8 772 bnez $t8, irq_dma_update 773 nop 774 775 /* Reset the source address. */ 776 777 li $s2, SCREEN_BASE 778 779 irq_dma_update: 780 781 la $v0, DCH0SSA 782 sw $s2, 0($v0) 783 784 irq_clear_dma: 785 786 /* Clear the DMA interrupt condition. */ 787 788 la $v0, IFS1 789 li $v1, (1 << 28) /* IFS1<28> = DMA0IF = 0 */ 790 sw $v1, CLR($v0) 791 792 irq_exit: 793 /* Save state. */ 794 795 li $k0, IRQ_STACK_LIMIT 796 sw $s0, -44($k0) 797 sw $s1, -48($k0) 798 sw $s2, -52($k0) 799 sw $s3, -56($k0) 800 801 /* Restore affected registers. */ 802 803 lw $v0, -4($k0) 804 lw $v1, -8($k0) 805 lw $s0, -12($k0) 806 lw $s1, -16($k0) 807 lw $s2, -20($k0) 808 lw $s3, -24($k0) 809 lw $t8, -28($k0) 810 lw $ra, -32($k0) 811 lw $sp, -36($k0) 812 813 eret 814 nop 815 816 817 818 exc_handler: 819 li $t9, 0x80000000 820 mfc0 $t6, CP0_CAUSE 821 nop 822 exc_loop: 823 and $t7, $t9, $t6 824 beqz $t7, exc_errorepc_zero 825 nop 826 exc_errorepc_one: 827 la $v0, PORTA 828 li $v1, (1 << 2) /* PORTA<2> = RA2 */ 829 sw $v1, SET($v0) 830 j exc_loop_wait 831 nop 832 exc_errorepc_zero: 833 la $v0, PORTA 834 li $v1, (1 << 3) /* PORTA<3> = RA3 */ 835 sw $v1, SET($v0) 836 exc_loop_wait: 837 li $t8, 5000000 838 exc_loop_delay: 839 addiu $t8, $t8, -1 840 bnez $t8, exc_loop_delay 841 nop 842 la $v0, PORTA 843 li $v1, (3 << 2) /* PORTA<3:2> = RA3, RA2 */ 844 sw $v1, CLR($v0) 845 exc_loop_wait_again: 846 li $t8, 2500000 847 exc_loop_delay_again: 848 addiu $t8, $t8, -1 849 bnez $t8, exc_loop_delay_again 850 nop 851 exc_errorepc_next: 852 srl $t9, $t9, 1 853 bnez $t9, exc_loop 854 nop 855 j exc_handler 856 nop 857 858 859 860 /* Event routines. */ 861 862 /* The vertical back porch. */ 863 864 vbp_active: 865 /* Test for visible region. */ 866 867 sltiu $v0, $s0, VISIBLE_START 868 bnez $v0, _vbp_active_ret 869 nop 870 871 /* Start the visible region. */ 872 873 la $s1, visible_active 874 875 /* Reset the line address. */ 876 877 move $s2, $s3 878 879 /* Enable the line channel. */ 880 881 la $v0, DCH0ECON 882 li $v1, (1 << 4) 883 sw $v1, SET($v0) 884 885 _vbp_active_ret: 886 jr $ra 887 nop 888 889 890 891 /* The visible region. */ 892 893 visible_active: 894 /* Test for front porch. */ 895 896 sltiu $v0, $s0, VFP_START 897 bnez $v0, _visible_active_ret 898 nop 899 900 /* Start the front porch region. */ 901 902 la $s1, vfp_active 903 904 /* Disable the line channel. */ 905 906 la $v0, DCH0ECON 907 li $v1, (1 << 4) 908 sw $v1, CLR($v0) 909 910 _visible_active_ret: 911 jr $ra 912 nop 913 914 915 916 /* Within the vertical front porch. */ 917 918 vfp_active: 919 /* Test for vsync. */ 920 921 sltiu $v0, $s0, VSYNC_START 922 bnez $v0, _vfp_active_ret 923 nop 924 925 /* Start the vsync. */ 926 927 la $s1, vsync_active 928 929 /* Bring vsync low when the next line starts. */ 930 931 la $v0, OC2CON 932 li $v1, 0b010 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */ 933 sw $v1, 0($v0) 934 935 _vfp_active_ret: 936 jr $ra 937 nop 938 939 940 941 /* The vsync period. */ 942 943 vsync_active: 944 /* Test for front porch. */ 945 946 sltiu $v0, $s0, VSYNC_END 947 bnez $v0, _vsync_active_ret 948 nop 949 950 /* Start the back porch. */ 951 952 move $s0, $zero 953 la $s1, vbp_active 954 955 /* Bring vsync high when the next line starts. */ 956 957 la $v0, OC2CON 958 li $v1, 0b001 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 001 (single compare, output driven high) */ 959 sw $v1, 0($v0) 960 961 _vsync_active_ret: 962 jr $ra 963 nop