#include #define UART0_BASE 0x81000000 #define UART0_SR (*((volatile uint32_t *) (UART0_BASE + 0x00))) #define UART0_DR (*((volatile uint32_t *) (UART0_BASE + 0x04))) #define UART0_SR_RX_DATA_EMPTY (1 << 0) #define UART0_SR_TX_FULL (1 << 1) /* // Set up stack pointer asm("_start:\ xor sp, sp, sp;\ lui sp, 0x80100;\ j main;\ "); */ __attribute__((noreturn)) void _start() { uint8_t state = 0; uint8_t c; uint8_t opcode; uint8_t *ptr; uint32_t length; /*while (1) { while (UART0_SR & UART0_SR_RX_DATA_EMPTY); c = UART0_DR; while (UART0_SR & UART0_SR_TX_FULL); UART0_DR = c; }*/ while (1) { while (UART0_SR & UART0_SR_RX_DATA_EMPTY); c = UART0_DR; state++; switch (state) { case 1: // Opcode opcode = c; ptr = 0; length = 0; if (c == 0) // NOP state = 0; break; case 2: // Address case 3: case 4: case 5: ptr = (uint8_t*)(((uint32_t)ptr << 8) | c); if (state != 5) break; if (opcode == 3) { // Jump //((void (*)()) ptr)(); asm("jalr %0" : : "r"(ptr)); // Not sure why, but the jump above causes GCC to save variables to the stack at the beginning of this function __builtin_unreachable(); } break; case 6: // Length case 7: case 8: case 9: length = (length << 8) | c; if (state != 9) break; if (opcode == 2) { // Read for (; length > 0; length--) { while (UART0_SR & UART0_SR_TX_FULL); UART0_DR = *(ptr); ptr++; } state = 0; } break; case 10: // Write byte by byte *(ptr++) = c; if (--length == 0) { state = 0; } else { state--; // Stay in this state } break; default: break; } } __builtin_unreachable(); }