LINUX.ORG.RU

История изменений

Исправление PPP328, (текущая версия) :

Ты можешь на сишных макросах сделать статические проверки ассемблерного кода, не позволяющие сунуть неправильный операнд не в ту инструкцию?

Да.

Можно даже с проверкой самих передаваемый значений, там же опкод меняется в зависимости от того, что сунули, ZP, адрес, значение или сахарок в виде A,X/A,Y/(A),Y/(A, X). Всё без проблем чекается. Мне даже в отличие от этой хаскеле-срани не нужно указывать какой тип передачи значения, я и сам могу задетектить:

// Concatenate
#define CAT__(x) x
#define CAT_(x, y) CAT__(x ## y)
#define CAT(x, y) CAT_(x, y)

// Count variadica arguments
#define ARGC(...) ARGC_(__VA_ARGS__, ARGC_RSEQ_N())
#define ARGC_(...) ARGC_N(__VA_ARGS__) 
#define ARGC_N(_1, _2, N, ...) N 
#define ARGC_RSEQ_N() 2, 1, 0

// Syntax routines
#define EXPAND(x) x
#define COMMA(x) ,

// OPCODES
#define OPCODE_LDA_ABSOLUTE                  0xAD // LDA 0x0000..0xFFFF
#define OPCODE_LDA_ABSOLUTE_INDEXX           0xBD // LDA 0x0000..0xFFFF, X
#define OPCODE_LDA_ABSOLUTE_INDEXY           0xB9 // LDA 0x0000..0xFFFF, Y
#define OPCODE_LDA_IMMEDIATE                 0xA9 // LDA #0x00..#0xFF
#define OPCODE_LDA_ZEROPAGE                  0xA5 // LDA 0x00..0xFF
#define OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX  0xA1 // LDA (0x00..0xFF, X)
#define OPCODE_LDA_ZEROPAGE_INDEXX           0xB5 // LDA 0x00..0xFF, X
#define OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY 0xB1 // LDA (0x00..0xFF), Y

#define OPCODE_CPX_ABSOLUTE                  0xEC // CPX 0x0000..0xFFFF
#define OPCODE_CPX_IMMEDIATE                 0xE0 // CPX #0x00..0xFF
#define OPCODE_CPX_ZEROPAGE                  0xE4 // CPX 0x00..0xFF

#define OPCODE_RTS                           0x60 // RTS

// VALUE CONVERTERS

// ZP tester
#define VALUE_TEST_0x00 0
#define VALUE_TEST_0x01 0
#define VALUE_TEST_0x02 0
#define VALUE_TEST_0x03 0
#define VALUE_TEST_0x04 0
#define VALUE_TEST_0x05 0
// ... I'm too lazy, works for MVP
#define VALUE_TEST_0xFF 0

// ADDRESS tester
#define VALUE_TEST_0x0001 1
#define VALUE_TEST_0x0002 1
#define VALUE_TEST_0x0003 1
#define VALUE_TEST_0x0004 1
#define VALUE_TEST_0x0005 1
// .. I'm too lazy, works for MVP
#define VALUE_TEST_0xFFFF 1

// IMMEDIATE tester
#define VALUE_TEST__0x00 2
#define VALUE_TEST__0x01 2
#define VALUE_TEST__0x02 2
#define VALUE_TEST__0x03 2
#define VALUE_TEST__0x04 2
// .. I'm too lazy, works for MVP
#define VALUE_TEST__0x05 2

// Immediate marker remover
#define VALUE_DEIM_0x00 0x00
#define VALUE_DEIM_0x01 0x01
#define VALUE_DEIM_0x02 0x02
#define VALUE_DEIM_0x03 0x03
#define VALUE_DEIM_0x04 0x04
// .. I'm too lazy, works for MVP
#define VALUE_DEIM_0xFF 0xFF

// U16 splitter
#define U16_SPLIT_0x0000 0x00, 0x00
#define U16_SPLIT_0x0001 0x01, 0x00
#define U16_SPLIT_0x0002 0x02, 0x00
#define U16_SPLIT_0x0003 0x03, 0x00
#define U16_SPLIT_0x0004 0x04, 0x00
// .. I'm too lazy, works for MVP
#define U16_SPLIT_0xFFFF 0xFF, 0xFF

// INSTRUCTION: LDA
/* Allowed LDA notations:
 * a  |   a, x  |  a, y  | #
 * zp | (zp, x) | zp, x  | (zp), y */

#define LDA_CHECKZPX(...) CAT(LDA_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define LDA_PROCESSOR_ARG1_1_0(val) OPCODE_LDA_ZEROPAGE, val,
// a
#define LDA_PROCESSOR_ARG1_1_1(val) OPCODE_LDA_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define LDA_PROCESSOR_ARG1_1_2(val) OPCODE_LDA_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define LDA_PROCESSOR_ARG1_1(x) CAT(LDA_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define LDA_PROCESSOR_ARG1_2_(x, reg) OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX, x,
#define LDA_PROCESSOR_ARG1_2(x) LDA_PROCESSOR_ARG1_2_ x

// a | _ | zp | (zp, x)
#define LDA_ARGC_1(x) CAT(LDA_PROCESSOR_ARG1_,ARGC(LDA_CHECKZPX x))(x)

// a,x
#define LDA_PROCESSOR_AXY_X(val) OPCODE_LDA_ABSOLUTE_INDEXX, CAT(U16_SPLIT_, val),
// a,y
#define LDA_PROCESSOR_AXY_Y(val) OPCODE_LDA_ABSOLUTE_INDEXY, CAT(U16_SPLIT_, val),

// a,x | a, y
#define LDA_PREOCESSOR_AXYZP_1(val, reg) CAT(LDA_PROCESSOR_AXY_, reg)(val)

// zp, x
#define LDA_PREOCESSOR_AXYZP_0(val, reg) OPCODE_LDA_ZEROPAGE_INDEXX, val,

// a, x | a, y | zp, x
#define LDA_PROCESSOR_ARG2_1(val, reg) CAT(LDA_PREOCESSOR_AXYZP_, CAT(VALUE_TEST_, val))(val, reg)

// (zp), y
#define LDA_PROCESSOR_ARG2_2(val, reg) OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY, EXPAND val,

// a, x | a, y | zp, x | (zp), y
#define LDA_ARGC_2(val, reg) CAT(LDA_PROCESSOR_ARG2_, ARGC(LDA_CHECKINDIR COMMA val))(val, reg)

#define LDA(...) CAT(LDA_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)


// INSTRUCTION: RTS
// Implied only
#define RTS OPCODE_RTS,

// INSTRUCTION: CPX
// a, #, zp

#define CPX_ARGC_2(a, b) "CPX cannot be used with 2 arguments"

#define CPX_CHECKZPX(...) CAT(CPX_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define CPX_PROCESSOR_ARG1_1_0(val) OPCODE_CPX_ZEROPAGE, val,
// a
#define CPX_PROCESSOR_ARG1_1_1(val) OPCODE_CPX_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define CPX_PROCESSOR_ARG1_1_2(val) OPCODE_CPX_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define CPX_PROCESSOR_ARG1_1(x) CAT(CPX_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define CPX_PROCESSOR_ARG1_2_(x, reg) "CPX cannot be used with ZP-indirect syntax"
#define CPX_PROCESSOR_ARG1_2(x) CPX_PROCESSOR_ARG1_2_ x

#define CPX_ARGC_1(x) CAT(CPX_PROCESSOR_ARG1_,ARGC(CPX_CHECKZPX x))(x)

#define CPX(...) CAT(CPX_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)


/* Notes: 
 *  * Use C-like hex ("0x" prefix)
 *  * For immediate value use "_" prefix. */

const unsigned char binary[] = {
    LDA (0x0001)        // 0xAD, 0x01, 0x00,
    LDA (0x0002, X)     // 0xBD, 0x02, 0x00,
    LDA (0x0003, Y)     // 0xB9, 0x03, 0x00,
    LDA (_0x04)         // 0xA9, 0x04,
    LDA (0x05)          // 0xA5, 0x05,
    LDA ((0x04, X))     // 0xA1, 0x06,
    LDA (0x03, X)       // 0xB5, 0x03,
    LDA ((0x02), Y)     // 0xB1, 0x02,

    CPX (0x0001)        // 0xEC, 0x01, 0x00,
    CPX (0x0002, X)     // <<<BAD
    CPX (0x0003, Y)     // <<<BAD
    CPX (_0x04)         // 0xE0, 0x04,
    CPX (0x05)          // 0xE4, 0x05,
    CPX ((0x04, X))     // <<<BAD
    CPX (0x03, X)       // <<<BAD
    CPX ((0x02), Y)     // <<<BAD

    RTS                 // 0x60
}

gcc -E main.c | grep -v "^#"

const unsigned char binary[] = {
    0xAD, 0x01, 0x00,
    0xBD, 0x02, 0x00,
    0xB9, 0x03, 0x00,
    0xA9, 0x04,
    0xA5, 0x05,
    0xA1, 0x04,
    0xB5, 0x03,
    0xB1, 0x02,

    0xEC, 0x01, 0x00,
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"
    0xE0, 0x04,
    0xE4, 0x05,
    "CPX cannot be used with ZP-indirect syntax"
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"

    0x60,
}

На компиляции, соответственно, высветит строку с ошибкой

P.S. Мне лень генерить чиселки, там сейчас саппорт от 0x0000 до 0x0005. Кому нужно - тот сам сгенерит строчки.

Исправление PPP328, :

Ты можешь на сишных макросах сделать статические проверки ассемблерного кода, не позволяющие сунуть неправильный операнд не в ту инструкцию?

Да.

Можно даже с проверкой самих передаваемый значений, там же опкод меняется в зависимости от того, что сунули, ZP, адрес, значение или сахарок в виде A,X/A,Y/(A),Y/(A, X). Всё без проблем чекается. Мне даже в отличие от этой хаскеле-срани не нужно указывать какой тип передачи значения, я и сам могу задетектить:

// Concatenate
#define CAT__(x) x
#define CAT_(x, y) CAT__(x ## y)
#define CAT(x, y) CAT_(x, y)

// Count variadica arguments
#define ARGC(...) ARGC_(__VA_ARGS__, ARGC_RSEQ_N())
#define ARGC_(...) ARGC_N(__VA_ARGS__) 
#define ARGC_N(_1, _2, N, ...) N 
#define ARGC_RSEQ_N() 2, 1, 0

// Syntax routines
#define EXPAND(x) x
#define COMMA(x) ,

// OPCODES
#define OPCODE_LDA_ABSOLUTE                  0xAD // LDA 0x0000..0xFFFF
#define OPCODE_LDA_ABSOLUTE_INDEXX           0xBD // LDA 0x0000..0xFFFF, X
#define OPCODE_LDA_ABSOLUTE_INDEXY           0xB9 // LDA 0x0000..0xFFFF, Y
#define OPCODE_LDA_IMMEDIATE                 0xA9 // LDA #0x00..#0xFF
#define OPCODE_LDA_ZEROPAGE                  0xA5 // LDA 0x00..0xFF
#define OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX  0xA1 // LDA (0x00..0xFF, X)
#define OPCODE_LDA_ZEROPAGE_INDEXX           0xB5 // LDA 0x00..0xFF, X
#define OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY 0xB1 // LDA (0x00..0xFF), Y

#define OPCODE_CPX_ABSOLUTE                  0xEC // CPX 0x0000..0xFFFF
#define OPCODE_CPX_IMMEDIATE                 0xE0 // CPX #0x00..0xFF
#define OPCODE_CPX_ZEROPAGE                  0xE4 // CPX 0x00..0xFF

#define OPCODE_RTS                           0x60 // RTS

// VALUE CONVERTERS

// ZP tester
#define VALUE_TEST_0x00 0
#define VALUE_TEST_0x01 0
#define VALUE_TEST_0x02 0
#define VALUE_TEST_0x03 0
#define VALUE_TEST_0x04 0
#define VALUE_TEST_0x05 0
// ... I'm too lazy, works for MVP
#define VALUE_TEST_0xFF 0

// ADDRESS tester
#define VALUE_TEST_0x0001 1
#define VALUE_TEST_0x0002 1
#define VALUE_TEST_0x0003 1
#define VALUE_TEST_0x0004 1
#define VALUE_TEST_0x0005 1
// .. I'm too lazy, works for MVP
#define VALUE_TEST_0xFFFF 1

// IMMEDIATE tester
#define VALUE_TEST__0x00 2
#define VALUE_TEST__0x01 2
#define VALUE_TEST__0x02 2
#define VALUE_TEST__0x03 2
#define VALUE_TEST__0x04 2
// .. I'm too lazy, works for MVP
#define VALUE_TEST__0x05 2

// Immediate marker remover
#define VALUE_DEIM_0x00 0x00
#define VALUE_DEIM_0x01 0x01
#define VALUE_DEIM_0x02 0x02
#define VALUE_DEIM_0x03 0x03
#define VALUE_DEIM_0x04 0x04
// .. I'm too lazy, works for MVP
#define VALUE_DEIM_0xFF 0xFF

// U16 splitter
#define U16_SPLIT_0x0000 0x00, 0x00
#define U16_SPLIT_0x0001 0x01, 0x00
#define U16_SPLIT_0x0002 0x02, 0x00
#define U16_SPLIT_0x0003 0x03, 0x00
#define U16_SPLIT_0x0004 0x04, 0x00
// .. I'm too lazy, works for MVP
#define U16_SPLIT_0xFFFF 0xFF, 0xFF

// INSTRUCTION: LDA
/* Allowed LDA notations:
 * a  |   a, x  |  a, y  | #
 * zp | (zp, x) | zp, x  | (zp), y */

#define LDA_CHECKZPX(...) CAT(LDA_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define LDA_PROCESSOR_ARG1_1_0(val) OPCODE_LDA_ZEROPAGE, val,
// a
#define LDA_PROCESSOR_ARG1_1_1(val) OPCODE_LDA_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define LDA_PROCESSOR_ARG1_1_2(val) OPCODE_LDA_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define LDA_PROCESSOR_ARG1_1(x) CAT(LDA_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define LDA_PROCESSOR_ARG1_2_(x, reg) OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX, x,
#define LDA_PROCESSOR_ARG1_2(x) LDA_PROCESSOR_ARG1_2_ x

// a | _ | zp | (zp, x)
#define LDA_ARGC_1(x) CAT(LDA_PROCESSOR_ARG1_,ARGC(LDA_CHECKZPX x))(x)

// a,x
#define LDA_PROCESSOR_AXY_X(val) OPCODE_LDA_ABSOLUTE_INDEXX, CAT(U16_SPLIT_, val),
// a,y
#define LDA_PROCESSOR_AXY_Y(val) OPCODE_LDA_ABSOLUTE_INDEXY, CAT(U16_SPLIT_, val),

// a,x | a, y
#define LDA_PREOCESSOR_AXYZP_1(val, reg) CAT(LDA_PROCESSOR_AXY_, reg)(val)

// zp, x
#define LDA_PREOCESSOR_AXYZP_0(val, reg) OPCODE_LDA_ZEROPAGE_INDEXX, val,

// a, x | a, y | zp, x
#define LDA_PROCESSOR_ARG2_1(val, reg) CAT(LDA_PREOCESSOR_AXYZP_, CAT(VALUE_TEST_, val))(val, reg)

// (zp), y
#define LDA_PROCESSOR_ARG2_2(val, reg) OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY, EXPAND val,

// a, x | a, y | zp, x | (zp), y
#define LDA_ARGC_2(val, reg) CAT(LDA_PROCESSOR_ARG2_, ARGC(LDA_CHECKINDIR COMMA val))(val, reg)

#define LDA(...) CAT(LDA_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)


// INSTRUCTION: RTS
// Implied only
#define RTS OPCODE_RTS,

// INSTRUCTION: CPX
// a, #, zp

#define CPX_ARGC_2(a, b) "CPX cannot be used with 2 arguments"

#define CPX_CHECKZPX(...) CAT(CPX_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define CPX_PROCESSOR_ARG1_1_0(val) OPCODE_CPX_ZEROPAGE, val,
// a
#define CPX_PROCESSOR_ARG1_1_1(val) OPCODE_CPX_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define CPX_PROCESSOR_ARG1_1_2(val) OPCODE_CPX_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define CPX_PROCESSOR_ARG1_1(x) CAT(CPX_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define CPX_PROCESSOR_ARG1_2_(x, reg) "CPX cannot be used with ZP-indirect syntax"
#define CPX_PROCESSOR_ARG1_2(x) CPX_PROCESSOR_ARG1_2_ x

#define CPX_ARGC_1(x) CAT(CPX_PROCESSOR_ARG1_,ARGC(CPX_CHECKZPX x))(x)

#define CPX(...) CAT(CPX_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)

// -------------------------------------------------------------------------

/* Notes: 
 *  * Use C-like hex ("0x" prefix)
 *  * For immediate value use "_" prefix. */

const unsigned char binary[] = {
    LDA (0x0001)        // 0xAD, 0x01, 0x00,
    LDA (0x0002, X)     // 0xBD, 0x02, 0x00,
    LDA (0x0003, Y)     // 0xB9, 0x03, 0x00,
    LDA (_0x04)         // 0xA9, 0x04,
    LDA (0x05)          // 0xA5, 0x05,
    LDA ((0x04, X))     // 0xA1, 0x06,
    LDA (0x03, X)       // 0xB5, 0x03,
    LDA ((0x02), Y)     // 0xB1, 0x02,

    CPX (0x0001)        // 0xEC, 0x01, 0x00,
    CPX (0x0002, X)     // <<<BAD
    CPX (0x0003, Y)     // <<<BAD
    CPX (_0x04)         // 0xE0, 0x04,
    CPX (0x05)          // 0xE4, 0x05,
    CPX ((0x04, X))     // <<<BAD
    CPX (0x03, X)       // <<<BAD
    CPX ((0x02), Y)     // <<<BAD

    RTS                 // 0x60
}

gcc -E main.c | grep -v "^#"

const unsigned char binary[] = {
    0xAD, 0x01, 0x00,
    0xBD, 0x02, 0x00,
    0xB9, 0x03, 0x00,
    0xA9, 0x04,
    0xA5, 0x05,
    0xA1, 0x04,
    0xB5, 0x03,
    0xB1, 0x02,

    0xEC, 0x01, 0x00,
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"
    0xE0, 0x04,
    0xE4, 0x05,
    "CPX cannot be used with ZP-indirect syntax"
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"

    0x60,
}

На компиляции, соответственно, высветит строку с ошибкой

P.S. Мне лень генерить чиселки, там сейчас саппорт от 0x0000 до 0x0005. Кому нужно - тот сам сгенерит строчки.

Исправление PPP328, :

Ты можешь на сишных макросах сделать статические проверки ассемблерного кода, не позволяющие сунуть неправильный операнд не в ту инструкцию?

Да.

Можно даже с проверкой самих передаваемый значений, там же опкод меняется в зависимости от того, что сунули, ZP, адрес, значение или сахарок в виде A,X/A,Y/(A),Y/(A, X). Всё без проблем чекается:

// Concatenate
#define CAT__(x) x
#define CAT_(x, y) CAT__(x ## y)
#define CAT(x, y) CAT_(x, y)

// Count variadica arguments
#define ARGC(...) ARGC_(__VA_ARGS__, ARGC_RSEQ_N())
#define ARGC_(...) ARGC_N(__VA_ARGS__) 
#define ARGC_N(_1, _2, N, ...) N 
#define ARGC_RSEQ_N() 2, 1, 0

// Syntax routines
#define EXPAND(x) x
#define COMMA(x) ,

// OPCODES
#define OPCODE_LDA_ABSOLUTE                  0xAD // LDA 0x0000..0xFFFF
#define OPCODE_LDA_ABSOLUTE_INDEXX           0xBD // LDA 0x0000..0xFFFF, X
#define OPCODE_LDA_ABSOLUTE_INDEXY           0xB9 // LDA 0x0000..0xFFFF, Y
#define OPCODE_LDA_IMMEDIATE                 0xA9 // LDA #0x00..#0xFF
#define OPCODE_LDA_ZEROPAGE                  0xA5 // LDA 0x00..0xFF
#define OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX  0xA1 // LDA (0x00..0xFF, X)
#define OPCODE_LDA_ZEROPAGE_INDEXX           0xB5 // LDA 0x00..0xFF, X
#define OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY 0xB1 // LDA (0x00..0xFF), Y

#define OPCODE_CPX_ABSOLUTE                  0xEC // CPX 0x0000..0xFFFF
#define OPCODE_CPX_IMMEDIATE                 0xE0 // CPX #0x00..0xFF
#define OPCODE_CPX_ZEROPAGE                  0xE4 // CPX 0x00..0xFF

#define OPCODE_RTS                           0x60 // RTS

// VALUE CONVERTERS

// ZP tester
#define VALUE_TEST_0x00 0
#define VALUE_TEST_0x01 0
#define VALUE_TEST_0x02 0
#define VALUE_TEST_0x03 0
#define VALUE_TEST_0x04 0
#define VALUE_TEST_0x05 0
// ... I'm too lazy, works for MVP
#define VALUE_TEST_0xFF 0

// ADDRESS tester
#define VALUE_TEST_0x0001 1
#define VALUE_TEST_0x0002 1
#define VALUE_TEST_0x0003 1
#define VALUE_TEST_0x0004 1
#define VALUE_TEST_0x0005 1
// .. I'm too lazy, works for MVP
#define VALUE_TEST_0xFFFF 1

// IMMEDIATE tester
#define VALUE_TEST__0x00 2
#define VALUE_TEST__0x01 2
#define VALUE_TEST__0x02 2
#define VALUE_TEST__0x03 2
#define VALUE_TEST__0x04 2
// .. I'm too lazy, works for MVP
#define VALUE_TEST__0x05 2

// Immediate marker remover
#define VALUE_DEIM_0x00 0x00
#define VALUE_DEIM_0x01 0x01
#define VALUE_DEIM_0x02 0x02
#define VALUE_DEIM_0x03 0x03
#define VALUE_DEIM_0x04 0x04
// .. I'm too lazy, works for MVP
#define VALUE_DEIM_0xFF 0xFF

// U16 splitter
#define U16_SPLIT_0x0000 0x00, 0x00
#define U16_SPLIT_0x0001 0x01, 0x00
#define U16_SPLIT_0x0002 0x02, 0x00
#define U16_SPLIT_0x0003 0x03, 0x00
#define U16_SPLIT_0x0004 0x04, 0x00
// .. I'm too lazy, works for MVP
#define U16_SPLIT_0xFFFF 0xFF, 0xFF

// INSTRUCTION: LDA
/* Allowed LDA notations:
 * a  |   a, x  |  a, y  | #
 * zp | (zp, x) | zp, x  | (zp), y */

#define LDA_CHECKZPX(...) CAT(LDA_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define LDA_PROCESSOR_ARG1_1_0(val) OPCODE_LDA_ZEROPAGE, val,
// a
#define LDA_PROCESSOR_ARG1_1_1(val) OPCODE_LDA_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define LDA_PROCESSOR_ARG1_1_2(val) OPCODE_LDA_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define LDA_PROCESSOR_ARG1_1(x) CAT(LDA_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define LDA_PROCESSOR_ARG1_2_(x, reg) OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX, x,
#define LDA_PROCESSOR_ARG1_2(x) LDA_PROCESSOR_ARG1_2_ x

// a | _ | zp | (zp, x)
#define LDA_ARGC_1(x) CAT(LDA_PROCESSOR_ARG1_,ARGC(LDA_CHECKZPX x))(x)

// a,x
#define LDA_PROCESSOR_AXY_X(val) OPCODE_LDA_ABSOLUTE_INDEXX, CAT(U16_SPLIT_, val),
// a,y
#define LDA_PROCESSOR_AXY_Y(val) OPCODE_LDA_ABSOLUTE_INDEXY, CAT(U16_SPLIT_, val),

// a,x | a, y
#define LDA_PREOCESSOR_AXYZP_1(val, reg) CAT(LDA_PROCESSOR_AXY_, reg)(val)

// zp, x
#define LDA_PREOCESSOR_AXYZP_0(val, reg) OPCODE_LDA_ZEROPAGE_INDEXX, val,

// a, x | a, y | zp, x
#define LDA_PROCESSOR_ARG2_1(val, reg) CAT(LDA_PREOCESSOR_AXYZP_, CAT(VALUE_TEST_, val))(val, reg)

// (zp), y
#define LDA_PROCESSOR_ARG2_2(val, reg) OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY, EXPAND val,

// a, x | a, y | zp, x | (zp), y
#define LDA_ARGC_2(val, reg) CAT(LDA_PROCESSOR_ARG2_, ARGC(LDA_CHECKINDIR COMMA val))(val, reg)

#define LDA(...) CAT(LDA_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)


// INSTRUCTION: RTS
// Implied only
#define RTS OPCODE_RTS,

// INSTRUCTION: CPX
// a, #, zp

#define CPX_ARGC_2(a, b) "CPX cannot be used with 2 arguments"

#define CPX_CHECKZPX(...) CAT(CPX_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define CPX_PROCESSOR_ARG1_1_0(val) OPCODE_CPX_ZEROPAGE, val,
// a
#define CPX_PROCESSOR_ARG1_1_1(val) OPCODE_CPX_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define CPX_PROCESSOR_ARG1_1_2(val) OPCODE_CPX_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define CPX_PROCESSOR_ARG1_1(x) CAT(CPX_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define CPX_PROCESSOR_ARG1_2_(x, reg) "CPX cannot be used with ZP-indirect syntax"
#define CPX_PROCESSOR_ARG1_2(x) CPX_PROCESSOR_ARG1_2_ x

#define CPX_ARGC_1(x) CAT(CPX_PROCESSOR_ARG1_,ARGC(CPX_CHECKZPX x))(x)

#define CPX(...) CAT(CPX_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)

// -------------------------------------------------------------------------

/* Notes: 
 *  * Use C-like hex ("0x" prefix)
 *  * For immediate value use "_" prefix. */

const unsigned char binary[] = {
    LDA (0x0001)        // 0xAD, 0x01, 0x00,
    LDA (0x0002, X)     // 0xBD, 0x02, 0x00,
    LDA (0x0003, Y)     // 0xB9, 0x03, 0x00,
    LDA (_0x04)         // 0xA9, 0x04,
    LDA (0x05)          // 0xA5, 0x05,
    LDA ((0x04, X))     // 0xA1, 0x06,
    LDA (0x03, X)       // 0xB5, 0x03,
    LDA ((0x02), Y)     // 0xB1, 0x02,

    CPX (0x0001)        // 0xEC, 0x01, 0x00,
    CPX (0x0002, X)     // <<<BAD
    CPX (0x0003, Y)     // <<<BAD
    CPX (_0x04)         // 0xE0, 0x04,
    CPX (0x05)          // 0xE4, 0x05,
    CPX ((0x04, X))     // <<<BAD
    CPX (0x03, X)       // <<<BAD
    CPX ((0x02), Y)     // <<<BAD

    RTS                 // 0x60
}

gcc -E main.c | grep -v "^#"

const unsigned char binary[] = {
    0xAD, 0x01, 0x00,
    0xBD, 0x02, 0x00,
    0xB9, 0x03, 0x00,
    0xA9, 0x04,
    0xA5, 0x05,
    0xA1, 0x04,
    0xB5, 0x03,
    0xB1, 0x02,

    0xEC, 0x01, 0x00,
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"
    0xE0, 0x04,
    0xE4, 0x05,
    "CPX cannot be used with ZP-indirect syntax"
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"

    0x60,
}

На компиляции, соответственно, высветит строку с ошибкой

P.S. Мне лень генерить чиселки, там сейчас саппорт от 0x0000 до 0x0005. Кому нужно - тот сам сгенерит строчки.

Исходная версия PPP328, :

Ты можешь на сишных макросах сделать статические проверки ассемблерного кода, не позволяющие сунуть неправильный операнд не в ту инструкцию?

Да.

Можно даже с проверкой самих передаваемый значений, там же опкод меняется в зависимости от того, что сунули, ZP, адрес, значение или сахарок в виде A,X/A,Y/(A),Y/(A, X). Всё без проблем чекается:

// Concatenate
#define CAT__(x) x
#define CAT_(x, y) CAT__(x ## y)
#define CAT(x, y) CAT_(x, y)

// Count variadica arguments
#define ARGC(...) ARGC_(__VA_ARGS__, ARGC_RSEQ_N())
#define ARGC_(...) ARGC_N(__VA_ARGS__) 
#define ARGC_N(_1, _2, N, ...) N 
#define ARGC_RSEQ_N() 2, 1, 0

// Syntax routines
#define EXPAND(x) x
#define COMMA(x) ,

// OPCODES
#define OPCODE_LDA_ABSOLUTE                  0xAD // LDA 0x0000..0xFFFF
#define OPCODE_LDA_ABSOLUTE_INDEXX           0xBD // LDA 0x0000..0xFFFF, X
#define OPCODE_LDA_ABSOLUTE_INDEXY           0xB9 // LDA 0x0000..0xFFFF, Y
#define OPCODE_LDA_IMMEDIATE                 0xA9 // LDA #0x00..#0xFF
#define OPCODE_LDA_ZEROPAGE                  0xA5 // LDA 0x00..0xFF
#define OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX  0xA1 // LDA (0x00..0xFF, X)
#define OPCODE_LDA_ZEROPAGE_INDEXX           0xB5 // LDA 0x00..0xFF, X
#define OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY 0xB1 // LDA (0x00..0xFF), Y

#define OPCODE_CPX_ABSOLUTE                  0xEC // CPX 0x0000..0xFFFF
#define OPCODE_CPX_IMMEDIATE                 0xE0 // CPX #0x00..0xFF
#define OPCODE_CPX_ZEROPAGE                  0xE4 // CPX 0x00..0xFF

#define OPCODE_RTS                           0x60 // RTS

// VALUE CONVERTERS

// ZP tester
#define VALUE_TEST_0x00 0
#define VALUE_TEST_0x01 0
#define VALUE_TEST_0x02 0
#define VALUE_TEST_0x03 0
#define VALUE_TEST_0x04 0
#define VALUE_TEST_0x05 0
// ... I'm too lazy, works for MVP
#define VALUE_TEST_0xFF 0

// ADDRESS tester
#define VALUE_TEST_0x0001 1
#define VALUE_TEST_0x0002 1
#define VALUE_TEST_0x0003 1
#define VALUE_TEST_0x0004 1
#define VALUE_TEST_0x0005 1
// .. I'm too lazy, works for MVP
#define VALUE_TEST_0xFFFF 1

// IMMEDIATE tester
#define VALUE_TEST__0x00 2
#define VALUE_TEST__0x01 2
#define VALUE_TEST__0x02 2
#define VALUE_TEST__0x03 2
#define VALUE_TEST__0x04 2
// .. I'm too lazy, works for MVP
#define VALUE_TEST__0x05 2

// Immediate marker remover
#define VALUE_DEIM_0x00 0x00
#define VALUE_DEIM_0x01 0x01
#define VALUE_DEIM_0x02 0x02
#define VALUE_DEIM_0x03 0x03
#define VALUE_DEIM_0x04 0x04
// .. I'm too lazy, works for MVP
#define VALUE_DEIM_0xFF 0xFF

// U16 splitter
#define U16_SPLIT_0x0000 0x00, 0x00
#define U16_SPLIT_0x0001 0x01, 0x00
#define U16_SPLIT_0x0002 0x02, 0x00
#define U16_SPLIT_0x0003 0x03, 0x00
#define U16_SPLIT_0x0004 0x04, 0x00
// .. I'm too lazy, works for MVP
#define U16_SPLIT_0xFFFF 0xFF, 0xFF

// INSTRUCTION: LDA
/* Allowed LDA notations:
 * a  |   a, x  |  a, y  | #
 * zp | (zp, x) | zp, x  | (zp), y */

#define LDA_CHECKZPX(...) CAT(LDA_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define LDA_PROCESSOR_ARG1_1_0(val) OPCODE_LDA_ZEROPAGE, val,
// a
#define LDA_PROCESSOR_ARG1_1_1(val) OPCODE_LDA_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define LDA_PROCESSOR_ARG1_1_2(val) OPCODE_LDA_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define LDA_PROCESSOR_ARG1_1(x) CAT(LDA_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define LDA_PROCESSOR_ARG1_2_(x, reg) OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX, x,
#define LDA_PROCESSOR_ARG1_2(x) LDA_PROCESSOR_ARG1_2_ x

// a | _ | zp | (zp, x)
#define LDA_ARGC_1(x) CAT(LDA_PROCESSOR_ARG1_,ARGC(LDA_CHECKZPX x))(x)

// a,x
#define LDA_PROCESSOR_AXY_X(val) OPCODE_LDA_ABSOLUTE_INDEXX, CAT(U16_SPLIT_, val),
// a,y
#define LDA_PROCESSOR_AXY_Y(val) OPCODE_LDA_ABSOLUTE_INDEXY, CAT(U16_SPLIT_, val),

// a,x | a, y
#define LDA_PREOCESSOR_AXYZP_1(val, reg) CAT(LDA_PROCESSOR_AXY_, reg)(val)

// zp, x
#define LDA_PREOCESSOR_AXYZP_0(val, reg) OPCODE_LDA_ZEROPAGE_INDEXX, val,

// a, x | a, y | zp, x
#define LDA_PROCESSOR_ARG2_1(val, reg) CAT(LDA_PREOCESSOR_AXYZP_, CAT(VALUE_TEST_, val))(val, reg)

// (zp), y
#define LDA_PROCESSOR_ARG2_2(val, reg) OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY, EXPAND val,

// a, x | a, y | zp, x | (zp), y
#define LDA_ARGC_2(val, reg) CAT(LDA_PROCESSOR_ARG2_, ARGC(LDA_CHECKINDIR COMMA val))(val, reg)

#define LDA(...) CAT(LDA_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)


// INSTRUCTION: RTS
// Implied only
#define RTS OPCODE_RTS,

// INSTRUCTION: CPX
// a, #, zp

#define CPX_ARGC_2(a, b) "CPX cannot be used with 2 arguments"

#define CPX_CHECKZPX(...) CAT(CPX_CHECKZPX_, ARGC(__VA_ARGS__)),

// zp
#define CPX_PROCESSOR_ARG1_1_0(val) OPCODE_CPX_ZEROPAGE, val,
// a
#define CPX_PROCESSOR_ARG1_1_1(val) OPCODE_CPX_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define CPX_PROCESSOR_ARG1_1_2(val) OPCODE_CPX_IMMEDIATE, CAT(VALUE_DEIM, val),

// a | _ | zp
#define CPX_PROCESSOR_ARG1_1(x) CAT(CPX_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)

// (zp, x)
#define CPX_PROCESSOR_ARG1_2_(x, reg) "CPX cannot be used with ZP-indirect syntax"
#define CPX_PROCESSOR_ARG1_2(x) CPX_PROCESSOR_ARG1_2_ x

#define CPX_ARGC_1(x) CAT(CPX_PROCESSOR_ARG1_,ARGC(CPX_CHECKZPX x))(x)

#define CPX(...) CAT(CPX_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)

// -------------------------------------------------------------------------

/* Notes: 
 *  * Use C-like hex ("0x" prefix)
 *  * For immediate value use "_" prefix. */

const unsigned char binary[] = {
    LDA (0x0001)        // 0xAD, 0x01, 0x00,
    LDA (0x0002, X)     // 0xBD, 0x02, 0x00,
    LDA (0x0003, Y)     // 0xB9, 0x03, 0x00,
    LDA (_0x04)         // 0xA9, 0x04,
    LDA (0x05)          // 0xA5, 0x05,
    LDA ((0x04, X))     // 0xA1, 0x06,
    LDA (0x03, X)       // 0xB5, 0x03,
    LDA ((0x02), Y)     // 0xB1, 0x02,

    CPX (0x0001)        // 0xEC, 0x01, 0x00,
    CPX (0x0002, X)     // <<<BAD
    CPX (0x0003, Y)     // <<<BAD
    CPX (_0x04)         // 0xE0, 0x04,
    CPX (0x05)          // 0xE4, 0x05,
    CPX ((0x04, X))     // <<<BAD
    CPX (0x03, X)       // <<<BAD
    CPX ((0x02), Y)     // <<<BAD

    RTS                 // 0x60
}

gcc -E main.c | grep -v «^#»

const unsigned char binary[] = {
    0xAD, 0x01, 0x00,
    0xBD, 0x02, 0x00,
    0xB9, 0x03, 0x00,
    0xA9, 0x04,
    0xA5, 0x05,
    0xA1, 0x04,
    0xB5, 0x03,
    0xB1, 0x02,

    0xEC, 0x01, 0x00,
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"
    0xE0, 0x04,
    0xE4, 0x05,
    "CPX cannot be used with ZP-indirect syntax"
    "CPX cannot be used with 2 arguments"
    "CPX cannot be used with 2 arguments"

    0x60,
}

На компиляции, соответственно, высветит строку с ошибкой

P.S. Мне лень генерить чиселки, там сейчас саппорт от 0x0000 до 0x0005. Кому нужно - тот сам сгенерит строчки.