Assembly Is Hard

I’m trying to learn Intel x86 assembly, and I’m discovering it’s actually hard. Like, proper hard. Let me explain…

Here’s some C code, it was created by Claude and looks pretty terrible to be fair

Pt path_segment_mid(const Pt *pts, int n, int smooth, int k)
{
    if (k < 0 || k + 1 >= n) return n ? pts[0] : (Pt){ 0, 0 };
    Pt p1 = pts[k], p2 = pts[k + 1];
    if (!smooth || n <= 2) return lerp(p1, p2, 0.5f);
    Pt p0 = k > 0 ? pts[k - 1] : (Pt){ 2 * p1.x - p2.x, 2 * p1.y - p2.y };
    Pt p3 = k + 2 < n ? pts[k + 2] : (Pt){ 2 * p2.x - p1.x, 2 * p2.y - p1.y };
    return catmull(p0, p1, p2, p3, 0.5f);
}

However, just by looking at this I can spot function calls, parameters to function calls, variables, maths and make some kind of guess about what the code means.

And while I might not know what a Pt or why we’re doing half of the code, I can at least recognise the way it’s constructed.

Here’s some assembly, it was written by a human

PUSH CX ; Save x coordinate
MOV AX,DX ; y coordinate to AX
MOV CX,320 ; Multiplier is 320 bytes per row
MUL CX ; AX = y times 320
MOV BX,AX ; Free AX and hold in BX
POP AX ; x coordinate from stack
ADD BX,AX ; Add in column value

I can’t look at this and know what it’s doing. If the comments weren’t there, I’d have no idea what the code is even trying to do.

All I see are moves and multiplies and seemingly arbitrary swapping of registers and pushing things onto the stack for some reason. I don’t see loops or the equivalent of if statements.

So that’s what I need to work on, learning the structure and how to think in assembly.

I was doing some research and got really confused why there’s a small collection of opcodes that appear to almost do the same thing. For example there’s SUB and CMP which both subtract one register from another, and both will set the zero flag if the answer is zero.

The only difference is CMPdoesn’t store the result, it just sets the flags which is handy if you’re then using the flags to control a jump instruction.

And that’s another thing. I quickly realised the CPU doesn’t remember its previous state. Suppose you have code like this in C

if (age > 18) {
    printf ("Adult\n");
} else {
    printf ("Not adult\n");
}

It’s natural to assume the first bit means “if we compare age to 18, do the next bit”, meaning the if is comparing the variable age to 18 and then going to the next line of code.

So when you see the CMP instruction, it’s natural to think you’re comparing wherever you put the contents of the age variable with a value. So like, put 18 in AX and compare it to 18 and then jump instruction then gets called because the comparison matches or something, right?

No. You put the value of age in AX, compare it to 18 which does a subtraction. That instruction is then done and forgotten about. All that remains is the flags. And that’s all the jump instructions look at.

It’s a whole different way of thinking.

What I need to do is the same thing my students do. Get annoyed, drop out and go do art or something. Or be given just enough information to complete a basic task, then be set a simple challenge. And a challenge I’m setting myself is to create a simple guessing game. Which I’ll write about next time.