The cpu DOES interpret these based on arguments particularly in x86. There is a byte, an operand and as your document and certainly a number of better documents show the table of operands and what they map to. Based on the operand it may need another byte (or more) but as it parse each byte after the initial operand the picture becomes clearer as to what the cpu is supposed to do, this is a mov but what kind, okay it is a register register, okay what two registers, ending up with some number of bits indicating each of the registers. This is a mov, okay what kind, register, immediate and then more operands indicating the register and the immediate.
But we dont think of these as a function with operands. So we dont normally write assembly language in this way. Instead the assembly language is designed to narrow in on the specific machine language bits for a very specific instruction
mov ax,1234h
mov ah,byte ptr[ax]
If I remember right intel may actually have dont care bits or other features where you can implement something two different ways that are valid (see the a86 documentation or is it as86?)
Intel did/does have an instruction set (not x86 or related) that did have a function based assembly language, and I wont say what it was but I just searched and there are legal and/or illegal class lectures, etc that show some of this syntax.
alu[x,--,y,+,z]
Likely intentionally resembles a function this is saying x = y + z;
I think you can use parens instead of brackets and the macro language looks like functions as well
mymacro(w,x,y,z)
where those items can be used in the macro however you want, like a define in C the macro just shoves the text as is so you have to make sure it conforms.
alu[w,--,x,+,y]
alu[w,--,w,and,z]
I have seen someone use C as an assembler and that was very very cool, super easy to implement the assembler as the C compiler took care of all of the parsing
add(r0,r1);
sub(r2,r3);
and you link/include the backend to your main "program"
#define r0 0
#define r1 1
#define r2 2
#define r3 3
void add ( unsigned int a, unsigned int b)
{
emit(0x4140|(ra<<3)|rb);
}
I dont remember how conditionals worked I think there was something like
label("hello");
...
bne("hello");
and forward references were patched up at the end...
could bang out an assembler in record time with an approach like that...but most folks have probably never seen this nor tried to use nor implement it.
Some processors are fixed length instructions and some are variable, variable is more like an opcode and the from that you figure out how many more opcodes then operands. fixed length still has operand fields/bits but then based on that interpret the rest of the bits, very easy to implement a decoder that settles in a single clock cycle, not that you couldnt have a wide shift register for x86 that settled in one clock too, but historically that was not how it worked and CISC leans toward microcoding.
an assembly language is defined by the assembler, the program that parses it. it is up to the author(s) of that assembler as to the syntax, so long as you conform to the machine language you can make any syntax you can dream up, there is no reason whatsoever you cant make an x86 assembler that is function like with operands.
addrr(ax,bx); //mov ax,bx
movbptr(ah,bx); //mov ah,byte ptr bx
so long as you can implement enough instructions to be useful, ideally the entire instruction set. and ideally each line creates a one specific instruction. The problem you would have at this point in history is finding a user/consumer of this tool. Hardly anyone codes purely assembly language, so the primary consumers are compilers, and those already have an assembly language they use as an output, one they didnt have to write (an existing one like gas).