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