+------------------------------------------------------------------------+ | 04 / Practical assembly patterns | +------------------------------------------------------------------------+ BIT TESTS AND COUNTED LOOPS mov r7,#10 again: ; body executes ten times djnz r7,again ret Starting R7 at zero executes 256 iterations, not zero. DJNZ changes no arithmetic flags. Conditional branches and SJMP have a signed -128..+127 byte displacement measured from the instruction AFTER the branch. For a far condition, invert the test and use LJMP. AJMP/ACALL require the target to share the 2-KB page of the NEXT instruction, so code near a page boundary needs special care. UNSIGNED COMPARE cjne a,#10,different ; A == 10; CY is now 0 sjmp done different: jc below ; unsigned A < 10 ; otherwise A > 10 below: done: ret PACKED BCD mov a,#0x45 add a,#0x38 da a ; A = 83 (hex encoding of decimal 83) ret DA is for the result of packed-BCD ADD/ADDC with valid BCD operands. It is not binary-to-decimal conversion and is not the correction for subtraction. DA may set CY; it does not clear an existing carry. AC/OV are not a decimal result test. LOOKUP TABLE (DOS/51 PROGRAM) mov dptr,#table mov a,#2 movc a,@a+dptr ; A = ASCII '2' ret table: db 48,49,50,51 CODE and XDATA overlap physically only where a BOARD wires them that way. DOS/51's upper RAM is deliberately visible in both spaces, allowing its assembler to write instructions with MOVX and then execute them. A generic 80C31 does not create executable RAM by itself. MULTIPLY / DIVIDE mov a,#200 mov 0xF0,#3 ; B mul ab ; B:A = 0x0258 (600), CY=0, OV=1 mov a,#200 mov 0xF0,#7 div ab ; A=28 quotient, B=4 remainder, CY=0 ret DIV by zero sets OV and leaves A/B results undefined. Check B before dividing. MUL and DIV are unsigned; signed multiplication/division need additional sign handling. ROTATE / SHIFT RL/RR rotate within A and leave CY alone. RLC/RRC rotate the nine-bit CY:A chain. For an unsigned right shift use CLR C followed by RRC A. A multi-byte right shift starts at the high byte; a left shift starts at the low byte. SWAP A exchanges nibbles; XCH exchanges whole bytes; XCHD exchanges only the low nibbles of A and an indirectly addressed RAM byte. MASKS anl 0x90,#0xFE ; clear P1.0, preserve other latches orl 0x90,#0x01 ; set P1.0, preserve other latches cpl 0x90 ; BIT address 90: toggle P1.0 These examples use numeric SFR/bit addresses for DOS/51 portability. In a desktop assembler, prefer named EQU/bit declarations from the exact chip header. Do not assume another assembler's names, macros or directives exist in the small on-board assembler. SUBROUTINE CONTRACTS Document inputs, outputs, scratch registers and flags. Save only what your calling convention requires, but do not silently change the register bank. RET restores PC, not A, B, PSW or DPTR. Interrupt entry likewise does not preserve your working registers. For a callable DOS/51 program, keep the shell's stack valid and restore any state you borrow. An endless loop, disabled interrupts or a changed UART can make the shell appear dead; Reset recovers execution but does not undo files overwritten on the RAM disk. [ DOS/51 FIELD MANUAL | IMANIA.DK | 2026-11-14 ]