asm/asm.c
static void emit(
        struct Context *ctx,
        struct Op *op,
        struct Token *start,
        enum Arg *args,
        struct Token **arg_tokens,
        usize num_args
    ) {
    u8 i0 = op->instruction << 4, i1 = 0;

    enum Arg *error_arg;
    asmchk(
        !(error_arg = check_args(ctx, op, args, num_args)),
        ctx, arg_tokens[(usize) (error_arg - args)],
        "Illegal argument or not enough arguments"
    );

    switch (op->instruction) {
        case I_LW:
            // reg, [HL/imm16]
            i0 = BIT_SET(i0, 3, num_args != 2);
            i0 |= token_register(arg_tokens[0]) & 0x7;
            BUFADD(ctx->out, i0);

            if (num_args == 2) {
                emit_addr(ctx, args[1], arg_tokens[1]);
            }
            break;
        case I_SW:
            // [HL/imm16], reg
            i0 = BIT_SET(i0, 3, num_args != 2);
            i0 |= token_register(arg_tokens[num_args == 1 ? 0 : 1]);
            BUFADD(ctx->out, i0);

            if (num_args == 2) {
                emit_addr(ctx, args[0], arg_tokens[0]);
            }
            break;
        case I_PUSH:
        case I_JNZ:
            i0 = BIT_SET(i0, 3, args[0] & A_REG);
            i0 |= (args[0] & A_REG) ? token_register(arg_tokens[0]) : 0;
            BUFADD(ctx->out, i0);

            if (!(args[0] & A_REG)) {
                assert(arg_tokens[0]->kind == TK_NUMBER);
                BUFADD(ctx->out, arg_tokens[0]->value);
            }
            break;
        case I_POP:
            i0 |= (1 << 3);
            i0 |= token_register(arg_tokens[0]);
            BUFADD(ctx->out, i0);
            break;
        case I_LDA:
            // [imm16]
            BUFADD(ctx->out, i0);
            emit_addr(ctx, args[0], arg_tokens[0]);
            break;
        case I_OUTB:
            // imm8/reg, reg
            i0 = BIT_SET(i0, 3, args[0] & A_REG);
            i0 |= token_register(arg_tokens[1]);

            if (args[0] & A_REG) {
                i1 = token_register(arg_tokens[0]);
            } else {
                assert(args[0] & A_IMM8);
                assert(arg_tokens[0]->kind == TK_NUMBER);
                i1 = arg_tokens[0]->value;
            }

            BUFADD(ctx->out, i0);
            BUFADD(ctx->out, i1);
            break;
        default:
            // reg, imm8/reg
            i0 = BIT_SET(i0, 3, args[1] & A_REG);
            i0 |= token_register(arg_tokens[0]) & 0x7;

            if (args[1] & A_REG) {
                i1 = token_register(arg_tokens[1]);
            } else {
                assert(args[1] & A_IMM8);
                assert(arg_tokens[1]->kind == TK_NUMBER);
                i1 = arg_tokens[1]->value;
            }

            BUFADD(ctx->out, i0);
            BUFADD(ctx->out, i1);
            break;
    }
}
// emits op starting at token start with the specified arguments/tokens to the
// output buffer
emits op starting at token start with the specified arguments/tokens to the
output buffer
emits op starting at token start with the specified arguments / tokens to the output buffer
static void emit(
        struct Context *ctx,
        struct Op *op,
        struct Token *start,
        enum Arg *args,
        struct Token **arg_tokens,
        usize num_args
    ) {
    u8 i0 = op->instruction << 4, i1 = 0;
    enum Arg *error_arg;
    asmchk(
        !(error_arg = check_args(ctx, op, args, num_args)),
        ctx, arg_tokens[(usize) (error_arg - args)],
        "Illegal argument or not enough arguments"
    );
    switch (op->instruction) {
        case I_LW:
            i0 = BIT_SET(i0, 3, num_args != 2);
            i0 |= token_register(arg_tokens[0]) & 0x7;
            BUFADD(ctx->out, i0);
            if (num_args == 2) {
                emit_addr(ctx, args[1], arg_tokens[1]);
            }
            break;
        case I_SW:
            i0 = BIT_SET(i0, 3, num_args != 2);
            i0 |= token_register(arg_tokens[num_args == 1 ? 0 : 1]);
            BUFADD(ctx->out, i0);
            if (num_args == 2) {
                emit_addr(ctx, args[0], arg_tokens[0]);
            }
            break;
        case I_PUSH:
        case I_JNZ:
            i0 = BIT_SET(i0, 3, args[0] & A_REG);
            i0 |= (args[0] & A_REG) ? token_register(arg_tokens[0]) : 0;
            BUFADD(ctx->out, i0);
            if (!(args[0] & A_REG)) {
                assert(arg_tokens[0]->kind == TK_NUMBER);
                BUFADD(ctx->out, arg_tokens[0]->value);
            }
            break;
        case I_POP:
            i0 |= (1 << 3);
            i0 |= token_register(arg_tokens[0]);
            BUFADD(ctx->out, i0);
            break;
        case I_LDA:
            BUFADD(ctx->out, i0);
            emit_addr(ctx, args[0], arg_tokens[0]);
            break;
        case I_OUTB:
            i0 = BIT_SET(i0, 3, args[0] & A_REG);
            i0 |= token_register(arg_tokens[1]);
            if (args[0] & A_REG) {
                i1 = token_register(arg_tokens[0]);
            } else {
                assert(args[0] & A_IMM8);
                assert(arg_tokens[0]->kind == TK_NUMBER);
                i1 = arg_tokens[0]->value;
            }
            BUFADD(ctx->out, i0);
            BUFADD(ctx->out, i1);
            break;
        default:
            i0 = BIT_SET(i0, 3, args[1] & A_REG);
            i0 |= token_register(arg_tokens[0]) & 0x7;
            if (args[1] & A_REG) {
                i1 = token_register(arg_tokens[1]);
            } else {
                assert(args[1] & A_IMM8);
                assert(arg_tokens[1]->kind == TK_NUMBER);
                i1 = arg_tokens[1]->value;
            }
            BUFADD(ctx->out, i0);
            BUFADD(ctx->out, i1);
            break;
    }
}
static void emit ( struct Context * ctx , struct Op * op , struct Token * start , enum Arg * args , struct Token * * arg_tokens , usize num_args ) { u8 i0 = op -> instruction << 4 , i1 = 0 ; enum Arg * error_arg ; asmchk ( ! ( error_arg = check_args ( ctx , op , args , num_args ) ) , ctx , arg_tokens [ ( usize ) ( error_arg - args ) ] , " " ) ; switch ( op -> instruction ) { case I_LW : i0 = BIT_SET ( i0 , 3 , num_args != 2 ) ; i0 |= token_register ( arg_tokens [ 0 ] ) & 0x7 ; BUFADD ( ctx -> out , i0 ) ; if ( num_args == 2 ) { emit_addr ( ctx , args [ 1 ] , arg_tokens [ 1 ] ) ; } break ; case I_SW : i0 = BIT_SET ( i0 , 3 , num_args != 2 ) ; i0 |= token_register ( arg_tokens [ num_args == 1 ? 0 : 1 ] ) ; BUFADD ( ctx -> out , i0 ) ; if ( num_args == 2 ) { emit_addr ( ctx , args [ 0 ] , arg_tokens [ 0 ] ) ; } break ; case I_PUSH : case I_JNZ : i0 = BIT_SET ( i0 , 3 , args [ 0 ] & A_REG ) ; i0 |= ( args [ 0 ] & A_REG ) ? token_register ( arg_tokens [ 0 ] ) : 0 ; BUFADD ( ctx -> out , i0 ) ; if ( ! ( args [ 0 ] & A_REG ) ) { assert ( arg_tokens [ 0 ] -> kind == TK_NUMBER ) ; BUFADD ( ctx -> out , arg_tokens [ 0 ] -> value ) ; } break ; case I_POP : i0 |= ( 1 << 3 ) ; i0 |= token_register ( arg_tokens [ 0 ] ) ; BUFADD ( ctx -> out , i0 ) ; break ; case I_LDA : BUFADD ( ctx -> out , i0 ) ; emit_addr ( ctx , args [ 0 ] , arg_tokens [ 0 ] ) ; break ; case I_OUTB : i0 = BIT_SET ( i0 , 3 , args [ 0 ] & A_REG ) ; i0 |= token_register ( arg_tokens [ 1 ] ) ; if ( args [ 0 ] & A_REG ) { i1 = token_register ( arg_tokens [ 0 ] ) ; } else { assert ( args [ 0 ] & A_IMM8 ) ; assert ( arg_tokens [ 0 ] -> kind == TK_NUMBER ) ; i1 = arg_tokens [ 0 ] -> value ; } BUFADD ( ctx -> out , i0 ) ; BUFADD ( ctx -> out , i1 ) ; break ; default : i0 = BIT_SET ( i0 , 3 , args [ 1 ] & A_REG ) ; i0 |= token_register ( arg_tokens [ 0 ] ) & 0x7 ; if ( args [ 1 ] & A_REG ) { i1 = token_register ( arg_tokens [ 1 ] ) ; } else { assert ( args [ 1 ] & A_IMM8 ) ; assert ( arg_tokens [ 1 ] -> kind == TK_NUMBER ) ; i1 = arg_tokens [ 1 ] -> value ; } BUFADD ( ctx -> out , i0 ) ; BUFADD ( ctx -> out , i1 ) ; break ; } }
emits op starting at token start with the specified arguments/tokens to the
output buffer
emits op starting at token start with the specified arguments / tokens to the output buffer
// reg, [HL/imm16] // [HL/imm16], reg // [imm16] // imm8/reg, reg // reg, imm8/reg