1.1 --- a/vga.S Thu May 11 00:23:47 2017 +0200
1.2 +++ b/vga.S Sun May 14 13:29:13 2017 +0200
1.3 @@ -20,14 +20,21 @@
1.4 #include "mips.h"
1.5 #include "pic32.h"
1.6
1.7 -#define HFREQ_LIMIT 627 /* 20MHz cycles */
1.8 -#define HSYNC_LIMIT 16 /* bytes or pixels */
1.9 -#define LINE_LIMIT 160 /* bytes or pixels */
1.10 +#define LINE_LENGTH 160 /* pixels */
1.11 +
1.12 +#define HFREQ_LIMIT 1254 /* 40MHz cycles */
1.13 +#define LINE_LIMIT 960 /* 40MHz cycles (3 per byte/pixel) */
1.14 +#define HSYNC_LIMIT 96 /* 40MHz cycles (3 per byte/pixel) */
1.15
1.16 -#define VSYNC_START 1 /* horizontal lines, front porch end */
1.17 -#define VISIBLE_START 4 /* horizontal lines */
1.18 -#define VBP_START 516 /* horizontal lines, back porch start */
1.19 -#define VBP_END 531 /* horizontal lines, back porch end */
1.20 +#define HSYNC_START LINE_LIMIT
1.21 +#define HSYNC_END (HSYNC_START + HSYNC_LIMIT)
1.22 +
1.23 +#define VISIBLE_START 15 /* horizontal lines, back porch end */
1.24 +#define VFP_START 527 /* horizontal lines, front porch start */
1.25 +#define VSYNC_START 529 /* horizontal lines, front porch end */
1.26 +#define VSYNC_END 531 /* horizontal lines, back porch start */
1.27 +
1.28 +#define SCREEN_SIZE (40 * 1024)
1.29
1.30 /* Disable JTAG functionality on pins. */
1.31
1.32 @@ -36,23 +43,31 @@
1.33
1.34 /*
1.35 Set the oscillator to be the FRC oscillator with PLL, with peripheral clock
1.36 -divided by 1, HS oscillator mode selected (for PLL), and FRCDIV+PLL selected.
1.37 -With a system clock of 20MHz, the peripheral clock is therefore 20MHz.
1.38 +divided by 1, and FRCDIV+PLL selected.
1.39 +
1.40 +The system clock and peripheral clock are therefore the same.
1.41
1.42 The watchdog timer (FWDTEN) is also disabled.
1.43 +
1.44 +The secondary oscillator pin (FSOSCEN) is disabled to avoid pin conflicts with
1.45 +RPB4.
1.46 */
1.47
1.48 .section .devcfg1, "a"
1.49 -.word 0xff7fcff9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */
1.50 +.word 0xff7fcfd9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0;
1.51 + DEVCFG1<5> = FSOSCEN = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */
1.52
1.53 /*
1.54 Set the FRC oscillator PLL function with an input division of 4, an output
1.55 division of 2, a multiplication of 20, yielding a multiplication of 2.5.
1.56 -This should take the FRC at 8MHz and produce a system clock of 20MHz.
1.57 +
1.58 +The FRC is apparently at 16MHz and this produces a system clock of 40MHz.
1.59 */
1.60
1.61 .section .devcfg2, "a"
1.62 -.word 0xfff9ffdb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 1; DEVCFG2<6:4> = FPLLMUL<2:0> = 101; DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */
1.63 +.word 0xfff9ffdb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 001;
1.64 + DEVCFG2<6:4> = FPLLMUL<2:0> = 101;
1.65 + DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */
1.66
1.67 .text
1.68 .globl _start
1.69 @@ -83,6 +98,11 @@
1.70 li $v1, KSEG0_BASE
1.71 addu $sp, $v0, $v1 /* sp = KSEG0_BASE + RAM size */
1.72
1.73 + /* Initialise framebuffer. */
1.74 +
1.75 + jal init_framebuffer
1.76 + nop
1.77 +
1.78 /* Initialise the globals pointer. */
1.79
1.80 lui $gp, %hi(_GLOBAL_OFFSET_TABLE_)
1.81 @@ -116,14 +136,9 @@
1.82 jal init_dma
1.83 nop
1.84
1.85 - /* Initialise OC2. */
1.86 + /* Initialise OC3 and OC2. */
1.87
1.88 - jal init_oc2
1.89 - nop
1.90 -
1.91 - /* Initialise framebuffer. */
1.92 -
1.93 - jal init_framebuffer
1.94 + jal init_oc
1.95 nop
1.96
1.97 /* Enable interrupts and loop. */
1.98 @@ -134,14 +149,24 @@
1.99 jal handle_error_level
1.100 nop
1.101
1.102 + /* Set initial sync conditions. */
1.103 +
1.104 + la $t0, PORTB
1.105 + li $t1, (1 << 5) /* PORTB<5> = RB5 */
1.106 + sw $t1, SET($t0)
1.107 +
1.108 + la $t0, PORTB
1.109 + li $t1, (1 << 10) /* PORTB<10> = RB10 */
1.110 + sw $t1, SET($t0)
1.111 +
1.112 /* Main program. */
1.113
1.114 - li $a1, 20000000 /* counter = 20000000 */
1.115 + li $a1, 5000000 /* counter = 5000000 */
1.116
1.117 /* Initialise the display state. */
1.118
1.119 li $s0, 0 /* line counter */
1.120 - la $s1, vfp_active /* current event */
1.121 + la $s1, vbp_active /* current event */
1.122 move $s2, $zero /* line address */
1.123
1.124 /* Monitoring loop. */
1.125 @@ -150,10 +175,10 @@
1.126 bnez $a1, loop /* until counter == 0 */
1.127 nop
1.128
1.129 - li $a1, 20000000 /* counter = 20000000 */
1.130 + li $a1, 5000000 /* counter = 5000000 */
1.131
1.132 la $t0, PORTB
1.133 - li $t1, (1 << 10) /* PORTB<10> = RB10 */
1.134 + li $t1, (1 << 11) /* PORTB<11> = RB11 */
1.135 sw $t1, INV($t0)
1.136
1.137 _next:
1.138 @@ -182,274 +207,11 @@
1.139 la $v0, TRISB
1.140 sw $zero, 0($v0)
1.141
1.142 - jr $ra
1.143 - nop
1.144 -
1.145 -
1.146 -
1.147 -/* Interrupt servicing. */
1.148 -
1.149 -.org 0x200
1.150 -
1.151 -interrupt_handler:
1.152 -
1.153 - /* Check for a timer interrupt condition. */
1.154 -
1.155 - la $v0, IFS0
1.156 - lw $v1, 0($v0)
1.157 - andi $v1, $v1, (1 << 9) /* T2IF */
1.158 - beqz $v1, irq_pmp
1.159 - nop
1.160 -
1.161 - /* Increment the line counter. */
1.162 -
1.163 - addiu $s0, $s0, 1
1.164 -
1.165 - /* Jump to the event handler. */
1.166 -
1.167 - jalr $s1
1.168 - nop
1.169 -
1.170 -irq_clear_timer:
1.171 -
1.172 - /* Clear the timer interrupt condition. */
1.173 -
1.174 - la $v0, IFS0
1.175 - li $v1, (1 << 9) /* IFS0<9> = T2IF = 0 */
1.176 - sw $v1, CLR($v0)
1.177 -
1.178 -irq_pmp:
1.179 -
1.180 - /* Check for a PMP interrupt condition. */
1.181 -
1.182 - la $v0, IFS1
1.183 - lw $v1, 0($v0)
1.184 - li $t8, (1 << 16) /* PMPIF */
1.185 - and $v1, $v1, $t8
1.186 - beqz $v1, irq_dma
1.187 - nop
1.188 -
1.189 -irq_clear_pmp:
1.190 -
1.191 - /* Clear the PMP interrupt condition. */
1.192 -
1.193 - la $v0, IFS1
1.194 - li $v1, (1 << 16) /* IFS1<16> = PMPIF = 0 */
1.195 - sw $v1, CLR($v0)
1.196 -
1.197 -irq_dma:
1.198 -
1.199 - /* Check for a DMA interrupt condition. */
1.200 -
1.201 - la $v0, IFS1
1.202 - lw $v1, 0($v0)
1.203 - li $t8, (1 << 28) /* DMA0IF */
1.204 - and $v1, $v1, $t8
1.205 - beqz $v1, irq_exit
1.206 - nop
1.207 -
1.208 - /* Test the block transfer completion interrupt flag. */
1.209 -
1.210 - la $v0, DCH0INT
1.211 - lw $v1, 0($v0)
1.212 - andi $v1, $v1, (1 << 3) /* CHBCIF */
1.213 - beqz $v1, irq_clear_dma
1.214 - nop
1.215 -
1.216 - /* Clear the block transfer completion interrupt flag. */
1.217 -
1.218 - li $v1, (1 << 3) /* CHBCIF = 0 */
1.219 - sw $v1, CLR($v0)
1.220 -
1.221 - /*
1.222 - Update the line data address if the line counter (referring to the
1.223 - next line) is even.
1.224 - */
1.225 -
1.226 - andi $t8, $s0, 1
1.227 - bnez $t8, irq_clear_dma
1.228 - nop
1.229 -
1.230 - /* Reference the next line and update the DMA source address. */
1.231 -
1.232 - addiu $s2, $s2, LINE_LIMIT
1.233 - la $v0, DCH0SSA
1.234 - sw $s2, 0($v0)
1.235 -
1.236 -irq_clear_dma:
1.237 -
1.238 - /* Clear the DMA interrupt condition. */
1.239 -
1.240 - la $v0, IFS1
1.241 - li $v1, (1 << 28) /* IFS1<28> = DMA0IF = 0 */
1.242 - sw $v1, CLR($v0)
1.243 -
1.244 -irq_exit:
1.245 - eret
1.246 - nop
1.247 -
1.248 -
1.249 -
1.250 -/* Utilities. */
1.251 -
1.252 -handle_error_level:
1.253 - mfc0 $t3, CP0_STATUS
1.254 - li $t4, ~(STATUS_ERL | STATUS_EXL)
1.255 - and $t3, $t3, $t4
1.256 - mtc0 $t3, CP0_STATUS
1.257 - jr $ra
1.258 - nop
1.259 -
1.260 -enable_interrupts:
1.261 - mfc0 $t3, CP0_STATUS
1.262 - nop
1.263 - ori $t3, $t3, STATUS_IRQ | STATUS_IE
1.264 - mtc0 $t3, CP0_STATUS
1.265 - jr $ra
1.266 - nop
1.267 -
1.268 -init_interrupts:
1.269 - mfc0 $t3, CP0_STATUS
1.270 - li $t4, ~STATUS_BEV
1.271 - and $t3, $t3, $t4
1.272 - mtc0 $t3, CP0_STATUS /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */
1.273 + la $v0, PORTA
1.274 + sw $zero, 0($v0)
1.275 + la $v0, PORTB
1.276 + sw $zero, 0($v0)
1.277
1.278 - la $t3, _start
1.279 - mtc0 $t3, CP0_EBASE /* EBASE = _start */
1.280 -
1.281 - li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */
1.282 - mtc0 $t3, CP0_CAUSE
1.283 -
1.284 - jr $ra
1.285 - nop
1.286 -
1.287 -
1.288 -
1.289 -/* Event routines. */
1.290 -
1.291 -/* Start of, and within, the vertical front porch. */
1.292 -
1.293 -vfp_start:
1.294 - /* Clear vsync. */
1.295 -
1.296 - la $v0, PORTB
1.297 - la $v1, (1 << 11) /* PORTB<11> = RB11 */
1.298 - sw $v1, CLR($v0)
1.299 -
1.300 - /* Enter the active region. */
1.301 -
1.302 - la $s1, vfp_active
1.303 -
1.304 -vfp_active:
1.305 - /* Test for vsync. */
1.306 -
1.307 - subu $v0, $s0, VSYNC_START
1.308 - bnez $v0, _vfp_active_ret
1.309 - nop
1.310 -
1.311 - /* Start the vsync region. */
1.312 -
1.313 - la $s1, vsync_start
1.314 -
1.315 -_vfp_active_ret:
1.316 - jr $ra
1.317 - nop
1.318 -
1.319 -
1.320 -
1.321 -/* Start of, and within, vsync. */
1.322 -
1.323 -vsync_start:
1.324 - /* Set vsync. */
1.325 -
1.326 - la $v0, PORTB
1.327 - la $v1, (1 << 11) /* PORTB<11> = RB11 */
1.328 - sw $v1, SET($v0)
1.329 -
1.330 - /* Enter the active region. */
1.331 -
1.332 - la $s1, vsync_active
1.333 -
1.334 -vsync_active:
1.335 - /* Test for visible region. */
1.336 -
1.337 - subu $v0, $s0, VISIBLE_START
1.338 - bnez $v0, _vsync_active_ret
1.339 - nop
1.340 -
1.341 - /* Start the visible region. */
1.342 -
1.343 - la $s1, visible_start
1.344 -
1.345 - /* Enable OC2 for the next line. */
1.346 -
1.347 - la $v0, OC2CON
1.348 - li $v1, (1 << 15)
1.349 - sw $v1, SET($v0)
1.350 -
1.351 - /* Set the start address for line data. */
1.352 -
1.353 - move $s2, $zero
1.354 - la $v0, DCH0SSA
1.355 - sw $s2, 0($v0)
1.356 -
1.357 -_vsync_active_ret:
1.358 - jr $ra
1.359 - nop
1.360 -
1.361 -
1.362 -
1.363 -/* Start of, and within, the visible period. */
1.364 -
1.365 -visible_start:
1.366 - /* Clear vsync. */
1.367 -
1.368 - la $v0, PORTB
1.369 - la $v1, (1 << 11) /* PORTB<11> = RB11 */
1.370 - sw $v1, CLR($v0)
1.371 -
1.372 - /* Enter the active region. */
1.373 -
1.374 - la $s1, visible_active
1.375 -
1.376 -visible_active:
1.377 - /* Test for back porch. */
1.378 -
1.379 - subu $v0, $s0, VBP_START
1.380 - bnez $v0, _visible_active_ret
1.381 - nop
1.382 -
1.383 - /* Start the back porch. */
1.384 -
1.385 - la $s1, vbp_active
1.386 -
1.387 - /* Disable OC2 for the next line. */
1.388 -
1.389 - la $v0, OC2CON
1.390 - li $v1, (1 << 15)
1.391 - sw $v1, CLR($v0)
1.392 -
1.393 -_visible_active_ret:
1.394 - jr $ra
1.395 - nop
1.396 -
1.397 -
1.398 -
1.399 -/* Within the vertical back porch. */
1.400 -
1.401 -vbp_active:
1.402 - /* Test for front porch. */
1.403 -
1.404 - subu $v0, $s0, VBP_END
1.405 - bnez $v0, _vbp_active_ret
1.406 - nop
1.407 -
1.408 - /* Start the front porch. */
1.409 -
1.410 - li $s0, 0
1.411 - la $s1, vfp_start
1.412 -
1.413 -_vbp_active_ret:
1.414 jr $ra
1.415 nop
1.416
1.417 @@ -504,14 +266,49 @@
1.418 /*
1.419 Output compare initialisation.
1.420
1.421 -Timer2 will be used to trigger two events: one initiating the hsync pulse, and
1.422 -one terminating the pulse. Upon the termination of the pulse, an interrupt
1.423 -condition will cause the line data to be transferred using DMA.
1.424 +Timer2 will be used to trigger two events using OC3: one initiating the hsync
1.425 +pulse, and one terminating the pulse. The pulse should appear after the line
1.426 +data has been transferred using DMA, but this is achieved by just choosing
1.427 +suitable start and end values.
1.428
1.429 +Using OC2, Timer 2 triggers a level shifting event and OC2 is reconfigured to
1.430 +reverse the level at a later point.
1.431 */
1.432
1.433 -init_oc2:
1.434 - /* Disable OC interrupts. */
1.435 +init_oc:
1.436 + /* Disable OC3 interrupts. */
1.437 +
1.438 + la $v0, IEC0
1.439 + li $v1, (1 << 17) /* IEC0<17> = OC3IE = 0 */
1.440 + sw $v1, CLR($v0)
1.441 +
1.442 + la $v0, IFS0
1.443 + li $v1, (1 << 17) /* IFS0<17> = OC3IF = 0 */
1.444 + sw $v1, CLR($v0)
1.445 +
1.446 + /* Initialise OC3. */
1.447 +
1.448 + la $v0, OC3CON
1.449 + li $v1, 0b101 /* OC3CON<2:0> = OCM<2:0> = 101 (dual compare, continuous pulse) */
1.450 + sw $v1, 0($v0)
1.451 +
1.452 + /* Pulse start and end. */
1.453 +
1.454 + la $v0, OC3R
1.455 + li $v1, HSYNC_END /* HSYNC_START for positive polarity */
1.456 + sw $v1, 0($v0)
1.457 +
1.458 + la $v0, OC3RS
1.459 + li $v1, HSYNC_START /* HSYNC_END for positive polarity */
1.460 + sw $v1, 0($v0)
1.461 +
1.462 + /* OC3 is enabled. */
1.463 +
1.464 + la $v0, OC3CON
1.465 + li $v1, (1 << 15)
1.466 + sw $v1, SET($v0)
1.467 +
1.468 + /* Disable OC2 interrupts. */
1.469
1.470 la $v0, IEC0
1.471 li $v1, (1 << 12) /* IEC0<12> = OC2IE = 0 */
1.472 @@ -524,26 +321,20 @@
1.473 /* Initialise OC2. */
1.474
1.475 la $v0, OC2CON
1.476 - li $v1, 0b101 /* OC2CON<2:0> = OCM<2:0> = 101 (dual compare, continuous pulse) */
1.477 + li $v1, 0b010 /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */
1.478 sw $v1, 0($v0)
1.479
1.480 - /* Set hsync start and end. */
1.481 + /* Set pulse position. */
1.482
1.483 la $v0, OC2R
1.484 - li $v1, 16
1.485 - sw $v1, 0($v0)
1.486 + sw $zero, 0($v0)
1.487
1.488 - la $v0, OC2RS
1.489 - li $v1, 32
1.490 - sw $v1, 0($v0)
1.491 -
1.492 - /* OC2 is enabled elsewhere when it needs to operate. */
1.493 + /* Enable OC2 later. */
1.494
1.495 jr $ra
1.496 nop
1.497
1.498
1.499 -
1.500 init_oc_pins:
1.501 /* Unlock the configuration register bits. */
1.502
1.503 @@ -559,6 +350,12 @@
1.504 li $v1, (1 << 13) /* IOLOCK = 0 */
1.505 sw $v1, CLR($v0)
1.506
1.507 + /* Map OC3 to RPB10. */
1.508 +
1.509 + la $v0, RPB10R
1.510 + li $v1, 0b0101 /* RPB10R<3:0> = 0101 (OC3) */
1.511 + sw $v1, 0($v0)
1.512 +
1.513 /* Map OC2 to RPB5. */
1.514
1.515 la $v0, RPB5R
1.516 @@ -637,9 +434,9 @@
1.517 /*
1.518 Direct Memory Access initialisation.
1.519
1.520 -Write 160 pixels to the PMP for the line data. This is initiated by an output
1.521 -compare interrupt. Upon completion of the transfer, a DMA interrupt initiates
1.522 -the address update routine, changing the source address of the DMA channel.
1.523 +Write 160 pixels to the PMP for the line data. This is initiated by a timer
1.524 +interrupt. Upon completion of the transfer, a DMA interrupt initiates the
1.525 +address update routine, changing the source address of the DMA channel.
1.526 */
1.527
1.528 init_dma:
1.529 @@ -682,20 +479,20 @@
1.530 /*
1.531 Initiate channel transfers when the initiating interrupt condition
1.532 occurs:
1.533 - DCHxECON<15:8> = CHSIRQ<7:0> = output compare 2 interrupt
1.534 + DCHxECON<15:8> = CHSIRQ<7:0> = timer 2 interrupt
1.535 */
1.536
1.537 la $v0, DCH0ECON
1.538 - li $v1, (12 << 8) | (1 << 4)
1.539 + li $v1, (9 << 8) | (1 << 4)
1.540 sw $v1, 0($v0)
1.541
1.542 /*
1.543 The line channel has a cell size of 160 bytes:
1.544 - DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LIMIT
1.545 + DCHxCSIZ<15:0> = CHCSIZ<15:0> = LINE_LENGTH
1.546 */
1.547
1.548 la $v0, DCH0CSIZ
1.549 - li $v1, LINE_LIMIT
1.550 + li $v1, LINE_LENGTH
1.551 sw $v1, 0($v0)
1.552
1.553 /*
1.554 @@ -704,7 +501,7 @@
1.555 */
1.556
1.557 la $v0, DCH0SSIZ
1.558 - li $v1, LINE_LIMIT
1.559 + li $v1, LINE_LENGTH
1.560 sw $v1, 0($v0)
1.561
1.562 /*
1.563 @@ -756,7 +553,7 @@
1.564
1.565 la $v0, IEC1
1.566 li $v1, (1 << 28) /* IEC1<28> = DMA0IE = 1 */
1.567 - /* sw $v1, SET($v0) */
1.568 + sw $v1, SET($v0)
1.569
1.570 /* Enable channels. */
1.571
1.572 @@ -773,8 +570,8 @@
1.573
1.574 init_framebuffer:
1.575 li $v0, KSEG0_BASE
1.576 - li $t8, 40 * 1024
1.577 - li $v1, 0xff
1.578 + li $t8, SCREEN_SIZE
1.579 + li $v1, 0xff031ce0
1.580
1.581 _init_fb_loop:
1.582 sw $v1, 0($v0)
1.583 @@ -785,3 +582,263 @@
1.584
1.585 jr $ra
1.586 nop
1.587 +
1.588 +
1.589 +
1.590 +/* Utilities. */
1.591 +
1.592 +handle_error_level:
1.593 + mfc0 $t3, CP0_STATUS
1.594 + li $t4, ~(STATUS_ERL | STATUS_EXL)
1.595 + and $t3, $t3, $t4
1.596 + mtc0 $t3, CP0_STATUS
1.597 + jr $ra
1.598 + nop
1.599 +
1.600 +enable_interrupts:
1.601 + mfc0 $t3, CP0_STATUS
1.602 + li $t4, ~STATUS_IRQ /* Clear interrupt priority bits. */
1.603 + and $t3, $t3, $t4
1.604 + li $t4, ~STATUS_BEV /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */
1.605 + and $t3, $t3, $t4
1.606 + ori $t3, $t3, STATUS_IE
1.607 + mtc0 $t3, CP0_STATUS
1.608 + jr $ra
1.609 + nop
1.610 +
1.611 +init_interrupts:
1.612 + mfc0 $t3, CP0_DEBUG
1.613 + li $t4, ~DEBUG_DM
1.614 + and $t3, $t3, $t4
1.615 + mtc0 $t3, CP0_DEBUG
1.616 +
1.617 + mfc0 $t3, CP0_STATUS
1.618 + li $t4, STATUS_BEV
1.619 + or $t3, $t3, $t4
1.620 + mtc0 $t3, CP0_STATUS
1.621 +
1.622 + la $t3, exception_handler
1.623 + mtc0 $t3, CP0_EBASE /* EBASE = exception_handler */
1.624 +
1.625 + li $t3, 0x20 /* Must be non-zero or the CPU gets upset */
1.626 + mtc0 $t3, CP0_INTCTL
1.627 +
1.628 + li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */
1.629 + mtc0 $t3, CP0_CAUSE
1.630 +
1.631 + /* Multiple vector...
1.632 + la $t3, INTCON
1.633 + li $t4, (1 << 12)
1.634 + sw $t4, SET($t3) */
1.635 +
1.636 + jr $ra
1.637 + nop
1.638 +
1.639 +
1.640 +
1.641 +/* Exception servicing. */
1.642 +
1.643 +.section .flash, "a"
1.644 +
1.645 +/* Exception servicing. */
1.646 +
1.647 +exception_handler:
1.648 + li $t8, 2500000
1.649 +exc_loop:
1.650 + addiu $t8, $t8, -1
1.651 + bnez $t8, exc_loop
1.652 + nop
1.653 + la $v0, PORTB
1.654 + li $v1, (1 << 11) /* PORTB<11> = RB11 */
1.655 + sw $v1, INV($v0)
1.656 + j exception_handler
1.657 + nop
1.658 +
1.659 +
1.660 +
1.661 +/* Interrupt servicing. */
1.662 +
1.663 +.org 0x200
1.664 +
1.665 +interrupt_handler:
1.666 +
1.667 + /* Check for a timer interrupt condition. */
1.668 +
1.669 + la $v0, IFS0
1.670 + lw $v1, 0($v0)
1.671 + andi $v1, $v1, (1 << 9) /* T2IF */
1.672 + beqz $v1, irq_dma
1.673 + nop
1.674 +
1.675 + /* Increment the line counter. */
1.676 +
1.677 + addiu $s0, $s0, 1
1.678 +
1.679 + /* Jump to the event handler. */
1.680 +
1.681 + jalr $s1
1.682 + nop
1.683 +
1.684 +irq_clear_timer:
1.685 +
1.686 + /* Clear the timer interrupt condition. */
1.687 +
1.688 + la $v0, IFS0
1.689 + li $v1, (1 << 9) /* IFS0<9> = T2IF = 0 */
1.690 + sw $v1, CLR($v0)
1.691 +
1.692 +irq_dma:
1.693 +
1.694 + /* Check for a DMA interrupt condition. */
1.695 +
1.696 + la $v0, IFS1
1.697 + lw $v1, 0($v0)
1.698 + li $t8, (1 << 28) /* DMA0IF */
1.699 + and $v1, $v1, $t8
1.700 + beqz $v1, irq_exit
1.701 + nop
1.702 +
1.703 + /* Test the block transfer completion interrupt flag. */
1.704 +
1.705 + la $v0, DCH0INT
1.706 + lw $v1, 0($v0)
1.707 + andi $v1, $v1, (1 << 3) /* CHBCIF */
1.708 + beqz $v1, irq_clear_dma
1.709 + nop
1.710 +
1.711 + /* Clear the block transfer completion interrupt flag. */
1.712 +
1.713 + li $v1, (1 << 3) /* CHBCIF = 0 */
1.714 + sw $v1, CLR($v0)
1.715 +
1.716 + /*
1.717 + Update the line data address if the line counter (referring to the
1.718 + next line) is even.
1.719 + */
1.720 +
1.721 + andi $t8, $s0, 1
1.722 + bnez $t8, irq_clear_dma
1.723 + nop
1.724 +
1.725 + /* Reference the next line and update the DMA source address. */
1.726 +
1.727 + addiu $s2, $s2, LINE_LENGTH
1.728 +
1.729 + /* Test for wraparound. */
1.730 +
1.731 + li $t8, SCREEN_SIZE
1.732 + sltu $t8, $s2, $t8
1.733 + bnez $t8, irq_dma_update
1.734 + nop
1.735 +
1.736 + /* Reset the source address. */
1.737 +
1.738 + move $s2, $zero
1.739 +
1.740 +irq_dma_update:
1.741 +
1.742 + la $v0, DCH0SSA
1.743 + sw $s2, 0($v0)
1.744 +
1.745 +irq_clear_dma:
1.746 +
1.747 + /* Clear the DMA interrupt condition. */
1.748 +
1.749 + la $v0, IFS1
1.750 + li $v1, (1 << 28) /* IFS1<28> = DMA0IF = 0 */
1.751 + sw $v1, CLR($v0)
1.752 +
1.753 +irq_exit:
1.754 + eret
1.755 + nop
1.756 +
1.757 +
1.758 +
1.759 +/* Event routines. */
1.760 +
1.761 +/* The vertical back porch. */
1.762 +
1.763 +vbp_active:
1.764 + /* Test for visible region. */
1.765 +
1.766 + sltiu $v0, $s0, VISIBLE_START
1.767 + bnez $v0, _vbp_active_ret
1.768 + nop
1.769 +
1.770 + /* Start the visible region. */
1.771 +
1.772 + la $s1, visible_active
1.773 +
1.774 +_vbp_active_ret:
1.775 + jr $ra
1.776 + nop
1.777 +
1.778 +
1.779 +
1.780 +/* The visible region. */
1.781 +
1.782 +visible_active:
1.783 + /* Test for front porch. */
1.784 +
1.785 + sltiu $v0, $s0, VFP_START
1.786 + bnez $v0, _visible_active_ret
1.787 + nop
1.788 +
1.789 + /* Start the front porch region. */
1.790 +
1.791 + la $s1, vfp_active
1.792 +
1.793 +_visible_active_ret:
1.794 + jr $ra
1.795 + nop
1.796 +
1.797 +
1.798 +
1.799 +/* Within the vertical front porch. */
1.800 +
1.801 +vfp_active:
1.802 + /* Test for vsync. */
1.803 +
1.804 + sltiu $v0, $s0, VSYNC_START
1.805 + bnez $v0, _vfp_active_ret
1.806 + nop
1.807 +
1.808 + /* Start the vsync. */
1.809 +
1.810 + la $s1, vsync_active
1.811 +
1.812 + /* Bring vsync low when the next line starts. */
1.813 +
1.814 + la $v0, OC2CON
1.815 + li $v1, 0b010 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 010 (single compare, output driven low) */
1.816 + sw $v1, 0($v0)
1.817 +
1.818 +_vfp_active_ret:
1.819 + jr $ra
1.820 + nop
1.821 +
1.822 +
1.823 +
1.824 +/* The vsync period. */
1.825 +
1.826 +vsync_active:
1.827 + /* Test for front porch. */
1.828 +
1.829 + sltiu $v0, $s0, VSYNC_END
1.830 + bnez $v0, _vsync_active_ret
1.831 + nop
1.832 +
1.833 + /* Start the back porch. */
1.834 +
1.835 + move $s0, $zero
1.836 + la $s1, vbp_active
1.837 +
1.838 + /* Bring vsync high when the next line starts. */
1.839 +
1.840 + la $v0, OC2CON
1.841 + li $v1, 0b001 | (1 << 15) /* OC2CON<2:0> = OCM<2:0> = 001 (single compare, output driven high) */
1.842 + sw $v1, 0($v0)
1.843 +
1.844 +_vsync_active_ret:
1.845 + jr $ra
1.846 + nop