1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/vga.S Sat May 06 17:53:56 2017 +0200
1.3 @@ -0,0 +1,277 @@
1.4 +/*
1.5 + * Generate a VGA signal using a PIC32 microcontroller.
1.6 + *
1.7 + * Copyright (C) 2017 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software: you can redistribute it and/or modify
1.10 + * it under the terms of the GNU General Public License as published by
1.11 + * the Free Software Foundation, either version 3 of the License, or
1.12 + * (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.21 + */
1.22 +
1.23 +#include "mips.h"
1.24 +#include "pic32.h"
1.25 +
1.26 +#define HFREQ_LIMIT 627 /* 20MHz cycles */
1.27 +
1.28 +/* Disable JTAG functionality on pins. */
1.29 +
1.30 +.section .devcfg0, "a"
1.31 +.word 0xfffffffb /* DEVCFG0<2> = JTAGEN = 0 */
1.32 +
1.33 +/*
1.34 +Set the oscillator to be the FRC oscillator with PLL, with peripheral clock
1.35 +divided by 1, HS oscillator mode selected (for PLL), and FRCDIV+PLL selected.
1.36 +With a system clock of 20MHz, the peripheral clock is therefore 20MHz.
1.37 +
1.38 +The watchdog timer (FWDTEN) is also disabled.
1.39 +*/
1.40 +
1.41 +.section .devcfg1, "a"
1.42 +.word 0xff7fcff9 /* DEVCFG1<23> = FWDTEN = 0; DEVCFG1<13:12> = FPBDIV<2:0> = 0; DEVCFG1<2:0> = FNOSC<2:0> = 001 */
1.43 +
1.44 +/*
1.45 +Set the FRC oscillator PLL function with an input division of 4, an output
1.46 +division of 2, a multiplication of 20, yielding a multiplication of 2.5.
1.47 +This should take the FRC at 8MHz and produce a system clock of 20MHz.
1.48 +*/
1.49 +
1.50 +.section .devcfg2, "a"
1.51 +.word 0xfff9ffdb /* DEVCFG2<18:16> = FPLLODIV<2:0> = 1; DEVCFG2<6:4> = FPLLMUL<2:0> = 101; DEVCFG2<2:0> = FPLLIDIV<2:0> = 011 */
1.52 +
1.53 +.text
1.54 +.globl _start
1.55 +
1.56 +_start:
1.57 + /*
1.58 + Configure RAM.
1.59 + See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization
1.60 + */
1.61 +
1.62 + la $v0, BMXCON
1.63 + li $v1, (1 << 6) /* BMXCON<6> = BMXWSDRM = 0 */
1.64 + sw $v1, CLR($v0)
1.65 +
1.66 + /* Enable caching. */
1.67 +
1.68 + li $v0, CONFIG_CM_CACHABLE_NONCOHERENT
1.69 + mtc0 $v0, CP0_CONFIG
1.70 + nop
1.71 +
1.72 + /* Get the RAM size. */
1.73 +
1.74 + la $v0, BMXDRMSZ
1.75 + lw $v0, 0($v0)
1.76 +
1.77 + /* Initialise the stack pointer. */
1.78 +
1.79 + lui $v1, 0x8000 /* 0x80000000 */
1.80 + addu $sp, $v0, $v1 /* sp = 0x80000000 + RAM size */
1.81 +
1.82 + /* Initialise the globals pointer. */
1.83 +
1.84 + lui $gp, %hi(_GLOBAL_OFFSET_TABLE_)
1.85 + ori $gp, $gp, %lo(_GLOBAL_OFFSET_TABLE_)
1.86 +
1.87 + /* Set pins for output and set outputs low. */
1.88 +
1.89 + jal init_pins
1.90 + nop
1.91 +
1.92 + /* Initialise the status register. */
1.93 +
1.94 + jal init_interrupts
1.95 + nop
1.96 +
1.97 + /* Initialise timer. */
1.98 +
1.99 + jal init_timer1
1.100 + nop
1.101 +
1.102 + /* Enable interrupts and loop. */
1.103 +
1.104 + jal enable_interrupts
1.105 + nop
1.106 +
1.107 + jal handle_error_level
1.108 + nop
1.109 +
1.110 + li $a1, 20000000 /* counter = 20000000 */
1.111 + li $a2, 31872
1.112 +loop:
1.113 + addiu $a1, $a1, -1 /* counter -= 1 */
1.114 + bnez $a1, loop /* until counter == 0 */
1.115 + nop
1.116 +
1.117 + li $a1, 20000000 /* counter = 20000000 */
1.118 +
1.119 + la $v0, PORTB
1.120 + la $v1, (1 << 10) /* PORTB<10> = RB10 */
1.121 + sw $v1, INV($v0)
1.122 +
1.123 +_next:
1.124 + j loop
1.125 + nop
1.126 +
1.127 +
1.128 +
1.129 +init_pins:
1.130 + /* DEVCFG<2> needs setting to 0 before the program is run. */
1.131 +
1.132 + la $v0, CFGCON
1.133 + li $v1, (1 << 3) /* CFGCON<3> = JTAGEN = 0 */
1.134 + sw $v1, CLR($v0)
1.135 +
1.136 + la $v0, TRISB
1.137 + li $v1, (7 << 7) | 15 /* TRISB<9:7> = RB9, RB8, RB7 = 0 */
1.138 + /* sw $v1, CLR($v0) */
1.139 +
1.140 + la $v0, PORTB
1.141 + li $v1, (7 << 7) | 15 /* PORTB<9:7> = RB9, RB8, RB7 = 0 */
1.142 + /* sw $v1, CLR($v0) */
1.143 +
1.144 +init_leds:
1.145 + la $v0, TRISA
1.146 + li $v1, (1 << 2) /* TRISA<2> = RA2 = 0 */
1.147 + sw $v1, CLR($v0)
1.148 +
1.149 + la $v0, TRISB
1.150 + li $v1, (3 << 10) /* TRISB<11:10> = RB11, RB10 = 0 */
1.151 + sw $v1, CLR($v0)
1.152 +
1.153 +clear_leds:
1.154 + la $v0, PORTA
1.155 + li $v1, (1 << 2) /* PORTA<2> = RA2 = 0 */
1.156 + sw $v1, CLR($v0)
1.157 +
1.158 + la $v0, PORTB
1.159 + li $v1, (3 << 10) /* PORTB<11:10> = RB11, RB10 = 0 */
1.160 + sw $v1, CLR($v0)
1.161 +
1.162 + jr $ra
1.163 + nop
1.164 +
1.165 +
1.166 +
1.167 +/* Utilities. */
1.168 +
1.169 +handle_error_level:
1.170 + mfc0 $t3, CP0_STATUS
1.171 + li $t4, ~(STATUS_ERL | STATUS_EXL)
1.172 + and $t3, $t3, $t4
1.173 + mtc0 $t3, CP0_STATUS
1.174 + jr $ra
1.175 + nop
1.176 +
1.177 +enable_interrupts:
1.178 + mfc0 $t3, CP0_STATUS
1.179 + nop
1.180 + ori $t3, $t3, STATUS_IRQ | STATUS_IE
1.181 + mtc0 $t3, CP0_STATUS
1.182 + jr $ra
1.183 + nop
1.184 +
1.185 +init_interrupts:
1.186 + mfc0 $t3, CP0_STATUS
1.187 + li $t4, ~STATUS_BEV
1.188 + and $t3, $t3, $t4
1.189 + mtc0 $t3, CP0_STATUS /* CP0_STATUS &= ~STATUS_BEV (use non-bootloader vectors) */
1.190 +
1.191 + la $t3, _start
1.192 + mtc0 $t3, CP0_EBASE /* EBASE = _start */
1.193 +
1.194 + li $t3, CAUSE_IV /* IV = 1 (use EBASE+0x200 for interrupts) */
1.195 + mtc0 $t3, CP0_CAUSE
1.196 +
1.197 + jr $ra
1.198 + nop
1.199 +
1.200 +
1.201 +
1.202 +/* Interrupt servicing. */
1.203 +
1.204 +.org 0x200
1.205 +
1.206 +interrupt_handler:
1.207 +
1.208 + /* Check for a timer interrupt condition. */
1.209 +
1.210 + la $v0, IFS0
1.211 + lw $v1, 0($v0)
1.212 + and $v1, $v1, (1 << 4) /* T1IF */
1.213 + beqz $v1, irq_exit
1.214 + nop
1.215 +
1.216 + addiu $a2, $a2, -1
1.217 + bnez $a2, irq_clear
1.218 + nop
1.219 +
1.220 + li $a2, 31872
1.221 +
1.222 + /* Invert an LED. */
1.223 +
1.224 + la $v0, PORTB
1.225 + la $v1, (1 << 11) /* PORTB<11> = RB11 */
1.226 + sw $v1, INV($v0)
1.227 +
1.228 +irq_clear:
1.229 +
1.230 + /* Clear the timer interrupt condition. */
1.231 +
1.232 + la $v0, IFS0
1.233 + li $v1, (1 << 4) /* IFS0<4> = T1IF = 0 */
1.234 + sw $v1, CLR($v0)
1.235 +
1.236 +irq_exit:
1.237 + eret
1.238 + nop
1.239 +
1.240 +
1.241 +
1.242 +/* Initialisation routines. */
1.243 +
1.244 +init_timer1:
1.245 +
1.246 + /* Initialise Timer1 interrupt. */
1.247 +
1.248 + la $v0, T1CON
1.249 + sw $zero, 0($v0) /* T1CON = 0 */
1.250 + nop
1.251 +
1.252 + la $v0, TMR1
1.253 + sw $zero, 0($v0) /* TMR1 = 0 */
1.254 + la $v0, PR1
1.255 + li $v1, HFREQ_LIMIT
1.256 + sw $v1, 0($v0) /* PR1 = HFREQ_LIMIT */
1.257 +
1.258 + /* Initialise Timer1 interrupt. */
1.259 +
1.260 + la $v0, IFS0
1.261 + li $v1, (1 << 4)
1.262 + sw $v1, CLR($v0) /* IFS0CLR: T1IF = 0 */
1.263 + la $v0, IPC1
1.264 + li $v1, (7 << 2)
1.265 + sw $v1, SET($v0) /* IPC1SET: T1IP = 7 */
1.266 + la $v0, IPC1
1.267 + li $v1, 3
1.268 + sw $v1, SET($v0) /* IPC1SET: T1IS = 3 */
1.269 + la $v0, IEC0
1.270 + li $v1, (1 << 4)
1.271 + sw $v1, SET($v0) /* IEC0SET: T1IE = 1 */
1.272 +
1.273 + /* Start timer. */
1.274 +
1.275 + la $v0, T1CON
1.276 + li $v1, (1 << 15) /* ON = 1 */
1.277 + sw $v1, SET($v0) /* T1CONSET: ON = 1 */
1.278 +
1.279 + jr $ra
1.280 + nop