diff options
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | Win.mk | 24 | ||||
-rw-r--r-- | src/boot.S | 122 | ||||
-rw-r--r-- | src/irq.S | 91 | ||||
-rw-r--r-- | src/kernel.S | 38 | ||||
-rw-r--r-- | src/lib.c | 121 | ||||
-rw-r--r-- | src/lib.h | 79 | ||||
-rw-r--r-- | src/time.c | 76 | ||||
-rw-r--r-- | src/uart.S | 112 |
9 files changed, 471 insertions, 193 deletions
@@ -2,3 +2,4 @@ https://www.raspberrypi.com/documentation/computers/processors.html https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf https://github.com/eggman/raspberrypi/tree/master/qemu-raspi2 +https://github.com/eggman/FreeRTOS-raspi3/blob/master/Demo/FreeRTOS_tick_config.c @@ -1,9 +1,9 @@ C_SOURCES = $(wildcard src/*.c) C_HEADERS = $(wildcard src/*.h) -C_OBJECTS = ${C_SOURCES:.c=.o} +C_OBJECTS = ${C_SOURCES:.c=.co} C_OBJECTD = ${subst src,obj,${C_OBJECTS}} A_SOURCES = $(wildcard src/*.S) -A_OBJECTS = ${A_SOURCES:.S=.o} +A_OBJECTS = ${A_SOURCES:.S=.ao} A_OBJECTD = ${subst src,obj,${A_OBJECTS}} CROSS = arm-none-eabi @@ -13,10 +13,17 @@ OBJCOPY = ${CROSS}-objcopy OBJDUMP = ${CROSS}-objdump QEMU = /mnt/c/qemu/qemu-system-arm.exe GDB = gdb-multiarch -CFLAGS = -mcpu=cortex-a7 -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -nostdlib -g -AFLAGS = -mcpu=cortex-a7 +CFLAGS = -mcpu=cortex-a7 -fpic -ffreestanding -std=gnu99 -O3 -Wall -Wextra -nostdlib -g +AFLAGS = -mcpu=cortex-a7 -g QFLAGS = -M raspi2 -cpu arm1176 -m 1G -serial mon:stdio -nographic +BSP ?= 2 + +ifeq ($(BSP),2) + BSP23 = 1 + CFLAGS += -DBSP23 +endif + .PHONY: clean run run-debug debug export default: clean build/kernel7.img @@ -36,8 +43,11 @@ build/kernel.elf: ${A_OBJECTD} ${C_OBJECTD} export: build/kernel.list cp build/kernel-g.elf /mnt/c/Local/ -obj/%.o: src/%.S - ${AS} ${AFLAGS} -g -c $< -o $@ +obj/%.co: src/%.c + ${CC} ${CFLAGS} -c $< -o $@ + +obj/%.ao: src/%.S + ${AS} ${AFLAGS} -c $< -o $@ run: build/kernel.elf ${QEMU} -kernel $< ${QFLAGS} @@ -49,4 +59,4 @@ debug: build/kernel-g.elf build/kernel.list ${GDB} $< -command=gdbinit clean: - rm -f obj/*.o build/*.elf build/*.list build/*.img + rm -f obj/*.o build/*.elf build/*.list build/*.img obj/*.ao obj/*.co @@ -6,100 +6,100 @@ _start: reset: - // disable core0,1,2. - mrc p15, #0, r1, c0, c0, #5 - and r1, r1, #3 - cmp r1, #0 - bne io_halt + // disable core0,1,2. + mrc p15, #0, r1, c0, c0, #5 + and r1, r1, #3 + cmp r1, #0 + bne io_halt - // set vector address. - ldr r0, =vector - mcr p15, 0, r0, c12, c0, 0 + // set vector address. + ldr r0, =vector + mcr p15, 0, r0, c12, c0, 0 - // save cpsr. - mrs r0, cpsr + // save cpsr. + mrs r0, cpsr - // setup sp in IRQ mode. - bic r1, r0, #0x1f - orr r1, r1, #0x12 - msr cpsr_c,r1 - mov sp,#0x4000 + // setup sp in IRQ mode. + bic r1, r0, #0x1f + orr r1, r1, #0x12 + msr cpsr_c,r1 + mov sp,#0x4000 - // restore cpsr. - msr cpsr_c, r0 + // restore cpsr. + msr cpsr_c, r0 - // setup the stack in SVC mode. - mov sp, #0x8000 + // setup the stack in SVC mode. + mov sp, #0x8000 - // Clear out bss. - ldr r4, =__bss_start - ldr r9, =__bss_end - mov r5, #0 - mov r6, #0 - mov r7, #0 - mov r8, #0 - b 2f + // Clear out bss. + ldr r4, =__bss_start + ldr r9, =__bss_end + mov r5, #0 + mov r6, #0 + mov r7, #0 + mov r8, #0 + b 2f 1: - // store multiple at r4. - stmia r4!, {r5-r8} + // store multiple at r4. + stmia r4!, {r5-r8} - // If we are still below bss_end, loop. + // If we are still below bss_end, loop. 2: - cmp r4, r9 - blo 1b + cmp r4, r9 + blo 1b - // Call kernel_main - ldr r3, =kernel_main - blx r3 + // Call kernel_main + ldr r3, =kernel_main + blx r3 irq: - push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} - bl a_irq_handler - pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} - subs pc, lr, #4 + push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + bl a_irq_handler + pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + subs pc, lr, #4 fiq: - push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} - bl a_fiq_handler - pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} - subs pc, lr, #4 + push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + bl a_fiq_handler + pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr} + subs pc, lr, #4 .globl io_halt io_halt: - wfi - b io_halt + wfi + b io_halt .globl enable_irq enable_irq: - cpsie i - bx lr + cpsie i + bx lr .globl disable_irq disable_irq: - cpsid i - bx lr + cpsid i + bx lr .globl enable_fiq enable_fiq: - cpsie f - bx lr + cpsie f + bx lr .globl disable_fiq disable_fiq: - cpsid f - bx lr + cpsid f + bx lr .align 5 vector: - ldr pc, reset_handler - ldr pc, undefined_handler - ldr pc, swi_handler - ldr pc, prefetch_handler - ldr pc, data_handler - ldr pc, unused_handler - ldr pc, irq_handler - ldr pc, fiq_handler + ldr pc, reset_handler + ldr pc, undefined_handler + ldr pc, swi_handler + ldr pc, prefetch_handler + ldr pc, data_handler + ldr pc, unused_handler + ldr pc, irq_handler + ldr pc, fiq_handler reset_handler: .word reset undefined_handler: .word io_halt @@ -1,51 +1,62 @@ .globl a_irq_handler a_irq_handler: - push {lr} - bl disable_irq - // r2 = CORE0_INTERRUPT_SOURCE - // if r2 & 0b100000000 - mov r2, #0x40000000 - ldr r3, [r2, #0x60] - tst r3, #256 - beq a_irq_handler.exit - // r2 = IRQ_PEND2 - // r2 & 1 << 25 - mov r2, #0xB208 - movt r2, #0x3F00 - ldr r3, [r2] - tst r3, #0x2000000 - beq a_irq_handler.exit - mov r2, #0x1040 - movt r2, #0x3F20 - ldr r3, [r2] - tst r3, #16 - beq a_irq_handler.exit - mov r2, #0x1000 - movt r2, #0x3F20 - ldrb r0, [r2] - push {r0} - bl enable_irq - pop {r0} - bl uart_char - ldr r0, =imsg - bl uart_string - pop {pc} + push {lr} + bl disable_irq + // r2 = CORE0_INTERRUPT_SOURCE + // if r2 & 0b100000000 + mov r2, #0x40000000 + ldr r3, [r2, #0x60] + tst r3, #256 + beq a_irq_handler.timer + // r2 = IRQ_PEND2 + // r2 & 1 << 25 + mov r2, #0xB208 + movt r2, #0x3F00 + ldr r3, [r2] + tst r3, #0x2000000 + beq a_irq_handler.exit + mov r2, #0x1040 + movt r2, #0x3F20 + ldr r3, [r2] + tst r3, #16 + beq a_irq_handler.exit + mov r2, #0x1000 + movt r2, #0x3F20 + ldrb r0, [r2] + push {r0} + bl enable_irq + pop {r0} + bl uart_char + ldr r0, =imsg + bl uart_string + pop {pc} +a_irq_handler.timer: + // r2 = CORE0_INTERRUPT_SOURCE + // if r2 & 0b1000 + mov r2, #0x40000000 + ldr r3, [r2, #0x60] + tst r3, #8 + beq a_irq_handler.exit + bl c_timer + //ldr r0, =imsg + //bl uart_string a_irq_handler.exit: - bl enable_irq - pop {pc} + bl enable_irq + pop {pc} .globl a_fiq_handler a_fiq_handler: - push {lr} - bl disable_fiq - ldr r0, =fmsg - bl uart_string + push {lr} + bl disable_fiq + bl c_timer + ldr r0, =fmsg + bl uart_string a_fiq_handler.exit: - bl enable_fiq - pop {pc} + bl enable_fiq + pop {pc} .section ".data" imsg: - .asciz " a_irq_handler\n" + .asciz " asm_irq_handler\n" fmsg: - .asciz " a_fiq_handler\n" + .asciz " asm_fiq_handler\n" diff --git a/src/kernel.S b/src/kernel.S index 46c8fab..02d8a5d 100644 --- a/src/kernel.S +++ b/src/kernel.S @@ -3,10 +3,11 @@ .globl kernel_main kernel_main: push {lr} - bl uart_init + //bl uart_init + bl sysinit bl enable_irq bl enable_fiq - bl check_fiq_status + bl chk_irq_stat ldr r0, =os_info bl uart_string kernel_main.loop: @@ -14,33 +15,10 @@ kernel_main.loop: b kernel_main.loop pop {lr} -check_fiq_status: - push {lr} - mov r2, #0xB20C - movt r2, #0x3F00 - mov r3, #0xC1 - movt r3, #0 - str r3, [r2] - ldr r3, [r2] - push {r3} - mov r0, r3 - bl uart_hex - pop {r3} - tst r3, #0x80 - beq check_fiq_status.off - ldr r0, =fiq_on - bl uart_string - b check_fiq_status.exit -check_fiq_status.off: - ldr r0, =fiq_off - bl uart_string -check_fiq_status.exit: - pop {pc} - .section ".data" +.globl os_info os_info: - .asciz "Sergey Bilovytskyy's Real Time Operating System\n Version 0.0a\n Interrupt 01: uart rx interrupt\n Exit : Ctrl-A x\n Monitor : Ctrl-A c\n\n" -fiq_on: - .asciz "\nFIQ - Enabled\n" -fiq_off: - .asciz "\nFIQ - Disabled\n" + .asciz "\033[93mInitialized the Real Time Operating System\033[0m\n\033[96mName\033[0m: \033[94mTarvaOS\033[0m\n\033[96mVersion\033[0m: \033[95m0.0a\033[0m\n\nQEMU\n====\n Exit : Ctrl-A x\n Monitor : Ctrl-A c\n\n" +.globl cntfrq +cntfrq: + .word 0,0,0,0 diff --git a/src/lib.c b/src/lib.c new file mode 100644 index 0000000..2d8a596 --- /dev/null +++ b/src/lib.c @@ -0,0 +1,121 @@ +#include "lib.h" + +extern void uart_char(unsigned char c); +extern void uart_hex(unsigned long data); +extern void uart_string(char* message); + +extern unsigned long read_cntv_tval(); +extern unsigned long read_cntfrq(); +extern void write_cntv_tval(unsigned long); // Clear cntv interrupt and set next 1 second timer +extern void routing_core0cntv_to_core0irq(); +extern void enable_cntv(); + +extern unsigned long cntfrq; + +static char* irq_on = " \033[92mEnabled\033[0m\n"; +static char* irq_off = " \033[91mDisabled\033[0m\n"; + +static inline unsigned long load32(unsigned long addr) { + return *(volatile unsigned long*)addr; +} + +static inline void store32(unsigned long value, unsigned long addr) { + *(volatile unsigned long*)addr = value; +} + +static inline void delay(unsigned long cycles) { + asm volatile("__delay_%=: subs%[cycles], %[cycles], #1;bne __delay_%=\n" + : "=r"(cycles): [cycles]"0"(cycles) : "cc"); +} + +void uart_hexn(unsigned long c_val) { + uart_hex(c_val); + uart_char(0x0a); +} + +void sysinit() { + // Mask Overrun of UART0 + store32(1<<4, UART0_IMSC); + + // Enable UART GPU IRQ + store32(1<<25, IRQ_ENABLE2); + + // Route GPU interrupts to Core 0 + store32(0x00, GPU_INTERRUPTS_ROUTING); + //*(unsigned long*) + + // Enable Timer + // As an IRQ + store32(1<<0, IRQ_BASIC_ENABLE); + // As a FIQ + //store32(0xC0, FIQ_CONTROL); + // Get the frequency + cntfrq = read_cntfrq(); + // Clear cntv interrupt and set next 1 second timer + write_cntv_tval(cntfrq); + // Route timer to core0 irq + routing_core0cntv_to_core0irq(); + // Enable timer + enable_cntv(); +} + +void c_timer() { + // Reset the counter + write_cntv_tval(cntfrq); + + // Output the value + uart_string((char*)"CNTV_TVAL: "); + uart_hexn(read_cntv_tval()); +} + +// Checks IRQ statuses +void chk_irq_stat() { + uart_string((char*)"Checking Enabled Services...\n"); + + // Basic IRQ + unsigned long ib_val = load32(IRQ_BASIC_ENABLE); + uart_string((char*)"IRQB Status: "); + uart_hexn(ib_val); + + // IRQ 1 + unsigned long i1_val = load32(IRQ_ENABLE1); + uart_string((char*)"IRQ1 Status: "); + uart_hexn(i1_val); + + // IRQ 2 + unsigned long i2_val = load32(IRQ_ENABLE2); + uart_string((char*)"IRQ2 Status: "); + uart_hexn(i2_val); + + // Check UART IRQ + uart_string((char*)" UART:"); + if (i2_val & (1<<25)) { + uart_string(irq_on); + } else { + uart_string(irq_off); + } + + // Check TIMER IRQ + uart_string((char*)" TIMER:"); + if (ib_val & (1<<0)) { + uart_string(irq_on); + // Output the frequency + uart_string((char*)" w/ CNTFRQ : "); + cntfrq = read_cntfrq(); + uart_hexn(cntfrq); + } else { + uart_string(irq_off); + } + + // Check FIQ + unsigned long f_val = load32(FIQ_CONTROL); + uart_string((char*)"FIQ Status: "); + uart_hexn(f_val); + if (f_val & 0x80) { + uart_string(irq_on); + } else { + uart_string(irq_off); + } + + uart_char(0x0a); +} diff --git a/src/lib.h b/src/lib.h new file mode 100644 index 0000000..936a582 --- /dev/null +++ b/src/lib.h @@ -0,0 +1,79 @@ +enum +{ + // The offset for the MMIO area +#ifdef BSP23 + MMIO_BASE = 0x3F000000, // For Raspberry Pi 2 and 3 +#else + MMIO_BASE = 0xFE000000, // For Raspberry Pi 2 and 3 +#endif + + // The offsets for reach register. + GPIO_BASE = (MMIO_BASE + 0x200000), + + // Controls actuation of pull up/down to ALL GPIO pins. + GPPUD = (GPIO_BASE + 0x94), + + // Controls actuation of pull up/down for specific GPIO pin. + GPPUDCLK0 = (GPIO_BASE + 0x98), + + // The base address for UART. + UART0_BASE = (GPIO_BASE + 0x1000), // for raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 0x20201000 for raspi1 + + // The offsets for reach register for the UART. + UART0_DR = (UART0_BASE + 0x00), + UART0_RSRECR = (UART0_BASE + 0x04), + UART0_FR = (UART0_BASE + 0x18), + UART0_ILPR = (UART0_BASE + 0x20), + UART0_IBRD = (UART0_BASE + 0x24), + UART0_FBRD = (UART0_BASE + 0x28), + UART0_LCRH = (UART0_BASE + 0x2C), + UART0_CR = (UART0_BASE + 0x30), + UART0_IFLS = (UART0_BASE + 0x34), + UART0_IMSC = (UART0_BASE + 0x38), + UART0_RIS = (UART0_BASE + 0x3C), + UART0_MIS = (UART0_BASE + 0x40), + UART0_ICR = (UART0_BASE + 0x44), + UART0_DMACR = (UART0_BASE + 0x48), + UART0_ITCR = (UART0_BASE + 0x80), + UART0_ITIP = (UART0_BASE + 0x84), + UART0_ITOP = (UART0_BASE + 0x88), + UART0_TDR = (UART0_BASE + 0x8C), + + // IRQ REGISTERS + IRQ_BASE = (MMIO_BASE + 0xB000), + IRQ_BASIC_PENDING = (IRQ_BASE + 0x200), + IRQ_PENDING1 = (IRQ_BASE + 0x204), + IRQ_PENDING2 = (IRQ_BASE + 0x208), + FIQ_CONTROL = (IRQ_BASE + 0x20C), + IRQ_ENABLE1 = (IRQ_BASE + 0x210), + IRQ_ENABLE2 = (IRQ_BASE + 0x214), + IRQ_BASIC_ENABLE = (IRQ_BASE + 0x218), + IRQ_DISABLE1 = (IRQ_BASE + 0x21C), + IRQ_DISABLE2 = (IRQ_BASE + 0x220), + IRQ_BASIC_DISABLE = (IRQ_BASE + 0x224), + + // Peripherals Interrupts + UART_IRQ = 57, + GPIO_IRQ_0 = 49, + GPIO_IRQ_1 = 50, + GPIO_IRQ_2 = 51, + GPIO_IRQ_3 = 52, + + FIQ_ENABLE_FLAG = 1<<7, + + // ARM Peripheral Interrupts + TIMER_ARM_IRQ = 0, + MAILBOX_ARM_IRQ = 1, + DOORBELL0_ARM_IRQ = 2, + DOORBELL1_ARM_IRQ = 3, + GPU0HALT_ARM_IRQ = 4, + GPU1HALT_ARM_IRQ = 5, + + // The offsets for Mailbox registers + MBOX_BASE = 0xB880, + MBOX_READ = (MBOX_BASE + 0x00), + MBOX_STATUS = (MBOX_BASE + 0x18), + MBOX_WRITE = (MBOX_BASE + 0x20), + + GPU_INTERRUPTS_ROUTING = 0x4000000C +}; diff --git a/src/time.c b/src/time.c new file mode 100644 index 0000000..d637612 --- /dev/null +++ b/src/time.c @@ -0,0 +1,76 @@ +static inline unsigned long load32(unsigned long addr) { + return *(volatile unsigned long*)addr; +} + +static inline void store32(unsigned long value, unsigned long addr) { + *(volatile unsigned long*)addr = value; +} + +#define uint64_t unsigned long long +#define uint32_t unsigned long + +#define CORE0_TIMER_IRQCNTL 0x40000040 +#define CORE0_IRQ_SOURCE 0x40000060 + +void routing_core0cntv_to_core0irq(void) +{ + // IRQ + store32(0x08, CORE0_TIMER_IRQCNTL); + // FIQ + //store32(0x80, CORE0_TIMER_IRQCNTL); +} + +uint32_t read_core0timer_pending(void) +{ + uint32_t tmp; + tmp = load32(CORE0_IRQ_SOURCE); + return tmp; +} + +void enable_cntv(void) +{ + uint32_t cntv_ctl; + cntv_ctl = 1; + asm volatile ("mcr p15, 0, %0, c14, c3, 1" :: "r"(cntv_ctl) ); // write CNTV_CTL +} + +void disable_cntv(void) +{ + uint32_t cntv_ctl; + cntv_ctl = 0; + asm volatile ("mcr p15, 0, %0, c14, c3, 1" :: "r"(cntv_ctl) ); // write CNTV_CTL +} + +uint64_t read_cntvct(void) +{ + uint64_t val; + asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (val)); + return (val); +} + +uint64_t read_cntvoff(void) +{ + uint64_t val; + asm volatile("mrrc p15, 4, %Q0, %R0, c14" : "=r" (val)); + return (val); +} + +uint32_t read_cntv_tval(void) +{ + uint32_t val; + asm volatile ("mrc p15, 0, %0, c14, c3, 0" : "=r"(val) ); + return val; +} + +void write_cntv_tval(uint32_t val) +{ + asm volatile ("mcr p15, 0, %0, c14, c3, 0" :: "r"(val) ); + return; +} + +uint32_t read_cntfrq(void) +{ + uint32_t val; + asm volatile ("mrc p15, 0, %0, c14, c0, 0" : "=r"(val) ); + return val; +} @@ -2,72 +2,74 @@ .globl uart_init uart_init: - //*UART0_IMSC = 1 << 4; - mov r2, #0x1038 - movt r2, #0x3F20 - mov r3, #0b10000 - str r3, [r2] - //*IRQ_ENABLE2 = 1 << 25; - mov r2, #0xB214 - movt r2, #0x3F00 - mov r3, #0 - movt r3, #0b1000000000 - str r3, [r2] - //*GPU_INTERRUPTS_ROUTING = 0x00; - mov r2, #0x000C - movt r2, #0x4000 - eor r3, r3 - str r3, [r2] - bx lr + //*UART0_IMSC = 1 << 4; + // IGNORE OVERRUN + mov r2, #0x1038 + movt r2, #0x3F20 + mov r3, #0b10000 + str r3, [r2] + //*IRQ_ENABLE2 = 1 << 25; + mov r2, #0xB214 + movt r2, #0x3F00 + mov r3, #0 + movt r3, #0b1000000000 + str r3, [r2] + //*GPU_INTERRUPTS_ROUTING = 0x00; + // Route GPU interrupts to Core0 + mov r2, #0x000C + movt r2, #0x4000 + eor r3, r3 + str r3, [r2] + bx lr .globl uart_char uart_char: - mov r2, #0x1000 - movt r2, #0x3f20 + mov r2, #0x1000 + movt r2, #0x3f20 uart_char.loop: - ldr r3, [r2, #24] - tst r3, #0b100000 - bne uart_char.loop - str r0, [r2] - bx lr + ldr r3, [r2, #24] + tst r3, #0b100000 + bne uart_char.loop + str r0, [r2] + bx lr .globl uart_string uart_string: - push {r4, lr} - mov r4, r0 - ldrb r0, [r0] - cmp r0, #0 - popeq {r4, pc} + push {r4, lr} + mov r4, r0 + ldrb r0, [r0] + cmp r0, #0 + popeq {r4, pc} uart_string.loop: - bl uart_char - ldrb r0, [r4, #1]! - cmp r0, #0 - bne uart_string.loop - pop {r4, pc} + bl uart_char + ldrb r0, [r4, #1]! + cmp r0, #0 + bne uart_string.loop + pop {r4, pc} .globl uart_hex uart_hex: - push {r4, lr} - mov r2, #0x1000 - movt r2, #0x3f20 + push {r4, lr} + mov r2, #0x1000 + movt r2, #0x3f20 uart_hex.loop: - ldr r3, [r2, #24] - tst r3, #0b100000 - bne uart_hex.loop - mov r3, #7 + ldr r3, [r2, #24] + tst r3, #0b100000 + bne uart_hex.loop + mov r3, #7 uart_hex.hloop: - mov r1, r0 - asr r1, r3 - asr r1, r3 - asr r1, r3 - asr r1, r3 - and r1, #0xf - add r1, #0x30 - cmp r1, #0x3A - blt uart_hex.print - add r1, #7 + mov r1, r0 + asr r1, r3 + asr r1, r3 + asr r1, r3 + asr r1, r3 + and r1, #0xf + add r1, #0x30 + cmp r1, #0x3A + blt uart_hex.print + add r1, #7 uart_hex.print: - str r1, [r2] - subs r3, #1 - bge uart_hex.hloop - pop {r4, pc} + str r1, [r2] + subs r3, #1 + bge uart_hex.hloop + pop {r4, pc} |