1.1 --- a/vga.S Tue Nov 07 14:38:33 2017 +0100
1.2 +++ b/vga.S Sun Nov 19 01:07:07 2017 +0100
1.3 @@ -57,9 +57,51 @@
1.4 .extern init_framebuffer
1.5 .extern init_framebuffer_with_pattern
1.6 .extern screendata
1.7 -.extern fontdata
1.8 .extern blit_string
1.9 -.extern message
1.10 +.extern message0
1.11 +.extern message1
1.12 +
1.13 +.macro load_affected
1.14 + lw $v0, -4($k0)
1.15 + lw $v1, -8($k0)
1.16 + lw $s0, -12($k0)
1.17 + lw $s1, -16($k0)
1.18 + lw $s2, -20($k0)
1.19 + lw $s3, -24($k0)
1.20 + lw $t8, -28($k0)
1.21 + lw $ra, -32($k0)
1.22 + lw $sp, -36($k0)
1.23 + lw $gp, -40($k0)
1.24 +.endm
1.25 +
1.26 +.macro load_state
1.27 + lw $s0, -44($k0)
1.28 + lw $s1, -48($k0)
1.29 + lw $s2, -52($k0)
1.30 + lw $s3, -56($k0)
1.31 + lw $gp, -60($k0)
1.32 +.endm
1.33 +
1.34 +.macro save_affected
1.35 + sw $v0, -4($k0)
1.36 + sw $v1, -8($k0)
1.37 + sw $s0, -12($k0)
1.38 + sw $s1, -16($k0)
1.39 + sw $s2, -20($k0)
1.40 + sw $s3, -24($k0)
1.41 + sw $t8, -28($k0)
1.42 + sw $ra, -32($k0)
1.43 + sw $sp, -36($k0)
1.44 + sw $gp, -40($k0)
1.45 +.endm
1.46 +
1.47 +.macro save_state
1.48 + sw $s0, -44($k0)
1.49 + sw $s1, -48($k0)
1.50 + sw $s2, -52($k0)
1.51 + sw $s3, -56($k0)
1.52 + sw $gp, -60($k0)
1.53 +.endm
1.54
1.55 _start:
1.56 /*
1.57 @@ -115,7 +157,7 @@
1.58 li $t1, (1 << 3) /* PORTA<3> = RA3 */
1.59 sw $t1, CLR($t0)
1.60
1.61 - jal init_oc_pins
1.62 + jal init_io_pins
1.63 nop
1.64
1.65 /* Initialise the status register. */
1.66 @@ -146,6 +188,11 @@
1.67 jal init_oc
1.68 nop
1.69
1.70 + /* Initialise UART for debugging. */
1.71 +
1.72 + jal init_uart
1.73 + nop
1.74 +
1.75 /* Initialise the display state. */
1.76
1.77 li $s0, 0 /* line counter */
1.78 @@ -156,10 +203,7 @@
1.79 /* Save the state for retrieval in the interrupt handler. */
1.80
1.81 li $k0, IRQ_STACK_LIMIT
1.82 - sw $s0, -44($k0)
1.83 - sw $s1, -48($k0)
1.84 - sw $s2, -52($k0)
1.85 - sw $s3, -56($k0)
1.86 + save_state
1.87
1.88 /* Enable interrupts and loop. */
1.89
1.90 @@ -186,6 +230,10 @@
1.91 li $t1, (1 << 3) /* PORTA<3> = RA3 */
1.92 sw $t1, INV($t0)
1.93
1.94 + la $v0, U1TXREG
1.95 + li $v1, '.'
1.96 + sw $v1, 0($v0)
1.97 +
1.98 bnez $a1, loop /* until counter == 0 */
1.99 nop
1.100
1.101 @@ -374,7 +422,7 @@
1.102 jr $ra
1.103 nop
1.104
1.105 -init_oc_pins:
1.106 +init_io_pins:
1.107 /* Unlock the configuration register bits. */
1.108
1.109 la $v0, SYSKEY
1.110 @@ -401,6 +449,12 @@
1.111 li $v1, 0b0101 /* RPA1R<3:0> = 0101 (OC2) */
1.112 sw $v1, 0($v0)
1.113
1.114 + /* Map U1TX to RPB15. */
1.115 +
1.116 + la $v0, RPB15R
1.117 + li $v1, 0b0001 /* RPB15R<3:0> = 0001 (U1TX) */
1.118 + sw $v1, 0($v0)
1.119 +
1.120 la $v0, CFGCON
1.121 sw $t8, 0($v0)
1.122
1.123 @@ -644,6 +698,34 @@
1.124
1.125
1.126
1.127 +/* UART initialisation. */
1.128 +
1.129 +init_uart:
1.130 + /* Initialise UART. */
1.131 +
1.132 + la $v0, U1BRG
1.133 + li $v1, 12 /* U1BRG<15:0> = BRG = (FPB / (16 * baudrate)) - 1 = (24000000 / (16 * 115200)) - 1 = 12 */
1.134 + sw $v1, 0($v0)
1.135 +
1.136 + la $v0, U1MODE
1.137 + li $v1, (1 << 15) /* U1MODE<15> = ON = 0 */
1.138 + sw $v1, CLR($v0)
1.139 +
1.140 + /* Start UART. */
1.141 +
1.142 + la $v0, U1STA
1.143 + li $v1, (1 << 10) /* U1STA<10> = UTXEN = 1 */
1.144 + sw $v1, SET($v0)
1.145 +
1.146 + la $v0, U1MODE
1.147 + li $v1, (1 << 15) /* U1MODE<15> = ON = 1 */
1.148 + sw $v1, SET($v0)
1.149 +
1.150 + jr $ra
1.151 + nop
1.152 +
1.153 +
1.154 +
1.155 /* Utilities. */
1.156
1.157 handle_error_level:
1.158 @@ -719,62 +801,39 @@
1.159
1.160 interrupt_handler:
1.161
1.162 - /* Store affected registers. */
1.163 + /*
1.164 + Save affected registers, restoring IRQ state and switching to the IRQ
1.165 + stack.
1.166 + */
1.167
1.168 li $k0, IRQ_STACK_LIMIT
1.169 - sw $v0, -4($k0)
1.170 - sw $v1, -8($k0)
1.171 - sw $s0, -12($k0)
1.172 - sw $s1, -16($k0)
1.173 - sw $s2, -20($k0)
1.174 - sw $s3, -24($k0)
1.175 - sw $t8, -28($k0)
1.176 - sw $ra, -32($k0)
1.177 - sw $sp, -36($k0)
1.178 + save_affected
1.179 + load_state
1.180 + li $sp, IRQ_STACK_TOP
1.181 +
1.182 + /*
1.183 + The timer interrupt will only occur outside the visible region, but the
1.184 + interrupt condition will still occur as the timer wraps around.
1.185
1.186 - /* Load state. */
1.187 + Here, we deliberately ignore the timer condition during the visible/
1.188 + active region.
1.189
1.190 - lw $s0, -44($k0)
1.191 - lw $s1, -48($k0)
1.192 - lw $s2, -52($k0)
1.193 - lw $s3, -56($k0)
1.194 + The DMA interrupt should only be active within the visible region.
1.195 + */
1.196
1.197 - li $sp, IRQ_STACK_TOP
1.198 + la $t8, visible_active
1.199 + beq $s1, $t8, irq_dma
1.200 + nop
1.201
1.202 /* Check for a timer interrupt condition. */
1.203
1.204 la $v0, IFS0
1.205 lw $v1, 0($v0)
1.206 andi $v1, $v1, (1 << 9) /* T2IF */
1.207 - beqz $v1, irq_dma
1.208 + beqz $v1, irq_exit
1.209 nop
1.210
1.211 - /* Clear the timer interrupt condition. */
1.212 -
1.213 - sw $v1, CLR($v0)
1.214 -
1.215 - /*
1.216 - The timer interrupt will only occur outside the visible region, but the
1.217 - interrupt condition will still occur as the timer wraps around.
1.218 - Therefore, the handling of other interrupts may find the timer interrupt
1.219 - condition set.
1.220 -
1.221 - For the visible region, the event handler is invoked when handling the
1.222 - DMA interrupt. Otherwise, the event handler is invoked in response to
1.223 - the timer interrupt.
1.224 - */
1.225 -
1.226 - la $t8, visible_active
1.227 - beq $s1, $t8, irq_dma
1.228 - nop
1.229 -
1.230 - /* Increment the line counter (only outside the visible region). */
1.231 -
1.232 - addiu $s0, $s0, 1
1.233 -
1.234 - /* Jump to the event handler (only outside the visible region). */
1.235 -
1.236 - jalr $s1
1.237 + j irq_handle
1.238 nop
1.239
1.240 irq_dma:
1.241 @@ -799,9 +858,8 @@
1.242 beqz $v1, irq_dma_next
1.243 nop
1.244
1.245 - /* Clear the block transfer completion interrupt flag. */
1.246 -
1.247 - sw $v1, CLR($v0)
1.248 + j irq_handle
1.249 + nop
1.250
1.251 irq_dma_next:
1.252 /* Test the block transfer completion interrupt flag. */
1.253 @@ -812,98 +870,35 @@
1.254 beqz $v1, irq_exit
1.255 nop
1.256
1.257 - /* Clear the block transfer completion interrupt flag. */
1.258 +irq_handle:
1.259 + /* Clear the interrupt condition. */
1.260
1.261 sw $v1, CLR($v0)
1.262
1.263 - /*
1.264 - The DMA interrupt should only be active within the visible region.
1.265 - The event handler is invoked here instead of in response to a timer
1.266 - interrupt within that region.
1.267 - */
1.268 -
1.269 - /* Increment the line counter (only within the visible region). */
1.270 + /* Increment the line counter. */
1.271
1.272 addiu $s0, $s0, 1
1.273
1.274 - /* Jump to the event handler (only within the visible region). */
1.275 + /* Jump to the event handler. */
1.276
1.277 jalr $s1
1.278 nop
1.279
1.280 - /* Jump to the DMA update routine. */
1.281 -
1.282 - j visible_update_address
1.283 - nop
1.284 -
1.285 irq_exit:
1.286 - /* Save state. */
1.287 + /*
1.288 + Save IRQ state and restore the affected registers, switching back to the
1.289 + original stack.
1.290 + */
1.291
1.292 li $k0, IRQ_STACK_LIMIT
1.293 - sw $s0, -44($k0)
1.294 - sw $s1, -48($k0)
1.295 - sw $s2, -52($k0)
1.296 - sw $s3, -56($k0)
1.297 -
1.298 - /* Restore affected registers. */
1.299 -
1.300 - lw $v0, -4($k0)
1.301 - lw $v1, -8($k0)
1.302 - lw $s0, -12($k0)
1.303 - lw $s1, -16($k0)
1.304 - lw $s2, -20($k0)
1.305 - lw $s3, -24($k0)
1.306 - lw $t8, -28($k0)
1.307 - lw $ra, -32($k0)
1.308 - lw $sp, -36($k0)
1.309 + save_state
1.310 + load_affected
1.311
1.312 eret
1.313 nop
1.314
1.315
1.316
1.317 -exc_handler:
1.318 - li $t9, 0x80000000
1.319 - mfc0 $t6, CP0_ERROREPC
1.320 - nop
1.321 -exc_loop:
1.322 - and $t7, $t9, $t6
1.323 - beqz $t7, exc_errorepc_zero
1.324 - nop
1.325 -exc_errorepc_one:
1.326 - la $v0, PORTA
1.327 - li $v1, (1 << 2) /* PORTA<2> = RA2 */
1.328 - sw $v1, SET($v0)
1.329 - j exc_loop_wait
1.330 - nop
1.331 -exc_errorepc_zero:
1.332 - la $v0, PORTA
1.333 - li $v1, (1 << 3) /* PORTA<3> = RA3 */
1.334 - sw $v1, SET($v0)
1.335 -exc_loop_wait:
1.336 - li $t8, 5000000
1.337 -exc_loop_delay:
1.338 - addiu $t8, $t8, -1
1.339 - bnez $t8, exc_loop_delay
1.340 - nop
1.341 - la $v0, PORTA
1.342 - li $v1, (3 << 2) /* PORTA<3:2> = RA3, RA2 */
1.343 - sw $v1, CLR($v0)
1.344 -exc_loop_wait_again:
1.345 - li $t8, 2500000
1.346 -exc_loop_delay_again:
1.347 - addiu $t8, $t8, -1
1.348 - bnez $t8, exc_loop_delay_again
1.349 - nop
1.350 -exc_errorepc_next:
1.351 - srl $t9, $t9, 1
1.352 - bnez $t9, exc_loop
1.353 - nop
1.354 - j exc_handler
1.355 - nop
1.356 -
1.357 -
1.358 -
1.359 /* Event routines. */
1.360
1.361 /* The vertical back porch. */
1.362 @@ -933,19 +928,12 @@
1.363 The condition still occurs, however.
1.364 */
1.365
1.366 - la $v0, IEC0
1.367 - li $v1, (1 << 9)
1.368 - sw $v1, CLR($v0) /* T2IE = 0 */
1.369 -
1.370 la $v0, IPC2
1.371 - li $v1, 0b11111
1.372 - sw $v1, CLR($v0) /* T2IP, T2IS = 0 */
1.373 - li $v1, 0b00111
1.374 - sw $v1, SET($v0) /* T2IP = 1; T2IS = 3 */
1.375 -
1.376 - la $v0, IEC0
1.377 - li $v1, (1 << 9)
1.378 - sw $v1, SET($v0) /* T2IE = 0 */
1.379 + lw $v1, 0($v0)
1.380 + li $t8, ~0b11111
1.381 + and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
1.382 + ori $v1, $v1, 0b00111 /* T2IP = 1; T2IS = 3 */
1.383 + sw $v1, 0($v0)
1.384
1.385 /* Enable the start channel for timer event transfer initiation. */
1.386
1.387 @@ -965,7 +953,7 @@
1.388 /* Test for front porch. */
1.389
1.390 sltiu $v0, $s0, VFP_START
1.391 - bnez $v0, _visible_active_ret
1.392 + bnez $v0, visible_update_address
1.393 nop
1.394
1.395 /* Start the front porch region. */
1.396 @@ -974,19 +962,18 @@
1.397
1.398 /* Restore delivery of the timer interrupt after the visible period. */
1.399
1.400 - la $v0, IEC0
1.401 - li $v1, (1 << 9)
1.402 - sw $v1, CLR($v0) /* T2IE = 0 */
1.403 + la $v0, IPC2
1.404 + lw $v1, 0($v0)
1.405 + li $t8, ~0b11111
1.406 + and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
1.407 + ori $v1, $v1, 0b11111 /* T2IP = 7; T2IS = 3 */
1.408 + sw $v1, 0($v0)
1.409
1.410 - la $v0, IPC2
1.411 - li $v1, 0b11111
1.412 - sw $v1, CLR($v0) /* T2IP, T2IS = 0 */
1.413 - li $v1, 0b11111
1.414 - sw $v1, SET($v0) /* T2IP = 7; T2IS = 3 */
1.415 + /* Disable the start channel. */
1.416
1.417 - la $v0, IEC0
1.418 - li $v1, (1 << 9)
1.419 - sw $v1, SET($v0) /* T2IE = 1 */
1.420 + la $v0, DCH0ECON
1.421 + li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 0 */
1.422 + sw $v1, CLR($v0)
1.423
1.424 _visible_active_ret:
1.425 jr $ra
1.426 @@ -998,23 +985,6 @@
1.427
1.428 visible_update_address:
1.429
1.430 - /* Test for the last visible line. */
1.431 -
1.432 - la $v0, vfp_active
1.433 - bne $s1, $v0, _visible_update_address
1.434 - nop
1.435 -
1.436 - /* Disable the start channel. */
1.437 -
1.438 - la $v0, DCH0ECON
1.439 - li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 0 */
1.440 - sw $v1, CLR($v0)
1.441 -
1.442 - j _visible_update_ret
1.443 - nop
1.444 -
1.445 -_visible_update_address:
1.446 -
1.447 /*
1.448 Update the line data address if the line counter (referring to the
1.449 next line) is even.
1.450 @@ -1047,7 +1017,7 @@
1.451 sw $s2, 0($v0)
1.452
1.453 _visible_update_ret:
1.454 - j irq_exit
1.455 + jr $ra
1.456 nop
1.457
1.458
1.459 @@ -1100,3 +1070,39 @@
1.460 _vsync_active_ret:
1.461 jr $ra
1.462 nop
1.463 +
1.464 +
1.465 +
1.466 +/* Exception handler. */
1.467 +
1.468 +exc_handler:
1.469 + mfc0 $t7, CP0_ERROREPC
1.470 + nop
1.471 +
1.472 +exc_write_word:
1.473 + li $t8, 32
1.474 + la $v0, U1TXREG
1.475 +exc_loop:
1.476 + addiu $t8, $t8, -4
1.477 + srlv $v1, $t7, $t8 /* $v1 = $t7 >> $t8 */
1.478 + andi $v1, $v1, 0xF
1.479 + addiu $t9, $v1, -10 /* $t9 >= 10? */
1.480 + bgez $t9, exc_alpha
1.481 + nop
1.482 +exc_digit:
1.483 + addiu $v1, $v1, 48 /* convert to digit: '0' */
1.484 + j exc_write
1.485 + nop
1.486 +exc_alpha:
1.487 + addiu $v1, $v1, 55 /* convert to alpha: 'A' - 10 */
1.488 +exc_write:
1.489 + sw $v1, 0($v0)
1.490 + bnez $t8, exc_loop
1.491 + nop
1.492 +exc_loop_end:
1.493 + li $v1, '\n'
1.494 + sw $v1, 0($v0)
1.495 +
1.496 +exc_handler_end:
1.497 + j exc_handler_end
1.498 + nop