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 679 /* Interrupt servicing. */ 680 681 .org 0x200 682 683 interrupt_handler: 684 685 /* Store affected registers. */ 686 687 li $k0, IRQ_STACK_LIMIT 688 sw $v0, -4($k0) 689 sw $v1, -8($k0) 690 sw $s0, -12($k0) 691 sw $s1, -16($k0) 692 sw $s2, -20($k0) 693 sw $s3, -24($k0) 694 sw $t8, -28($k0) 695 sw $ra, -32($k0) 696 sw $sp, -36($k0) 697 698 /* Load state. */ 699 700 lw $s0, -44($k0) 701 lw $s1, -48($k0) 702 lw $s2, -52($k0) 703 lw $s3, -56($k0) 704 705 li $sp, IRQ_STACK_TOP 706 707 /* Check for a timer interrupt condition. */ 708 709 la $v0, IFS0 710 lw $v1, 0($v0) 711 andi $v1, $v1, (1 << 9) /* T2IF */ 712 beqz $v1, irq_dma 713 nop 714 715 /* Clear the timer interrupt condition. */ 716 717 li $v1, (1 << 9) /* IFS0<9> = T2IF = 0 */ 718 sw $v1, CLR($v0) 719 720 /* Increment the line counter. */ 721 722 addiu $s0, $s0, 1 723 724 /* Jump to the event handler. */ 725 726 jalr $s1 727 nop 728 729 irq_dma: 730 /* Check for a DMA interrupt condition. */ 731 732 la $v0, IFS1 733 lw $v1, 0($v0) 734 li $t8, (1 << 28) /* DMA0IF */ 735 and $v1, $v1, $t8 736 beqz $v1, irq_exit 737 nop 738 739 /* Clear the DMA interrupt condition. */ 740 741 li $v1, (1 << 28) /* IFS1<28> = DMA0IF = 0 */ 742 sw $v1, CLR($v0) 743 744 /* Test the block transfer completion interrupt flag. */ 745 746 la $v0, DCH0INT 747 lw $v1, 0($v0) 748 andi $v1, $v1, (1 << 3) /* CHBCIF */ 749 beqz $v1, irq_exit 750 nop 751 752 /* Clear the block transfer completion interrupt flag. */ 753 754 li $v1, (1 << 3) /* CHBCIF = 0 */ 755 sw $v1, CLR($v0) 756 757 /* Jump to the DMA update routine. */ 758 759 j visible_update_address 760 nop 761 762 irq_exit: 763 /* Save state. */ 764 765 li $k0, IRQ_STACK_LIMIT 766 sw $s0, -44($k0) 767 sw $s1, -48($k0) 768 sw $s2, -52($k0) 769 sw $s3, -56($k0) 770 771 /* Restore affected registers. */ 772 773 lw $v0, -4($k0) 774 lw $v1, -8($k0) 775 lw $s0, -12($k0) 776 lw $s1, -16($k0) 777 lw $s2, -20($k0) 778 lw $s3, -24($k0) 779 lw $t8, -28($k0) 780 lw $ra, -32($k0) 781 lw $sp, -36($k0) 782 783 eret 784 nop 785 786 787 788 exc_handler: 789 li $t9, 0x80000000 790 mfc0 $t6, CP0_ERROREPC 791 nop 792 exc_loop: 793 and $t7, $t9, $t6 794 beqz $t7, exc_errorepc_zero 795 nop 796 exc_errorepc_one: 797 la $v0, PORTA 798 li $v1, (1 << 2) /* PORTA<2> = RA2 */ 799 sw $v1, SET($v0) 800 j exc_loop_wait 801 nop 802 exc_errorepc_zero: 803 la $v0, PORTA 804 li $v1, (1 << 3) /* PORTA<3> = RA3 */ 805 sw $v1, SET($v0) 806 exc_loop_wait: 807 li $t8, 5000000 808 exc_loop_delay: 809 addiu $t8, $t8, -1 810 bnez $t8, exc_loop_delay 811 nop 812 la $v0, PORTA 813 li $v1, (3 << 2) /* PORTA<3:2> = RA3, RA2 */ 814 sw $v1, CLR($v0) 815 exc_loop_wait_again: 816 li $t8, 2500000 817 exc_loop_delay_again: 818 addiu $t8, $t8, -1 819 bnez $t8, exc_loop_delay_again 820 nop 821 exc_errorepc_next: 822 srl $t9, $t9, 1 823 bnez $t9, exc_loop 824 nop 825 j exc_handler 826 nop 827 828 829 830 /* Event routines. */ 831 832 /* The vertical back porch. */ 833 834 vbp_active: 835 /* Test for visible region. */ 836 837 sltiu $v0, $s0, VISIBLE_START 838 bnez $v0, _vbp_active_ret 839 nop 840 841 /* Start the visible region. */ 842 843 la $s1, visible_active 844 845 /* Reset the line address. */ 846 847 move $s2, $s3 848 849 /* Update the source address. */ 850 851 la $v0, DCH0SSA 852 sw $s2, 0($v0) 853 854 /* Enable the line channel. */ 855 856 la $v0, DCH0ECON 857 li $v1, (1 << 4) 858 sw $v1, SET($v0) 859 860 _vbp_active_ret: 861 jr $ra 862 nop 863 864 865 866 /* The visible region. */ 867 868 visible_active: 869 /* Test for front porch. */ 870 871 sltiu $v0, $s0, VFP_START 872 bnez $v0, _visible_active_ret 873 nop 874 875 /* Start the front porch region. */ 876 877 la $s1, vfp_active 878 879 _visible_active_ret: 880 jr $ra 881 nop 882 883 884 885 /* DMA update routine. */ 886 887 visible_update_address: 888 889 /* Test for the last visible line. */ 890 891 la $v0, vfp_active 892 bne $s1, $v0, _visible_update_address 893 nop 894 895 /* Disable the line channel. */ 896 897 la $v0, DCH0ECON 898 li $v1, (1 << 4) 899 sw $v1, CLR($v0) 900 901 j _visible_update_ret 902 nop 903 904 _visible_update_address: 905 906 /* 907 Update the line data address if the line counter (referring to the 908 next line) is even. 909 */ 910 911 andi $t8, $s0, 1 912 bnez $t8, _visible_update_ret 913 nop 914 915 /* Reference the next line and update the DMA source address. */ 916 917 addiu $s2, $s2, LINE_LENGTH 918 919 /* Test for wraparound. */ 920 921 li $t8, (SCREEN_BASE + SCREEN_SIZE) 922 sltu $t8, $s2, $t8 923 bnez $t8, _visible_dma_update 924 nop 925 926 /* Reset the source address. */ 927 928 li $s2, SCREEN_BASE 929 930 _visible_dma_update: 931 932 /* Disable line channel. */ 933 934 la $v0, DCH0CON 935 li $v1, 0b10000000 936 sw $v1, CLR($v0) 937 938 /* Update the source address. */ 939 940 la $v0, DCH0SSA 941 sw $s2, 0($v0) 942 943 /* Enable line channel. */ 944 945 la $v0, DCH0CON 946 sw $v1, SET($v0) 947 948 _visible_update_ret: 949 j irq_exit 950 nop 951 952 953 954 /* Within the vertical front porch. */ 955 956 vfp_active: 957 /* Test for vsync. */ 958 959 sltiu $v0, $s0, VSYNC_START 960 bnez $v0, _vfp_active_ret 961 nop 962 963 /* Start the vsync. */ 964 965 la $s1, vsync_active 966 967 /* Bring vsync low when the next line starts. */ 968 969 la $v0, OC2CON 970 li $v1, 0b010 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */ 971 sw $v1, 0($v0) 972 973 _vfp_active_ret: 974 jr $ra 975 nop 976 977 978 979 /* The vsync period. */ 980 981 vsync_active: 982 /* Test for front porch. */ 983 984 sltiu $v0, $s0, VSYNC_END 985 bnez $v0, _vsync_active_ret 986 nop 987 988 /* Start the back porch. */ 989 990 move $s0, $zero 991 la $s1, vbp_active 992 993 /* Bring vsync high when the next line starts. */ 994 995 la $v0, OC2CON 996 li $v1, 0b001 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 001 (single compare, output driven high) */ 997 sw $v1, 0($v0) 998 999 _vsync_active_ret: 1000 jr $ra 1001 nop