3.1 --- a/vga.S Tue Nov 07 14:38:33 2017 +0100
3.2 +++ b/vga.S Sun Nov 19 00:39:57 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 @@ -393,7 +441,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 @@ -420,6 +468,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 @@ -661,6 +715,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 @@ -736,62 +818,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 @@ -816,98 +875,35 @@
3.242 beqz $v1, irq_exit
3.243 nop
3.244
3.245 - /* Clear the block transfer completion interrupt flag. */
3.246 +irq_handle:
3.247 + /* Clear the interrupt condition. */
3.248
3.249 sw $v1, CLR($v0)
3.250
3.251 - /*
3.252 - The DMA interrupt should only be active within the visible region.
3.253 - The event handler is invoked here instead of in response to a timer
3.254 - interrupt within that region.
3.255 - */
3.256 -
3.257 - /* Increment the line counter (only within the visible region). */
3.258 + /* Increment the line counter. */
3.259
3.260 addiu $s0, $s0, 1
3.261
3.262 - /* Jump to the event handler (only within the visible region). */
3.263 + /* Jump to the event handler. */
3.264
3.265 jalr $s1
3.266 nop
3.267
3.268 - /* Jump to the DMA update routine. */
3.269 -
3.270 - j visible_update_address
3.271 - nop
3.272 -
3.273 irq_exit:
3.274 - /* Save state. */
3.275 + /*
3.276 + Save IRQ state and restore the affected registers, switching back to the
3.277 + original stack.
3.278 + */
3.279
3.280 li $k0, IRQ_STACK_LIMIT
3.281 - sw $s0, -44($k0)
3.282 - sw $s1, -48($k0)
3.283 - sw $s2, -52($k0)
3.284 - sw $s3, -56($k0)
3.285 -
3.286 - /* Restore affected registers. */
3.287 -
3.288 - lw $v0, -4($k0)
3.289 - lw $v1, -8($k0)
3.290 - lw $s0, -12($k0)
3.291 - lw $s1, -16($k0)
3.292 - lw $s2, -20($k0)
3.293 - lw $s3, -24($k0)
3.294 - lw $t8, -28($k0)
3.295 - lw $ra, -32($k0)
3.296 - lw $sp, -36($k0)
3.297 + save_state
3.298 + load_affected
3.299
3.300 eret
3.301 nop
3.302
3.303
3.304
3.305 -exc_handler:
3.306 - li $t9, 0x80000000
3.307 - mfc0 $t6, CP0_ERROREPC
3.308 - nop
3.309 -exc_loop:
3.310 - and $t7, $t9, $t6
3.311 - beqz $t7, exc_errorepc_zero
3.312 - nop
3.313 -exc_errorepc_one:
3.314 - la $v0, PORTA
3.315 - li $v1, (1 << 2) /* PORTA<2> = RA2 */
3.316 - sw $v1, SET($v0)
3.317 - j exc_loop_wait
3.318 - nop
3.319 -exc_errorepc_zero:
3.320 - la $v0, PORTA
3.321 - li $v1, (1 << 3) /* PORTA<3> = RA3 */
3.322 - sw $v1, SET($v0)
3.323 -exc_loop_wait:
3.324 - li $t8, 5000000
3.325 -exc_loop_delay:
3.326 - addiu $t8, $t8, -1
3.327 - bnez $t8, exc_loop_delay
3.328 - nop
3.329 - la $v0, PORTA
3.330 - li $v1, (3 << 2) /* PORTA<3:2> = RA3, RA2 */
3.331 - sw $v1, CLR($v0)
3.332 -exc_loop_wait_again:
3.333 - li $t8, 2500000
3.334 -exc_loop_delay_again:
3.335 - addiu $t8, $t8, -1
3.336 - bnez $t8, exc_loop_delay_again
3.337 - nop
3.338 -exc_errorepc_next:
3.339 - srl $t9, $t9, 1
3.340 - bnez $t9, exc_loop
3.341 - nop
3.342 - j exc_handler
3.343 - nop
3.344 -
3.345 -
3.346 -
3.347 /* Event routines. */
3.348
3.349 /* The vertical back porch. */
3.350 @@ -937,19 +933,12 @@
3.351 The condition still occurs, however.
3.352 */
3.353
3.354 - la $v0, IEC0
3.355 - li $v1, (1 << 9)
3.356 - sw $v1, CLR($v0) /* T2IE = 0 */
3.357 -
3.358 la $v0, IPC2
3.359 - li $v1, 0b11111
3.360 - sw $v1, CLR($v0) /* T2IP, T2IS = 0 */
3.361 - li $v1, 0b00111
3.362 - sw $v1, SET($v0) /* T2IP = 1; T2IS = 3 */
3.363 -
3.364 - la $v0, IEC0
3.365 - li $v1, (1 << 9)
3.366 - sw $v1, SET($v0) /* T2IE = 0 */
3.367 + lw $v1, 0($v0)
3.368 + li $t8, ~0b11111
3.369 + and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
3.370 + ori $v1, $v1, 0b00111 /* T2IP = 1; T2IS = 3 */
3.371 + sw $v1, 0($v0)
3.372
3.373 /* Enable the line channel for timer event transfer initiation. */
3.374
3.375 @@ -969,7 +958,7 @@
3.376 /* Test for front porch. */
3.377
3.378 sltiu $v0, $s0, VFP_START
3.379 - bnez $v0, _visible_active_ret
3.380 + bnez $v0, visible_update_address
3.381 nop
3.382
3.383 /* Start the front porch region. */
3.384 @@ -978,19 +967,18 @@
3.385
3.386 /* Restore delivery of the timer interrupt after the visible period. */
3.387
3.388 - la $v0, IEC0
3.389 - li $v1, (1 << 9)
3.390 - sw $v1, CLR($v0) /* T2IE = 0 */
3.391 + la $v0, IPC2
3.392 + lw $v1, 0($v0)
3.393 + li $t8, ~0b11111
3.394 + and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
3.395 + ori $v1, $v1, 0b11111 /* T2IP = 7; T2IS = 3 */
3.396 + sw $v1, 0($v0)
3.397
3.398 - la $v0, IPC2
3.399 - li $v1, 0b11111
3.400 - sw $v1, CLR($v0) /* T2IP, T2IS = 0 */
3.401 - li $v1, 0b11111
3.402 - sw $v1, SET($v0) /* T2IP = 7; T2IS = 3 */
3.403 + /* Disable the line channel. */
3.404
3.405 - la $v0, IEC0
3.406 - li $v1, (1 << 9)
3.407 - sw $v1, SET($v0) /* T2IE = 1 */
3.408 + la $v0, DCH0ECON
3.409 + li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 0 */
3.410 + sw $v1, CLR($v0)
3.411
3.412 _visible_active_ret:
3.413 jr $ra
3.414 @@ -1002,23 +990,6 @@
3.415
3.416 visible_update_address:
3.417
3.418 - /* Test for the last visible line. */
3.419 -
3.420 - la $v0, vfp_active
3.421 - bne $s1, $v0, _visible_update_address
3.422 - nop
3.423 -
3.424 - /* Disable the line channel. */
3.425 -
3.426 - la $v0, DCH0ECON
3.427 - li $v1, (1 << 4) /* DCH0ECON<4> = SIRQEN = 0 */
3.428 - sw $v1, CLR($v0)
3.429 -
3.430 - j _visible_update_ret
3.431 - nop
3.432 -
3.433 -_visible_update_address:
3.434 -
3.435 /*
3.436 Update the line data address if the line counter (referring to the
3.437 next line) is even.
3.438 @@ -1051,7 +1022,7 @@
3.439 sw $s2, 0($v0)
3.440
3.441 _visible_update_ret:
3.442 - j irq_exit
3.443 + jr $ra
3.444 nop
3.445
3.446
3.447 @@ -1104,3 +1075,39 @@
3.448 _vsync_active_ret:
3.449 jr $ra
3.450 nop
3.451 +
3.452 +
3.453 +
3.454 +/* Exception handler. */
3.455 +
3.456 +exc_handler:
3.457 + mfc0 $t7, CP0_ERROREPC
3.458 + nop
3.459 +
3.460 +exc_write_word:
3.461 + li $t8, 32
3.462 + la $v0, U1TXREG
3.463 +exc_loop:
3.464 + addiu $t8, $t8, -4
3.465 + srlv $v1, $t7, $t8 /* $v1 = $t7 >> $t8 */
3.466 + andi $v1, $v1, 0xF
3.467 + addiu $t9, $v1, -10 /* $t9 >= 10? */
3.468 + bgez $t9, exc_alpha
3.469 + nop
3.470 +exc_digit:
3.471 + addiu $v1, $v1, 48 /* convert to digit: '0' */
3.472 + j exc_write
3.473 + nop
3.474 +exc_alpha:
3.475 + addiu $v1, $v1, 55 /* convert to alpha: 'A' - 10 */
3.476 +exc_write:
3.477 + sw $v1, 0($v0)
3.478 + bnez $t8, exc_loop
3.479 + nop
3.480 +exc_loop_end:
3.481 + li $v1, '\n'
3.482 + sw $v1, 0($v0)
3.483 +
3.484 +exc_handler_end:
3.485 + j exc_handler_end
3.486 + nop