# HG changeset patch # User Paul Boddie # Date 1511195693 -3600 # Node ID 0b12a93c210e51640145bcfd48f8fc7dae40a7d4 # Parent ef577181c119552145f1f798b3cc2198b1ea0cde# Parent 078c1d57677c7755c9d865f338c59215b2ebd90f Merged changes from a parallel development branch. Retained DMA interrupt usage even though it should be superfluous because it appears to be needed in making the channel chaining work. diff -r ef577181c119 -r 0b12a93c210e vga.S --- a/vga.S Sun Nov 19 15:31:08 2017 +0100 +++ b/vga.S Mon Nov 20 17:34:53 2017 +0100 @@ -230,9 +230,9 @@ li $t1, (1 << 3) /* PORTA<3> = RA3 */ sw $t1, INV($t0) - la $v0, U1TXREG + la $v0, U1TXREG li $v1, '.' - sw $v1, 0($v0) + sw $v1, 0($v0) bnez $a1, loop /* until counter == 0 */ nop @@ -306,7 +306,11 @@ -/* Initialisation routines. */ +/* +Timer initialisation. + +Timer2 is used to drive the output compare and DMA peripherals. +*/ init_timers: @@ -332,8 +336,8 @@ la $v0, IPC2 li $v1, 0b11111 sw $v1, CLR($v0) /* T2IP, T2IS = 0 */ - li $v1, 0b11111 - sw $v1, SET($v0) /* T2IP = 7; T2IS = 3 */ + li $v1, 0b00111 + sw $v1, SET($v0) /* T2IP = 1; T2IS = 3 */ la $v0, IEC0 li $v1, (1 << 9) @@ -396,18 +400,21 @@ Using OC2, Timer2 triggers a level shifting event and OC2 is reconfigured to reverse the level at a later point. In this way, the vsync pulse is generated and is synchronised to the display lines. + +The OC1 interrupt is used to update the state machine handling the display, +also invoking the address update routine at the end of each visible display +line, changing the source address of the DMA channel. */ init_oc: /* Disable OC1 interrupts. */ - la $v0, IEC0 - li $v1, (1 << 7) /* IEC0<7> = OC1IE = 0 */ - sw $v1, CLR($v0) + li $v1, (1 << 7) + la $v0, IEC0 + sw $v1, CLR($v0) /* IEC0<7> = OC1IE = 0 */ la $v0, IFS0 - li $v1, (1 << 7) /* IFS0<7> = OC1IF = 0 */ - sw $v1, CLR($v0) + sw $v1, CLR($v0) /* IFS0<7> = OC1IF = 0 */ /* Initialise OC1. */ @@ -425,6 +432,16 @@ li $v1, HSYNC_START /* HSYNC_END for positive polarity */ sw $v1, 0($v0) + /* Enable interrupt for address updating. */ + + la $v0, IPC1 + li $v1, (0b11111 << 16) + sw $v1, SET($v0) /* OC1IP = 7; OC1IS = 3 */ + + la $v0, IEC0 + li $v1, (1 << 7) + sw $v1, SET($v0) /* IEC0<7> = OC1IE = 1 */ + /* OC1 is enabled. */ la $v0, OC1CON @@ -433,13 +450,12 @@ /* Disable OC2 interrupts. */ - la $v0, IEC0 - li $v1, (1 << 12) /* IEC0<12> = OC2IE = 0 */ - sw $v1, CLR($v0) + li $v1, (1 << 12) + la $v0, IEC0 + sw $v1, CLR($v0) /* IEC0<12> = OC2IE = 0 */ la $v0, IFS0 - li $v1, (1 << 12) /* IFS0<12> = OC2IF = 0 */ - sw $v1, CLR($v0) + sw $v1, CLR($v0) /* IFS0<12> = OC2IF = 0 */ /* Initialise OC2. */ @@ -508,8 +524,7 @@ Direct Memory Access initialisation. Write 160 pixels to PORTB for the line data. This is initiated by a timer -interrupt. Upon completion of the transfer, a DMA interrupt initiates the -address update routine, changing the source address of the DMA channel. +interrupt. */ init_dma: @@ -693,21 +708,16 @@ la $v0, DCH2DSA sw $v1, 0($v0) - /* - Use the block transfer completion interrupt to indicate when the source - address can be updated. - */ + /* Enable interrupt for channel chaining. */ la $v0, DCH0INT li $v1, (1 << 19) /* CHBCIE = 1 */ sw $v1, 0($v0) - /* Enable interrupt for address updating. */ - la $v0, IPC10 li $v1, 0b11111 /* DMA0IP, DMA0IS = 0 */ sw $v1, CLR($v0) - li $v1, 0b11111 /* DMA0IP = 7, DMA0IS = 3 */ + li $v1, 0b10011 /* DMA0IP = 4, DMA0IS = 3 */ sw $v1, SET($v0) la $v0, IEC1 @@ -731,7 +741,12 @@ -/* UART initialisation. */ +/* +UART initialisation. + +Initialise UART transmission at 115200 baud. This is sensitive to the peripheral +clock frequency. +*/ init_uart: /* Initialise UART. */ @@ -844,51 +859,12 @@ load_state li $sp, IRQ_STACK_TOP - /* - The timer interrupt will only occur outside the visible region, but the - interrupt condition will still occur as the timer wraps around. - - Here, we deliberately ignore the timer condition during the visible/ - active region. - - The DMA interrupt should only be active within the visible region. - */ - - la $t8, visible_active - beq $s1, $t8, irq_dma - nop - - /* Check for a timer interrupt condition. */ + /* Check for the output compare interrupt condition. */ la $v0, IFS0 lw $v1, 0($v0) - andi $v1, $v1, (1 << 9) /* T2IF */ - beqz $v1, irq_exit - nop - - j irq_handle - nop - -irq_dma: - /* Check for a DMA interrupt condition. */ - - la $v0, IFS1 - lw $v1, 0($v0) - li $t8, (1 << 28) /* DMA0IF */ - and $v1, $v1, $t8 - beqz $v1, irq_exit - nop - - /* Clear the DMA interrupt condition. */ - - sw $v1, CLR($v0) - - /* Test the block transfer completion interrupt flag. */ - - la $v0, DCH0INT - lw $v1, 0($v0) - andi $v1, $v1, (1 << 3) /* CHBCIF */ - beqz $v1, irq_exit + andi $v1, $v1, (1 << 7) /* OC1IF */ + beqz $v1, irq_dma nop irq_handle: @@ -905,6 +881,26 @@ jalr $s1 nop +irq_dma: + /* Clear the DMA channel completion condition. */ + + la $v0, IFS1 + lw $v1, 0($v0) + li $t8, (1 << 28) + and $v1, $v1, $t8 + beqz $v1, irq_exit + nop + + sw $v1, CLR($v0) /* IFS1<28> = DMA0IF = 0 */ + + la $v0, DCH0INT + lw $v1, 0($v0) + andi $v1, $v1, (1 << 3) + beqz $v1, irq_exit + nop + + sw $v1, CLR($v0) /* CHBCIF = 0 */ + irq_exit: /* Save IRQ state and restore the affected registers, switching back to the @@ -944,18 +940,6 @@ la $v0, DCH0SSA sw $s2, 0($v0) - /* - Suspend delivery of the timer interrupt during the visible period. - The condition still occurs, however. - */ - - la $v0, IPC2 - lw $v1, 0($v0) - li $t8, ~0b11111 - and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */ - ori $v1, $v1, 0b00111 /* T2IP = 1; T2IS = 3 */ - sw $v1, 0($v0) - /* Enable the line channel for timer event transfer initiation. */ la $v0, DCH0ECON @@ -981,15 +965,6 @@ la $s1, vfp_active - /* Restore delivery of the timer interrupt after the visible period. */ - - la $v0, IPC2 - lw $v1, 0($v0) - li $t8, ~0b11111 - and $v1, $v1, $t8 /* T2IP = 0; T2IS = 0 */ - ori $v1, $v1, 0b11111 /* T2IP = 7; T2IS = 3 */ - sw $v1, 0($v0) - /* Disable the line channel. */ la $v0, DCH0ECON @@ -1099,6 +1074,7 @@ exc_handler: mfc0 $t7, CP0_ERROREPC nop + la $ra, exc_handler_end exc_write_word: li $t8, 32 @@ -1121,9 +1097,9 @@ bnez $t8, exc_loop nop exc_loop_end: - li $v1, '\n' + li $v1, ' ' sw $v1, 0($v0) exc_handler_end: - j exc_handler_end + jr $ra nop