I'm trying to understand how the earliest assemblers were written. If an assembler translates readable assembly instructions into machine-code bytes, how could the very first one exist before another assembler or compiler was available? Was it written directly in machine code, and could someone still create an assembler for a modern CPU that way?
3 Answers
An assembly-language translator is usually called an assembler, not a compiler. The first assembler was deliberately tiny: it recognized a small set of mnemonics, converted each one to the corresponding opcode, and produced the machine-code sequence. Once that worked, programmers could use it to assemble a better version of the assembler, gradually adding labels, constants, macros, and other features. That process is called bootstrapping.
The earliest programs were often hand-assembled. Programmers looked up each instruction’s binary or hexadecimal encoding in the processor manual, worked out the program’s logic and jump addresses on paper, and entered the resulting numbers using switches, punched media, or other early input methods. It was tedious, but there was no requirement that the program be written in a readable language first.
Early computers had much simpler instruction sets than modern CPUs. An instruction might contain only a small opcode plus an address, so hand-converting a short program was manageable for experienced programmers. Some systems did not have an assembler at all for a while; people simply memorized or referenced the instruction encodings and entered programs directly.

That makes sense—the initial version only has to be powerful enough to build the next version.