3.1 --- a/vga.S Thu May 11 00:23:47 2017 +0200
3.2 +++ b/vga.S Sun May 14 13:29:13 2017 +0200
3.3 @@ -20,14 +20,21 @@
3.4 #include "mips.h"
3.5 #include "pic32.h"
3.6
3.7 -#define HFREQ_LIMIT 627 /* 20MHz cycles */
3.8 -#define HSYNC_LIMIT 16 /* bytes or pixels */
3.9 -#define LINE_LIMIT 160 /* bytes or pixels */
3.10 +#define LINE_LENGTH 160 /* pixels */
3.11 +
3.12 +#define HFREQ_LIMIT 1254 /* 40MHz cycles */
3.13 +#define LINE_LIMIT 960 /* 40MHz cycles (3 per byte/pixel) */
3.14 +#define HSYNC_LIMIT 96 /* 40MHz cycles (3 per byte/pixel) */
3.15
3.16 -#define VSYNC_START 1 /* horizontal lines, front porch end */
3.17 -#define VISIBLE_START 4 /* horizontal lines */
3.18 -#define VBP_START 516 /* horizontal lines, back porch start */
3.19 -#define VBP_END 531 /* horizontal lines, back porch end */
3.20 +#define HSYNC_START LINE_LIMIT
3.21 +#define HSYNC_END (HSYNC_START + HSYNC_LIMIT)
3.22 +
3.23 +#define VISIBLE_START 15 /* horizontal lines, back porch end */
3.24 +#define VFP_START 527 /* horizontal lines, front porch start */
3.25 +#define VSYNC_START 529 /* horizontal lines, front porch end */
3.26 +#define VSYNC_END 531 /* horizontal lines, back porch start */
3.27 +
3.28 +#define SCREEN_SIZE (40 * 1024)
3.29
3.30 /* Disable JTAG functionality on pins. */
3.31
3.32 @@ -36,23 +43,31 @@
3.33
3.34 /*
3.35 Set the oscillator to be the FRC oscillator with PLL, with peripheral clock
3.36 -divided by 1, HS oscillator mode selected (for PLL), and FRCDIV+PLL selected.
3.37 -With a system clock of 20MHz, the peripheral clock is therefore 20MHz.
3.38 +divided by 1, and FRCDIV+PLL selected.
3.39 +
3.40 +The system clock and peripheral clock are therefore the same.
3.41
3.42 The watchdog timer (FWDTEN) is also disabled.
3.43 +
3.44 +The secondary oscillator pin (FSOSCEN) is disabled to avoid pin conflicts with
3.45 +RPB4.
3.46 */
3.47
3.48 .section .devcfg1, "a"
3.49 -.word 0xff7fcff9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */
3.50 +.word 0xff7fcfd9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0;
3.51 + DEVCFG1<5> = FSOSCEN = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */
3.52
3.53 /*
3.54 Set the FRC oscillator PLL function with an input division of 4, an output
3.55 division of 2, a multiplication of 20, yielding a multiplication of 2.5.
3.56 -This should take the FRC at 8MHz and produce a system clock of 20MHz.
3.57 +
3.58 +The FRC is apparently at 16MHz and this produces a system clock of 40MHz.
3.59 */
3.60
3.61 .section .devcfg2, "a"
3.62 -.word 0xfff9ffdb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 1; DEVCFG2<6:4> = FPLLMUL<2:0> = 101; DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */
3.63 +.word 0xfff9ffdb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 001;
3.64 + DEVCFG2<6:4> = FPLLMUL<2:0> = 101;
3.65 + DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */
3.66
3.67 .text
3.68 .globl _start
3.69 @@ -83,6 +98,11 @@
3.70 li $v1, KSEG0_BASE
3.71 addu $sp, $v0, $v1 /* sp = KSEG0_BASE + RAM size */
3.72
3.73 + /* Initialise framebuffer. */
3.74 +
3.75 + jal init_framebuffer
3.76 + nop
3.77 +
3.78 /* Initialise the globals pointer. */
3.79
3.80 lui $gp, %hi(_GLOBAL_OFFSET_TABLE_)
3.81 @@ -116,14 +136,9 @@
3.82 jal init_dma
3.83 nop
3.84
3.85 - /* Initialise OC2. */
3.86 + /* Initialise OC3 and OC2. */
3.87
3.88 - jal init_oc2
3.89 - nop
3.90 -
3.91 - /* Initialise framebuffer. */
3.92 -
3.93 - jal init_framebuffer
3.94 + jal init_oc
3.95 nop
3.96
3.97 /* Enable interrupts and loop. */
3.98 @@ -134,14 +149,24 @@
3.99 jal handle_error_level
3.100 nop
3.101
3.102 + /* Set initial sync conditions. */
3.103 +
3.104 + la $t0, PORTB
3.105 + li $t1, (1 << 5) /* PORTB<5> = RB5 */
3.106 + sw $t1, SET($t0)
3.107 +
3.108 + la $t0, PORTB
3.109 + li $t1, (1 << 10) /* PORTB<10> = RB10 */
3.110 + sw $t1, SET($t0)
3.111 +
3.112 /* Main program. */
3.113
3.114 - li $a1, 20000000 /* counter = 20000000 */
3.115 + li $a1, 5000000 /* counter = 5000000 */
3.116
3.117 /* Initialise the display state. */
3.118
3.119 li $s0, 0 /* line counter */
3.120 - la $s1, vfp_active /* current event */
3.121 + la $s1, vbp_active /* current event */
3.122 move $s2, $zero /* line address */
3.123
3.124 /* Monitoring loop. */
3.125 @@ -150,10 +175,10 @@
3.126 bnez $a1, loop /* until counter == 0 */
3.127 nop
3.128
3.129 - li $a1, 20000000 /* counter = 20000000 */
3.130 + li $a1, 5000000 /* counter = 5000000 */
3.131
3.132 la $t0, PORTB
3.133 - li $t1, (1 << 10) /* PORTB<10> = RB10 */
3.134 + li $t1, (1 << 11) /* PORTB<11> = RB11 */
3.135 sw $t1, INV($t0)
3.136
3.137 _next:
3.138 @@ -182,274 +207,11 @@
3.139 la $v0, TRISB
3.140 sw $zero, 0($v0)
3.141
3.142 - jr $ra
3.143 - nop
3.144 -
3.145 -
3.146 -
3.147 -/* Interrupt servicing. */
3.148 -
3.149 -.org 0x200
3.150 -
3.151 -interrupt_handler:
3.152 -
3.153 - /* Check for a timer interrupt condition. */
3.154 -
3.155 - la $v0, IFS0
3.156 - lw $v1, 0($v0)
3.157 - andi $v1, $v1, (1 << 9) /* T2IF */
3.158 - beqz $v1, irq_pmp
3.159 - nop
3.160 -
3.161 - /* Increment the line counter. */
3.162 -
3.163 - addiu $s0, $s0, 1
3.164 -
3.165 - /* Jump to the event handler. */
3.166 -
3.167 - jalr $s1
3.168 - nop
3.169 -
3.170 -irq_clear_timer:
3.171 -
3.172 - /* Clear the timer interrupt condition. */
3.173 -
3.174 - la $v0, IFS0
3.175 - li $v1, (1 << 9) /* IFS0<9> = T2IF = 0 */
3.176 - sw $v1, CLR($v0)
3.177 -
3.178 -irq_pmp:
3.179 -
3.180 - /* Check for a PMP interrupt condition. */
3.181 -
3.182 - la $v0, IFS1
3.183 - lw $v1, 0($v0)
3.184 - li $t8, (1 << 16) /* PMPIF */
3.185 - and $v1, $v1, $t8
3.186 - beqz $v1, irq_dma
3.187 - nop
3.188 -
3.189 -irq_clear_pmp:
3.190 -
3.191 - /* Clear the PMP interrupt condition. */
3.192 -
3.193 - la $v0, IFS1
3.194 - li $v1, (1 << 16) /* IFS1<16> = PMPIF = 0 */
3.195 - sw $v1, CLR($v0)
3.196 -
3.197 -irq_dma:
3.198 -
3.199 - /* Check for a DMA interrupt condition. */
3.200 -
3.201 - la $v0, IFS1
3.202 - lw $v1, 0($v0)
3.203 - li $t8, (1 << 28) /* DMA0IF */
3.204 - and $v1, $v1, $t8
3.205 - beqz $v1, irq_exit
3.206 - nop
3.207 -
3.208 - /* Test the block transfer completion interrupt flag. */
3.209 -
3.210 - la $v0, DCH0INT
3.211 - lw $v1, 0($v0)
3.212 - andi $v1, $v1, (1 << 3) /* CHBCIF */
3.213 - beqz $v1, irq_clear_dma
3.214 - nop
3.215 -
3.216 - /* Clear the block transfer completion interrupt flag. */
3.217 -
3.218 - li $v1, (1 << 3) /* CHBCIF = 0 */
3.219 - sw $v1, CLR($v0)
3.220 -
3.221 - /*
3.222 - Update the line data address if the line counter (referring to the
3.223 - next line) is even.
3.224 - */
3.225 -
3.226 - andi $t8, $s0, 1
3.227 - bnez $t8, irq_clear_dma
3.228 - nop
3.229 -
3.230 - /* Reference the next line and update the DMA source address. */
3.231 -
3.232 - addiu $s2, $s2, LINE_LIMIT
3.233 - la $v0, DCH0SSA
3.234 - sw $s2, 0($v0)
3.235 -
3.236 -irq_clear_dma:
3.237 -
3.238 - /* Clear the DMA interrupt condition. */
3.239 -
3.240 - la $v0, IFS1
3.241 - li $v1, (1 << 28) /* IFS1<28> = DMA0IF = 0 */
3.242 - sw $v1, CLR($v0)
3.243 -
3.244 -irq_exit:
3.245 - eret
3.246 - nop
3.247 -
3.248 -
3.249 -
3.250 -/* Utilities. */
3.251 -
3.252 -handle_error_level:
3.253 - mfc0 $t3, CP0_STATUS
3.254 - li $t4, ~(STATUS_ERL | STATUS_EXL)
3.255 - and $t3, $t3, $t4
3.256 - mtc0 $t3, CP0_STATUS
3.257 - jr $ra
3.258 - nop
3.259 -
3.260 -enable_interrupts:
3.261 - mfc0 $t3, CP0_STATUS
3.262 - nop
3.263 - ori $t3, $t3, STATUS_IRQ | STATUS_IE
3.264 - mtc0 $t3, CP0_STATUS
3.265 - jr $ra
3.266 - nop
3.267 -
3.268 -init_interrupts:
3.269 - mfc0 $t3, CP0_STATUS
3.270 - li $t4, ~STATUS_BEV
3.271 - and $t3, $t3, $t4
3.272 - mtc0 $t3, CP0_STATUS /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */
3.273 + la $v0, PORTA
3.274 + sw $zero, 0($v0)
3.275 + la $v0, PORTB
3.276 + sw $zero, 0($v0)
3.277
3.278 - la $t3, _start
3.279 - mtc0 $t3, CP0_EBASE /* EBASE = _start */
3.280 -
3.281 - li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */
3.282 - mtc0 $t3, CP0_CAUSE
3.283 -
3.284 - jr $ra
3.285 - nop
3.286 -
3.287 -
3.288 -
3.289 -/* Event routines. */
3.290 -
3.291 -/* Start of, and within, the vertical front porch. */
3.292 -
3.293 -vfp_start:
3.294 - /* Clear vsync. */
3.295 -
3.296 - la $v0, PORTB
3.297 - la $v1, (1 << 11) /* PORTB<11> = RB11 */
3.298 - sw $v1, CLR($v0)
3.299 -
3.300 - /* Enter the active region. */
3.301 -
3.302 - la $s1, vfp_active
3.303 -
3.304 -vfp_active:
3.305 - /* Test for vsync. */
3.306 -
3.307 - subu $v0, $s0, VSYNC_START
3.308 - bnez $v0, _vfp_active_ret
3.309 - nop
3.310 -
3.311 - /* Start the vsync region. */
3.312 -
3.313 - la $s1, vsync_start
3.314 -
3.315 -_vfp_active_ret:
3.316 - jr $ra
3.317 - nop
3.318 -
3.319 -
3.320 -
3.321 -/* Start of, and within, vsync. */
3.322 -
3.323 -vsync_start:
3.324 - /* Set vsync. */
3.325 -
3.326 - la $v0, PORTB
3.327 - la $v1, (1 << 11) /* PORTB<11> = RB11 */
3.328 - sw $v1, SET($v0)
3.329 -
3.330 - /* Enter the active region. */
3.331 -
3.332 - la $s1, vsync_active
3.333 -
3.334 -vsync_active:
3.335 - /* Test for visible region. */
3.336 -
3.337 - subu $v0, $s0, VISIBLE_START
3.338 - bnez $v0, _vsync_active_ret
3.339 - nop
3.340 -
3.341 - /* Start the visible region. */
3.342 -
3.343 - la $s1, visible_start
3.344 -
3.345 - /* Enable OC2 for the next line. */
3.346 -
3.347 - la $v0, OC2CON
3.348 - li $v1, (1 << 15)
3.349 - sw $v1, SET($v0)
3.350 -
3.351 - /* Set the start address for line data. */
3.352 -
3.353 - move $s2, $zero
3.354 - la $v0, DCH0SSA
3.355 - sw $s2, 0($v0)
3.356 -
3.357 -_vsync_active_ret:
3.358 - jr $ra
3.359 - nop
3.360 -
3.361 -
3.362 -
3.363 -/* Start of, and within, the visible period. */
3.364 -
3.365 -visible_start:
3.366 - /* Clear vsync. */
3.367 -
3.368 - la $v0, PORTB
3.369 - la $v1, (1 << 11) /* PORTB<11> = RB11 */
3.370 - sw $v1, CLR($v0)
3.371 -
3.372 - /* Enter the active region. */
3.373 -
3.374 - la $s1, visible_active
3.375 -
3.376 -visible_active:
3.377 - /* Test for back porch. */
3.378 -
3.379 - subu $v0, $s0, VBP_START
3.380 - bnez $v0, _visible_active_ret
3.381 - nop
3.382 -
3.383 - /* Start the back porch. */
3.384 -
3.385 - la $s1, vbp_active
3.386 -
3.387 - /* Disable OC2 for the next line. */
3.388 -
3.389 - la $v0, OC2CON
3.390 - li $v1, (1 << 15)
3.391 - sw $v1, CLR($v0)
3.392 -
3.393 -_visible_active_ret:
3.394 - jr $ra
3.395 - nop
3.396 -
3.397 -
3.398 -
3.399 -/* Within the vertical back porch. */
3.400 -
3.401 -vbp_active:
3.402 - /* Test for front porch. */
3.403 -
3.404 - subu $v0, $s0, VBP_END
3.405 - bnez $v0, _vbp_active_ret
3.406 - nop
3.407 -
3.408 - /* Start the front porch. */
3.409 -
3.410 - li $s0, 0
3.411 - la $s1, vfp_start
3.412 -
3.413 -_vbp_active_ret:
3.414 jr $ra
3.415 nop
3.416
3.417 @@ -504,14 +266,49 @@
3.418 /*
3.419 Output compare initialisation.
3.420
3.421 -Timer2 will be used to trigger two events: one initiating the hsync pulse, and
3.422 -one terminating the pulse. Upon the termination of the pulse, an interrupt
3.423 -condition will cause the line data to be transferred using DMA.
3.424 +Timer2 will be used to trigger two events using OC3: one initiating the hsync
3.425 +pulse, and one terminating the pulse. The pulse should appear after the line
3.426 +data has been transferred using DMA, but this is achieved by just choosing
3.427 +suitable start and end values.
3.428
3.429 +Using OC2, Timer 2 triggers a level shifting event and OC2 is reconfigured to
3.430 +reverse the level at a later point.
3.431 */
3.432
3.433 -init_oc2:
3.434 - /* Disable OC interrupts. */
3.435 +init_oc:
3.436 + /* Disable OC3 interrupts. */
3.437 +
3.438 + la $v0, IEC0
3.439 + li $v1, (1 << 17) /* IEC0<17> = OC3IE = 0 */
3.440 + sw $v1, CLR($v0)
3.441 +
3.442 + la $v0, IFS0
3.443 + li $v1, (1 << 17) /* IFS0<17> = OC3IF = 0 */
3.444 + sw $v1, CLR($v0)
3.445 +
3.446 + /* Initialise OC3. */
3.447 +
3.448 + la $v0, OC3CON
3.449 + li $v1, 0b101 /* OC3CON<2:0> = OCM<2:0> = 101 (dual compare, continuous pulse) */
3.450 + sw $v1, 0($v0)
3.451 +
3.452 + /* Pulse start and end. */
3.453 +
3.454 + la $v0, OC3R
3.455 + li $v1, HSYNC_END /* HSYNC_START for positive polarity */
3.456 + sw $v1, 0($v0)
3.457 +
3.458 + la $v0, OC3RS
3.459 + li $v1, HSYNC_START /* HSYNC_END for positive polarity */
3.460 + sw $v1, 0($v0)
3.461 +
3.462 + /* OC3 is enabled. */
3.463 +
3.464 + la $v0, OC3CON
3.465 + li $v1, (1 << 15)
3.466 + sw $v1, SET($v0)
3.467 +
3.468 + /* Disable OC2 interrupts. */
3.469
3.470 la $v0, IEC0
3.471 li $v1, (1 << 12) /* IEC0<12> = OC2IE = 0 */
3.472 @@ -524,26 +321,20 @@
3.473 /* Initialise OC2. */
3.474
3.475 la $v0, OC2CON
3.476 - li $v1, 0b101 /* OC2CON<2:0> = OCM<2:0> = 101 (dual compare, continuous pulse) */
3.477 + li $v1, 0b010 /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */
3.478 sw $v1, 0($v0)
3.479
3.480 - /* Set hsync start and end. */
3.481 + /* Set pulse position. */
3.482
3.483 la $v0, OC2R
3.484 - li $v1, 16
3.485 - sw $v1, 0($v0)
3.486 + sw $zero, 0($v0)
3.487
3.488 - la $v0, OC2RS
3.489 - li $v1, 32
3.490 - sw $v1, 0($v0)
3.491 -
3.492 - /* OC2 is enabled elsewhere when it needs to operate. */
3.493 + /* Enable OC2 later. */
3.494
3.495 jr $ra
3.496 nop
3.497
3.498
3.499 -
3.500 init_oc_pins:
3.501 /* Unlock the configuration register bits. */
3.502
3.503 @@ -559,6 +350,12 @@
3.504 li $v1, (1 << 13) /* IOLOCK = 0 */
3.505 sw $v1, CLR($v0)
3.506
3.507 + /* Map OC3 to RPB10. */
3.508 +
3.509 + la $v0, RPB10R
3.510 + li $v1, 0b0101 /* RPB10R<3:0> = 0101 (OC3) */
3.511 + sw $v1, 0($v0)
3.512 +
3.513 /* Map OC2 to RPB5. */
3.514
3.515 la $v0, RPB5R
3.516 @@ -637,9 +434,9 @@
3.517 /*
3.518 Direct Memory Access initialisation.
3.519
3.520 -Write 160 pixels to the PMP for the line data. This is initiated by an output
3.521 -compare interrupt. Upon completion of the transfer, a DMA interrupt initiates
3.522 -the address update routine, changing the source address of the DMA channel.
3.523 +Write 160 pixels to the PMP for the line data. This is initiated by a timer
3.524 +interrupt. Upon completion of the transfer, a DMA interrupt initiates the
3.525 +address update routine, changing the source address of the DMA channel.
3.526 */
3.527
3.528 init_dma:
3.529 @@ -682,20 +479,20 @@
3.530 /*
3.531 Initiate channel transfers when the initiating interrupt condition
3.532 occurs:
3.533 - DCHxECON<15:8> = CHSIRQ<7:0> = output compare 2 interrupt
3.534 + DCHxECON<15:8> = CHSIRQ<7:0> = timer 2 interrupt
3.535 */
3.536
3.537 la $v0, DCH0ECON
3.538 - li $v1, (12 << 8) | (1 << 4)
3.539 + li $v1, (9 << 8) | (1 << 4)
3.540 sw $v1, 0($v0)
3.541
3.542 /*
3.543 The line channel has a cell size of 160 bytes:
3.544 - DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LIMIT
3.545 + DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LENGTH
3.546 */
3.547
3.548 la $v0, DCH0CSIZ
3.549 - li $v1, LINE_LIMIT
3.550 + li $v1, LINE_LENGTH
3.551 sw $v1, 0($v0)
3.552
3.553 /*
3.554 @@ -704,7 +501,7 @@
3.555 */
3.556
3.557 la $v0, DCH0SSIZ
3.558 - li $v1, LINE_LIMIT
3.559 + li $v1, LINE_LENGTH
3.560 sw $v1, 0($v0)
3.561
3.562 /*
3.563 @@ -756,7 +553,7 @@
3.564
3.565 la $v0, IEC1
3.566 li $v1, (1 << 28) /* IEC1<28> = DMA0IE = 1 */
3.567 - /* sw $v1, SET($v0) */
3.568 + sw $v1, SET($v0)
3.569
3.570 /* Enable channels. */
3.571
3.572 @@ -773,8 +570,8 @@
3.573
3.574 init_framebuffer:
3.575 li $v0, KSEG0_BASE
3.576 - li $t8, 40 * 1024
3.577 - li $v1, 0xff
3.578 + li $t8, SCREEN_SIZE
3.579 + li $v1, 0xff031ce0
3.580
3.581 _init_fb_loop:
3.582 sw $v1, 0($v0)
3.583 @@ -785,3 +582,263 @@
3.584
3.585 jr $ra
3.586 nop
3.587 +
3.588 +
3.589 +
3.590 +/* Utilities. */
3.591 +
3.592 +handle_error_level:
3.593 + mfc0 $t3, CP0_STATUS
3.594 + li $t4, ~(STATUS_ERL | STATUS_EXL)
3.595 + and $t3, $t3, $t4
3.596 + mtc0 $t3, CP0_STATUS
3.597 + jr $ra
3.598 + nop
3.599 +
3.600 +enable_interrupts:
3.601 + mfc0 $t3, CP0_STATUS
3.602 + li $t4, ~STATUS_IRQ /* Clear interrupt priority bits. */
3.603 + and $t3, $t3, $t4
3.604 + li $t4, ~STATUS_BEV /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */
3.605 + and $t3, $t3, $t4
3.606 + ori $t3, $t3, STATUS_IE
3.607 + mtc0 $t3, CP0_STATUS
3.608 + jr $ra
3.609 + nop
3.610 +
3.611 +init_interrupts:
3.612 + mfc0 $t3, CP0_DEBUG
3.613 + li $t4, ~DEBUG_DM
3.614 + and $t3, $t3, $t4
3.615 + mtc0 $t3, CP0_DEBUG
3.616 +
3.617 + mfc0 $t3, CP0_STATUS
3.618 + li $t4, STATUS_BEV
3.619 + or $t3, $t3, $t4
3.620 + mtc0 $t3, CP0_STATUS
3.621 +
3.622 + la $t3, exception_handler
3.623 + mtc0 $t3, CP0_EBASE /* EBASE = exception_handler */
3.624 +
3.625 + li $t3, 0x20 /* Must be non-zero or the CPU gets upset */
3.626 + mtc0 $t3, CP0_INTCTL
3.627 +
3.628 + li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */
3.629 + mtc0 $t3, CP0_CAUSE
3.630 +
3.631 + /* Multiple vector...
3.632 + la $t3, INTCON
3.633 + li $t4, (1 << 12)
3.634 + sw $t4, SET($t3) */
3.635 +
3.636 + jr $ra
3.637 + nop
3.638 +
3.639 +
3.640 +
3.641 +/* Exception servicing. */
3.642 +
3.643 +.section .flash, "a"
3.644 +
3.645 +/* Exception servicing. */
3.646 +
3.647 +exception_handler:
3.648 + li $t8, 2500000
3.649 +exc_loop:
3.650 + addiu $t8, $t8, -1
3.651 + bnez $t8, exc_loop
3.652 + nop
3.653 + la $v0, PORTB
3.654 + li $v1, (1 << 11) /* PORTB<11> = RB11 */
3.655 + sw $v1, INV($v0)
3.656 + j exception_handler
3.657 + nop
3.658 +
3.659 +
3.660 +
3.661 +/* Interrupt servicing. */
3.662 +
3.663 +.org 0x200
3.664 +
3.665 +interrupt_handler:
3.666 +
3.667 + /* Check for a timer interrupt condition. */
3.668 +
3.669 + la $v0, IFS0
3.670 + lw $v1, 0($v0)
3.671 + andi $v1, $v1, (1 << 9) /* T2IF */
3.672 + beqz $v1, irq_dma
3.673 + nop
3.674 +
3.675 + /* Increment the line counter. */
3.676 +
3.677 + addiu $s0, $s0, 1
3.678 +
3.679 + /* Jump to the event handler. */
3.680 +
3.681 + jalr $s1
3.682 + nop
3.683 +
3.684 +irq_clear_timer:
3.685 +
3.686 + /* Clear the timer interrupt condition. */
3.687 +
3.688 + la $v0, IFS0
3.689 + li $v1, (1 << 9) /* IFS0<9> = T2IF = 0 */
3.690 + sw $v1, CLR($v0)
3.691 +
3.692 +irq_dma:
3.693 +
3.694 + /* Check for a DMA interrupt condition. */
3.695 +
3.696 + la $v0, IFS1
3.697 + lw $v1, 0($v0)
3.698 + li $t8, (1 << 28) /* DMA0IF */
3.699 + and $v1, $v1, $t8
3.700 + beqz $v1, irq_exit
3.701 + nop
3.702 +
3.703 + /* Test the block transfer completion interrupt flag. */
3.704 +
3.705 + la $v0, DCH0INT
3.706 + lw $v1, 0($v0)
3.707 + andi $v1, $v1, (1 << 3) /* CHBCIF */
3.708 + beqz $v1, irq_clear_dma
3.709 + nop
3.710 +
3.711 + /* Clear the block transfer completion interrupt flag. */
3.712 +
3.713 + li $v1, (1 << 3) /* CHBCIF = 0 */
3.714 + sw $v1, CLR($v0)
3.715 +
3.716 + /*
3.717 + Update the line data address if the line counter (referring to the
3.718 + next line) is even.
3.719 + */
3.720 +
3.721 + andi $t8, $s0, 1
3.722 + bnez $t8, irq_clear_dma
3.723 + nop
3.724 +
3.725 + /* Reference the next line and update the DMA source address. */
3.726 +
3.727 + addiu $s2, $s2, LINE_LENGTH
3.728 +
3.729 + /* Test for wraparound. */
3.730 +
3.731 + li $t8, SCREEN_SIZE
3.732 + sltu $t8, $s2, $t8
3.733 + bnez $t8, irq_dma_update
3.734 + nop
3.735 +
3.736 + /* Reset the source address. */
3.737 +
3.738 + move $s2, $zero
3.739 +
3.740 +irq_dma_update:
3.741 +
3.742 + la $v0, DCH0SSA
3.743 + sw $s2, 0($v0)
3.744 +
3.745 +irq_clear_dma:
3.746 +
3.747 + /* Clear the DMA interrupt condition. */
3.748 +
3.749 + la $v0, IFS1
3.750 + li $v1, (1 << 28) /* IFS1<28> = DMA0IF = 0 */
3.751 + sw $v1, CLR($v0)
3.752 +
3.753 +irq_exit:
3.754 + eret
3.755 + nop
3.756 +
3.757 +
3.758 +
3.759 +/* Event routines. */
3.760 +
3.761 +/* The vertical back porch. */
3.762 +
3.763 +vbp_active:
3.764 + /* Test for visible region. */
3.765 +
3.766 + sltiu $v0, $s0, VISIBLE_START
3.767 + bnez $v0, _vbp_active_ret
3.768 + nop
3.769 +
3.770 + /* Start the visible region. */
3.771 +
3.772 + la $s1, visible_active
3.773 +
3.774 +_vbp_active_ret:
3.775 + jr $ra
3.776 + nop
3.777 +
3.778 +
3.779 +
3.780 +/* The visible region. */
3.781 +
3.782 +visible_active:
3.783 + /* Test for front porch. */
3.784 +
3.785 + sltiu $v0, $s0, VFP_START
3.786 + bnez $v0, _visible_active_ret
3.787 + nop
3.788 +
3.789 + /* Start the front porch region. */
3.790 +
3.791 + la $s1, vfp_active
3.792 +
3.793 +_visible_active_ret:
3.794 + jr $ra
3.795 + nop
3.796 +
3.797 +
3.798 +
3.799 +/* Within the vertical front porch. */
3.800 +
3.801 +vfp_active:
3.802 + /* Test for vsync. */
3.803 +
3.804 + sltiu $v0, $s0, VSYNC_START
3.805 + bnez $v0, _vfp_active_ret
3.806 + nop
3.807 +
3.808 + /* Start the vsync. */
3.809 +
3.810 + la $s1, vsync_active
3.811 +
3.812 + /* Bring vsync low when the next line starts. */
3.813 +
3.814 + la $v0, OC2CON
3.815 + li $v1, 0b010 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */
3.816 + sw $v1, 0($v0)
3.817 +
3.818 +_vfp_active_ret:
3.819 + jr $ra
3.820 + nop
3.821 +
3.822 +
3.823 +
3.824 +/* The vsync period. */
3.825 +
3.826 +vsync_active:
3.827 + /* Test for front porch. */
3.828 +
3.829 + sltiu $v0, $s0, VSYNC_END
3.830 + bnez $v0, _vsync_active_ret
3.831 + nop
3.832 +
3.833 + /* Start the back porch. */
3.834 +
3.835 + move $s0, $zero
3.836 + la $s1, vbp_active
3.837 +
3.838 + /* Bring vsync high when the next line starts. */
3.839 +
3.840 + la $v0, OC2CON
3.841 + li $v1, 0b001 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 001 (single compare, output driven high) */
3.842 + sw $v1, 0($v0)
3.843 +
3.844 +_vsync_active_ret:
3.845 + jr $ra
3.846 + nop