3.1 --- a/vga.S Tue Nov 07 14:38:33 2017 +0100
3.2 +++ b/vga.S Sun Nov 19 01:07:07 2017 +0100
3.3 @@ -57,9 +57,51 @@
3.4 .extern init_framebuffer
3.5 .extern init_framebuffer_with_pattern
3.6 .extern screendata
3.7 -.extern fontdata
3.8 .extern blit_string
3.9 -.extern message
3.10 +.extern message0
3.11 +.extern message1
3.12 +
3.13 +.macro load_affected
3.14 + lw $v0, -4($k0)
3.15 + lw $v1, -8($k0)
3.16 + lw $s0, -12($k0)
3.17 + lw $s1, -16($k0)
3.18 + lw $s2, -20($k0)
3.19 + lw $s3, -24($k0)
3.20 + lw $t8, -28($k0)
3.21 + lw $ra, -32($k0)
3.22 + lw $sp, -36($k0)
3.23 + lw $gp, -40($k0)
3.24 +.endm
3.25 +
3.26 +.macro load_state
3.27 + lw $s0, -44($k0)
3.28 + lw $s1, -48($k0)
3.29 + lw $s2, -52($k0)
3.30 + lw $s3, -56($k0)
3.31 + lw $gp, -60($k0)
3.32 +.endm
3.33 +
3.34 +.macro save_affected
3.35 + sw $v0, -4($k0)
3.36 + sw $v1, -8($k0)
3.37 + sw $s0, -12($k0)
3.38 + sw $s1, -16($k0)
3.39 + sw $s2, -20($k0)
3.40 + sw $s3, -24($k0)
3.41 + sw $t8, -28($k0)
3.42 + sw $ra, -32($k0)
3.43 + sw $sp, -36($k0)
3.44 + sw $gp, -40($k0)
3.45 +.endm
3.46 +
3.47 +.macro save_state
3.48 + sw $s0, -44($k0)
3.49 + sw $s1, -48($k0)
3.50 + sw $s2, -52($k0)
3.51 + sw $s3, -56($k0)
3.52 + sw $gp, -60($k0)
3.53 +.endm
3.54
3.55 _start:
3.56 /*
3.57 @@ -115,7 +157,7 @@
3.58 li $t1, (1 << 3) /* PORTA<3> = RA3 */
3.59 sw $t1, CLR($t0)
3.60
3.61 - jal init_oc_pins
3.62 + jal init_io_pins
3.63 nop
3.64
3.65 /* Initialise the status register. */
3.66 @@ -146,6 +188,11 @@
3.67 jal init_oc
3.68 nop
3.69
3.70 + /* Initialise UART for debugging. */
3.71 +
3.72 + jal init_uart
3.73 + nop
3.74 +
3.75 /* Initialise the display state. */
3.76
3.77 li $s0, 0 /* line counter */
3.78 @@ -156,10 +203,7 @@
3.79 /* Save the state for retrieval in the interrupt handler. */
3.80
3.81 li $k0, IRQ_STACK_LIMIT
3.82 - sw $s0, -44($k0)
3.83 - sw $s1, -48($k0)
3.84 - sw $s2, -52($k0)
3.85 - sw $s3, -56($k0)
3.86 + save_state
3.87
3.88 /* Enable interrupts and loop. */
3.89
3.90 @@ -186,6 +230,10 @@
3.91 li $t1, (1 << 3) /* PORTA<3> = RA3 */
3.92 sw $t1, INV($t0)
3.93
3.94 + la $v0, U1TXREG
3.95 + li $v1, '.'
3.96 + sw $v1, 0($v0)
3.97 +
3.98 bnez $a1, loop /* until counter == 0 */
3.99 nop
3.100
3.101 @@ -374,7 +422,7 @@
3.102 jr $ra
3.103 nop
3.104
3.105 -init_oc_pins:
3.106 +init_io_pins:
3.107 /* Unlock the configuration register bits. */
3.108
3.109 la $v0, SYSKEY
3.110 @@ -401,6 +449,12 @@
3.111 li $v1, 0b0101 /* RPA1R<3:0> = 0101 (OC2) */
3.112 sw $v1, 0($v0)
3.113
3.114 + /* Map U1TX to RPB15. */
3.115 +
3.116 + la $v0, RPB15R
3.117 + li $v1, 0b0001 /* RPB15R<3:0> = 0001 (U1TX) */
3.118 + sw $v1, 0($v0)
3.119 +
3.120 la $v0, CFGCON
3.121 sw $t8, 0($v0)
3.122
3.123 @@ -644,6 +698,34 @@
3.124
3.125
3.126
3.127 +/* UART initialisation. */
3.128 +
3.129 +init_uart:
3.130 + /* Initialise UART. */
3.131 +
3.132 + la $v0, U1BRG
3.133 + li $v1, 12 /* U1BRG<15:0> = BRG = (FPB / (16 * baudrate)) - 1 = (24000000 / (16 * 115200)) - 1 = 12 */
3.134 + sw $v1, 0($v0)
3.135 +
3.136 + la $v0, U1MODE
3.137 + li $v1, (1 << 15) /* U1MODE<15> = ON = 0 */
3.138 + sw $v1, CLR($v0)
3.139 +
3.140 + /* Start UART. */
3.141 +
3.142 + la $v0, U1STA
3.143 + li $v1, (1 << 10) /* U1STA<10> = UTXEN = 1 */
3.144 + sw $v1, SET($v0)
3.145 +
3.146 + la $v0, U1MODE
3.147 + li $v1, (1 << 15) /* U1MODE<15> = ON = 1 */
3.148 + sw $v1, SET($v0)
3.149 +
3.150 + jr $ra
3.151 + nop
3.152 +
3.153 +
3.154 +
3.155 /* Utilities. */
3.156
3.157 handle_error_level:
3.158 @@ -719,62 +801,39 @@
3.159
3.160 interrupt_handler:
3.161
3.162 - /* Store affected registers. */
3.163 + /*
3.164 + Save affected registers, restoring IRQ state and switching to the IRQ
3.165 + stack.
3.166 + */
3.167
3.168 li $k0, IRQ_STACK_LIMIT
3.169 - sw $v0, -4($k0)
3.170 - sw $v1, -8($k0)
3.171 - sw $s0, -12($k0)
3.172 - sw $s1, -16($k0)
3.173 - sw $s2, -20($k0)
3.174 - sw $s3, -24($k0)
3.175 - sw $t8, -28($k0)
3.176 - sw $ra, -32($k0)
3.177 - sw $sp, -36($k0)
3.178 + save_affected
3.179 + load_state
3.180 + li $sp, IRQ_STACK_TOP
3.181 +
3.182 + /*
3.183 + The timer interrupt will only occur outside the visible region, but the
3.184 + interrupt condition will still occur as the timer wraps around.
3.185
3.186 - /* Load state. */
3.187 + Here, we deliberately ignore the timer condition during the visible/
3.188 + active region.
3.189
3.190 - lw $s0, -44($k0)
3.191 - lw $s1, -48($k0)
3.192 - lw $s2, -52($k0)
3.193 - lw $s3, -56($k0)
3.194 + The DMA interrupt should only be active within the visible region.
3.195 + */
3.196
3.197 - li $sp, IRQ_STACK_TOP
3.198 + la $t8, visible_active
3.199 + beq $s1, $t8, irq_dma
3.200 + nop
3.201
3.202 /* Check for a timer interrupt condition. */
3.203
3.204 la $v0, IFS0
3.205 lw $v1, 0($v0)
3.206 andi $v1, $v1, (1 << 9) /* T2IF */
3.207 - beqz $v1, irq_dma
3.208 + beqz $v1, irq_exit
3.209 nop
3.210
3.211 - /* Clear the timer interrupt condition. */
3.212 -
3.213 - sw $v1, CLR($v0)
3.214 -
3.215 - /*
3.216 - The timer interrupt will only occur outside the visible region, but the
3.217 - interrupt condition will still occur as the timer wraps around.
3.218 - Therefore, the handling of other interrupts may find the timer interrupt
3.219 - condition set.
3.220 -
3.221 - For the visible region, the event handler is invoked when handling the
3.222 - DMA interrupt. Otherwise, the event handler is invoked in response to
3.223 - the timer interrupt.
3.224 - */
3.225 -
3.226 - la $t8, visible_active
3.227 - beq $s1, $t8, irq_dma
3.228 - nop
3.229 -
3.230 - /* Increment the line counter (only outside the visible region). */
3.231 -
3.232 - addiu $s0, $s0, 1
3.233 -
3.234 - /* Jump to the event handler (only outside the visible region). */
3.235 -
3.236 - jalr $s1
3.237 + j irq_handle
3.238 nop
3.239
3.240 irq_dma:
3.241 @@ -799,9 +858,8 @@
3.242 beqz $v1, irq_dma_next
3.243 nop
3.244
3.245 - /* Clear the block transfer completion interrupt flag. */
3.246 -
3.247 - sw $v1, CLR($v0)
3.248 + j irq_handle
3.249 + nop
3.250
3.251 irq_dma_next:
3.252 /* Test the block transfer completion interrupt flag. */
3.253 @@ -812,98 +870,35 @@
3.254 beqz $v1, irq_exit
3.255 nop
3.256
3.257 - /* Clear the block transfer completion interrupt flag. */
3.258 +irq_handle:
3.259 + /* Clear the interrupt condition. */
3.260
3.261 sw $v1, CLR($v0)
3.262
3.263 - /*
3.264 - The DMA interrupt should only be active within the visible region.
3.265 - The event handler is invoked here instead of in response to a timer
3.266 - interrupt within that region.
3.267 - */
3.268 -
3.269 - /* Increment the line counter (only within the visible region). */
3.270 + /* Increment the line counter. */
3.271
3.272 addiu $s0, $s0, 1
3.273
3.274 - /* Jump to the event handler (only within the visible region). */
3.275 + /* Jump to the event handler. */
3.276
3.277 jalr $s1
3.278 nop
3.279
3.280 - /* Jump to the DMA update routine. */
3.281 -
3.282 - j visible_update_address
3.283 - nop
3.284 -
3.285 irq_exit:
3.286 - /* Save state. */
3.287 + /*
3.288 + Save IRQ state and restore the affected registers, switching back to the
3.289 + original stack.
3.290 + */
3.291
3.292 li $k0, IRQ_STACK_LIMIT
3.293 - sw $s0, -44($k0)
3.294 - sw $s1, -48($k0)
3.295 - sw $s2, -52($k0)
3.296 - sw $s3, -56($k0)
3.297 -
3.298 - /* Restore affected registers. */
3.299 -
3.300 - lw $v0, -4($k0)
3.301 - lw $v1, -8($k0)
3.302 - lw $s0, -12($k0)
3.303 - lw $s1, -16($k0)
3.304 - lw $s2, -20($k0)
3.305 - lw $s3, -24($k0)
3.306 - lw $t8, -28($k0)
3.307 - lw $ra, -32($k0)
3.308 - lw $sp, -36($k0)
3.309 + save_state
3.310 + load_affected
3.311
3.312 eret
3.313 nop
3.314
3.315
3.316
3.317 -exc_handler:
3.318 - li $t9, 0x80000000
3.319 - mfc0 $t6, CP0_ERROREPC
3.320 - nop
3.321 -exc_loop:
3.322 - and $t7, $t9, $t6
3.323 - beqz $t7, exc_errorepc_zero
3.324 - nop
3.325 -exc_errorepc_one:
3.326 - la $v0, PORTA
3.327 - li $v1, (1 << 2) /* PORTA<2> = RA2 */
3.328 - sw $v1, SET($v0)
3.329 - j exc_loop_wait
3.330 - nop
3.331 -exc_errorepc_zero:
3.332 - la $v0, PORTA
3.333 - li $v1, (1 << 3) /* PORTA<3> = RA3 */
3.334 - sw $v1, SET($v0)
3.335 -exc_loop_wait:
3.336 - li $t8, 5000000
3.337 -exc_loop_delay:
3.338 - addiu $t8, $t8, -1
3.339 - bnez $t8, exc_loop_delay
3.340 - nop
3.341 - la $v0, PORTA
3.342 - li $v1, (3 << 2) /* PORTA<3:2> = RA3, RA2 */
3.343 - sw $v1, CLR($v0)
3.344 -exc_loop_wait_again:
3.345 - li $t8, 2500000
3.346 -exc_loop_delay_again:
3.347 - addiu $t8, $t8, -1
3.348 - bnez $t8, exc_loop_delay_again
3.349 - nop
3.350 -exc_errorepc_next:
3.351 - srl $t9, $t9, 1
3.352 - bnez $t9, exc_loop
3.353 - nop
3.354 - j exc_handler
3.355 - nop
3.356 -
3.357 -
3.358 -
3.359 /* Event routines. */
3.360
3.361 /* The vertical back porch. */
3.362 @@ -933,19 +928,12 @@
3.363 The condition still occurs, however.
3.364 */
3.365
3.366 - la $v0, IEC0
3.367 - li $v1, (1 << 9)
3.368 - sw $v1, CLR($v0) /* T2IE = 0 */
3.369 -
3.370 la $v0, IPC2
3.371 - li $v1, 0b11111
3.372 - sw $v1, CLR($v0) /* T2IP, T2IS = 0 */
3.373 - li $v1, 0b00111
3.374 - sw $v1, SET($v0) /* T2IP = 1; T2IS = 3 */
3.375 -
3.376 - la $v0, IEC0
3.377 - li $v1, (1 << 9)
3.378 - sw $v1, SET($v0) /* T2IE = 0 */
3.379 + lw $v1, 0($v0)
3.380 + li $t8, ~0b11111
3.381 + and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
3.382 + ori $v1, $v1, 0b00111 /* T2IP = 1; T2IS = 3 */
3.383 + sw $v1, 0($v0)
3.384
3.385 /* Enable the start channel for timer event transfer initiation. */
3.386
3.387 @@ -965,7 +953,7 @@
3.388 /* Test for front porch. */
3.389
3.390 sltiu $v0, $s0, VFP_START
3.391 - bnez $v0, _visible_active_ret
3.392 + bnez $v0, visible_update_address
3.393 nop
3.394
3.395 /* Start the front porch region. */
3.396 @@ -974,19 +962,18 @@
3.397
3.398 /* Restore delivery of the timer interrupt after the visible period. */
3.399
3.400 - la $v0, IEC0
3.401 - li $v1, (1 << 9)
3.402 - sw $v1, CLR($v0) /* T2IE = 0 */
3.403 + la $v0, IPC2
3.404 + lw $v1, 0($v0)
3.405 + li $t8, ~0b11111
3.406 + and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
3.407 + ori $v1, $v1, 0b11111 /* T2IP = 7; T2IS = 3 */
3.408 + sw $v1, 0($v0)
3.409
3.410 - la $v0, IPC2
3.411 - li $v1, 0b11111
3.412 - sw $v1, CLR($v0) /* T2IP, T2IS = 0 */
3.413 - li $v1, 0b11111
3.414 - sw $v1, SET($v0) /* T2IP = 7; T2IS = 3 */
3.415 + /* Disable the start channel. */
3.416
3.417 - la $v0, IEC0
3.418 - li $v1, (1 << 9)
3.419 - sw $v1, SET($v0) /* T2IE = 1 */
3.420 + la $v0, DCH0ECON
3.421 + li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 0 */
3.422 + sw $v1, CLR($v0)
3.423
3.424 _visible_active_ret:
3.425 jr $ra
3.426 @@ -998,23 +985,6 @@
3.427
3.428 visible_update_address:
3.429
3.430 - /* Test for the last visible line. */
3.431 -
3.432 - la $v0, vfp_active
3.433 - bne $s1, $v0, _visible_update_address
3.434 - nop
3.435 -
3.436 - /* Disable the start channel. */
3.437 -
3.438 - la $v0, DCH0ECON
3.439 - li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 0 */
3.440 - sw $v1, CLR($v0)
3.441 -
3.442 - j _visible_update_ret
3.443 - nop
3.444 -
3.445 -_visible_update_address:
3.446 -
3.447 /*
3.448 Update the line data address if the line counter (referring to the
3.449 next line) is even.
3.450 @@ -1047,7 +1017,7 @@
3.451 sw $s2, 0($v0)
3.452
3.453 _visible_update_ret:
3.454 - j irq_exit
3.455 + jr $ra
3.456 nop
3.457
3.458
3.459 @@ -1100,3 +1070,39 @@
3.460 _vsync_active_ret:
3.461 jr $ra
3.462 nop
3.463 +
3.464 +
3.465 +
3.466 +/* Exception handler. */
3.467 +
3.468 +exc_handler:
3.469 + mfc0 $t7, CP0_ERROREPC
3.470 + nop
3.471 +
3.472 +exc_write_word:
3.473 + li $t8, 32
3.474 + la $v0, U1TXREG
3.475 +exc_loop:
3.476 + addiu $t8, $t8, -4
3.477 + srlv $v1, $t7, $t8 /* $v1 = $t7 >> $t8 */
3.478 + andi $v1, $v1, 0xF
3.479 + addiu $t9, $v1, -10 /* $t9 >= 10? */
3.480 + bgez $t9, exc_alpha
3.481 + nop
3.482 +exc_digit:
3.483 + addiu $v1, $v1, 48 /* convert to digit: '0' */
3.484 + j exc_write
3.485 + nop
3.486 +exc_alpha:
3.487 + addiu $v1, $v1, 55 /* convert to alpha: 'A' - 10 */
3.488 +exc_write:
3.489 + sw $v1, 0($v0)
3.490 + bnez $t8, exc_loop
3.491 + nop
3.492 +exc_loop_end:
3.493 + li $v1, '\n'
3.494 + sw $v1, 0($v0)
3.495 +
3.496 +exc_handler_end:
3.497 + j exc_handler_end
3.498 + nop