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_timers 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_timers: 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 /* Start timer. */ 331 332 la $v0, T2CON 333 li $v1, (1 << 15) 334 sw $v1, SET($v0) /* ON = 1 */ 335 336 /* Initialise Timer3 for line DMA cell transfer. */ 337 338 la $v0, T3CON 339 sw $zero, 0($v0) /* T3CON = 0 */ 340 nop 341 342 la $v0, TMR3 343 sw $zero, 0($v0) /* TMR3 = 0 */ 344 345 la $v0, PR3 346 li $v1, 1 347 sw $v1, 0($v0) /* PR3 = 1 */ 348 349 /* Start timer. */ 350 351 la $v0, T3CON 352 li $v1, (1 << 15) 353 sw $v1, SET($v0) /* ON = 1 */ 354 355 jr $ra 356 nop 357 358 359 360 /* 361 Output compare initialisation. 362 363 Timer2 will be used to trigger two events using OC1: one initiating the hsync 364 pulse, and one terminating the pulse. The pulse should appear after the line 365 data has been transferred using DMA, but this is achieved by just choosing 366 suitable start and end values. 367 368 Using OC2, Timer2 triggers a level shifting event and OC2 is reconfigured to 369 reverse the level at a later point. In this way, the vsync pulse is generated 370 and is synchronised to the display lines. 371 372 The OC1 interrupt is used to update the state machine handling the display, 373 also invoking the address update routine at the end of each visible display 374 line, changing the source address of the DMA channel. 375 */ 376 377 init_oc: 378 /* Disable OC1 interrupts. */ 379 380 li $v1, (1 << 7) 381 382 la $v0, IEC0 383 sw $v1, CLR($v0) /* IEC0<7> = OC1IE = 0 */ 384 la $v0, IFS0 385 sw $v1, CLR($v0) /* IFS0<7> = OC1IF = 0 */ 386 387 /* Initialise OC1. */ 388 389 la $v0, OC1CON 390 li $v1, 0b101 /* OC1CON<2:0> = OCM<2:0> = 101 (dual compare, continuous pulse) */ 391 sw $v1, 0($v0) 392 393 /* Pulse start and end. */ 394 395 la $v0, OC1R 396 li $v1, HSYNC_END /* HSYNC_START for positive polarity */ 397 sw $v1, 0($v0) 398 399 la $v0, OC1RS 400 li $v1, HSYNC_START /* HSYNC_END for positive polarity */ 401 sw $v1, 0($v0) 402 403 /* Enable interrupt for address updating. */ 404 405 la $v0, IPC1 406 li $v1, (0b11111 << 16) 407 sw $v1, SET($v0) /* OC1IP = 7; OC1IS = 3 */ 408 409 la $v0, IEC0 410 li $v1, (1 << 7) 411 sw $v1, SET($v0) /* IEC0<7> = OC1IE = 1 */ 412 413 /* OC1 is enabled. */ 414 415 la $v0, OC1CON 416 li $v1, (1 << 15) 417 sw $v1, SET($v0) 418 419 /* Disable OC2 interrupts. */ 420 421 li $v1, (1 << 12) 422 423 la $v0, IEC0 424 sw $v1, CLR($v0) /* IEC0<12> = OC2IE = 0 */ 425 la $v0, IFS0 426 sw $v1, CLR($v0) /* IFS0<12> = OC2IF = 0 */ 427 428 /* Initialise OC2. */ 429 430 la $v0, OC2CON 431 li $v1, 0b010 /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */ 432 sw $v1, 0($v0) 433 434 /* Set pulse position. */ 435 436 la $v0, OC2R 437 sw $zero, 0($v0) 438 439 /* Enable OC2 later. */ 440 441 jr $ra 442 nop 443 444 init_io_pins: 445 /* Unlock the configuration register bits. */ 446 447 la $v0, SYSKEY 448 sw $zero, 0($v0) 449 li $v1, 0xAA996655 450 sw $v1, 0($v0) 451 li $v1, 0x556699AA 452 sw $v1, 0($v0) 453 454 la $v0, CFGCON 455 lw $t8, 0($v0) 456 li $v1, (1 << 13) /* IOLOCK = 0 */ 457 sw $v1, CLR($v0) 458 459 /* Map OC1 to RPA0. */ 460 461 la $v0, RPA0R 462 li $v1, 0b0101 /* RPA0R<3:0> = 0101 (OC1) */ 463 sw $v1, 0($v0) 464 465 /* Map OC2 to RPA1. */ 466 467 la $v0, RPA1R 468 li $v1, 0b0101 /* RPA1R<3:0> = 0101 (OC2) */ 469 sw $v1, 0($v0) 470 471 /* Map U1TX to RPB15. */ 472 473 la $v0, RPB15R 474 li $v1, 0b0001 /* RPB15R<3:0> = 0001 (U1TX) */ 475 sw $v1, 0($v0) 476 477 la $v0, CFGCON 478 sw $t8, 0($v0) 479 480 /* Lock the oscillator control register again. */ 481 482 la $v0, SYSKEY 483 li $v1, 0x33333333 484 sw $v1, 0($v0) 485 486 jr $ra 487 nop 488 489 490 491 /* 492 Direct Memory Access initialisation. 493 494 Write 160 pixels to PORTB for the line data. This is initiated by a timer 495 interrupt. 496 */ 497 498 init_dma: 499 /* Disable DMA interrupts. */ 500 501 li $v1, (0b111 << 28) 502 503 la $v0, IEC1 504 sw $v1, CLR($v0) /* IEC1<30:28> = DMA2IE, DMA1IE, DMA0IE = 0 */ 505 506 /* Clear DMA interrupt flags. */ 507 508 la $v0, IFS1 509 sw $v1, CLR($v0) /* IFS1<30:28> = DMA2IF, DMA1IF, DMA0IF = 0 */ 510 511 /* Enable DMA. */ 512 513 la $v0, DMACON 514 li $v1, (1 << 15) 515 sw $v1, SET($v0) 516 517 /* 518 Initialise a line channel. 519 The line channel will be channel 0 (x = 0). 520 521 Specify a priority of 3: 522 DCHxCON<1:0> = CHPRI<1:0> = 3 523 524 Auto-enable the channel: 525 DCHxCON<4> = CHAEN = 1 526 */ 527 528 la $v0, DCH0CON 529 li $v1, 0b10011 530 sw $v1, 0($v0) 531 532 /* 533 Initialise a level reset channel. 534 The reset channel will be channel 1 (x = 1). 535 536 Specify a priority of 3: 537 DCHxCON<1:0> = CHPRI<1:0> = 3 538 539 Chain the channel to channel 0: 540 DCHxCON<5> = CHCHN = 1 541 */ 542 543 la $v0, DCH1CON 544 li $v1, 0b100011 545 sw $v1, 0($v0) 546 547 la $v0, DCH2CON 548 li $v1, 0b100011 549 sw $v1, 0($v0) 550 551 /* 552 Initiate channel transfers when the initiating interrupt condition 553 occurs: 554 DCHxECON<15:8> = CHSIRQ<7:0> = timer 2 interrupt 555 DCHxECON<4> = SIRQEN = 1 556 557 For now, however, prevent initiation by not setting SIRQEN. 558 */ 559 560 la $v0, DCH0ECON 561 li $v1, (9 << 8) 562 sw $v1, 0($v0) 563 564 /* 565 Initiate reset channel transfer when channel 0 is finished; 566 timer 3 causes cell transfers: 567 DCHxECON<15:8> = CHSIRQ<7:0> = timer 3 interrupt 568 DCHxECON<4> = SIRQEN = 1 569 */ 570 571 la $v0, DCH1ECON 572 li $v1, (14 << 8) | (1 << 4) 573 sw $v1, 0($v0) 574 575 la $v0, DCH2ECON 576 li $v1, (14 << 8) | (1 << 4) 577 sw $v1, 0($v0) 578 579 /* 580 The line channel has a cell size of the number bytes in a line: 581 DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LENGTH 582 */ 583 584 la $v0, DCH0CSIZ 585 li $v1, LINE_LENGTH 586 sw $v1, 0($v0) 587 588 /* 589 The reset channel has a cell size of a single zero byte: 590 DCHxCSIZ<15:0> = CHCSIZ<15:0> = 1 591 */ 592 593 la $v0, DCH1CSIZ 594 li $v1, 1 595 sw $v1, 0($v0) 596 597 la $v0, DCH2CSIZ 598 sw $v1, 0($v0) 599 600 /* 601 The source has a size identical to the cell size: 602 DCHxSSIZ<15:0> = CHSSIZ<15:0> = LINE_LENGTH or 1 603 */ 604 605 la $v0, DCH0SSIZ 606 li $v1, LINE_LENGTH 607 sw $v1, 0($v0) 608 609 la $v0, DCH1SSIZ 610 li $v1, 1 611 sw $v1, 0($v0) 612 613 la $v0, DCH2SSIZ 614 sw $v1, 0($v0) 615 616 /* 617 The source address is the physical address of the line data: 618 DCHxSSA = physical(line data address) 619 */ 620 621 la $v0, DCH0SSA 622 li $v1, SCREEN_BASE 623 sw $v1, 0($v0) 624 625 /* 626 For the reset channel, a single byte of zero is transferred: 627 DCHxSSA = physical(zero data address) 628 */ 629 630 la $v0, DCH1SSA 631 la $v1, fulldata 632 li $t8, KSEG0_BASE 633 subu $v1, $v1, $t8 634 sw $v1, 0($v0) 635 636 la $v0, DCH2SSA 637 la $v1, zerodata 638 li $t8, KSEG0_BASE 639 subu $v1, $v1, $t8 640 sw $v1, 0($v0) 641 642 /* 643 The destination has a size of 1 byte: 644 DCHxDSIZ<15:0> = CHDSIZ<15:0> = 1 645 */ 646 647 li $v1, 1 648 649 la $v0, DCH0DSIZ 650 sw $v1, 0($v0) 651 652 la $v0, DCH1DSIZ 653 sw $v1, 0($v0) 654 655 la $v0, DCH2DSIZ 656 sw $v1, 0($v0) 657 658 /* 659 The destination address is the physical address of PORTB: 660 DCHxDSA = physical(PORTB) 661 */ 662 663 li $v1, PORTB 664 li $t8, KSEG1_BASE 665 subu $v1, $v1, $t8 666 667 la $v0, DCH0DSA 668 sw $v1, 0($v0) 669 670 la $v0, DCH1DSA 671 sw $v1, 0($v0) 672 673 la $v0, DCH2DSA 674 sw $v1, 0($v0) 675 676 /* Enable interrupt for channel chaining. */ 677 678 la $v0, DCH0INT 679 li $v1, (1 << 19) /* CHBCIE = 1 */ 680 sw $v1, 0($v0) 681 682 la $v0, IPC10 683 li $v1, 0b11111 /* DMA0IP, DMA0IS = 0 */ 684 sw $v1, CLR($v0) 685 li $v1, 0b10011 /* DMA0IP = 4, DMA0IS = 3 */ 686 sw $v1, SET($v0) 687 688 la $v0, IEC1 689 li $v1, (1 << 28) /* IEC1<28> = DMA0IE = 1 */ 690 sw $v1, SET($v0) 691 692 /* Enable line channel. */ 693 694 la $v0, DCH0CON 695 li $v1, 0b10000000 696 sw $v1, SET($v0) 697 698 jr $ra 699 nop 700 701 zerodata: 702 .word 0 703 704 fulldata: 705 .word 255 706 707 708 709 /* 710 UART initialisation. 711 712 Initialise UART transmission at 115200 baud. This is sensitive to the peripheral 713 clock frequency. 714 */ 715 716 init_uart: 717 /* Initialise UART. */ 718 719 la $v0, U1BRG 720 li $v1, 12 /* U1BRG<15:0> = BRG = (FPB / (16 * baudrate)) - 1 = (24000000 / (16 * 115200)) - 1 = 12 */ 721 sw $v1, 0($v0) 722 723 la $v0, U1MODE 724 li $v1, (1 << 15) /* U1MODE<15> = ON = 0 */ 725 sw $v1, CLR($v0) 726 727 /* Start UART. */ 728 729 la $v0, U1STA 730 li $v1, (1 << 10) /* U1STA<10> = UTXEN = 1 */ 731 sw $v1, SET($v0) 732 733 la $v0, U1MODE 734 li $v1, (1 << 15) /* U1MODE<15> = ON = 1 */ 735 sw $v1, SET($v0) 736 737 jr $ra 738 nop 739 740 741 742 /* Utilities. */ 743 744 handle_error_level: 745 mfc0 $t3, CP0_STATUS 746 li $t4, ~(STATUS_ERL | STATUS_EXL) 747 and $t3, $t3, $t4 748 mtc0 $t3, CP0_STATUS 749 jr $ra 750 nop 751 752 enable_interrupts: 753 mfc0 $t3, CP0_STATUS 754 li $t4, ~STATUS_IRQ /* Clear interrupt priority bits. */ 755 and $t3, $t3, $t4 756 ori $t3, $t3, (3 << STATUS_IRQ_SHIFT) 757 li $t4, ~STATUS_BEV /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */ 758 and $t3, $t3, $t4 759 ori $t3, $t3, STATUS_IE 760 mtc0 $t3, CP0_STATUS 761 jr $ra 762 nop 763 764 init_interrupts: 765 mfc0 $t3, CP0_DEBUG 766 li $t4, ~DEBUG_DM 767 and $t3, $t3, $t4 768 mtc0 $t3, CP0_DEBUG 769 770 mfc0 $t3, CP0_STATUS 771 li $t4, STATUS_BEV /* BEV = 1 or EBASE cannot be set */ 772 or $t3, $t3, $t4 773 mtc0 $t3, CP0_STATUS 774 775 la $t3, exception_handler 776 mtc0 $t3, CP0_EBASE /* EBASE = exception_handler */ 777 778 li $t3, 0x20 /* Must be non-zero or the CPU gets upset */ 779 mtc0 $t3, CP0_INTCTL 780 781 li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */ 782 mtc0 $t3, CP0_CAUSE 783 784 jr $ra 785 nop 786 787 788 789 /* Exception servicing. */ 790 791 .section .flash, "a" 792 793 /* TLB error servicing. */ 794 795 tlb_handler: 796 j exception_handler 797 nop 798 799 800 801 /* General exception servicing. */ 802 803 .org 0x180 804 805 exception_handler: 806 j exc_handler 807 nop 808 809 810 811 /* Interrupt servicing. */ 812 813 .org 0x200 814 815 interrupt_handler: 816 817 /* 818 Save affected registers, restoring IRQ state and switching to the IRQ 819 stack. 820 */ 821 822 li $k0, IRQ_STACK_LIMIT 823 save_affected 824 load_state 825 li $sp, IRQ_STACK_TOP 826 827 /* Check for the output compare interrupt condition. */ 828 829 la $v0, IFS0 830 lw $v1, 0($v0) 831 andi $v1, $v1, (1 << 7) /* OC1IF */ 832 beqz $v1, irq_dma 833 nop 834 835 irq_handle: 836 /* Clear the interrupt condition. */ 837 838 sw $v1, CLR($v0) 839 840 /* Increment the line counter. */ 841 842 addiu $s0, $s0, 1 843 844 /* Jump to the event handler. */ 845 846 jalr $s1 847 nop 848 849 irq_dma: 850 /* Clear the DMA channel completion condition. */ 851 852 la $v0, IFS1 853 lw $v1, 0($v0) 854 li $t8, (1 << 28) 855 and $v1, $v1, $t8 856 beqz $v1, irq_exit 857 nop 858 859 sw $v1, CLR($v0) /* IFS1<28> = DMA0IF = 0 */ 860 861 la $v0, DCH0INT 862 lw $v1, 0($v0) 863 andi $v1, $v1, (1 << 3) 864 beqz $v1, irq_exit 865 nop 866 867 sw $v1, CLR($v0) /* CHBCIF = 0 */ 868 869 irq_exit: 870 /* 871 Save IRQ state and restore the affected registers, switching back to the 872 original stack. 873 */ 874 875 li $k0, IRQ_STACK_LIMIT 876 save_state 877 load_affected 878 879 eret 880 nop 881 882 883 884 /* Event routines. */ 885 886 /* The vertical back porch. */ 887 888 vbp_active: 889 /* Test for visible region. */ 890 891 sltiu $v0, $s0, VISIBLE_START 892 bnez $v0, _vbp_active_ret 893 nop 894 895 /* Start the visible region. */ 896 897 la $s1, visible_active 898 899 /* Reset the line address. */ 900 901 move $s2, $s3 902 903 /* Update the source address. */ 904 905 la $v0, DCH0SSA 906 sw $s2, 0($v0) 907 908 /* Enable the line channel for timer event transfer initiation. */ 909 910 la $v0, DCH0ECON 911 li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 1 */ 912 sw $v1, SET($v0) 913 914 _vbp_active_ret: 915 jr $ra 916 nop 917 918 919 920 /* The visible region. */ 921 922 visible_active: 923 /* Test for front porch. */ 924 925 sltiu $v0, $s0, VFP_START 926 bnez $v0, visible_update_address 927 nop 928 929 /* Start the front porch region. */ 930 931 la $s1, vfp_active 932 933 /* Disable the line channel. */ 934 935 la $v0, DCH0ECON 936 li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 0 */ 937 sw $v1, CLR($v0) 938 939 _visible_active_ret: 940 jr $ra 941 nop 942 943 944 945 /* DMA update routine. */ 946 947 visible_update_address: 948 949 /* 950 Update the line data address if the line counter (referring to the 951 next line) is even. 952 */ 953 954 andi $t8, $s0, 1 955 bnez $t8, _visible_update_ret 956 nop 957 958 /* Reference the next line and update the DMA source address. */ 959 960 addiu $s2, $s2, LINE_LENGTH 961 962 /* Test for wraparound. */ 963 964 li $t8, (SCREEN_BASE + SCREEN_SIZE) 965 sltu $t8, $s2, $t8 966 bnez $t8, _visible_dma_update 967 nop 968 969 /* Reset the source address. */ 970 971 li $s2, SCREEN_BASE 972 973 _visible_dma_update: 974 975 /* Update the source address. */ 976 977 la $v0, DCH0SSA 978 sw $s2, 0($v0) 979 980 _visible_update_ret: 981 jr $ra 982 nop 983 984 985 986 /* Within the vertical front porch. */ 987 988 vfp_active: 989 /* Test for vsync. */ 990 991 sltiu $v0, $s0, VSYNC_START 992 bnez $v0, _vfp_active_ret 993 nop 994 995 /* Start the vsync. */ 996 997 la $s1, vsync_active 998 999 /* Bring vsync low when the next line starts. */ 1000 1001 la $v0, OC2CON 1002 li $v1, 0b010 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */ 1003 sw $v1, 0($v0) 1004 1005 _vfp_active_ret: 1006 jr $ra 1007 nop 1008 1009 1010 1011 /* The vsync period. */ 1012 1013 vsync_active: 1014 /* Test for front porch. */ 1015 1016 sltiu $v0, $s0, VSYNC_END 1017 bnez $v0, _vsync_active_ret 1018 nop 1019 1020 /* Start the back porch. */ 1021 1022 move $s0, $zero 1023 la $s1, vbp_active 1024 1025 /* Bring vsync high when the next line starts. */ 1026 1027 la $v0, OC2CON 1028 li $v1, 0b001 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 001 (single compare, output driven high) */ 1029 sw $v1, 0($v0) 1030 1031 _vsync_active_ret: 1032 jr $ra 1033 nop 1034 1035 1036 1037 /* Exception handler. */ 1038 1039 exc_handler: 1040 mfc0 $t7, CP0_ERROREPC 1041 nop 1042 la $ra, exc_handler_end 1043 1044 exc_write_word: 1045 li $t8, 32 1046 la $v0, U1TXREG 1047 exc_loop: 1048 addiu $t8, $t8, -4 1049 srlv $v1, $t7, $t8 /* $v1 = $t7 >> $t8 */ 1050 andi $v1, $v1, 0xF 1051 addiu $t9, $v1, -10 /* $t9 >= 10? */ 1052 bgez $t9, exc_alpha 1053 nop 1054 exc_digit: 1055 addiu $v1, $v1, 48 /* convert to digit: '0' */ 1056 j exc_write 1057 nop 1058 exc_alpha: 1059 addiu $v1, $v1, 55 /* convert to alpha: 'A' - 10 */ 1060 exc_write: 1061 sw $v1, 0($v0) 1062 bnez $t8, exc_loop 1063 nop 1064 exc_loop_end: 1065 li $v1, ' ' 1066 sw $v1, 0($v0) 1067 1068 exc_handler_end: 1069 jr $ra 1070 nop