For later hardware like the TRS-80, the BASIC interpreter had an escape hatch: PEEK(addr), POKE addr, value, and USR1(). Basically you could read and write single bytes to RAM, and there was a magic 2-byte value which was a branch address that calling USR1() would jump into. So you could bootstrap some simple tooling that way. (I'd been writing hex machine code before I got any hardware, just pencil and graph paper, so it wasn't a big step to go from that to stuffing bytes into memory.) Of course there were tools available but actually paying for them was enough of a challenge that a lot of bootstrapping got done entirely anti-economically :-) (Once floppy drives and disk operating systems were a thing, you'd usually have a simple debugger where you could just type hex digits in, and load register values directly - so if you wanted to read raw disk sectors, you'd drop CD 75 46 18 FB somewhere, R DE track/sector, R HL where to read, R BC 0001 and then single step the code and it would read a single sector.)
(Note that while a lot of this was unjustifiable even then - my first real paying job in computers was down a step, working on a Z80-modded IMSAI so while most code was on 8" floppies, doing machine code in binary on toggle switches "just to fix something quick" was a legitimate skill :-)
(Note that while a lot of this was unjustifiable even then - my first real paying job in computers was down a step, working on a Z80-modded IMSAI so while most code was on 8" floppies, doing machine code in binary on toggle switches "just to fix something quick" was a legitimate skill :-)