Overview
Macros provide higher-level operations that expand into multiple assembly instructions. They are invoked with the# prefix.
Built-in macros
CALL - Long subroutine call
Syntax:#CALL address
Performs a subroutine call that can cross page boundaries. This is more robust than JMS for larger programs.
#CALL macro expands to a sequence that:
- Saves the return address (current PC + 14) to registers 8R-10R
- Pushes each nibble of the return address to the stack using
PUSH_4_FROM_8R - Performs an unconditional jump to the target address
The
#CALL macro requires PUSH_4_FROM_8R to be defined in your program. See the example code in main.4004 for the implementation.Use
#CALL instead of JMS when:- Your subroutine might be on a different 256-byte page
- You need a consistent calling convention across your program
- You’re building a larger application with many subroutines
LJCN - Long conditional jump
Syntax:#LJCN condition target
Performs a conditional jump that can cross page boundaries. The standard JCN instruction can only jump within the current 256-byte page.
Z?- Jump if accumulator is zeroNZ?- Jump if accumulator is not zeroC?- Jump if carry flag is setNC?- Jump if carry flag is clear
#LJCN macro inverts the condition, skips ahead 4 bytes if the inverted condition is true, then performs an unconditional jump:
Example usage
Here’s a practical example from the sample code showing both macros in use:Return from CALL
Functions called with#CALL must return using the RETURN label instead of BBL. The sample code includes a RETURN implementation that:
- Pops the return address from the stack
- Writes a
JUNinstruction to a specific location in program RAM - Jumps to that self-modified code to return
The
RETURN mechanism uses self-modifying code via the WPM instruction. This is a clever technique specific to the 4004’s architecture.Macro argument requirements
CALL
- Arguments: 1 (the target address)
- Type: Address or label
- Error: Returns
error.not_1_call_argumentif argument count is wrong
LJCN
- Arguments: 2 (condition and target address)
- Type: Condition code (
C?,NC?,Z?,NZ?) and address/label - Errors:
error.not_2_ljcn_argumentsif argument count is wrongerror.invalid_conditionif condition is not recognized
Writing custom macros
The assembler does not currently support user-defined macros. Only the two built-in macros (CALL and LJCN) are available. Custom macros would require modifying the getMacroReplacement function in assembler.zig.