aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Cunningham <cc@localhost>2021-12-02 23:17:41 -0700
committerChristian Cunningham <cc@localhost>2021-12-02 23:17:41 -0700
commit733a45d82e84de894be0c3d29b3305219827f448 (patch)
tree26317833fd1dcd25ee348692f6f86cfada2230c9 /src
parent9a38691f8635c7cbac413cd7f54e5105ca9cb464 (diff)
Initial Commit
Diffstat (limited to 'src')
-rw-r--r--src/boot.S122
-rw-r--r--src/irq.S91
-rw-r--r--src/kernel.S38
-rw-r--r--src/lib.c121
-rw-r--r--src/lib.h79
-rw-r--r--src/time.c76
-rw-r--r--src/uart.S112
7 files changed, 453 insertions, 186 deletions
diff --git a/src/boot.S b/src/boot.S
index 0662f70..7aa21fc 100644
--- a/src/boot.S
+++ b/src/boot.S
@@ -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
diff --git a/src/irq.S b/src/irq.S
index 5434a60..53a749e 100644
--- a/src/irq.S
+++ b/src/irq.S
@@ -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;
+}
diff --git a/src/uart.S b/src/uart.S
index 94ce458..0864b7e 100644
--- a/src/uart.S
+++ b/src/uart.S
@@ -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}