commit:f3d09d36041972b29e004f5ff351f4c21f0c8d30
author:Chip Black
committer:Chip Black
date:Thu Nov 27 12:04:07 2025 -0600
parents:
Kind of working?
diff --git a/.gitignore b/.gitignore
line changes: +2/-0
index 0000000..20596b5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+*.elf

diff --git a/Makefile b/Makefile
line changes: +9/-0
index 0000000..d1032fc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+ASFLAGS = -mthumb -mcpu=cortex-m0
+
+all: vm.elf
+
+%.o: %.s
+	arm-none-eabi-as $(ASFLAGS) -g $< -o $@
+
+vm.elf: vectors.o vm.o
+	arm-none-eabi-ld -T cortex-m0.ld $^ -o $@

diff --git a/cortex-m0.ld b/cortex-m0.ld
line changes: +23/-0
index 0000000..52d1bfb
--- /dev/null
+++ b/cortex-m0.ld
@@ -0,0 +1,23 @@
+MEMORY {
+	FLASH (RX) : ORIGIN = 0x00000000, LENGTH = 256K
+	SRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 16K
+}
+
+ENTRY(Reset_Handler)
+
+SECTIONS {
+	.text : {
+		KEEP(*(.vectors))
+		*(.text)
+	} > FLASH
+
+	.rodata : {
+		*(.rodata)
+	} > FLASH
+
+	.data : {
+		*(.data)
+	} > SRAM
+}
+
+_stack_top = ORIGIN(SRAM) + LENGTH(SRAM);

diff --git a/vectors.s b/vectors.s
line changes: +13/-0
index 0000000..0023a31
--- /dev/null
+++ b/vectors.s
@@ -0,0 +1,13 @@
+.section .vectors
+.word	_stack_top      // stack origin
+.word	Reset_Handler   // RESET
+.word   _err_halt       // NMI
+.word   _err_halt       // HardFault
+.rept 7
+.word   _err_halt       // Reserved
+.endr
+.word   _err_halt       // SVCall
+.word   _err_halt       // Reserved
+.word   _err_halt       // Reserved
+.word   _err_halt       // PendSV
+.word   _err_halt       // SysTick

diff --git a/vm.s b/vm.s
line changes: +295/-0
index 0000000..3ed9b40
--- /dev/null
+++ b/vm.s
@@ -0,0 +1,295 @@
+.section .text
+
+.align 2
+.global _err_halt
+_err_halt:
+	b _err_halt
+
+.macro pstring str:req
+	.byte 2f - 1f
+1:
+	.ascii "\str"
+2:
+.endm
+
+.macro def_word name:req
+	.align 2
+\name:
+	.short _\name\()_end - \name         // total size
+	.short _\name - \name                // offset to code start
+	.short _\name\()_code_end - _\name   // code length (minus bx lr)
+	.short 1f - . - 2                    // name length
+	.ascii "\name"
+1:
+	.align 1
+_\name:
+.endm
+
+.macro def_end name:req
+_\name\()_code_end:
+	bx lr
+.align 2
+_\name\()_end:
+.endm
+
+def_word halt
+	b _halt
+def_end halt
+
+def_word push  /* r0 onto stack */
+	push {r0}
+def_end push
+
+def_word pop   /* r0 off of stack */
+	pop {r0}
+def_end pop
+
+def_word dup   // ( a -- a a )
+	pop {r0}
+	push {r0}
+	push {r0}
+def_end dup
+
+def_word add   // ( a b -- a + b)
+	pop {r0, r1}
+	add r0, r0, r1
+	push {r0}
+def_end add
+
+def_word rep
+	mov r0, pc
+	push {r0}
+def_end rep
+
+def_word jump
+	pop {pc}
+def_end jump
+
+.align 2
+.global Reset_Handler
+Reset_Handler:
+/*
+	ldr r4, =program
+	ldr r5, =output
+	bl compiler
+*/
+	bl setup_input
+	bl setup_words
+	ldr r0, =add
+	bl add_rom_word
+	ldr r0, =halt
+	bl add_rom_word
+	ldr r0, =rep
+	bl add_rom_word
+	ldr r0, =jump
+	bl add_rom_word
+
+	bl interpret_input
+	b _err_halt
+
+setup_input:
+/*
+	ldr r5, =input_counter
+	mov r0, #0
+	str r0, [r5]
+*/
+	ldr r0, =program
+	ldrb r1, [r0]
+	add r1, #1
+	ldr r2, =input_counter
+	mov r3, #0
+1:	ldrb r4, [r0, r3]
+	strb r4, [r2, r3]
+	add r3, #1
+	cmp r3, r1
+	bne 1b
+	bx lr
+
+setup_words:
+	ldr r5, =output
+	add r1, r5, #4
+	str r1, [r5]      // head (address where next word goes)
+	mov r0, #0
+	str r0, [r5, #4]  // next (offset to next word or 0 for end of list)
+	bx lr
+
+add_rom_word:
+	ldr r5, =output
+	ldr r1, [r5]      // r1 is now the head address
+	ldrh r2, [r0]     // get the number of bytes for the ROM word
+	mov r4, #0        // counter value
+
+1: 	ldr r3, [r0, r4]  // load
+	str r3, [r1, r4]  // store
+	add r4, #4
+	cmp r4, r2        // if we've copied all the bytes
+	bne 1b            // loop
+
+	add r1, r2        // add to get the next head location
+	str r1, [r5]      // store the new head
+	mov r2, #0
+	str r2, [r1]      // And make sure the next word offset is end of list
+	
+	bx lr
+
+// r8  - in,     input buffer address
+// r10 - in/out, input buffer word position
+// r11 - out,    word length
+get_next_word:
+	mov r4, r8            // r4 is the local input buffer address
+	ldr r5, =input_counter
+	ldrb r5, [r5]         // r5 is the input buffer length
+	mov r0, r10           // r0 is word position
+1:
+	ldrb r2, [r4, r0]     // load char
+	cmp r2, #' '          // is it a space?
+	bne 2f                // if so, continue
+	add r0, #1            // increment word position
+	cmp r0, r5
+	beq 4f                // have we hit the input buffer length?
+	b 1b
+
+2:	mov r1, r0
+3:	ldrb r2, [r4, r1]
+	cmp r2, #' '
+	beq 4f                // is it not a space?
+	add r1, #1
+	cmp r1, r5            // have we hit the input buffer length?
+	bne 3b                // if not, continue
+
+4:	sub r1, r0            // word length is end - beginning
+	mov r11, r1
+	mov r10, r0
+	bx lr
+
+// based on the input string starting at r10 and with length r11, set r0 to the
+// beginning of the word's code or else set r0 to 0 if the word was not found.
+search_dict:
+	ldr r4, =output
+	add r4, #4           // start at the beginning
+1:	ldrh r0, [r4]        // load the offset to next word
+	beq 5f               // if it's zero, we're done
+
+	mov r0, r4
+	add r0, #8           // r0 is the beginning of the word name string
+	ldrh r3, [r4, #6]    // load the name length
+	cmp r3, r11          // is it the same as the length we have in r11?
+	beq 3f               // string lengths are the same; compare bytes
+2:	ldrh r0, [r4]        // else load the offset to next word
+	add r4, r0           // add to the word pointer
+	b 1b                 // and try the next word
+
+3:	ldr r5, =input_buffer
+	ldr r6, =input_counter
+	ldrb r6, [r6]
+	mov r1, r10          // load input buffer position
+	add r5, r1           // r5 is now a pointer into the input buffer
+	mov r1, #0           // set up index
+4:	ldrb r2, [r0, r1]    // load dict byte
+	ldrb r3, [r5, r1]    // load input byte
+	cmp r2, r3           // are they the same?
+	bne 2b               // if not, go to the next dict word
+	add r1, #1           // increment
+	cmp r1, r11          // have we checked all the bytes in the word?
+	bne 4b               // if not, check next byte
+
+	// success!
+	ldrh r2, [r4, #2]    // get offset to code start
+	add r0, r4, r2       // add to word start
+	add r0, #1           // and add one to make it a valid thumb interwork address
+	bx lr
+
+	// the word was not found; set r0 to 0
+5:	mov r0, #0
+	bx lr
+
+interpret_input:
+	mov r9, lr
+	ldr r0, =input_buffer
+	mov r8, r0
+	mov r0, #0
+	mov r10, r0
+1:
+	bl get_next_word
+	mov r4, r8
+	mov r0, r10
+	ldrb r2, [r4, r0]     // get the first character
+	cmp r2, #'\''         // is it an apostrophe?
+	bne 2f
+	bl push_str
+	b 4f
+2:	cmp r2, #'$'          // is it a dollar sign?
+	bne 3f
+	bl push_lit
+	b 4f
+3:	bl search_dict        // must be a word
+	beq 5f                // word not found
+	blx r0                // branch to found word
+4:	mov r0, r10
+	mov r1, r11
+	add r0, r1            // move input pointer to next word
+	mov r10, r0
+	b 1b
+5:	bx r9
+
+push_str:
+	mov r0, r10
+	add r0, #1            // increment character counter
+	mov r4, r8
+1:	ldrb r1, [r4, r0]     // load character
+	push {r1}             // push to stack
+	add r0, #1            // increment counter
+	cmp r0, r11           // at the end?
+	bne 1b                // repeat
+	bx lr
+
+push_lit:
+	mov r0, r10           // input position
+	mov r1, r11           // input length
+	mov r4, r8            // input buffer
+	add r5, r0, r1        // calculate end position
+	add r3, r0, #1        // increment character counter
+	mov r7, #0            // accumulator
+1:	ldrb r2, [r4, r3]     // load character
+	cmp r2, #'0'
+	blt 4f                // not in range
+	cmp r2, #'9'
+	bgt 3f                // not a digit, but possibly still a letter
+	// a digit
+	sub r2, #0x30         // subtract 0x30
+	b 5f
+
+3:	cmp r2, #'a'
+	blt 4f                // not in range
+	cmp r2, #'f'
+	bgt 4f                // not in range
+	// a letter a-f
+	sub r2, #0x57         // subtract offset to get value
+	b 5f
+
+	// error parsing literal
+4:	push {lr}
+	bl _err_halt
+
+5:
+	lsl r7, #4            // shift accumulator
+	orr r7, r2            // or in the value
+	add r3, #1            // add to the counter
+	cmp r3, r5            // are we done?
+	bne 1b                // continue
+	push {r7}
+	bx lr
+
+.ltorg
+
+.section .rodata
+.align 2
+program:
+	pstring "$0 rep $100 add jump halt"
+
+.section .data
+input_counter:
+	.skip 1
+input_buffer:
+	.skip 255
+output: