A side: Doing arithmetic

A A side · Assembly · The first language of the machine
How do you tell the machine to calculate?
23 Assembly words in this segment
19 Fortran words for the same job
What Assembly says well here
- Each verb is one exact processor step. You know the cost of each step.
- Whole-number and fixed-point arithmetic is very fast.
- A table in memory replaces a slow verb. MUL by 320 costs about 120 cycles on the 8086. One indexed MOV from a table of row starts costs about 20. The B side makes the table.
- A byte register wraps at 256 by itself: add al, 64 turns a sine into a cosine. The A side wraps for free.
What is hard to say
- One formula needs many verbs and many registers.
- The 8086 has no verbs for decimal-point numbers. It needs an extra maths chip (the 8087) or slow software.
- The 8086 has no decimal flag. After ADD or ADC on packed decimal bytes, write DAA. After SUB or SBB, write DAS. If you forget it, 09 + 1 becomes 0A, not 10.
- y * 320 = (y << 8) + (y << 6), because 320 = 256 + 64. Join the two shifts with ADD, never with OR. An OR loses every carry.
One sentence in Assembly
mov ax, [speed]add ax, [boost]mov [speed], axIt says: Make speed equal to speed plus boost. This takes three verbs.
The words, by part of speech
Verbs (22) · An instruction: one step that the processor does, for example MOV or ADD.
| Word | Meaning | Example |
|---|---|---|
| ADD | Add a value. | add ax, bx |
| SUB | Subtract a value. | sub ax, 1 |
| INC | Add 1. | inc cx |
| DEC | Subtract 1. | dec cx |
| MUL | Multiply numbers that have no sign. The result goes in DX and AX. | mul bx |
| IMUL | Multiply numbers that have a sign. | imul bx |
| DIV | Divide numbers that have no sign. AX gets the result, DX gets the remainder. | div bx |
| IDIV | Divide numbers that have a sign. | idiv bx |
| NEG | Change the sign. 5 becomes -5. | neg ax |
| SHL | Move the bits left. Each step multiplies by 2. | shl ax, 1 |
| SHR | Move the bits right. Each step divides by 2 (numbers with no sign). | shr ax, 1 |
| SAR | Move the bits right and keep the sign. Each step divides a signed number by 2. | sar ax, 1 |
| AND | Keep only the bits that are 1 in the two values. | and al, 0Fh |
| OR | Set each bit that is 1 in one value or the other. | or al, 80h |
| XOR | Flip the bits where the two values differ. xor ax, ax sets AX to 0. | xor ax, ax |
| NOT | Flip every bit. | not al |
| ADC | Add, plus the carry flag. It joins the high bytes of a number that is longer than one register. | adc al, 0 |
| SBB | Subtract, minus the carry flag (the borrow). The partner of ADC. | sbb al, 0 |
| DAA | Decimal adjust after an ADD or ADC of two packed decimal bytes in AL. It makes 09h + 1 into 10h and sets CF after 99. | add al, 1
daa |
| DAS | Decimal adjust after a SUB or SBB of two packed decimal bytes in AL. The partner of DAA. | sub al, 1
das |
| AAM | Divide AL by 10. The tens go to AH, the units stay in AL: a ready-made step to show a number under 100. | aam |
| CBW / CWD | Make a signed number longer: CBW copies the sign of AL into AH. CWD copies the sign of AX into DX, before IDIV. | cwd
idiv cx |
Nouns (1) · A register, a flag, a port or a place in memory, for example AX.
| Word | Meaning | Example |
|---|---|---|
| CF | Carry flag. It is 1 when a result is too large for its size. |
Watch out: what the review found
These notes come from the review of the AI-written guides. They show traps that are easy to miss.
- D02-C2 critical The document contradicts itself. Without an FPU, Fortran
REALarithmetic runs on software emulation. The document praises Assembly because it prevents this latency (L41). A Fortran simulation with much float math on a 1980s computer is slow. - D01-M2 major This names the wrong layer. The CPU hardware does out-of-order execution, and the hardware disambiguates memory at runtime. Aliasing (two names for the same memory) limits compiler reordering and vectorization.
- D01-m1 minor "replacement of integer division with modular multiplication invariants". The usual term is multiplication by a fixed-point reciprocal (the "magic number").
- D14-C3 critical The video gives the idea only: from the X and Y of the player, with the scroll, find the tile under the player (05:50). Inkbox says this is "faster" than a loop over a wall list (06:11). He gives no formula, no complexity class and no cycle counts. The formula in image 9 is also wrong. Shift results must be added, not combined with OR: for Y = 3, (3<<7) OR (3<<6) = 448, but 3*192 = 576. The OR between Y*192 and X also fails, for example Y = 1, X = 65 gives 193, not 257. The formula also assumes one byte per tile. The video says that 192 x 168 tiles fill 63 K (06:53), so each tile takes two bytes. The video does not describe timer routines for off-screen enemies. Object collision uses generic hitbox functions against the player (38:55) and against on-screen chickens (42:19).
- D14-M3 major The 5A22 has a hardware unit: $4202 and $4203 start an 8 x 8 unsigned multiply, $4204 to $4206 start a 16 / 8 unsigned divide, and $4214 to $4217 give the quotient, product and remainder (snes.nesdev.org MMIO registers, checked by WebFetch). The video does not give division cost as the reason. Inkbox uses BCD because "it makes the math so much easier" (32:05). The document also omits the trap of the video: INC and DEC ignore the decimal flag, so he must use ADC (32:05 to 32:26). The "LSR and AND" nibble split at L106 is not in the video.