1.1 --- a/vga.S Sun Nov 19 00:19:20 2017 +0100
1.2 +++ b/vga.S Mon Nov 20 16:33:06 2017 +0100
1.3 @@ -230,9 +230,9 @@
1.4 li $t1, (1 << 3) /* PORTA<3> = RA3 */
1.5 sw $t1, INV($t0)
1.6
1.7 - la $v0, U1TXREG
1.8 + la $v0, U1TXREG
1.9 li $v1, '.'
1.10 - sw $v1, 0($v0)
1.11 + sw $v1, 0($v0)
1.12
1.13 bnez $a1, loop /* until counter == 0 */
1.14 nop
1.15 @@ -306,7 +306,11 @@
1.16
1.17
1.18
1.19 -/* Initialisation routines. */
1.20 +/*
1.21 +Timer initialisation.
1.22 +
1.23 +Timer2 is used to drive the output compare and DMA peripherals.
1.24 +*/
1.25
1.26 init_timer2:
1.27
1.28 @@ -332,8 +336,8 @@
1.29 la $v0, IPC2
1.30 li $v1, 0b11111
1.31 sw $v1, CLR($v0) /* T2IP, T2IS = 0 */
1.32 - li $v1, 0b11111
1.33 - sw $v1, SET($v0) /* T2IP = 7; T2IS = 3 */
1.34 + li $v1, 0b00111
1.35 + sw $v1, SET($v0) /* T2IP = 1; T2IS = 3 */
1.36
1.37 la $v0, IEC0
1.38 li $v1, (1 << 9)
1.39 @@ -361,18 +365,21 @@
1.40 Using OC2, Timer2 triggers a level shifting event and OC2 is reconfigured to
1.41 reverse the level at a later point. In this way, the vsync pulse is generated
1.42 and is synchronised to the display lines.
1.43 +
1.44 +The OC1 interrupt is used to update the state machine handling the display,
1.45 +also invoking the address update routine at the end of each visible display
1.46 +line, changing the source address of the DMA channel.
1.47 */
1.48
1.49 init_oc:
1.50 /* Disable OC1 interrupts. */
1.51
1.52 - la $v0, IEC0
1.53 - li $v1, (1 << 7) /* IEC0<7> = OC1IE = 0 */
1.54 - sw $v1, CLR($v0)
1.55 + li $v1, (1 << 7)
1.56
1.57 + la $v0, IEC0
1.58 + sw $v1, CLR($v0) /* IEC0<7> = OC1IE = 0 */
1.59 la $v0, IFS0
1.60 - li $v1, (1 << 7) /* IFS0<7> = OC1IF = 0 */
1.61 - sw $v1, CLR($v0)
1.62 + sw $v1, CLR($v0) /* IFS0<7> = OC1IF = 0 */
1.63
1.64 /* Initialise OC1. */
1.65
1.66 @@ -390,6 +397,16 @@
1.67 li $v1, HSYNC_START /* HSYNC_END for positive polarity */
1.68 sw $v1, 0($v0)
1.69
1.70 + /* Enable interrupt for address updating. */
1.71 +
1.72 + la $v0, IPC1
1.73 + li $v1, (0b11111 << 16)
1.74 + sw $v1, SET($v0) /* OC1IP = 7; OC1IS = 3 */
1.75 +
1.76 + la $v0, IEC0
1.77 + li $v1, (1 << 7)
1.78 + sw $v1, SET($v0) /* IEC0<7> = OC1IE = 1 */
1.79 +
1.80 /* OC1 is enabled. */
1.81
1.82 la $v0, OC1CON
1.83 @@ -398,13 +415,12 @@
1.84
1.85 /* Disable OC2 interrupts. */
1.86
1.87 - la $v0, IEC0
1.88 - li $v1, (1 << 12) /* IEC0<12> = OC2IE = 0 */
1.89 - sw $v1, CLR($v0)
1.90 + li $v1, (1 << 12)
1.91
1.92 + la $v0, IEC0
1.93 + sw $v1, CLR($v0) /* IEC0<12> = OC2IE = 0 */
1.94 la $v0, IFS0
1.95 - li $v1, (1 << 12) /* IFS0<12> = OC2IF = 0 */
1.96 - sw $v1, CLR($v0)
1.97 + sw $v1, CLR($v0) /* IFS0<12> = OC2IF = 0 */
1.98
1.99 /* Initialise OC2. */
1.100
1.101 @@ -473,8 +489,7 @@
1.102 Direct Memory Access initialisation.
1.103
1.104 Write 160 pixels to PORTB for the line data. This is initiated by a timer
1.105 -interrupt. Upon completion of the transfer, a DMA interrupt initiates the
1.106 -address update routine, changing the source address of the DMA channel.
1.107 +interrupt.
1.108 */
1.109
1.110 init_dma:
1.111 @@ -631,21 +646,16 @@
1.112 la $v0, DCH1DSA
1.113 sw $v1, 0($v0)
1.114
1.115 - /*
1.116 - Use the block transfer completion interrupt to indicate when the source
1.117 - address can be updated.
1.118 - */
1.119 + /* Enable interrupt for channel chaining. */
1.120
1.121 la $v0, DCH0INT
1.122 li $v1, (1 << 19) /* CHBCIE = 1 */
1.123 sw $v1, 0($v0)
1.124
1.125 - /* Enable interrupt for address updating. */
1.126 -
1.127 la $v0, IPC10
1.128 li $v1, 0b11111 /* DMA0IP, DMA0IS = 0 */
1.129 sw $v1, CLR($v0)
1.130 - li $v1, 0b11111 /* DMA0IP = 7, DMA0IS = 3 */
1.131 + li $v1, 0b10011 /* DMA0IP = 4, DMA0IS = 3 */
1.132 sw $v1, SET($v0)
1.133
1.134 la $v0, IEC1
1.135 @@ -666,7 +676,12 @@
1.136
1.137
1.138
1.139 -/* UART initialisation. */
1.140 +/*
1.141 +UART initialisation.
1.142 +
1.143 +Initialise UART transmission at 115200 baud. This is sensitive to the peripheral
1.144 +clock frequency.
1.145 +*/
1.146
1.147 init_uart:
1.148 /* Initialise UART. */
1.149 @@ -779,51 +794,12 @@
1.150 load_state
1.151 li $sp, IRQ_STACK_TOP
1.152
1.153 - /*
1.154 - The timer interrupt will only occur outside the visible region, but the
1.155 - interrupt condition will still occur as the timer wraps around.
1.156 -
1.157 - Here, we deliberately ignore the timer condition during the visible/
1.158 - active region.
1.159 -
1.160 - The DMA interrupt should only be active within the visible region.
1.161 - */
1.162 -
1.163 - la $t8, visible_active
1.164 - beq $s1, $t8, irq_dma
1.165 - nop
1.166 -
1.167 - /* Check for a timer interrupt condition. */
1.168 + /* Check for the output compare interrupt condition. */
1.169
1.170 la $v0, IFS0
1.171 lw $v1, 0($v0)
1.172 - andi $v1, $v1, (1 << 9) /* T2IF */
1.173 - beqz $v1, irq_exit
1.174 - nop
1.175 -
1.176 - j irq_handle
1.177 - nop
1.178 -
1.179 -irq_dma:
1.180 - /* Check for a DMA interrupt condition. */
1.181 -
1.182 - la $v0, IFS1
1.183 - lw $v1, 0($v0)
1.184 - li $t8, (1 << 28) /* DMA0IF */
1.185 - and $v1, $v1, $t8
1.186 - beqz $v1, irq_exit
1.187 - nop
1.188 -
1.189 - /* Clear the DMA interrupt condition. */
1.190 -
1.191 - sw $v1, CLR($v0)
1.192 -
1.193 - /* Test the block transfer completion interrupt flag. */
1.194 -
1.195 - la $v0, DCH0INT
1.196 - lw $v1, 0($v0)
1.197 - andi $v1, $v1, (1 << 3) /* CHBCIF */
1.198 - beqz $v1, irq_exit
1.199 + andi $v1, $v1, (1 << 7) /* OC1IF */
1.200 + beqz $v1, irq_dma
1.201 nop
1.202
1.203 irq_handle:
1.204 @@ -840,6 +816,26 @@
1.205 jalr $s1
1.206 nop
1.207
1.208 +irq_dma:
1.209 + /* Clear the DMA channel completion condition. */
1.210 +
1.211 + la $v0, IFS1
1.212 + lw $v1, 0($v0)
1.213 + li $t8, (1 << 28)
1.214 + and $v1, $v1, $t8
1.215 + beqz $v1, irq_exit
1.216 + nop
1.217 +
1.218 + sw $v1, CLR($v0) /* IFS1<28> = DMA0IF = 0 */
1.219 +
1.220 + la $v0, DCH0INT
1.221 + lw $v1, 0($v0)
1.222 + andi $v1, $v1, (1 << 3)
1.223 + beqz $v1, irq_exit
1.224 + nop
1.225 +
1.226 + sw $v1, CLR($v0) /* CHBCIF = 0 */
1.227 +
1.228 irq_exit:
1.229 /*
1.230 Save IRQ state and restore the affected registers, switching back to the
1.231 @@ -879,18 +875,6 @@
1.232 la $v0, DCH0SSA
1.233 sw $s2, 0($v0)
1.234
1.235 - /*
1.236 - Suspend delivery of the timer interrupt during the visible period.
1.237 - The condition still occurs, however.
1.238 - */
1.239 -
1.240 - la $v0, IPC2
1.241 - lw $v1, 0($v0)
1.242 - li $t8, ~0b11111
1.243 - and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
1.244 - ori $v1, $v1, 0b00111 /* T2IP = 1; T2IS = 3 */
1.245 - sw $v1, 0($v0)
1.246 -
1.247 /* Enable the line channel for timer event transfer initiation. */
1.248
1.249 la $v0, DCH0ECON
1.250 @@ -916,15 +900,6 @@
1.251
1.252 la $s1, vfp_active
1.253
1.254 - /* Restore delivery of the timer interrupt after the visible period. */
1.255 -
1.256 - la $v0, IPC2
1.257 - lw $v1, 0($v0)
1.258 - li $t8, ~0b11111
1.259 - and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */
1.260 - ori $v1, $v1, 0b11111 /* T2IP = 7; T2IS = 3 */
1.261 - sw $v1, 0($v0)
1.262 -
1.263 /* Disable the line channel. */
1.264
1.265 la $v0, DCH0ECON
1.266 @@ -1034,6 +1009,7 @@
1.267 exc_handler:
1.268 mfc0 $t7, CP0_ERROREPC
1.269 nop
1.270 + la $ra, exc_handler_end
1.271
1.272 exc_write_word:
1.273 li $t8, 32
1.274 @@ -1056,9 +1032,9 @@
1.275 bnez $t8, exc_loop
1.276 nop
1.277 exc_loop_end:
1.278 - li $v1, '\n'
1.279 + li $v1, ' '
1.280 sw $v1, 0($v0)
1.281
1.282 exc_handler_end:
1.283 - j exc_handler_end
1.284 + jr $ra
1.285 nop