💾 Archived View for gemini.sensorstation.co › ~winduptoy › project.x.log.gmi captured on 2023-07-22 at 16:23:36. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-07-10)

🚧 View Differences

-=-=-=-=-=-=-

Project X

I'm finally at the point in my life and career where I can set down the typical grind work and pursue something really unique, something that really pushes my limits and creativity, something that when I look at even now I think "there's no way I'll ever be smart enough to pull that off." I currently struggle to see a future for myself with much fulfillment if I were to follow a safer career path.

2023-07-15

i spent the last few days reviewing my plan and thinking about next steps. i was originally planning to have the low-level core operate as an ultra-minimal FORTH and graft the multitasking and multi-dictionary functionality on top, in FORTH. i now think that's more complex than it's worth and doesn't have any obvious benefits over just designing those things into the core. so my next major tasks are:

many core words need to be aware of these facilities. i think i should back up a bit and try to define fewer words in assembly and more in FORTH to achieve a bit of reuse; maybe start with : and try to port that to FORTH and work backwards from there. my gut tells me that whenever i get to a point like this where things feel slightly overwhelming, stepping back and slowing down even more to aggressively simplify is the right answer.

2023-07-11

manual debugging of interpreter. took a while to debug an alignment issue: words loaded with the `ldr` instruction must be word-aligned. obvious in hindsight, but when the debugger doesn't give the exact instruction that caused the error and the chip just locks up, you have to manually step through each instruction and compare register states to figure it out. spent the rest of the day debugging some off-by-one errors and other subtle assembly issues.

i think i have a working interpreter!

2023-07-10

trying to get the interpreter working. i need some kind of UART feedback or something.

read about UART hardware flow control (CTS/RTS). too bad the pico debug probe doesn't have pins available for CTS/RTS. that led to reading about DMA. for TX, the DMA should be passed an address and length and the CPU should sleep until the transfer completes. for RX, the DMA should read input into the TIB and the interpreter should be invoked when a newline is detected. however, this would involve reading each byte (in an interrupt?) and negate all of the benefits of the DMA. maybe TIB could be treated as a ring buffer somehow? maybe interpret loop should be redesigned as a state machine that functions on one character at a time? that's how KEY works... WTF is wrong with me?

2023-07-09

a little work on error handling.

2023-07-08

implemented multiple execution contexts, which reduced instruction count slightly in most cases and removed all global variables.

2023-07-07

began manually testing words in the debugger from the beginning.

issue 1: gdb is unaware of the assembly source for any section that isn't text. adding the "x" flag (executable) to the section definition (e.g. `.section .dictionary "ax"`) makes gdb aware of the source when stepping through the debugger.

issue 2: naming symbols in the linker script with a dot (or matching the name of a section) cause them to have the incorrect value. maybe because it uses the end of the section as the value.

/* WRONG */
.data : ALIGN(4) {
	.data = .;
	*(.data*)
	.data_end = .;
} > SRAM AT> FLASH

`.data` has the expected value when running `objdump -x`, but I think that it's getting overridden by the section name.

/* RIGHT */
.data : ALIGN(4) {
	_data = .;
	*(.data*)
	_data_end = .;
} > SRAM AT> FLASH

`_data` has the expected value.

issue 3: modifying a symbol across multiple translation units is causing issues. it seems that the linker can't take a symbol for the end of the dictionary in the core TU and allow a secondary TU to build off it and move the dictionary head pointer further. i solved this by creating a "unity build" where the platform assembly entrypoint just includes the core content.

2023-07-05

finished initial pass of DOES>.

tried to start manually testing everything in the debugger, but ran into an issue where the chip would reset immediately and just mount over USB to accept a new firmware image. spent a while trying to track that down, thinking it was something to do with the OpenOCD/gdb setup. turns out I had an output section in my ELF file that was not marked as "ALLOC" (gnu assembler `.section name, "a"`) and therefore the linker allowed another section to overlap at the same address. this was the bootloader section, so it was just producing garbage.

ran into the next issue where symbols didn't have the expected value. in the linker script:

/* WRONG */
.data : ALIGN(4) {
	.data = .;
	*(.data*)
	.data_end = .;
	.data_len = SIZEOF(.data);
} > SRAM AT> FLASH

.data_len has a value equal to .data_end (???). the solution turned out to be moving it outside of the section block:

/* RIGHT */
.data : ALIGN(4) {
	.data = .;
	*(.data*)
	.data_end = .;
} > SRAM AT> FLASH
.data_len = SIZEOF(.data);

2023-07-04

started implementation of DOES>.

2023-07-03

implemented /MOD.

2023-06-30

implemented NUMBER.

2023-06-27

took a little time off to make a little money. started hacking again today. made good progress on INTERPRET and getting to the point to start compiling words. next step is to start copying inline code or branching to regular words, then write a number/digit interpreter.

2023-06-20

implemented CREATE.

2023-06-19

finished WORD and implemented FIND.

2023-06-16

started implementing WORD. thinking about linker organization and how to get static forth code burned to flash and then executed on boot.

when running objdump I see that some sections aren't incrementing the VMA. I had to add the "a" attribute (allocatable). would be nice if there was a way to do this in the linker script rather than the assembly.

2023-06-15

wrote copy routines, more cleanup. broke open the stage 2 bootloader to see how it worked.

2023-06-14

received my hardware today. set up OpenOCD and got it talking to the debugger after a few hiccups. spent a lot of time getting burned by another Thumb™ crash when the LSB is not set.

.section .text
.global myfunc

// ---- HERE! ---------
// The assembler doesn't set the LSB when referencing this
// label without this directive
.type myfunc, %function
// --------------------
myfunc:
	movs r0, 42
	...

tried loading a PC-relative value and then jumping over that value (when not using static pools) to discover that PC is current instruction + 4 when word-aligned and current instruction + 6 otherwise (Thumb).

2023-06-12

started thinking about how to encode pushing literal values onto the stack.

tried the following simulators to execute armv6m instructions with no luck. they either fail to compile or vomit on the assembly I throw at them. I've tried everything on Google/GitHub and I'm stunned that I can't get any of them to work. makes me wonder why ARM doesn't supply an official simulator; maybe to sell more hardware dev kits.

at the end of the day I just gave up and ordered some hardware. luckily the RP2040 and Pico Debug Probe are cheap.

2023-06-09

validated bl instruction encoding (33 instructions!) using cemu. not beautiful but it got the job done. implemented encoding of 32-bit literals into dictionary definitions (loading forward in memory, pushing to stack, and then jumping over the literal).

2023-06-08

started implementation of offset-to-bl-instruction encoding. investigating a faster-simpler armv6-m emulator that supports Thumb 2.

2023-06-07

working on moving to a subroutine threaded forth. inlining code into a definition is straightforward, but encoding a call to each subroutine seems to be tricky because with Thumb instructions you can't just put a 32-bit address down and jump to it. You can either `ldr` it from a static pool somewhere (slow), mash one together with a bunch of `mov`s and shifting (lame), or `bl` to a PC-relative address. opting for the latter, ARM's encoding scheme is a lot more complicated than just [opcode][offset]! so while trying to decipher this with a test case, I ran a disassembly on an object file with `objdump -D`. after tedious bit manipulation and head scratching, I realized that the offset in a `bl` instruction is meaningless until after it's linked. wasted a few hours on that one. all good now though: the PC is assumed to be AFTER the 32-bit `bl` instruction.

2023-06-06

refamiliarized myself with the JonesForth code. decided to move to a subroutine threaded forth after reading Brad Rodriguez's "Making Forth."

2023-06-05

just finished reading Starting Forth and Thinking Forth.

I spent the last few days documenting the memory layout I expect the system to have and the interaction among the core forth systems (dictionary, scheduler). I think this will help me pick a target for what to work on next rather than just opening the text editor and making a guess every day.

I used Richard Hipp's awesome pikchr tool to create inline diagrams.

previous work

april 2023

I spent a few weeks getting the Unicorn emulator up and running in a python environment. the python version is easy to install with pip, but has slower than expected execution. I suspect there's a high cost of crossing the C/Python barrier to run memory callbacks.

july 2022

I experimented and built the core of JonesForth, translating from x86 to ARM as I went. it accepts input over UART, although waiting in a busy loop so it's not pretty. things I learned during this period: