Assemblers are one of those things that's very simple for the simple cases, and then when you add more complex cases you start to think it's actually very hard until you go back and add the proper abstractions, which would have seemed needlessly complicated for the simple cases.
So people doing something trivial thinks they're easy (like this example), people trying to do a bit more think they're really hard, and people doing an entire assembler think they're easy again.
I might try doing this as a learning project. Can you offer any advice as to what those abstractions are? I'd appreciate some advice to avoid the "going back" part.
Most simple cases of instructions seem straightforward to just go ahead and emit the bytes for. Then when you start to use more addressing modes you realise it gets to be a lot of code and it turns out there's a common pattern for everything.
Here's a concrete example from an industrial assembler.
Almost every simple instruction boils down this helper method which is parameterised by a bunch of flags and can then deal with all of them.
Ideally, the common fields and patterns noted by the manufacturer in the ISA description would be implemented, and then the remainder of the logic can be (relatively) simple table-lookup.
So people doing something trivial thinks they're easy (like this example), people trying to do a bit more think they're really hard, and people doing an entire assembler think they're easy again.