From c93eb647daeaf9f3974fa9824b780d7863a60a39 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Fri, 3 Nov 2023 18:02:01 +0100 Subject: [PATCH 01/16] Make subtraction tests binary only --- tests/test_sub_negative/subtraction.x | Bin 0 -> 6152 bytes tests/test_sub_negative/test.sh | 4 ---- tests/test_sub_too_big/subtraction.x | Bin 0 -> 6152 bytes tests/test_sub_too_big/test.sh | 5 ----- tests/test_subtraction/subtraction.x | Bin 0 -> 6152 bytes tests/test_subtraction/test.sh | 3 --- 6 files changed, 12 deletions(-) create mode 100644 tests/test_sub_negative/subtraction.x create mode 100644 tests/test_sub_too_big/subtraction.x create mode 100644 tests/test_subtraction/subtraction.x diff --git a/tests/test_sub_negative/subtraction.x b/tests/test_sub_negative/subtraction.x new file mode 100644 index 0000000000000000000000000000000000000000..ba3c6c58400cbca09e345246edf713cba2fb6466 GIT binary patch literal 6152 zcmeIuJqmy@42I$Oe=BDd3f@49Itbdq_5j6;Ym0aQN9E1X/dev/null 1>&2 || true -rm *.x 2>/dev/null 1>&2 || true -../../saimport *.s if ../../salink 2>/dev/null 1>&2 ; then exit 1 fi -rm *.x - diff --git a/tests/test_sub_too_big/subtraction.x b/tests/test_sub_too_big/subtraction.x new file mode 100644 index 0000000000000000000000000000000000000000..f9ce5f1725b620398182a3fed7e0c84fac949d94 GIT binary patch literal 6152 zcmeIuO%6aX5QgEY-&V5xa$(e~?XW}e!L6}b-ab(LwM zqQs@hr>YfEzNdV$?w}Ar009ILKmY**5I_I{1pX?JOQWatoqk{RJa^+dxR2+(8@-vg Z&5Zy82p|xUKpfC6tbzanUj;Jh&l^dV6^;M^ literal 0 HcmV?d00001 diff --git a/tests/test_sub_too_big/test.sh b/tests/test_sub_too_big/test.sh index b906d54..6847fd9 100755 --- a/tests/test_sub_too_big/test.sh +++ b/tests/test_sub_too_big/test.sh @@ -2,12 +2,7 @@ set -e rm subtraction 2>/dev/null 1>&2 || true -rm *.x 2>/dev/null 1>&2 || true -../../saimport *.s if ../../salink 2>/dev/null 1>&2 ; then exit 1 fi - -rm *.x - diff --git a/tests/test_subtraction/subtraction.x b/tests/test_subtraction/subtraction.x new file mode 100644 index 0000000000000000000000000000000000000000..3ffae2e76da2f4e04eb951200b8fae6642d39ec7 GIT binary patch literal 6152 zcmeIuO$viB5C!0;CT@F%UZlJJLctr9l$Ih`7uAK!kWG&rXQJMqP=uF7X5PTS@LfDS z5CinZW4X<8qopt=p6pK3!O9OQGDF;(lmc>HI9G^iJ<}9m=Akr_3Qb-rP3Q&Lo6!^Em23J?N%N5<*HO=kWK4dWUUGF|U?)7V)BK6aB*s)H44}@ P9?hF6K!G0#lo-Av5}zn| literal 0 HcmV?d00001 diff --git a/tests/test_subtraction/test.sh b/tests/test_subtraction/test.sh index 0ab8669..f6e2e9b 100755 --- a/tests/test_subtraction/test.sh +++ b/tests/test_subtraction/test.sh @@ -2,9 +2,7 @@ set -e rm subtraction 2>/dev/null 1>&2 || true -rm *.x 2>/dev/null 1>&2 || true -../../saimport *.s ../../salink 2>/dev/null 1>&2 diff=`od -An -tx1 -N3 subtraction | xargs` if [ "$diff" != "21 ff 00" ]; then @@ -32,5 +30,4 @@ if [ "$diff" != "ff" ]; then fi rm subtraction -rm *.x From 86a56c1b0c174152e177029f4f9e04ea2a0a5fbf Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 4 Nov 2023 21:48:21 +0100 Subject: [PATCH 02/16] Remove label subtraction from Specasm Label subtraction was deprecated in version v5 of Specasm and is now removed in v7 to free up space needed for the next opcodes. It's still supported by the linker however, so .x files created with pre v7 versions of Specasm can still be linked with the v7 linker. The Specasm editor will rewrite instructions that use label subtraction to use the new expression syntax when it loads .x files. Signed-off-by: Mark Ryan --- docs/specasm.md | 12 +-- src/line_dump.c | 4 +- src/line_parse.c | 129 +++------------------------------ src/test_content.c | 177 +++++++++------------------------------------ 4 files changed, 57 insertions(+), 265 deletions(-) diff --git a/docs/specasm.md b/docs/specasm.md index 80ae8f0..de0f27f 100644 --- a/docs/specasm.md +++ b/docs/specasm.md @@ -359,7 +359,7 @@ Here, the expression used in the im statement is invalid as it resolves to a val #### Label Subtraction > **Warning** -> Label subtraction outside of expressions is deprecated. This syntax has been rendered redundant by the introduction of expressions in Specasm v5. It will probably be removed in the future to reclaim the bytes used to implement it. +> Label subtraction outside of expressions is no longer supported as of Specasm v7. This syntax was rendered redundant by the introduction of expressions in Specasm v5 and deprecated in that release. The linker does still support label substraction so any .x files assembled with an earlier version of Specasm can sill be linked and binary compatibility with older versions of Specasm is maintained. Any instructions in .x files that use label subtraction will be rewritten to use the new expression syntax when loaded into Specasm's editor. The remainder of this section exists purely for historical value. Direct label subtraction is supported in certain instructions and directives without the use of an expression. @@ -442,7 +442,7 @@ db -1, -2, -3, $20 ; Mixing characters and decimals db 'A', 1, 2 ``` -In addition to encoding numbers the **dw** directive can be used to encode the address of a label and also the difference between the addresses of two labels. When used in this format, the **dw** directive can only contain a single argument. +In addition to encoding numbers the **dw** directive can be used to encode the address of a label. When used in this format, the **dw** directive can only contain a single argument. For example, @@ -450,10 +450,10 @@ For example, dw data ``` -will encode the address of the label data directly into the program. The address is encoded at link time when salink has figured out the final address of the label. +will encode the address of the label data directly into the program. The address is encoded at link time when salink has figured out the final address of the label. To encode the difference between two labels the expression syntax must be used. ``` -dw end-start +dw =end-start .start dw 10, 10 db 1 @@ -462,10 +462,10 @@ db 1 will store the value 5 in a 16 bit word in the final program. -The **db** directive cannot be used to encode the address of a label as the address is unlikely to fit into a single byte. It can however, be used to encode the difference between two labels providing the difference does fit into a byte. If the labels are too far apart an error will be generated at link time. When used in this form, no other numbers can follow the label subtraction on the same line. For example, +The **db** directive cannot be used to encode the address of a label as the address is unlikely to fit into a single byte. It can however, be used to encode the difference between two labels using the expression syntax, providing the difference does fit into a byte. If the labels are too far apart an error will be generated at link time. When used in this form, no other numbers can follow the label subtraction on the same line. For example, ``` -db end-start +db =end-start .start dw 10, 10 db 1 diff --git a/src/line_dump.c b/src/line_dump.c index de71e43..c2c0d53 100644 --- a/src/line_dump.c +++ b/src/line_dump.c @@ -946,7 +946,9 @@ static uint8_t prv_dump_subtraction_e(const specasm_line_t *line, char *buf, { char *str; - str = prv_dump_jump_label_fmt_e(line, buf, line->data.op_code[id_buf], + buf[0] = '='; + str = prv_dump_jump_label_fmt_e(line, buf + 1, + line->data.op_code[id_buf], line->data.op_code[id_buf + 2]); if (err_type != SPECASM_ERROR_OK) return 0; diff --git a/src/line_parse.c b/src/line_parse.c index 2d448e0..29446cd 100644 --- a/src/line_parse.c +++ b/src/line_parse.c @@ -974,53 +974,6 @@ static uint8_t prv_parse_djnz_e(const char *args, specasm_line_t *line, return args - start; } -static const char *prv_parse_labels_e(const char *args, specasm_line_t *line, - uint8_t *count, uint16_t *val, - uint8_t *addr_fmt2) -{ - const char *args2; - uint8_t *labels = (uint8_t *)val; - - /* - * So this is all a bit hacky. There aren't enough bits in - * flags to store the address type for two addresses. There's - * only room for one. However, all the instructions that allow - * address subtraction have at least one spare byte in the opcode. - * So, we'll store the flags for the second label in the address - * bits, and the flags for the first label in the first unused - * byte in the opcode. If we ever add a second flags byte we - * can clean this up. - */ - - *count = 1; - while (*args == ' ') - ++args; - args = prv_parse_jump_label_e(args, line, &labels[0]); - if (err_type != SPECASM_ERROR_OK) - return 0; - args2 = args; - while (*args2 == ' ') - ++args2; - *addr_fmt2 = 0; - if (*args2 == '-') { - *addr_fmt2 = specasm_line_get_addr_type(line); - - /* Again this is hacky. specasm_line_set_addr_type assumes - * that the address bits are zero. If they're not it doesn't - * work correctly. We need to explicitly reset them. - */ - - line->flags &= 0xF3; - *count = 2; - args2++; - while (*args2 == ' ') - ++args2; - args = prv_parse_jump_label_e(args2, line, &labels[1]); - } - - return args; -} - static uint8_t prv_signed_ok(uint8_t flags, uint8_t val) { return (flags == SPECASM_FLAGS_NUM_SIGNED) || @@ -1050,8 +1003,6 @@ static uint8_t prv_parse_db_e(const char *args, specasm_line_t *line, uint8_t flags; uint8_t flags2; uint8_t signed_ok; - uint8_t label_count; - uint8_t *op_code; const char *args2; uint8_t i = 0; const char *start = args; @@ -1065,25 +1016,8 @@ static uint8_t prv_parse_db_e(const char *args, specasm_line_t *line, } args = prv_get_byte_imm_e(args, &val, &flags); - if (err_type == SPECASM_ERROR_NUM_TOO_BIG) + if (err_type != SPECASM_ERROR_OK) return 0; - if (err_type != SPECASM_ERROR_OK) { - args2 = start; - err_type = SPECASM_ERROR_OK; - op_code = &line->data.op_code[0]; - args = prv_parse_labels_e(args2, line, &label_count, - (uint16_t *)op_code, &op_code[2]); - - if (err_type != SPECASM_ERROR_OK) - return 0; - if (label_count != 2) { - err_type = SPECASM_ERROR_BAD_LABEL; - return 0; - } - - line->type = SPECASM_LINE_TYPE_DB_SUB; - return args - start; - } line->data.op_code[0] = val; for (i = 1; i < 4; i++) { @@ -1126,7 +1060,6 @@ static uint8_t prv_parse_dw_e(const char *args, specasm_line_t *line, uint8_t flags; uint8_t flags2; uint8_t signed_ok; - uint8_t label_count; uint8_t *op_code; const char *args2 = args; int8_t sz = 1; @@ -1170,12 +1103,11 @@ static uint8_t prv_parse_dw_e(const char *args, specasm_line_t *line, } else { err_type = SPECASM_ERROR_OK; op_code = &line->data.op_code[0]; - args = prv_parse_labels_e(args2, line, &label_count, - (uint16_t *)op_code, &op_code[2]); + while (*args2 == ' ') + ++args2; + args = prv_parse_jump_label_e(args2, line, op_code); if (err_type != SPECASM_ERROR_OK) return 0; - if (label_count == 2) - line->type = SPECASM_LINE_TYPE_DW_SUB; } specasm_line_set_size(line, sz); @@ -1810,40 +1742,6 @@ static void prv_ld_reg_reg_e(specasm_line_t *line, uint8_t reg, uint8_t off, err_type = SPECASM_ERROR_BAD_REG; } -static void prv_parse_ld_labels_e(specasm_line_t *line, uint8_t reg, - uint16_t val, uint8_t label_count, - uint8_t addr_fmt2) -{ - uint8_t flags2; - uint8_t *op_code; - - flags2 = specasm_line_get_format(line); - if (reg <= SPECASM_BYTE_REG_A) { - if (label_count != 2) { - err_type = SPECASM_ERROR_BAD_LABEL; - return; - } - op_code = &line->data.op_code[0]; - op_code[0] = 0x6 | (reg << 3); - *((uint16_t *)&op_code[1]) = val; - specasm_line_set_format(line, flags2); - specasm_line_set_size(line, 1); - line->type = SPECASM_LINE_TYPE_LD_IMM_8_SUB; - } else { - prv_ld_16bit_imm_e(line, reg, flags2, val); - if (err_type != SPECASM_ERROR_OK) - return; - if (label_count > 1) { - if (specasm_line_get_size(line) == 3) { - err_type = SPECASM_ERROR_BAD_LABEL; - return; - } - line->type = SPECASM_LINE_TYPE_LD_IMM_16_SUB; - } - } - line->data.op_code[3] = addr_fmt2; -} - static uint8_t prv_parse_ld_e(const char *args, specasm_line_t *line, const specasm_opcode_t *op_entry) { @@ -1857,8 +1755,6 @@ static uint8_t prv_parse_ld_e(const char *args, specasm_line_t *line, uint8_t flags; uint8_t flags2; uint8_t read; - uint8_t label_count; - uint8_t addr_fmt2; uint8_t label_type; uint8_t v; const char *start = args; @@ -1937,20 +1833,19 @@ static uint8_t prv_parse_ld_e(const char *args, specasm_line_t *line, } else if (err_type == SPECASM_ERROR_OK) { prv_ld_imm_e(line, reg, off, flags, flags2, val); return args2 - start; - } else if ((reg <= SPECASM_BYTE_REG_A) || - (reg >= SPECASM_BYTE_REG_BC && reg <= SPECASM_BYTE_REG_IY)) { + } else if ((reg >= SPECASM_BYTE_REG_BC) && + (reg <= SPECASM_BYTE_REG_IY)) { /* - * For 16 bit immediate loads we allow labels and label - * subtraction in place of the 16 bit value. For 8 bit - * immediate loads we allow label subtraction. + * For 16 bit immediate loads we allow labels in place of + * the 16 bit value. */ err_type = SPECASM_ERROR_OK; - args2 = prv_parse_labels_e(args, line, &label_count, &val, - &addr_fmt2); + args2 = prv_parse_jump_label_e(args, line, &v); + val = v; if (err_type == SPECASM_ERROR_OK) { - prv_parse_ld_labels_e(line, reg, val, label_count, - addr_fmt2); + prv_ld_16bit_imm_e(line, reg, + specasm_line_get_format(line), val); return args2 - start; } } diff --git a/src/test_content.c b/src/test_content.c index ce09e61..458b11f 100644 --- a/src/test_content.c +++ b/src/test_content.c @@ -857,18 +857,7 @@ const test_t opcode_tests[] = { {"ld a , -1", "ld a, -1", 2 , {0x3E, 0xFF}}, {"ld a, 'A'", "ld a, 'A'", 2, {0x3E, 0x41}}, {"ld a , 'A'", "ld a, 'A'", 2 , {0x3E, 0x41}}, - {"ld a, label1-label", "ld a, label1-label", 2, {0x3E, 0x05, 0x00, - 0x04}}, - {"ld a , label1 - label", "ld a, label1-label", 2, {0x3E, 0x05, 0x00, - 0x04}}, - {"ld a, label1-verylonglabel", "ld a, label1-verylonglabel", 2, - {0x3E, 0x05, 0x00, 0x04}}, - {"ld a , label1 - verylonglabel", "ld a, label1-verylonglabel", 2, - {0x3E, 0x05, 0x00, 0x04}}, - {"ld a, verylonglabel-label1", "ld a, verylonglabel-label1", 2, - {0x3E, 0x0, 0x05, 0x08}}, - {"ld a , verylonglabel - label1", "ld a, verylonglabel-label1", 2, - {0x3E, 0x0, 0x05, 0x08}}, + {"ld a , =label", "ld a, =label", 2 , {0x3E, 0x0}}, {"ld a ,= label", "ld a, =label", 2 , {0x3E, 0x0}}, {"ld a, =label1", "ld a, =label1", 2 , {0x3E, 0x5}}, @@ -936,18 +925,6 @@ const test_t opcode_tests[] = { {"ld b , 128", "ld b, 128", 2 , {0x06, 0x80}}, {"ld b, 'A'", "ld b, 'A'", 2, {0x06, 0x41}}, {"ld b , 'A'", "ld b, 'A'", 2 , {0x06, 0x41}}, - {"ld b, label1-label", "ld b, label1-label", 2, {0x06, 0x05, 0x00, - 0x04}}, - {"ld b , label1 - label", "ld b, label1-label", 2, {0x06, 0x05, 0x00, - 0x04}}, - {"ld b, label1-verylonglabel", "ld b, label1-verylonglabel", 2, - {0x06, 0x05, 0x00, 0x04}}, - {"ld b , label1 - verylonglabel", "ld b, label1-verylonglabel", 2, - {0x06, 0x05, 0x00, 0x04}}, - {"ld b, verylonglabel-label1", "ld b, verylonglabel-label1", 2, - {0x06, 0x0, 0x05, 0x08}}, - {"ld b , verylonglabel - label1", "ld b, verylonglabel-label1", 2, - {0x06, 0x0, 0x05, 0x08}}, {"ld b, =label", "ld b, =label", 2, {0x06, 0x0}}, {"ld b ,= label", "ld b, =label", 2 , {0x06, 0x0}}, @@ -962,19 +939,9 @@ const test_t opcode_tests[] = { {"ld bc, (label)", "ld bc, (label)", 4, {0xED, 0x4B, 0x00, 0x0}}, {"ld bc , ( label )", "ld bc, (label)", 4, {0xED, 0x4B, 0x00, 0x0}}, {"ld bc, label", "ld bc, label", 3, {0x1, 0x00, 0x00}}, + {"ld bc, verylonglabel1", "ld bc, verylonglabel1", 3, + {0x1, 0x01, 0x00, 0x00}}, {"ld bc, label", "ld bc, label", 3, {0x1, 0x00, 0x00}}, - {"ld bc, label1-label", "ld bc, label1-label", 3, {0x1, 0x05, 0x00, - 0x04}}, - {"ld bc , label1 - label", "ld bc, label1-label", 3, {0x1, 0x05, 0x00, - 0x04}}, - {"ld bc, label1-verylonglabel", "ld bc, label1-verylonglabel", 3, - {0x1, 0x05, 0x00, 0x04}}, - {"ld bc , label1 - verylonglabel", "ld bc, label1-verylonglabel", 3, - {0x1, 0x05, 0x00, 0x04}}, - {"ld bc, verylonglabel-label1", "ld bc, verylonglabel-label1", 3, - {0x1, 0x0, 0x05, 0x08}}, - {"ld bc , verylonglabel - label1", "ld bc, verylonglabel-label1", 3, - {0x1, 0x0, 0x05, 0x08}}, {"ld bc, =label", "ld bc, =label", 3, {0x1, 0x00, 0x00}}, {"ld bc,= label", "ld bc, =label", 3, {0x1, 0x00, 0x00}}, {"ld bc, (=label1)", "ld bc, (=label1)", 4, {0xED, 0x4B, 0x05, 0x0}}, @@ -1020,18 +987,6 @@ const test_t opcode_tests[] = { {"ld c , 128", "ld c, 128", 2 , {0x0E, 0x80}}, {"ld c, 'A'", "ld c, 'A'", 2, {0x0E, 0x41}}, {"ld c , 'A'", "ld c, 'A'", 2 , {0x0E, 0x41}}, - {"ld c, label1-label", "ld c, label1-label", 2, {0x0E, 0x05, 0x00, - 0x04}}, - {"ld c , label1 - label", "ld c, label1-label", 2, {0x0E, 0x05, 0x00, - 0x04}}, - {"ld c, label1-verylonglabel", "ld c, label1-verylonglabel", 2, - {0x0E, 0x05, 0x00, 0x04}}, - {"ld c , label1 - verylonglabel", "ld c, label1-verylonglabel", 2, - {0x0E, 0x05, 0x00, 0x04}}, - {"ld c, verylonglabel-label1", "ld c, verylonglabel-label1", 2, - {0x0E, 0x0, 0x05, 0x08}}, - {"ld c , verylonglabel - label1", "ld c, verylonglabel-label1", 2, - {0x0E, 0x0, 0x05, 0x08}}, {"ld c, =label", "ld c, =label", 2, {0x0E, 0x0}}, {"ld c,= label", "ld c, =label", 2, {0x0E, 0x0}}, @@ -1073,18 +1028,6 @@ const test_t opcode_tests[] = { {"ld d , 128", "ld d, 128", 2 , {0x16, 0x80}}, {"ld d, 'A'", "ld d, 'A'", 2, {0x16, 0x41}}, {"ld d , 'A'", "ld d, 'A'", 2 , {0x16, 0x41}}, - {"ld d, label1-label", "ld d, label1-label", 2, {0x16, 0x05, 0x00, - 0x04}}, - {"ld d , label1 - label", "ld d, label1-label", 2, {0x16, 0x05, 0x00, - 0x04}}, - {"ld d, label1-verylonglabel", "ld d, label1-verylonglabel", 2, - {0x16, 0x05, 0x00, 0x04}}, - {"ld d , label1 - verylonglabel", "ld d, label1-verylonglabel", 2, - {0x16, 0x05, 0x00, 0x04}}, - {"ld d, verylonglabel-label1", "ld d, verylonglabel-label1", 2, - {0x16, 0x0, 0x05, 0x08}}, - {"ld d , verylonglabel - label1", "ld d, verylonglabel-label1", 2, - {0x16, 0x0, 0x05, 0x08}}, {"ld d, =label", "ld d, =label", 2, {0x16, 0x0}}, {"ld d ,= label", "ld d, =label", 2 , {0x16, 0x0}}, @@ -1102,18 +1045,8 @@ const test_t opcode_tests[] = { {"ld de , ( label )", "ld de, (label)", 4, {0xED, 0x5B, 0x00, 0x0}}, {"ld de, label", "ld de, label", 3, {0x11, 0x00, 0x00}}, {"ld de, label", "ld de, label", 3, {0x11, 0x00, 0x00}}, - {"ld de, label1-label", "ld de, label1-label", 3, {0x11, 0x05, 0x00, - 0x04}}, - {"ld de , label1 - label", "ld de, label1-label", 3, {0x11, 0x05, 0x00, - 0x04}}, - {"ld de, label1-verylonglabel", "ld de, label1-verylonglabel", 3, - {0x11, 0x05, 0x00, 0x04}}, - {"ld de , label1 - verylonglabel", "ld de, label1-verylonglabel", 3, - {0x11, 0x05, 0x00, 0x04}}, - {"ld de, verylonglabel-label1", "ld de, verylonglabel-label1", 3, - {0x11, 0x0, 0x05, 0x08}}, - {"ld de , verylonglabel - label1", "ld de, verylonglabel-label1", 3, - {0x11, 0x0, 0x05, 0x08}}, + {"ld de, verylonglabel1", "ld de, verylonglabel1", 3, + {0x11, 0x01, 0x00, 0x00}}, {"ld de, =label", "ld de, =label", 3, {0x11, 0x00, 0x00}}, {"ld de,= label", "ld de, =label", 3, {0x11, 0x00, 0x00}}, {"ld de, =label1", "ld de, =label1", 3, {0x11, 0x05, 0x00}}, @@ -1162,18 +1095,6 @@ const test_t opcode_tests[] = { {"ld e , 128", "ld e, 128", 2 , {0x1E, 0x80}}, {"ld e, 'B'", "ld e, 'B'", 2, {0x1E, 0x42}}, {"ld e , 'B'", "ld e, 'B'", 2 , {0x1E, 0x42}}, - {"ld e, label1-label", "ld e, label1-label", 2, {0x1E, 0x05, 0x00, - 0x04}}, - {"ld e , label1 - label", "ld e, label1-label", 2, {0x1E, 0x05, 0x00, - 0x04}}, - {"ld e, label1-verylonglabel", "ld e, label1-verylonglabel", 2, - {0x1E, 0x05, 0x00, 0x04}}, - {"ld e , label1 - verylonglabel", "ld e, label1-verylonglabel", 2, - {0x1E, 0x05, 0x00, 0x04}}, - {"ld e, verylonglabel-label1", "ld e, verylonglabel-label1", 2, - {0x1E, 0x0, 0x05, 0x08}}, - {"ld e , verylonglabel - label1", "ld e, verylonglabel-label1", 2, - {0x1E, 0x0, 0x05, 0x08}}, {"ld e, =label", "ld e, =label", 2, {0x1E, 0x0}}, {"ld e,= label", "ld e, =label", 2, {0x1E, 0x0}}, {"ld e,=label1", "ld e, =label1", 2, {0x1E, 0x5}}, @@ -1216,18 +1137,6 @@ const test_t opcode_tests[] = { {"ld h , -2", "ld h, -2", 2 , {0x26, 0xFE}}, {"ld h, 'C'", "ld h, 'C'", 2, {0x26, 0x43}}, {"ld h , 'C'", "ld h, 'C'", 2 , {0x26, 0x43}}, - {"ld h, label1-label", "ld h, label1-label", 2, {0x26, 0x05, 0x00, - 0x04}}, - {"ld h , label1 - label", "ld h, label1-label", 2, {0x26, 0x05, 0x00, - 0x04}}, - {"ld h, label1-verylonglabel", "ld h, label1-verylonglabel", 2, - {0x26, 0x05, 0x00, 0x04}}, - {"ld h , label1 - verylonglabel", "ld h, label1-verylonglabel", 2, - {0x26, 0x05, 0x00, 0x04}}, - {"ld h, verylonglabel-label1", "ld h, verylonglabel-label1", 2, - {0x26, 0x0, 0x05, 0x08}}, - {"ld h , verylonglabel - label1", "ld h, verylonglabel-label1", 2, - {0x26, 0x0, 0x05, 0x08}}, {"ld h, =label", "ld h, =label", 2, {0x26, 0x0}}, {"ld h,= label", "ld h, =label", 2, {0x26, 0x0}}, @@ -1245,18 +1154,8 @@ const test_t opcode_tests[] = { {"ld hl , ( label )", "ld hl, (label)", 3, {0x2A, 0x00, 0x0}}, {"ld hl, label", "ld hl, label", 3, {0x21, 0x00, 0x00}}, {"ld hl, label", "ld hl, label", 3, {0x21, 0x00, 0x00}}, - {"ld hl, label1-label", "ld hl, label1-label", 3, {0x21, 0x05, 0x00, - 0x04}}, - {"ld hl , label1 - label", "ld hl, label1-label", 3, {0x21, 0x05, 0x00, - 0x04}}, - {"ld hl, label1-verylonglabel", "ld hl, label1-verylonglabel", 3, - {0x21, 0x05, 0x00, 0x04}}, - {"ld hl , label1 - verylonglabel", "ld hl, label1-verylonglabel", 3, - {0x21, 0x05, 0x00, 0x04}}, - {"ld hl, verylonglabel-label1", "ld hl, verylonglabel-label1", 3, - {0x21, 0x0, 0x05, 0x08}}, - {"ld hl , verylonglabel - label1", "ld hl, verylonglabel-label1", 3, - {0x21, 0x0, 0x05, 0x08}}, + {"ld hl, verylonglabel1", "ld hl, verylonglabel1", 3, + {0x21, 0x01, 0x00, 0x00}}, {"ld hl, =label", "ld hl, =label", 3, {0x21, 0x00, 0x00}}, {"ld hl,= label", "ld hl, =label", 3, {0x21, 0x00, 0x00}}, {"ld hl, =label1", "ld hl, =label1", 3, {0x21, 0x05, 0x00}}, @@ -1291,6 +1190,11 @@ const test_t opcode_tests[] = { {"ld ix , 'A'", "ld ix, 'A'", 4, {0xDD, 0x21, 0x41, 0x00}}, {"ld ix, label", "ld ix, label", 4, {0xDD, 0x21, 0x0, 0x00}}, {"ld ix , label", "ld ix, label", 4, {0xDD, 0x21, 0x0, 0x00}}, + {"ld ix, verylonglabel1", "ld ix, verylonglabel1", 4, + {0xDD, 0x21, 0x01, 0x00}}, + {"ld ix ,verylonglabel1", "ld ix, verylonglabel1", 4, + {0xDD, 0x21, 0x01, 0x00}}, + {"ld ix, =label", "ld ix, =label", 4, {0xDD, 0x21, 0x0, 0x00}}, {"ld ix ,= label", "ld ix, =label", 4, {0xDD, 0x21, 0x0, 0x00}}, {"ld ix, =label1", "ld ix, =label1", 4, {0xDD, 0x21, 0x5, 0x00}}, @@ -1319,6 +1223,10 @@ const test_t opcode_tests[] = { {"ld iy , -2", "ld iy, -2", 4, {0xFD, 0x21, 0xFE, 0xFF}}, {"ld iy, label", "ld iy, label", 4, {0xFD, 0x21, 0x0, 0x00}}, {"ld iy , label", "ld iy, label", 4, {0xFD, 0x21, 0x0, 0x00}}, + {"ld iy, verylonglabel1", "ld iy, verylonglabel1", 4, + {0xFD, 0x21, 0x01, 0x00}}, + {"ld iy , verylonglabel1", "ld iy, verylonglabel1", 4, + {0xFD, 0x21, 0x01, 0x00}}, {"ld iy, =label", "ld iy, =label", 4, {0xFD, 0x21, 0x0, 0x00}}, {"ld iy ,= label", "ld iy, =label", 4, {0xFD, 0x21, 0x0, 0x00}}, {"ld iy, =label1", "ld iy, =label1", 4, {0xFD, 0x21, 0x5, 0x00}}, @@ -1396,18 +1304,10 @@ const test_t opcode_tests[] = { {"ld sp , 'A'", "ld sp, 'A'", 3, {0x31, 0x41, 0x0}}, {"ld sp, label", "ld sp, label", 3, {0x31, 0x00, 0x00}}, {"ld sp , label", "ld sp, label", 3, {0x31, 0x0, 0x00}}, - {"ld sp, label1-label", "ld sp, label1-label", 3, {0x31, 0x05, 0x00, - 0x04}}, - {"ld sp , label1 - label", "ld sp, label1-label", 3, {0x31, 0x05, 0x00, - 0x04}}, - {"ld sp, label1-verylonglabel", "ld sp, label1-verylonglabel", 3, - {0x31, 0x05, 0x00, 0x04}}, - {"ld sp , label1 - verylonglabel", "ld sp, label1-verylonglabel", 3, - {0x31, 0x05, 0x00, 0x04}}, - {"ld sp, verylonglabel-label1", "ld sp, verylonglabel-label1", 3, - {0x31, 0x0, 0x05, 0x08}}, - {"ld sp , verylonglabel - label1", "ld sp, verylonglabel-label1", 3, - {0x31, 0x0, 0x05, 0x08}}, + {"ld sp, verylonglabel1", "ld sp, verylonglabel1", 3, + {0x31, 0x01, 0x00}}, + {"ld sp ,verylonglabel1", "ld sp, verylonglabel1", 3, + {0x31, 0x01, 0x00}}, {"ld sp, =label", "ld sp, =label", 3, {0x31, 0x00, 0x00}}, {"ld sp ,= label", "ld sp, =label", 3, {0x31, 0x0, 0x00}}, {"ld sp, =label1", "ld sp, =label1", 3, {0x31, 0x05, 0x00}}, @@ -2302,11 +2202,6 @@ const test_t opcode_tests[] = { 4, {0x7F, 0xFE, 0xFD, 0x4}}, {"db 127,-2, -3,4", "db 127,-2,-3,4", 4, {0x7F, 0xFE, 0xFD, 0x4}}, - {"db label1-label", "db label1-label", 1, {0x05, 0x00, 0x04, 0x00}}, - {"db label1-verylonglabel", "db label1-verylonglabel", 1, - {0x05, 0x00, 0x04, 0x00}}, - {"db verylonglabel-label1", "db verylonglabel-label1", 1, - {0x00, 0x05, 0x08, 0x00}}, {"db =label1", "db =label1", 1, {0x5}}, {"db =verylonglabel1", "db =verylonglabel1", 1, {0x1}}, @@ -2335,11 +2230,6 @@ const test_t opcode_tests[] = { {"dw -32768, -32768", "dw -32768,-32768", 4, {0x00, 0x80, 0x00, 0x80}}, {"dw label", "dw label", 2, {0x00, 0x00}}, - {"dw label1-label", "dw label1-label", 2, {0x05, 0x00, 0x04, 0x00}}, - {"dw label1-verylonglabel", "dw label1-verylonglabel", 2, - {0x05, 0x00, 0x04, 0x00}}, - {"dw verylonglabel-label1", "dw verylonglabel-label1", 2, - {0x00, 0x05, 0x08, 0x00}}, {"dw =label1", "dw =label1", 2, {0x5}}, {"dw =verylonglabel1", "dw =verylonglabel1", 2, {0x1}}, @@ -2425,12 +2315,6 @@ const format_test_t format_tests[] = { SPECASM_LINE_TYPE_STR_AMP_LONG}, { "@unterminated string", "@unterminated string ", SPECASM_LINE_TYPE_STR_AMP_LONG}, - {"dw label1-label", "dw label1-label", SPECASM_LINE_TYPE_DW_SUB}, - {"dw label1 - label", "dw label1-label", - SPECASM_LINE_TYPE_DW_SUB}, - {"db label1-label", "db label1-label", SPECASM_LINE_TYPE_DB_SUB}, - {"db label1 - label", "db label1-label", - SPECASM_LINE_TYPE_DB_SUB}, {"ds $10, 10", "ds $10, 10", SPECASM_LINE_TYPE_DS}, {"- sub.x", "-sub.x", SPECASM_LINE_TYPE_INC_SHORT}, {"-/a/very/long/path", "-/a/very/long/path", @@ -2693,7 +2577,7 @@ const bad_test_t bad_tests[] = { {"ld a, (=)", SPECASM_ERROR_BAD_EXPRESSION }, {"ld a, bc", SPECASM_ERROR_BAD_REG }, {"ld a, $100", SPECASM_ERROR_NUM_TOO_BIG }, - {"ld a, label", SPECASM_ERROR_BAD_LABEL }, + {"ld a, label", SPECASM_ERROR_BAD_REG }, {"ld 1, 1", SPECASM_ERROR_BAD_REG }, {"ld bc, (label", SPECASM_ERROR_BAD_REG }, {"ld bc, ($10000)", SPECASM_ERROR_NUM_TOO_BIG }, @@ -2727,8 +2611,17 @@ const bad_test_t bad_tests[] = { {"ld sp, (-1)", SPECASM_ERROR_NUM_NEG }, {"ld sp, (=)", SPECASM_ERROR_BAD_EXPRESSION }, {"ld sp, a", SPECASM_ERROR_BAD_REG }, - {"ld ix, label1-label2", SPECASM_ERROR_BAD_LABEL }, - {"ld iy, label1-label2", SPECASM_ERROR_BAD_LABEL }, + {"ld a, label1-label2", SPECASM_ERROR_BAD_REG }, + {"ld b, label1-label2", SPECASM_ERROR_BAD_REG }, + {"ld c, label1-label2", SPECASM_ERROR_BAD_REG }, + {"ld d, label1-label2", SPECASM_ERROR_BAD_REG }, + {"ld e, label1-label2", SPECASM_ERROR_BAD_REG }, + {"ld hl, label1-label2", SPECASM_ERROR_BAD_COMMENT }, + {"ld bc, label1-label2", SPECASM_ERROR_BAD_COMMENT }, + {"ld de, label1-label2", SPECASM_ERROR_BAD_COMMENT }, + {"ld sp, label1-label2", SPECASM_ERROR_BAD_COMMENT }, + {"ld ix, label1-label2", SPECASM_ERROR_BAD_COMMENT }, + {"ld iy, label1-label2", SPECASM_ERROR_BAD_COMMENT }, {"ld a, =", SPECASM_ERROR_BAD_EXPRESSION }, {"ld hl, (=(label1+1", SPECASM_ERROR_BAD_EXPRESSION }, @@ -2992,7 +2885,8 @@ const bad_test_t bad_tests[] = { {"xor f", SPECASM_ERROR_BAD_REG }, {"xor (ix+=label)", SPECASM_ERROR_BAD_NUM }, - {"db label", SPECASM_ERROR_BAD_LABEL }, + {"db label", SPECASM_ERROR_BAD_NUM }, + {"db label-label1", SPECASM_ERROR_BAD_NUM }, {"db 256", SPECASM_ERROR_NUM_TOO_BIG }, {"db 255, -1", SPECASM_ERROR_BAD_NUM }, {"db -1, 255", SPECASM_ERROR_BAD_NUM }, @@ -3000,7 +2894,7 @@ const bad_test_t bad_tests[] = { {"db -1, -2, -3, $20", SPECASM_ERROR_BAD_NUM }, {"db 'A', 1, 2", SPECASM_ERROR_BAD_NUM }, {"db 1, 'A', 2", SPECASM_ERROR_BAD_NUM }, - {"db", SPECASM_ERROR_BAD_LABEL }, + {"db", SPECASM_ERROR_BAD_NUM }, {"db $100", SPECASM_ERROR_NUM_TOO_BIG }, {"dw", SPECASM_ERROR_BAD_LABEL }, {"dw 65536", SPECASM_ERROR_NUM_TOO_BIG }, @@ -3008,6 +2902,7 @@ const bad_test_t bad_tests[] = { {"dw $1000, -1", SPECASM_ERROR_BAD_NUM }, {"dw -1, 32768", SPECASM_ERROR_BAD_NUM }, {"dw 'A', $fff", SPECASM_ERROR_BAD_NUM }, + {"dw label-label1", SPECASM_ERROR_BAD_COMMENT }, {"ds 0, $10", SPECASM_ERROR_BAD_NUM }, {"ds 1000, $100", SPECASM_ERROR_NUM_TOO_BIG }, From 2af2abb072fea75a97fda9a50a67f2c634b7edf9 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 26 Nov 2023 22:31:44 +0100 Subject: [PATCH 03/16] Move 48kb build directories To build/48 to prepare the way for adding a separate set of Next build files. Signed-off-by: Mark Ryan --- .gitignore | 4 ++-- README.md | 6 ++--- build/{ => 48/specasm}/Make.include | 0 build/{ => 48/specasm}/Makefile | 12 +++++----- unitzx/Make.include | 14 ----------- unitzx/Makefile | 37 ----------------------------- 6 files changed, 11 insertions(+), 62 deletions(-) rename build/{ => 48/specasm}/Make.include (100%) rename build/{ => 48/specasm}/Makefile (85%) delete mode 100644 unitzx/Make.include delete mode 100644 unitzx/Makefile diff --git a/.gitignore b/.gitignore index 9b5a26c..e491629 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,6 @@ SAIMPORT.X SAEXPORT.X build.sh compile.sh -build/release -build/unitzx +build/48/specasm/release +build/48/unit .DS_Store diff --git a/README.md b/README.md index 93b2141..6b7db43 100644 --- a/README.md +++ b/README.md @@ -62,14 +62,14 @@ Once the linker has finished a binary file will be created. The name of the fil ## Building Specasm -Specasm is built with [z88dk](https://github.com/z88dk/z88dk) and GNU Make. Install a recent version and then +Specasm is built with [z88dk](https://github.com/z88dk/z88dk) and GNU Make. To build Specasm for the 48k Spectrum clone the repoistory and type ``` -cd build +cd build/48/specasm make -j ``` -And then wait. All the tap and dotx files will be created in the build directory. +and then wait. All the tap and dotx files will be created in the build directory. To create a zip file with all the files that need to be copied onto the spectrum, type diff --git a/build/Make.include b/build/48/specasm/Make.include similarity index 100% rename from build/Make.include rename to build/48/specasm/Make.include diff --git a/build/Makefile b/build/48/specasm/Makefile similarity index 85% rename from build/Makefile rename to build/48/specasm/Makefile index 0b3f8db..9fbce6c 100644 --- a/build/Makefile +++ b/build/48/specasm/Makefile @@ -1,4 +1,4 @@ -VPATH=../src +VPATH=../../../src .PHONY: all all: specasm.tap salink.tap SAEXPORT SAIMPORT @@ -54,7 +54,7 @@ SAIMPORT = \ specasm.tap: $(SPECASM) $(CC) $(CFLAGS) -zorg=24310 -startup=31 -o specasm-bare $^ -create-app - cat ../bas/SPECLD.TAP specasm-bare.tap > specasm.tap + cat ../../../bas/SPECLD.TAP specasm-bare.tap > specasm.tap salink.tap: $(SALINK) $(CC) $(CFLAGS) -zorg=24000 -startup=31 -o salink $^ -create-app @@ -74,8 +74,8 @@ release: - rm -rf release mkdir -p release/specasm cp specasm.tap salink.tap release/specasm - cp ../COPYING release/specasm + cp ../../../COPYING release/specasm cp SAIMPORT SAEXPORT *.X release/specasm - cp ../bas/INSTALL release/specasm - cp ../bas/REMOVE release/specasm - cd release && zip -r specasm.zip specasm + cp ../../../bas/INSTALL release/specasm + cp ../../../bas/REMOVE release/specasm + cd release && zip -r specasm48.zip specasm diff --git a/unitzx/Make.include b/unitzx/Make.include deleted file mode 100644 index 14b16c1..0000000 --- a/unitzx/Make.include +++ /dev/null @@ -1,14 +0,0 @@ -error.o: ../src/error.c ../src/error.h -state_base.o: ../src/state_base.c ../src/peer.h ../src/error.h ../src/peer_unit.h \ - ../src/line.h ../src/state_base.h ../src/strings.h -line.o: ../src/line.c ../src/peer.h ../src/error.h ../src/peer_unit.h ../src/line.h \ - ../src/state.h ../src/state_base.h ../src/strings.h -state.o: ../src/state.c ../src/state.h ../src/state_base.h ../src/line.h ../src/error.h \ - ../src/strings.h -peer_file_zx.o: ../src/peer_file.h ../src/error.h -util_print_zx.o: ../src/util_print_zx.h -peer_unit.o: ../src/peer_unit.c ../src/peer.h ../src/error.h ../src/peer_unit.h \ - ../src/line.h ../src/state.h ../src/state_base.h ../src/strings.h -unittests_zx.o: ../src/unittests_zx.c ../src/error.h ../src/line.h \ - ../src/state.h ../src/state_base.h ../src/strings.h ../src/test_content.h ../src/test_content_zx.h \ - ../src/peer.h ../src/peer_file.h ../src/peer_unit.h diff --git a/unitzx/Makefile b/unitzx/Makefile deleted file mode 100644 index 586664a..0000000 --- a/unitzx/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -VPATH=../src - -.PHONY: all -all: unitzx.tap - -CC=zcc -CFLAGS=+zx -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warning 85" -clib=sdcc_iy -DUNITTESTS - -include Make.include - -UNITZX = \ - error.o \ - state_base.o \ - line_common.o \ - line_dump.o \ - line_parse.o \ - state_dump.o \ - state_parse.o \ - peer_file_zx.o \ - util_print_zx.o \ - peer_unit.o \ - unittests_zx.o - -unitzx.tap: $(UNITZX) - $(CC) $(CFLAGS) -zorg=24000 -startup=31 -o unitzx $^ -create-app - -clean: - - rm -rf unitzx - - rm *.X *.o *.bin *.tap - -.PHONY: tests -tests: - - rm -rf tests - mkdir tests - cd .. && make -f Makefile test_content_zx - ../test_content_zx tests - cp unitzx.tap tests From 8c5cf4951f777320259794c4078f88314bf96b0e Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Thu, 2 Nov 2023 23:16:00 +0100 Subject: [PATCH 04/16] Add Spectrum Next support New binaries are created for the Next. These are all dotn files rather than .tap files. All the new Next opcodes are supported in addition to one pseudo op, nbrk, which is aliases to nextreg 2, 8 which starts the next debugger. Next .x file and 48Kb .x files are sadly not compatible. The Next opcodes are not available in the 48k builds. No new functionality is added for now to the Next dotn files, even though more memory is available. salink and specasm run at 28Mhz but restore the CPU to the user selected speed on exit. They also force the Next into the 48k screen mode, which is restored naturally by the Next when the programs exit. Signed-off-by: Mark Ryan --- .github/workflows/c-cpp.yml | 5 + .gitignore | 8 +- Makefile | 8 + build/48/specasm/Make.include | 79 +- build/48/specasm/Makefile | 8 + build/48/unit/Make.include | 26 + build/48/unit/Makefile | 40 + build/next/specasm/Make.include | 59 ++ build/next/specasm/Makefile | 109 +++ build/next/specasm/zpragma.inc | 1 + build/next/unit/Make.include | 51 ++ build/next/unit/Makefile | 51 ++ build/next/unit/zpragma.inc | 1 + docs/specasm.md | 112 +-- src/editor.c | 1 + src/expression.c | 15 +- src/ld_parse.c | 498 ++++++++++++ src/ld_parse.h | 22 + src/line.h | 70 +- src/line_common.c | 37 + src/line_dump.c | 320 +++++--- src/line_dump_common.c | 100 +++ src/line_parse.c | 1234 +++++++---------------------- src/line_parse_common.c | 502 ++++++++++++ src/line_parse_common.h | 49 ++ src/link_obj.c | 1144 +++++++++++++++++++++++++++ src/link_obj.h | 26 + src/map.c | 179 +++++ src/map.h | 22 + src/peer.h | 4 +- src/peer_file.h | 16 +- src/peer_file_next.c | 67 ++ src/peer_file_zx.c | 2 +- src/peer_next.c | 70 ++ src/peer_zx.h | 8 +- src/salink.c | 1304 +------------------------------ src/salink.h | 27 + src/salink_trampolines.c | 81 ++ src/specasm_next.c | 94 +++ src/specasm_trampolines.c | 104 +++ src/test_content.c | 131 +++- src/unittests_zx.c | 27 +- src/util_print_next.c | 181 +++++ tests/test_equ_next/equ.s | 11 + tests/test_equ_next/test.sh | 60 ++ tests/test_sub_negative/test.sh | 5 + tests/test_sub_too_big/test.sh | 5 + tests/test_subtraction/test.sh | 5 + 48 files changed, 4581 insertions(+), 2398 deletions(-) create mode 100644 build/48/unit/Make.include create mode 100644 build/48/unit/Makefile create mode 100644 build/next/specasm/Make.include create mode 100644 build/next/specasm/Makefile create mode 100644 build/next/specasm/zpragma.inc create mode 100644 build/next/unit/Make.include create mode 100644 build/next/unit/Makefile create mode 100644 build/next/unit/zpragma.inc create mode 100644 src/ld_parse.c create mode 100644 src/ld_parse.h create mode 100644 src/line_dump_common.c create mode 100644 src/line_parse_common.c create mode 100644 src/line_parse_common.h create mode 100644 src/link_obj.c create mode 100644 src/link_obj.h create mode 100644 src/map.c create mode 100644 src/map.h create mode 100644 src/peer_file_next.c create mode 100644 src/peer_next.c create mode 100644 src/salink_trampolines.c create mode 100644 src/specasm_next.c create mode 100644 src/specasm_trampolines.c create mode 100644 src/util_print_next.c create mode 100644 tests/test_equ_next/equ.s create mode 100755 tests/test_equ_next/test.sh diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 233c7f4..e713d88 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -25,3 +25,8 @@ jobs: make clean CFLAGS="-g -fsanitize=address -fno-omit-frame-pointer -Wall -Werror" make ./unittests + - name: no-next + run: | + make clean + CFLAGS="-DSPECASM_TARGET_NEXT_OPCODES" make -j + cd tests && SPECASM_TARGET_NEXT_OPCODES=1 ./tests.sh diff --git a/.gitignore b/.gitignore index e491629..0dfca08 100644 --- a/.gitignore +++ b/.gitignore @@ -12,10 +12,16 @@ specasm-bare test_content_zx SAIMPORT SAEXPORT +SPECASM +*.map +SALINK SAIMPORT.X SAEXPORT.X +UNITNEXT +UNITZX build.sh compile.sh build/48/specasm/release -build/48/unit +build/48/unit/tests +build/next/unit/tests .DS_Store diff --git a/Makefile b/Makefile index 407806d..bd14ec1 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,10 @@ SRCS =\ editor.c \ editor_tests.c \ editor_test_content.c \ + ld_parse.c \ line_parse.c \ + line_parse_common.c \ + line_dump_common.c \ line_dump.c \ peer_unit.c \ peer_posix_screen.c \ @@ -28,16 +31,21 @@ POSIX = \ peer_text_screen.c SAIMPORT =\ + ld_parse.c \ line_parse.c \ + line_parse_common.c \ saimport.c \ state_parse.c SAEXPORT =\ line_dump.c \ + line_dump_common.c \ state_dump.c \ saexport.c SALINK =\ + link_obj.c \ + map.c \ salink.c \ expression.c diff --git a/build/48/specasm/Make.include b/build/48/specasm/Make.include index c3a8eb8..788de1f 100644 --- a/build/48/specasm/Make.include +++ b/build/48/specasm/Make.include @@ -1,33 +1,52 @@ -error.o: ../src/error.c ../src/error.h -expression.o: ../src/expression.c ../src/error.h ../src/expression.h ../src/salink.h \ - ../src/line.h ../src/state_base.h ../src/strings.h -state_base.o: ../src/state_base.c ../src/peer.h ../src/error.h ../src/peer_zx.h \ - ../src/line.h ../src/state_base.h ../src/strings.h -state_dump.o: ../src/state_dump.c ../src/state.h ../src/state_base.h ../src/line.h \ - ../src/error.h ../src/strings.h -state_parse.o: ../src/state_parse.c ../src/state.h ../src/state_base.h ../src/line.h \ - ../src/error.h ../src/strings.h -line_common.o: ../src/line_common.c ../src/line_common.h ../src/line.h ../src/error.h -line_dump.o: ../src/line_dump.c ../src/line_common.h ../src/line.h ../src/error.h \ - ../src/peer.h ../src/peer_zx.h ../src/state.h ../src/state_base.h ../src/strings.h -line_parse.o: ../src/line_parse.c ../src/line_common.h ../src/line.h ../src/error.h \ - ../src/peer.h ../src/peer_zx.h ../src/state.h ../src/state_base.h ../src/strings.h -editor.o: ../src/editor.c ../src/editor.h ../src/peer.h ../src/error.h \ - ../src/peer_zx.h ../src/line.h ../src/state.h ../src/state_base.h ../src/strings.h \ - ../src/util_print_zx.h -saimport.o: ../src/saimport.c ../src/peer.h ../src/error.h ../src/peer_zx.h \ - ../src/line.h ../src/peer_file.h ../src/state.h ../src/state_base.h ../src/strings.h \ - ../src/util_print_zx.h -saexport.o: ../src/saexport.c ../src/peer.h ../src/error.h ../src/peer_zx.h \ - ../src/line.h ../src/peer_file.h ../src/state.h ../src/state_base.h ../src/strings.h \ - ../src/util_print_zx.h -salink.o: ../src/salink.c ../src/peer.h ../src/error.h ../src/peer_zx.h ../src/line.h \ - ../src/peer_file.h ../src/state_base.h ../src/strings.h \ - ../src/util_print_zx.h -peer_zx.o: ../src/peer_zx.c ../src/peer.h ../src/state.h ../src/state_base.h ../src/line.h \ - ../src/strings.h ../src/error.h ../src/util_print_zx.h -peer_file_zx.o: ../src/peer_file_zx.c ../src/error.h ../src/peer_file.h -util_print_zx.o: ../src/util_print_zx.h +error.o: error.c error.h +expression.o: expression.c error.h expression.h salink.h \ + line.h state_base.h strings.h +state_base.o: state_base.c peer.h error.h peer_zx.h \ + line.h state_base.h strings.h +state_dump.o: state_dump.c state.h state_base.h line.h \ + error.h strings.h +state_parse.o: state_parse.c state.h state_base.h line.h \ + error.h strings.h +ld_parse.o: ld_parse.c line_common.h line.h error.h \ + line_parse_common.h peer.h peer_unit.h state.h \ + state_base.h strings.h +ld_parse_banked.o: ld_parse.c line_common.h line.h error.h \ + line_parse_common.h peer.h peer_unit.h state.h \ + state_base.h strings.h +line_common.o: line_common.c line_common.h line.h error.h +line_dump.o: line_dump.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +line_dump_common.o: line_dump_common.c line_common.h line.h \ + error.h peer.h peer_zx.h state.h state_base.h \ + strings.h +line_parse.o: line_parse.c line_common.h line.h error.h\ + peer.h peer_zx.h state.h state_base.h strings.h +line_parse_common.o: line_parse_common.c line.h error.h \ + line_common.h line_parse_common.h state.h state_base.h \ + strings.h +link_obj.o: link_obj.c expression.h salink.h line.h \ + error.h peer_file.h map.h peer.h peer_unit.h \ + state_base.h strings.h +map.o: map.c map.h peer.h error.h peer_zx.h \ + line.h salink.h peer_file.h state_base.h strings.h +editor.o: editor.c editor.h peer.h error.h \ + peer_zx.h line.h state.h state_base.h strings.h \ + util_print_zx.h +saimport.o: saimport.c peer.h error.h peer_zx.h \ + line.h peer_file.h state.h state_base.h strings.h \ + util_print_zx.h +saexport.o: saexport.c peer.h error.h peer_zx.h \ + line.h peer_file.h state.h state_base.h strings.h \ + util_print_zx.h +salink.o: salink.c peer.h error.h peer_zx.h line.h \ + peer_file.h state_base.h strings.h \ + util_print_zx.h +specasm.o: specasm.c editor.h peer.h error.h \ + line.h peer_file.h state.h state_base.h strings.h +peer_zx.o: peer_zx.c peer.h state.h state_base.h line.h \ + strings.h error.h util_print_zx.h +peer_file_zx.o: peer_file_zx.c error.h peer_file.h +util_print_zx.o: util_print_zx.h %.o: %.c zcc $(CFLAGS) -o $@ -c $< diff --git a/build/48/specasm/Makefile b/build/48/specasm/Makefile index 9fbce6c..e006e4e 100644 --- a/build/48/specasm/Makefile +++ b/build/48/specasm/Makefile @@ -10,9 +10,12 @@ include Make.include SPECASM = \ specasm.o \ + ld_parse.o \ line_common.o \ line_dump.o \ + line_dump_common.o \ line_parse.o \ + line_parse_common.o \ error.o \ state_base.o \ state_dump.o \ @@ -25,6 +28,8 @@ SALINK = \ salink.o \ error.o \ expression.o \ + link_obj.o \ + map.o \ state_base.o \ peer_file_zx.o \ util_print_zx.o \ @@ -35,6 +40,7 @@ SAEXPORT = \ line_common.o \ state_base.o \ line_dump.o \ + line_dump_common.o \ state_dump.o \ peer_file_zx.o \ util_print_zx.o \ @@ -44,8 +50,10 @@ SAEXPORT = \ SAIMPORT = \ error.o \ state_base.o \ + ld_parse.o \ line_common.o \ line_parse.o \ + line_parse_common.o \ state_parse.o \ peer_file_zx.o \ util_print_zx.o \ diff --git a/build/48/unit/Make.include b/build/48/unit/Make.include new file mode 100644 index 0000000..ecdedd2 --- /dev/null +++ b/build/48/unit/Make.include @@ -0,0 +1,26 @@ +error.o: ../src/error.c ../src/error.h +state_base.o: ../src/state_base.c ../src/peer.h ../src/error.h ../src/peer_unit.h \ + ../src/line.h ../src/state_base.h ../src/strings.h +ld_parse.o: ../src/ld_parse.c ../src/line_common.h ../src/line.h ../src/error.h \ + ../src/line_parse_common.h ../src/peer.h ../src/peer_unit.h ../src/state.h \ + ../src/state_base.h ../src/strings.h +line_parse_common.o: ../src/line_parse_common.c ../src/line.h ../src/error.h \ + ../src/line_common.h ../src/line_parse_common.h ../src/state.h ../src/state_base.h \ + ../src/strings.h +line_dump.o: ../src/line_dump.c ../src/line_common.h ../src/line.h ../src/error.h \ + ../src/peer.h ../src/peer_zx.h ../src/state.h ../src/state_base.h ../src/strings.h +line_dump_common.o: ../src/line_dump_common.c ../src/line_common.h ../src/line.h \ + ../src/error.h ../src/peer.h ../src/peer_zx.h ../src/state.h ../src/state_base.h \ + ../src/strings.h +line_common.o: ../src/line_common.c ../src/line_common.h ../src/line.h ../src/error.h +line.o: ../src/line.c ../src/peer.h ../src/error.h ../src/peer_unit.h ../src/line.h \ + ../src/state.h ../src/state_base.h ../src/strings.h +state.o: ../src/state.c ../src/state.h ../src/state_base.h ../src/line.h ../src/error.h \ + ../src/strings.h +peer_file_zx.o: ../src/peer_file.h ../src/error.h +util_print_zx.o: ../src/util_print_zx.h +peer_unit.o: ../src/peer_unit.c ../src/peer.h ../src/error.h ../src/peer_unit.h \ + ../src/line.h ../src/state.h ../src/state_base.h ../src/strings.h +unittests_zx.o: ../src/unittests_zx.c ../src/error.h ../src/line.h \ + ../src/state.h ../src/state_base.h ../src/strings.h ../src/test_content.h ../src/test_content_zx.h \ + ../src/peer.h ../src/peer_file.h ../src/peer_unit.h diff --git a/build/48/unit/Makefile b/build/48/unit/Makefile new file mode 100644 index 0000000..a958147 --- /dev/null +++ b/build/48/unit/Makefile @@ -0,0 +1,40 @@ +VPATH=../../../src + +.PHONY: all +all: unitzx.tap + +CC=zcc +CFLAGS=+zx -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warning 85" -clib=sdcc_iy -DUNITTESTS + +include Make.include + +UNITZX = \ + error.o \ + state_base.o \ + ld_parse.o \ + line_common.o \ + line_dump.o \ + line_dump_common.o \ + line_parse.o \ + line_parse_common.o \ + state_dump.o \ + state_parse.o \ + peer_file_zx.o \ + util_print_zx.o \ + peer_unit.o \ + unittests_zx.o + +unitzx.tap: $(UNITZX) + $(CC) $(CFLAGS) -zorg=24000 -startup=31 -o unitzx $^ -create-app + +clean: + - rm -rf unitzx + - rm *.X *.o *.bin *.tap + +.PHONY: tests +tests: + - rm -rf tests + mkdir tests + cd ../../.. && make -f Makefile test_content_zx + ../../../test_content_zx tests + cp unitzx.tap tests diff --git a/build/next/specasm/Make.include b/build/next/specasm/Make.include new file mode 100644 index 0000000..e41e9e3 --- /dev/null +++ b/build/next/specasm/Make.include @@ -0,0 +1,59 @@ +error.o: error.c error.h +expression_banked.o: expression.c error.h expression.h salink.h \ + line.h state_base.h strings.h +state_base.o: state_base.c peer.h error.h peer_zx.h \ + line.h state_base.h strings.h +state_dump.o: state_dump.c state.h state_base.h line.h \ + error.h strings.h +state_parse.o: state_parse.c state.h state_base.h line.h \ + error.h strings.h +specasm_next.o: specasm_next.c editor.h peer.h error.h \ + line.h peer_file.h state.h state_base.h strings.h +ld_parse.o: ld_parse.c line_common.h line.h error.h \ + line_parse_common.h peer.h peer_unit.h state.h \ + state_base.h strings.h +ld_parse_banked.o: ld_parse.c line_common.h line.h error.h \ + line_parse_common.h peer.h peer_unit.h state.h \ + state_base.h strings.h +line_common.o: line_common.c line_common.h line.h error.h +line_dump.o: line_dump.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +line_dump_common.o: line_dump_common.c line_common.h line.h \ + error.h peer.h peer_zx.h state.h state_base.h \ + strings.h +line_dump_banked.o: line_dump.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +link_obj_banked.o: link_obj.c expression.h salink.h line.h \ + error.h peer_file.h map.h peer.h peer_unit.h \ + state_base.h strings.h +line_parse.o: line_parse.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +line_parse_common.o: line_parse_common.c line.h error.h \ + line_common.h line_parse_common.h state.h state_base.h \ + strings.h +line_parse_banked.o: line_parse.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +map_banked.o: map.c map.h peer.h error.h peer_zx.h \ + line.h salink.h peer_file.h state_base.h strings.h +editor.o: editor.c editor.h peer.h error.h \ + peer_zx.h line.h state.h state_base.h strings.h \ + util_print_zx.h +saimport.o: saimport.c peer.h error.h peer_zx.h \ + line.h peer_file.h state.h state_base.h strings.h \ + util_print_zx.h +saexport.o: saexport.c peer.h error.h peer_zx.h \ + line.h peer_file.h state.h state_base.h strings.h \ + util_print_zx.h +salink.o: salink.c peer.h error.h peer_zx.h line.h \ + peer_file.h state_base.h strings.h \ + util_print_zx.h +salink_trampolines.o: salink_trampolines.c salink.h line.h \ + peer_file.h error.h +specasm_trampolines.o: specasm_trampolines.c line.h error.h +peer_next.o: peer_next.c peer.h state.h state_base.h line.h \ + strings.h error.h util_print_zx.h +peer_file_next.o: peer_file_next.c error.h peer_file.h +util_print_next.o: util_print_next.c util_print_zx.h + +%.o: %.c + zcc $(CFLAGS) -o $@ -c $< diff --git a/build/next/specasm/Makefile b/build/next/specasm/Makefile new file mode 100644 index 0000000..7e068dd --- /dev/null +++ b/build/next/specasm/Makefile @@ -0,0 +1,109 @@ +VPATH=../../../src + +.PHONY: all +all: SPECASM SALINK SAEXPORT SAIMPORT + +CC=zcc +CFLAGS=+zxn -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warning 85" -clib=sdcc_iy -DSPECASM_TARGET_NEXT -DSPECASM_TARGET_NEXT_OPCODES +CZFLAGS=-Cz="--clean --fullsize --main-fence 0xDFE0" + +line_parse_banked.o: line_parse.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_43_H --constsegBANK_43_H --datasegBANK_43_H -c $< + +line_dump_banked.o: line_dump.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_44_H --constsegBANK_44_H --datasegBANK_44_H -c $< + +ld_parse_banked.o: ld_parse.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $< + +expression_banked.o: expression.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_43_H --constsegBANK_43_H --datasegBANK_43_H -c $< + +map_banked.o: map.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_44_H --constsegBANK_44_H --datasegBANK_44_H -c $< + +link_obj_banked.o: link_obj.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $< + +include Make.include + +SPECASM = \ + specasm_next.o \ + specasm_trampolines.o \ + line_common.o \ + ld_parse_banked.o \ + line_dump_banked.o \ + line_dump_common.o \ + line_parse_banked.o \ + line_parse_common.o \ + error.o \ + state_base.o \ + state_dump.o \ + state_parse.o \ + editor.o \ + util_print_next.o \ + peer_next.o + +SALINK = \ + salink.o \ + error.o \ + expression_banked.o \ + link_obj_banked.o \ + map_banked.o \ + salink_trampolines.o \ + state_base.o \ + peer_file_next.o \ + util_print_next.o \ + peer_next.o + +SAEXPORT = \ + error.o \ + line_common.o \ + state_base.o \ + line_dump.o \ + line_dump_common.o \ + state_dump.o \ + peer_file_next.o \ + util_print_zx.o \ + peer_next.o \ + saexport.o + +SAIMPORT = \ + error.o \ + state_base.o \ + ld_parse.o \ + line_common.o \ + line_parse.o \ + line_parse_common.o \ + state_parse.o \ + peer_file_next.o \ + util_print_zx.o \ + peer_next.o \ + saimport.o + +SPECASM: $(SPECASM) + $(CC) $(CFLAGS) -m -startup=31 -o $@ $^ -pragma-include:zpragma.inc -subtype=dotn $(CZFLAGS) -create-app + +SALINK: $(SALINK) + $(CC) $(CFLAGS) -m -startup=31 -o $@ $^ -pragma-include:zpragma.inc -subtype=dotn $(CZFLAGS) -create-app + +SAEXPORT: $(SAEXPORT) + $(CC) $(CFLAGS) -startup=30 -o $@ $(SAEXPORT) -subtype=dotn -Cz"--clean" -create-app + +SAIMPORT: $(SAIMPORT) + $(CC) $(CFLAGS) -startup=30 -o $@ $(SAIMPORT) -subtype=dotn -Cz"--clean" -create-app + +clean: + - rm -rf specasm *.zip + - rm *.X *.o *.bin SPECASM SALINK SAIMPORT SAEXPORT + +.PHONY: release +release: + - rm -rf release + mkdir -p release/specasm + cp SPECASM SALINK release/specasm + cp ../../../COPYING release/specasm + cp SAIMPORT SAEXPORT *.X release/specasm + cp ../../../bas/INSTALL release/specasm + cp ../../../bas/REMOVE release/specasm + cd release && zip -r specasm48.zip specasm diff --git a/build/next/specasm/zpragma.inc b/build/next/specasm/zpragma.inc new file mode 100644 index 0000000..31ef5ef --- /dev/null +++ b/build/next/specasm/zpragma.inc @@ -0,0 +1 @@ +#pragma output REGISTER_SP = 0xDFE0 diff --git a/build/next/unit/Make.include b/build/next/unit/Make.include new file mode 100644 index 0000000..6779ceb --- /dev/null +++ b/build/next/unit/Make.include @@ -0,0 +1,51 @@ +error.o: error.c error.h +state_base.o: state_base.c peer.h error.h peer_zx.h \ + line.h state_base.h strings.h +state_dump.o: state_dump.c state.h state_base.h line.h \ + error.h strings.h +state_parse.o: state_parse.c state.h state_base.h line.h \ + error.h strings.h +specasm_next.o: specasm_next.c editor.h peer.h error.h \ + line.h peer_file.h state.h state_base.h strings.h +ld_parse.o: ld_parse.c line_common.h line.h error.h \ + line_parse_common.h peer.h peer_unit.h state.h \ + state_base.h strings.h +ld_parse_banked.o: ld_parse.c line_common.h line.h error.h \ + line_parse_common.h peer.h peer_unit.h state.h \ + state_base.h strings.h +line_common.o: line_common.c line_common.h line.h error.h +line_dump.o: line_dump.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +line_dump_common.o: line_dump_common.c line_common.h line.h \ + error.h peer.h peer_zx.h state.h state_base.h \ + strings.h +line_dump_banked.o: line_dump.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +link_obj_banked.o: link_obj.c expression.h salink.h line.h \ + error.h peer_file.h map.h peer.h peer_unit.h \ + state_base.h strings.h +line_parse.o: line_parse.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +line_parse_common.o: line_parse_common.c line.h error.h \ + line_common.h line_parse_common.h state.h state_base.h \ + strings.h +line_parse_banked.o: line_parse.c line_common.h line.h error.h \ + peer.h peer_zx.h state.h state_base.h strings.h +editor.o: editor.c editor.h peer.h error.h \ + peer_zx.h line.h state.h state_base.h strings.h \ + util_print_zx.h +specasm_trampolines.o: specasm_trampolines.c line.h error.h +peer_next.o: peer_next.c peer.h state.h state_base.h line.h \ + strings.h error.h util_print_zx.h +peer_file_next.o: peer_file_next.c error.h peer_file.h +util_print_next.o: util_print_next.c util_print_zx.h +samake.o: samake.c peer.h error.h peer_zx.h line.h \ + salink.h peer_file.h state_base.h strings.h + +unittests_zx.o: ../src/unittests_zx.c ../src/error.h ../src/line.h \ + ../src/state.h ../src/state_base.h ../src/strings.h ../src/test_content.h ../src/test_content_zx.h \ + ../src/peer.h ../src/peer_file.h ../src/peer_unit.h + +%.o: %.c + zcc $(CFLAGS) -o $@ -c $< + diff --git a/build/next/unit/Makefile b/build/next/unit/Makefile new file mode 100644 index 0000000..a992236 --- /dev/null +++ b/build/next/unit/Makefile @@ -0,0 +1,51 @@ +VPATH=../../../src + +.PHONY: all +all: UNITNEXT + +CC=zcc +CFLAGS=+zxn -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warning 85" -clib=sdcc_iy -DSPECASM_TARGET_NEXT -DSPECASM_TARGET_NEXT_OPCODES -DUNITTESTS +CZFLAGS=-Cz="--clean --fullsize --main-fence 0xDFE0" + +line_parse_banked.o: line_parse.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_43_H --constsegBANK_43_H --datasegBANK_43_H -c $< + +line_dump_banked.o: line_dump.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_44_H --constsegBANK_44_H --datasegBANK_44_H -c $< + +ld_parse_banked.o: ld_parse.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $< + +include Make.include + +UNITNEXT = \ + error.o \ + state_base.o \ + specasm_trampolines.o \ + line_common.o \ + ld_parse_banked.o \ + line_dump_banked.o \ + line_dump_common.o \ + line_parse_banked.o \ + line_parse_common.o \ + state_dump.o \ + state_parse.o \ + peer_file_next.o \ + util_print_next.o \ + peer_unit.o \ + unittests_zx.o + +UNITNEXT: $(UNITNEXT) + $(CC) $(CFLAGS) -m -startup=31 -o $@ $^ -pragma-include:zpragma.inc -subtype=dotn $(CZFLAGS) -create-app + +clean: + - rm -rf unitzx + - rm *.X *.o *.bin + +.PHONY: tests +tests: + - rm -rf tests + mkdir tests + cd ../../.. && CFLAGS=-DSPECASM_TARGET_NEXT make -f Makefile test_content_zx + ../../../test_content_zx tests + cp unitnext tests diff --git a/build/next/unit/zpragma.inc b/build/next/unit/zpragma.inc new file mode 100644 index 0000000..31ef5ef --- /dev/null +++ b/build/next/unit/zpragma.inc @@ -0,0 +1 @@ +#pragma output REGISTER_SP = 0xDFE0 diff --git a/docs/specasm.md b/docs/specasm.md index de0f27f..7595744 100644 --- a/docs/specasm.md +++ b/docs/specasm.md @@ -261,58 +261,66 @@ The first two call statements in this example generate the same machine code ins The following instructions support expressions. -| Instruction / Directive | -|--------------------------| -| adc a, =expression | -| add a, =expression | -| and =expression | -| call =expression | -| cp =expression | -| jp =expression | -| in a, (=expression) | -| ld a, =expression | -| ld a, (=expression) | -| ld (=expression), a | -| ld b, =expression | -| ld c, =expression | -| ld d, =expression | -| ld e, =expression | -| ld h, =expression | -| ld l, =expression | -| ld bc, =expression | -| ld de, =expression | -| ld hl, =expression | -| ld hl, (=expression) | -| ld (hl), =expression | -| ld (=expression), hl | -| ld sp, =expression | -| out (=expression), a | -| or =expression | -| rst =expression | -| sbc a, =expression | -| sub a, expression | -| xor =expressio | -| bit =expression, [a-l] | -| bit =expression, (hl) | -| ld ix, =expression | -| ld iy, =expression | -| ld bc, (=expression) | -| ld (=expression), bc | -| ld de, (=expression) | -| ld (=expression), de | -| ld ix, (=expression) | -| ld (=expression), ix | -| ld iy, (=expression) | -| ld (=expression), iy | -| ld (=expression), sp | -| ld sp, (=expression) | -| im =expression | -| res =expression, [a-l] | -| res =expression, (hl) | -| set =expression, [a-l] | -| set =expression, (hl) | -| dw =expression | -| db =expression | +| Instruction / Directive | +|-------------------------------| +| adc a, =expression | +| add a, =expression | +| add hl, =expression (nx) | +| add de, =expression (nx) | +| add bc, =expression (nx) | +| and =expression | +| call =expression | +| cp =expression | +| jp =expression | +| in a, (=expression) | +| ld a, =expression | +| ld a, (=expression) | +| ld (=expression), a | +| ld b, =expression | +| ld c, =expression | +| ld d, =expression | +| ld e, =expression | +| ld h, =expression | +| ld l, =expression | +| ld bc, =expression | +| ld de, =expression | +| ld hl, =expression | +| ld hl, (=expression) | +| ld (hl), =expression | +| ld (=expression), hl | +| ld sp, =expression | +| out (=expression), a | +| or =expression | +| rst =expression | +| sbc a, =expression | +| sub a, expression | +| xor =expressio | +| bit =expression, [a-l] | +| bit =expression, (hl) | +| ld ix, =expression | +| ld iy, =expression | +| ld bc, (=expression) | +| ld (=expression), bc | +| ld de, (=expression) | +| ld (=expression), de | +| ld ix, (=expression) | +| ld (=expression), ix | +| ld iy, (=expression) | +| ld (=expression), iy | +| ld (=expression), sp | +| ld sp, (=expression) | +| im =expression | +| nextreg =expression, imm (nx) | +| nextreg =expression, a (nx) | +| res =expression, [a-l] | +| res =expression, (hl) | +| set =expression, [a-l] | +| set =expression, (hl) | +| test =expression (nx) | +| dw =expression | +| db =expression | + +The (nx) suffix indicates that the instruction is a Spectrum Next specific instruction. The nextreg instruction only allows an expression in the first parameter. Expressions cannot be used as an offset in combination with an index register, e.g., diff --git a/src/editor.c b/src/editor.c index 4a955d5..ce446fc 100644 --- a/src/editor.c +++ b/src/editor.c @@ -16,6 +16,7 @@ #include "editor.h" #include "line.h" +#include "line_parse_common.h" #include "peer.h" #include "state.h" diff --git a/src/expression.c b/src/expression.c index bcbd220..cafaa65 100644 --- a/src/expression.c +++ b/src/expression.c @@ -110,8 +110,11 @@ const char *prv_get_token_e(const char *buf, salink_obj_t *obj, char c; long lval; int len; +#ifndef SPECASM_NEXT_BANKED + const char simple_ops[] = "()/*+-&|^~"; +#else static const char simple_ops[] = "()/*+-&|^~"; - +#endif if (!buf) { tok->type = SALINK_TOKEN_EOF; return NULL; @@ -580,7 +583,12 @@ static void prv_check_equ_err(salink_obj_t *obj, const char *name, prv_check_exp_err(obj, scratch, line_no, exact_line); } +#ifdef SPECASM_NEXT_BANKED +int16_t salink_equ_eval_banked_e(salink_obj_t *obj, const char *str, + uint16_t line_no) +#else int16_t salink_equ_eval_e(salink_obj_t *obj, const char *str, uint16_t line_no) +#endif { int16_t e; @@ -615,8 +623,13 @@ static void prv_equ_eval_local_e(salink_obj_t *obj, salink_label_t *label, label->type = SALINK_LABEL_TYPE_EQU_EVAL_LONG; } +#ifdef SPECASM_NEXT_BANKED +void salink_equ_eval_global_banked_e(salink_obj_t *obj, salink_global_t *global, + salink_label_t *label, uint8_t depth) +#else void salink_equ_eval_global_e(salink_obj_t *obj, salink_global_t *global, salink_label_t *label, uint8_t depth) +#endif { const char *name = global->name + strlen(global->name) + 1; diff --git a/src/ld_parse.c b/src/ld_parse.c new file mode 100644 index 0000000..3a626e7 --- /dev/null +++ b/src/ld_parse.c @@ -0,0 +1,498 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include + +#include "line_common.h" +#include "line_parse_common.h" +#include "peer.h" +#include "state.h" + +typedef struct specasm_opcode_t_ specasm_opcode_t; + +static const char *prv_get_word_imm_ind_e(const char *args, uint16_t *val, + uint8_t *flags) +{ + while (*args == ' ') + ++args; + + if (*args != '(') { + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; + } + args = specasm_get_uword_imm_e(args + 1, val, flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + + while (*args == ' ') + ++args; + + if (*args != ')') { + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; + } + + return args + 1; +} + +static const char *prv_get_label_ind_e(const char *args, specasm_line_t *line, + uint8_t *val) +{ + while (*args == ' ') + ++args; + + if (*args++ != '(') { + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; + } + + while (*args == ' ') + ++args; + + args = specasm_parse_label_or_exp_e(args, line, val); + if (err_type != SPECASM_ERROR_OK) + return 0; + + while (*args == ' ') + ++args; + + if (*args != ')') { + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; + } + + return args + 1; +} + +static void prv_ld_16bit_imm_e(specasm_line_t *line, uint8_t reg, uint8_t flags, + uint16_t val) +{ + uint8_t *op_code; + uint8_t rind; + uint8_t i = 0; + + op_code = &line->data.op_code[0]; + switch (reg) { + case SPECASM_BYTE_REG_BC: + rind = 0x1; + break; + case SPECASM_BYTE_REG_DE: + rind = 0x11; + break; + case SPECASM_BYTE_REG_HL: + rind = 0x21; + break; + case SPECASM_BYTE_REG_SP: + rind = 0x31; + break; + case SPECASM_BYTE_REG_IX: + op_code[0] = 0xDD; + rind = 0x21; + i++; + break; + case SPECASM_BYTE_REG_IY: + op_code[0] = 0xFD; + rind = 0x21; + i++; + break; + default: + err_type = SPECASM_ERROR_BAD_REG; + return; + } + + op_code[i++] = rind; + *((uint16_t *)&op_code[i]) = val; + specasm_line_set_size(line, i + 1); + specasm_line_set_format(line, flags); +} + +static void prv_ld_imm_e(specasm_line_t *line, uint8_t reg, uint8_t off, + uint8_t flags, uint8_t flags2, uint16_t val) +{ + uint8_t sz = 1; + int16_t sval; + uint8_t *op_code; + + if (reg <= SPECASM_BYTE_REG_A) { + op_code = &line->data.op_code[0]; + op_code[0] = 0x6 | (reg << 3); + op_code[1] = (uint8_t)val; + goto set_format; + } + + if (reg == SPECASM_BYTE_REG_HL_IND) { + op_code = &line->data.op_code[0]; + op_code[0] = 0x36; + op_code[1] = (uint8_t)val; + goto set_format; + } + + if ((reg == SPECASM_BYTE_REG_IX_OFF) || + (reg == SPECASM_BYTE_REG_IY_OFF)) { + op_code = &line->data.op_code[0]; + op_code[0] = (reg == SPECASM_BYTE_REG_IX_OFF) ? 0xDD : 0xFD; + op_code[1] = 0x36; + op_code[2] = off; + op_code[3] = (uint8_t)val; + sz = 3; + specasm_line_set_format(line, flags); + specasm_line_set_format2(line, flags2); + goto check_byte; + } + + prv_ld_16bit_imm_e(line, reg, flags2, val); + + return; + +set_format: + specasm_line_set_format(line, flags2); + +check_byte: + sval = (int16_t)val; + if ((sval > 255) || (sval < -128)) { + err_type = SPECASM_ERROR_NUM_TOO_BIG; + return; + } + + specasm_line_set_size(line, sz); +} + +static void prv_set_ld_imm_ind_e(specasm_line_t *line, uint16_t val, + uint8_t reg, uint8_t mod) +{ + + uint8_t opc1; + uint8_t opc2 = 0; + uint8_t lab_loc = 2; + uint8_t *op_code; + + switch (reg) { + case SPECASM_BYTE_REG_A: + opc1 = 0x32; + lab_loc = 1; + break; + case SPECASM_BYTE_REG_BC: + opc1 = 0xED; + opc2 = 0x43; + break; + case SPECASM_BYTE_REG_DE: + opc1 = 0xED; + opc2 = 0x53; + break; + case SPECASM_BYTE_REG_HL: + opc1 = 0x22; + lab_loc = 1; + break; + case SPECASM_BYTE_REG_SP: + opc1 = 0xED; + opc2 = 0x73; + break; + case SPECASM_BYTE_REG_IX: + opc1 = 0xDD; + opc2 = 0x22; + break; + case SPECASM_BYTE_REG_IY: + opc1 = 0xFD; + opc2 = 0x22; + break; + default: + err_type = SPECASM_ERROR_BAD_REG; + return; + } + + op_code = &line->data.op_code[0]; + op_code[0] = opc1; + op_code[1] = opc2; + op_code[lab_loc - 1] |= mod; + + *((uint16_t *)&op_code[lab_loc]) = val; + specasm_line_set_size(line, lab_loc + 1); +} + +static const char *prv_parse_st_ind_imm_e(const char *args, + specasm_line_t *line, uint16_t val) +{ + uint8_t reg; + uint8_t off; + uint8_t flags; + + while (*args == ' ') + ++args; + + if (*args++ != ',') { + err_type = SPECASM_ERROR_COMMA_EXPECTED; + return 0; + } + + args += specasm_parse_reg_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + + prv_set_ld_imm_ind_e(line, val, reg, 0); + + return args; +} + +static void prv_store_ind_reg_e(specasm_line_t *line, uint8_t reg, uint8_t reg2) +{ + uint8_t *op_code = &line->data.op_code[0]; + + /* + * Size is implicitly set to 0. + */ + + if (reg == SPECASM_BYTE_REG_HL_IND) { + if (reg2 > SPECASM_BYTE_REG_A) + goto fail; + op_code[0] = 0x70 | reg2; + return; + } + + if (reg2 != SPECASM_BYTE_REG_A) + goto fail; + + if (reg == SPECASM_BYTE_REG_BC_IND) + op_code[0] = 0x2; + else if (reg == SPECASM_BYTE_REG_DE_IND) + op_code[0] = 0x12; + else + goto fail; + + return; + +fail: + err_type = SPECASM_ERROR_BAD_REG; +} + +static void prv_ld_reg_reg_e(specasm_line_t *line, uint8_t reg, uint8_t off, + uint8_t reg2, uint8_t off2, uint8_t flags) +{ + uint8_t *op_code; + uint8_t sz = 0; + uint8_t hl_ind[] = {0x46, 0x4E, 0x56, 0x5E, 0x66, 0x6E, 0, 0x7E}; + + if (reg & SPECASM_IND_MOD) { + prv_store_ind_reg_e(line, reg, reg2); + return; + } else if (reg <= SPECASM_BYTE_REG_A) { + if (reg2 <= SPECASM_BYTE_REG_A) { + op_code = &line->data.op_code[0]; + op_code[0] = 0x40 | reg2 | (reg << 3); + } else if ((reg2 == SPECASM_BYTE_REG_IX_OFF) || + (reg2 == SPECASM_BYTE_REG_IY_OFF)) { + op_code = &line->data.op_code[0]; + op_code[0] = + (reg2 == SPECASM_BYTE_REG_IX_OFF) ? 0xdd : 0xfd; + op_code[1] = 0x46 | (reg << 3); + op_code[2] = off2; + specasm_line_set_format(line, flags); + sz = 2; + } else if (reg2 == SPECASM_BYTE_REG_HL_IND) { + op_code = &line->data.op_code[0]; + op_code[0] = hl_ind[reg]; + } else if (reg == SPECASM_BYTE_REG_A) { + switch (reg2) { + case SPECASM_BYTE_REG_BC_IND: + op_code = &line->data.op_code[0]; + op_code[0] = 0xA; + break; + case SPECASM_BYTE_REG_DE_IND: + op_code = &line->data.op_code[0]; + op_code[0] = 0x1A; + break; + case SPECASM_BYTE_REG_I: + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = 0x57; + sz = 1; + break; + case SPECASM_BYTE_REG_R: + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = 0x5F; + sz = 1; + break; + default: + goto fail; + } + } else { + goto fail; + } + } else if ((reg == SPECASM_BYTE_REG_I) || (reg == SPECASM_BYTE_REG_R)) { + if (reg2 != SPECASM_BYTE_REG_A) + goto fail; + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = reg == SPECASM_BYTE_REG_I ? 0x47 : 0x4F; + sz = 1; + } else if (reg == SPECASM_BYTE_REG_SP) { + switch (reg2) { + case SPECASM_BYTE_REG_HL: + op_code = &line->data.op_code[0]; + op_code[0] = 0xF9; + break; + case SPECASM_BYTE_REG_IX: + op_code = &line->data.op_code[0]; + op_code[0] = 0xDD; + op_code[1] = 0xF9; + sz = 1; + break; + case SPECASM_BYTE_REG_IY: + op_code = &line->data.op_code[0]; + op_code[0] = 0xFD; + op_code[1] = 0xF9; + sz = 1; + break; + default: + goto fail; + } + } else if (reg2 <= SPECASM_BYTE_REG_A) { + if ((reg == SPECASM_BYTE_REG_IX_OFF) || + (reg == SPECASM_BYTE_REG_IY_OFF)) { + op_code = &line->data.op_code[0]; + op_code[0] = + (reg == SPECASM_BYTE_REG_IX_OFF) ? 0xdd : 0xfd; + op_code[1] = 0x70 | reg2; + op_code[2] = off; + specasm_line_set_format(line, flags); + sz = 2; + } else { + goto fail; + } + } else { + goto fail; + } + + specasm_line_set_size(line, sz); + + return; + +fail: + err_type = SPECASM_ERROR_BAD_REG; +} + +#ifdef SPECASM_NEXT_BANKED +uint8_t specasm_parse_ld_banked_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) +#else +uint8_t specasm_parse_ld_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) +#endif +{ + const char *args2; + uint16_t val; + uint8_t label; + uint8_t reg; + uint8_t off; + uint8_t reg2; + uint8_t off2; + uint8_t flags; + uint8_t flags2; + uint8_t v; + const char *start = args; + + args2 = prv_get_word_imm_ind_e(args, &val, &flags); + if (err_type == SPECASM_ERROR_OK) { + specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); + specasm_line_set_format(line, flags); + } else if ((err_type == SPECASM_ERROR_NUM_TOO_BIG) || + (err_type == SPECASM_ERROR_NUM_NEG)) { + return 0; + } else { + err_type = SPECASM_ERROR_OK; + args2 = prv_get_label_ind_e(args, line, &label); + if (err_type == SPECASM_ERROR_BAD_EXPRESSION) + return 0; + val = label; + } + if (err_type == SPECASM_ERROR_OK) { + args = prv_parse_st_ind_imm_e(args2, line, val); + return args - start; + } + + /* + For some reason using prv_parse_reg_comma_e here actually makes + the program bigger. + */ + + err_type = SPECASM_ERROR_OK; + args += specasm_parse_reg_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + + while (*args == ' ') + ++args; + + if (*args++ != ',') { + err_type = SPECASM_ERROR_COMMA_EXPECTED; + return 0; + } + args2 = prv_get_word_imm_ind_e(args, &val, &flags); + if ((err_type == SPECASM_ERROR_NUM_TOO_BIG) || + (err_type == SPECASM_ERROR_NUM_NEG)) { + return 0; + } else if (err_type == SPECASM_ERROR_OK) { + specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); + specasm_line_set_format(line, flags); + } else { + err_type = SPECASM_ERROR_OK; + args2 = prv_get_label_ind_e(args, line, &label); + if (err_type == SPECASM_ERROR_BAD_EXPRESSION) + return 0; + val = label; + } + if (err_type == SPECASM_ERROR_OK) { + prv_set_ld_imm_ind_e(line, val, reg, 0x8); + return args2 - start; + } + err_type = SPECASM_ERROR_OK; + + while (*args == ' ') + ++args; + args2 = specasm_parse_word_imm_or_exp_e(args, line, &val, &flags2); + if ((err_type == SPECASM_ERROR_NUM_TOO_BIG) || + (err_type == SPECASM_ERROR_BAD_EXPRESSION)) { + return 0; + } else if (err_type == SPECASM_ERROR_OK) { + prv_ld_imm_e(line, reg, off, flags, flags2, val); + return args2 - start; + } else if ((reg >= SPECASM_BYTE_REG_BC) && + (reg <= SPECASM_BYTE_REG_IY)) { + /* + * For 16 bit immediate loads we allow labels in place of + * the 16 bit value. + */ + + err_type = SPECASM_ERROR_OK; + args2 = specasm_parse_jump_label_e(args, line, &v); + val = v; + if (err_type == SPECASM_ERROR_OK) { + prv_ld_16bit_imm_e(line, reg, + specasm_line_get_format(line), val); + return args2 - start; + } + } + + err_type = SPECASM_ERROR_OK; + args += specasm_parse_reg_e(args, ®2, &off2, &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + + prv_ld_reg_reg_e(line, reg, off, reg2, off2, flags); + + return args - start; +} diff --git a/src/ld_parse.h b/src/ld_parse.h new file mode 100644 index 0000000..f406943 --- /dev/null +++ b/src/ld_parse.h @@ -0,0 +1,22 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef LD_PARSE_H +#define LD_PARSE_H + +uint8_t specasm_parse_ld_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry); +#endif diff --git a/src/line.h b/src/line.h index f69a8df..fa1ea29 100644 --- a/src/line.h +++ b/src/line.h @@ -92,7 +92,59 @@ #define SPECASM_LINE_TYPE_DW_SUB 70 #define SPECASM_LINE_TYPE_LD_IMM_16_SUB 71 #define SPECASM_LINE_TYPE_LD_IMM_8_SUB 72 + +/* + * Start Spectrum Next instructions. + */ + +/* + * This is a real mess. Putting the Next instructions here + * means that 48Kb .x files cannot be used on a Next, even + * though the Next instruction set is a superset of the 48ks. + * + * They can't be moved later though as then the type will + * overflow when the SPECASM_LINE_TYPE_EXP_ADJ is added when + * the instructions appear in an expression. + */ + +/* + * We reserve two spare instructions so we can add some new + * instructions without breaking backwards compatibility. + */ + +#define SPECASM_LINE_TYPE_NEXT_START (SPECASM_LINE_TYPE_LD_IMM_8_SUB + 3) + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_LINE_TYPE_LDDRX SPECASM_LINE_TYPE_NEXT_START +#define SPECASM_LINE_TYPE_LDDX (SPECASM_LINE_TYPE_NEXT_START + 1) +#define SPECASM_LINE_TYPE_LDIRX (SPECASM_LINE_TYPE_NEXT_START + 2) +#define SPECASM_LINE_TYPE_LDIX (SPECASM_LINE_TYPE_NEXT_START + 3) +#define SPECASM_LINE_TYPE_LDPIRX (SPECASM_LINE_TYPE_NEXT_START + 4) +#define SPECASM_LINE_TYPE_LDWS (SPECASM_LINE_TYPE_NEXT_START + 5) +#define SPECASM_LINE_TYPE_BRLC (SPECASM_LINE_TYPE_NEXT_START + 6) +#define SPECASM_LINE_TYPE_BSLA (SPECASM_LINE_TYPE_NEXT_START + 7) +#define SPECASM_LINE_TYPE_BSRA (SPECASM_LINE_TYPE_NEXT_START + 8) +#define SPECASM_LINE_TYPE_BSRF (SPECASM_LINE_TYPE_NEXT_START + 9) +#define SPECASM_LINE_TYPE_BSRL (SPECASM_LINE_TYPE_NEXT_START + 10) +#define SPECASM_LINE_TYPE_OUTINB (SPECASM_LINE_TYPE_NEXT_START + 11) +#define SPECASM_LINE_TYPE_SWAPNIB (SPECASM_LINE_TYPE_NEXT_START + 12) +#define SPECASM_LINE_TYPE_PIXELAD (SPECASM_LINE_TYPE_NEXT_START + 13) +#define SPECASM_LINE_TYPE_PIXELDN (SPECASM_LINE_TYPE_NEXT_START + 14) +#define SPECASM_LINE_TYPE_SETAE (SPECASM_LINE_TYPE_NEXT_START + 15) +#define SPECASM_LINE_TYPE_TEST (SPECASM_LINE_TYPE_NEXT_START + 16) +#define SPECASM_LINE_TYPE_MIRROR (SPECASM_LINE_TYPE_NEXT_START + 17) +#define SPECASM_LINE_TYPE_MUL (SPECASM_LINE_TYPE_NEXT_START + 18) +#define SPECASM_LINE_TYPE_NEXTREG (SPECASM_LINE_TYPE_NEXT_START + 19) +#define SPECASM_LINE_TYPE_NBRK (SPECASM_LINE_TYPE_NEXT_START + 20) + +#define SPECASM_LINE_TYPE_SIMPLE_MAX SPECASM_LINE_TYPE_NBRK +#else #define SPECASM_LINE_TYPE_SIMPLE_MAX SPECASM_LINE_TYPE_LD_IMM_8_SUB +#endif +/* + * End Spectrum Next instructions. + */ + #define SPECASM_LINE_TYPE_DS (SPECASM_LINE_TYPE_SIMPLE_MAX + 1) #define SPECASM_LINE_TYPE_ORG (SPECASM_LINE_TYPE_SIMPLE_MAX + 2) #define SPECASM_LINE_TYPE_MAP (SPECASM_LINE_TYPE_SIMPLE_MAX + 3) @@ -124,7 +176,7 @@ #define SPECASM_LINE_TYPE_EXP_ADJ 160 -#define SPECASM_MAX_MNEMOM 5 +#define SPECASM_MAX_MNEMOM 7 #define SPECASM_MAX_LINES 512 #define SPECASM_MAX_ROWS 23 #define SPECASM_LINE_MAX_LEN 32 @@ -208,12 +260,21 @@ * res =expression, (hl) * set =expression, [a-l] * set =expression, (hl) - * * In these cases the label id in op_code[2] and set the addr flag * to indicate whether it's a long or short label. op_code[1] is * set as though the integer was 0. * + * test = expression + * nextreg =expression, a + * nextreg =expression, imm + * add hl, =expression + * add de, =expression + * add bc, =expression + * + * In these cases the label id in op_code[2] and set the addr flag + * to indicate whether it's a long or short label. + * * db =expression * dw =expression * @@ -271,11 +332,12 @@ struct specasm_lines_t_ { typedef struct specasm_lines_t_ specasm_lines_t; void specasm_init_dump_table(void); -char *specasm_get_long_imm_e(const char *str, long *val, uint8_t *flags); uint8_t specasm_parse_mnemomic_e(const char *str, uint8_t i, specasm_line_t *line); uint8_t specasm_parse_exp_e(const char *str, uint8_t *label1, uint8_t *label1_type); uint8_t specasm_dump_opcode_e(const specasm_line_t *line, char *buf); - +uint8_t specasm_dump_byte(char *buf, uint8_t v, uint8_t flags); +uint8_t specasm_dump_word(char *buf, uint16_t v, uint8_t flags); +char *specasm_dump_index(const uint8_t *op_code, char *buf, uint8_t flags); #endif diff --git a/src/line_common.c b/src/line_common.c index 1e07db8..ed4ccfa 100644 --- a/src/line_common.c +++ b/src/line_common.c @@ -30,6 +30,13 @@ const specasm_mnemomic_t mnemomics_table[] = { { "align", SPECASM_LINE_TYPE_ALIGN, }, { "and", SPECASM_LINE_TYPE_AND, }, { "bit", SPECASM_LINE_TYPE_BIT, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "brlc", SPECASM_LINE_TYPE_BRLC, }, + { "bsla", SPECASM_LINE_TYPE_BSLA, }, + { "bsra", SPECASM_LINE_TYPE_BSRA, }, + { "bsrf", SPECASM_LINE_TYPE_BSRF, }, + { "bsrl", SPECASM_LINE_TYPE_BSRL, }, +#endif { "call", SPECASM_LINE_TYPE_CALL, }, { "ccf", SPECASM_LINE_TYPE_CCF, }, { "cp", SPECASM_LINE_TYPE_CP, }, @@ -61,10 +68,28 @@ const specasm_mnemomic_t mnemomics_table[] = { { "ld", SPECASM_LINE_TYPE_LD, }, { "ldd", SPECASM_LINE_TYPE_LDD, }, { "lddr", SPECASM_LINE_TYPE_LDDR, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "lddrx", SPECASM_LINE_TYPE_LDDRX, }, + { "lddx", SPECASM_LINE_TYPE_LDDX, }, +#endif { "ldi", SPECASM_LINE_TYPE_LDI, }, { "ldir", SPECASM_LINE_TYPE_LDIR, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "ldirx", SPECASM_LINE_TYPE_LDIRX, }, + { "ldix", SPECASM_LINE_TYPE_LDIX, }, + { "ldpirx", SPECASM_LINE_TYPE_LDPIRX, }, + { "ldws", SPECASM_LINE_TYPE_LDWS, }, +#endif { "map", SPECASM_LINE_TYPE_MAP, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "mirror", SPECASM_LINE_TYPE_MIRROR, }, + { "mul", SPECASM_LINE_TYPE_MUL, }, + { "nbrk", SPECASM_LINE_TYPE_NBRK, }, +#endif { "neg", SPECASM_LINE_TYPE_NEG, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "nextreg", SPECASM_LINE_TYPE_NEXTREG, }, +#endif { "nop", SPECASM_LINE_TYPE_NOP, }, { "or", SPECASM_LINE_TYPE_OR, }, { "org", SPECASM_LINE_TYPE_ORG, }, @@ -73,6 +98,11 @@ const specasm_mnemomic_t mnemomics_table[] = { { "out", SPECASM_LINE_TYPE_OUT, }, { "outd", SPECASM_LINE_TYPE_OUTD, }, { "outi", SPECASM_LINE_TYPE_OUTI, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "outinb", SPECASM_LINE_TYPE_OUTINB, }, + { "pixelad", SPECASM_LINE_TYPE_PIXELAD, }, + { "pixeldn", SPECASM_LINE_TYPE_PIXELDN, }, +#endif { "pop", SPECASM_LINE_TYPE_POP, }, { "push", SPECASM_LINE_TYPE_PUSH, }, { "res", SPECASM_LINE_TYPE_RES, }, @@ -93,10 +123,17 @@ const specasm_mnemomic_t mnemomics_table[] = { { "sbc", SPECASM_LINE_TYPE_SBC, }, { "scf", SPECASM_LINE_TYPE_SCF, }, { "set", SPECASM_LINE_TYPE_SET, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "setae", SPECASM_LINE_TYPE_SETAE, }, +#endif { "sla", SPECASM_LINE_TYPE_SLA, }, { "sra", SPECASM_LINE_TYPE_SRA, }, { "srl", SPECASM_LINE_TYPE_SRL, }, { "sub", SPECASM_LINE_TYPE_SUB, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { "swapnib", SPECASM_LINE_TYPE_SWAPNIB, }, + { "test", SPECASM_LINE_TYPE_TEST, }, +#endif { "xor", SPECASM_LINE_TYPE_XOR, }, }; diff --git a/src/line_dump.c b/src/line_dump.c index c2c0d53..f89f7b5 100644 --- a/src/line_dump.c +++ b/src/line_dump.c @@ -33,78 +33,6 @@ struct specasm_line_opcode_dump_t_ { typedef struct specasm_line_opcode_dump_t_ specasm_line_opcode_dump_t; -static uint8_t prv_dump_byte(char *buf, uint8_t v, uint8_t flags) -{ - int iv; - unsigned char radix; - const char *start; - - if (flags == SPECASM_FLAGS_NUM_CHAR) { - buf[0] = '\''; - buf[1] = v; - buf[2] = '\''; - buf[3] = 0; - return 3; - } - - start = buf; - if (flags == SPECASM_FLAGS_NUM_HEX) { - *buf++ = '$'; - radix = 16; - iv = v; - } else { - radix = 10; - if (flags == SPECASM_FLAGS_NUM_SIGNED) - iv = (int8_t)v; - else - iv = v; - } - (void)itoa(iv, buf, radix); - return strlen(start); -} - -static uint8_t prv_dump_word(char *buf, uint16_t v, uint8_t flags) -{ - const char *start; - unsigned char radix; - - if (flags == SPECASM_FLAGS_NUM_CHAR) { - buf[0] = '\''; - buf[1] = (uint8_t)v; - buf[2] = '\''; - buf[3] = 0; - return 3; - } - - start = buf; - if (flags == SPECASM_FLAGS_NUM_SIGNED) { - (void)itoa(v, buf, 10); - } else { - if (flags == SPECASM_FLAGS_NUM_HEX) { - *buf++ = '$'; - radix = 16; - } else { - radix = 10; - } - - (void)utoa(v, buf, radix); - } - - return strlen(start); -} - -static char *prv_dump_index(const uint8_t *op_code, char *buf, uint8_t flags) -{ - buf[0] = '('; - buf[1] = 'i'; - buf[2] = op_code[0] == 0xDD ? 'x' : 'y'; - buf[3] = '+'; - buf += 4; - buf += prv_dump_byte(buf, op_code[2], flags); - *buf++ = ')'; - return buf; -} - static char *prv_byte_and_hl_ind(uint8_t code, char *buf) { code &= 7; @@ -127,13 +55,13 @@ static char *prv_dump_arith_e(const specasm_line_t *line, char *buf, if (op_code[0] == 0xDD || op_code[0] == 0xFD) { buf = - prv_dump_index(op_code, buf, specasm_line_get_format(line)); + specasm_dump_index(op_code, buf, specasm_line_get_format(line)); } else if (op_code[0] == imm) { if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) buf = prv_dump_exp_e(line, buf, op_code[1]); else - buf += prv_dump_byte(buf, op_code[1], + buf += specasm_dump_byte(buf, op_code[1], specasm_line_get_format(line)); } else if ((op_code[0] & reg) == reg) { buf = prv_byte_and_hl_ind(op_code[0], buf); @@ -186,7 +114,7 @@ static char *prv_dump_16bit_unary_e(const specasm_line_t *line, char *buf, if (op_code[0] == 0xFD || op_code[0] == 0xDD) { if (op_code[1] == hl_ind) { - buf = prv_dump_index(op_code, buf, + buf = specasm_dump_index(op_code, buf, specasm_line_get_format(line)); } else { *buf++ = 'i'; @@ -208,7 +136,7 @@ static char *prv_dump_jump_label_fmt_e(const specasm_line_t *line, char *buf, const char *label; if (fmt == SPECASM_FLAGS_ADDR_NUM) { - buf += prv_dump_word(buf, id, specasm_line_get_format(line)); + buf += specasm_dump_word(buf, id, specasm_line_get_format(line)); } else { if (fmt == SPECASM_FLAGS_ADDR_LONG) label = specasm_state_get_long_e((uint8_t)id); @@ -243,9 +171,13 @@ static char *prv_dump_exp_e(const specasm_line_t *line, char *buf, uint8_t id) static char *prv_dump_cc(const uint8_t cc, char *buf) { +#ifndef SPECASM_NEXT_BANKED static const char *codes[] = {"nz", "z", "nc", "c", "po", "pe", "p", "m"}; - +#else + const char *codes[] = {"nz", "z", "nc", "c", + "po", "pe", "p", "m"}; +#endif const char *code = codes[cc & 7]; while (*code) *buf++ = *code++; @@ -283,9 +215,18 @@ static uint8_t prv_dump_adc_e(const specasm_line_t *line, char *buf) static uint8_t prv_dump_add_e(const specasm_line_t *line, char *buf) { uint8_t reg1; +#ifdef SPECASM_TARGET_NEXT_OPCODES + uint16_t val; + uint8_t reg2 = 0; +#else uint8_t reg2; +#endif const char *code; +#ifndef SPECASM_NEXT_BANKED static const char *codes[] = {"bc", "de", "hl", "sp", "ix", "iy"}; +#else + const char *codes[] = {"bc", "de", "hl", "sp", "ix", "iy"}; +#endif const uint8_t *op_code = line->data.op_code; char *start = buf; @@ -303,6 +244,14 @@ static uint8_t prv_dump_add_e(const specasm_line_t *line, char *buf) reg2 = (op_code[1] >> 4) & 3; if (reg2 == 2) reg2 = 4; +#ifdef SPECASM_TARGET_NEXT_OPCODES + } else if (op_code[0] == 0xED) { + if (op_code[1] <= 0x33) + reg1 = 0x31; + else + reg1 = 0x34; + reg1 = 2 - (op_code[1] - reg1); +#endif } else { buf[0] = 'a'; buf[1] = ','; @@ -317,6 +266,22 @@ static uint8_t prv_dump_add_e(const specasm_line_t *line, char *buf) buf[0] = ','; buf[1] = ' '; buf += 2; +#ifdef SPECASM_TARGET_NEXT_OPCODES + if (op_code[0] == 0xED) { + if (op_code[1] <= 0x33) { + buf[0] = 'a'; + buf++; + } else { + memcpy(&val, &op_code[2], 2); + if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) + buf = prv_dump_jump_label_e(line, buf, val); + else + buf += specasm_dump_word( + buf, val, specasm_line_get_format(line)); + } + return buf - start; + } +#endif code = codes[reg2]; while (*code) *buf++ = *code++; @@ -352,11 +317,24 @@ static uint8_t prv_dump_bit_e(const specasm_line_t *line, char *buf) buf = prv_byte_and_hl_ind(op_code[1], buf); else buf = - prv_dump_index(op_code, buf, specasm_line_get_format(line)); + specasm_dump_index(op_code, buf, specasm_line_get_format(line)); return buf - start; } +#ifdef SPECASM_TARGET_NEXT_OPCODES +static uint8_t prv_dump_barrel_e(const specasm_line_t *line, char *buf) +{ + buf[0] = 'd'; + buf[1] = 'e'; + buf[2] = ','; + buf[3] = ' '; + buf[4] = 'b'; + + return 5; +} +#endif + static uint8_t prv_dump_jr_call_e(const specasm_line_t *line, char *buf, uint8_t abs_ind, uint8_t mask) { @@ -374,7 +352,7 @@ static uint8_t prv_dump_jr_call_e(const specasm_line_t *line, char *buf, if (fmt == SPECASM_FLAGS_ADDR_NUM) { val = *((uint16_t *)&op_code[1]); - buf += prv_dump_word(buf, val, specasm_line_get_format(line)); + buf += specasm_dump_word(buf, val, specasm_line_get_format(line)); } else { buf = prv_dump_jump_label_e(line, buf, op_code[1]); } @@ -416,7 +394,11 @@ static uint8_t prv_dump_djnz_e(const specasm_line_t *line, char *buf) static uint8_t prv_dump_ex_e(const specasm_line_t *line, char *buf) { +#ifndef SPECASM_NEXT_BANKED static const char *com[] = { +#else + const char *com[] = { +#endif "\x08" "af, af'", "\xeb" @@ -465,15 +447,16 @@ static uint8_t prv_dump_im_e(const specasm_line_t *line, char *buf) return 1; } -static char *prv_dump_byte_imm_ind_e(const specasm_line_t *line, char *buf) +static char *specasm_dump_byte_imm_ind_e(const specasm_line_t *line, char *buf, + uint8_t index) { const uint8_t *op_code = line->data.op_code; if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) - return prv_dump_exp_e(line, buf, op_code[1]); + return prv_dump_exp_e(line, buf, op_code[index]); - return buf + - prv_dump_byte(buf, op_code[1], specasm_line_get_format(line)); + return buf + specasm_dump_byte(buf, op_code[index], + specasm_line_get_format(line)); } static uint8_t prv_dump_in_e(const specasm_line_t *line, char *buf) @@ -491,7 +474,7 @@ static uint8_t prv_dump_in_e(const specasm_line_t *line, char *buf) buf[3] = '('; buf += 4; if (op_code[0] == 0xDB) - buf = prv_dump_byte_imm_ind_e(line, buf); + buf = specasm_dump_byte_imm_ind_e(line, buf, 1); else *buf++ = 'c'; buf[0] = ')'; @@ -508,13 +491,21 @@ static uint8_t prv_dump_inc_e(const specasm_line_t *line, char *buf) static uint8_t prv_dump_jp_e(const specasm_line_t *line, char *buf) { const char *start = buf; +#ifndef SPECASM_NEXT_BANKED static const char *com[] = { +#else + const char *com[] = { +#endif "\xe9" "(hl)", "\xdd" "(ix)", "\xfd" "(iy)", +#ifdef SPECASM_TARGET_NEXT_OPCODES + "\xed" + "(c)", +#endif }; const uint8_t *op_code = line->data.op_code; @@ -522,6 +513,9 @@ static uint8_t prv_dump_jp_e(const specasm_line_t *line, char *buf) case 0xDD: case 0xFD: case 0xE9: +#ifdef SPECASM_TARGET_NEXT_OPCODES + case 0xED: +#endif return prv_dump_fixed_e(op_code[0], buf, com, sizeof(com) / sizeof(uint8_t *)); default: @@ -573,7 +567,11 @@ static char *prv_dump_label_ind_e(const specasm_line_t *line, char *buf, static char *prv_dump_label_ind_reg_e(const specasm_line_t *line, char *buf, uint8_t opcode0) { +#ifndef SPECASM_NEXT_BANKED static const char *com[] = { +#else + const char *com[] = { +#endif "\x3a" "a", "\xed" @@ -631,9 +629,13 @@ static char *prv_dump_ld_16bit_imm_e(const specasm_line_t *line, char *buf, uint8_t reg, uint16_t val) { const char *code; +#ifndef SPECASM_NEXT_BANKED static const char *codes[] = {"bc", "de", "hl", "sp", "ix", "iy", "(hl)"}; - +#else + const char *codes[] = {"bc", "de", "hl", "sp", + "ix", "iy", "(hl)"}; +#endif code = codes[reg]; while (*code) *buf++ = *code++; @@ -645,7 +647,7 @@ static char *prv_dump_ld_16bit_imm_e(const specasm_line_t *line, char *buf, specasm_line_get_addr_type(line)) { buf = prv_dump_jump_label_e(line, buf, val); } else { - buf += prv_dump_word(buf, val, specasm_line_get_format(line)); + buf += specasm_dump_word(buf, val, specasm_line_get_format(line)); } return buf; @@ -708,7 +710,11 @@ static char *prv_dump_lda_ind(uint8_t opcode0, char *buf) static char *prv_dump_ld_hl_ind_e(uint8_t opcode0, char *buf) { +#ifndef SPECASM_NEXT_BANKED static const char *com[] = { +#else + const char *com[] = { +#endif "\x46" "b", "\x4E" @@ -743,13 +749,13 @@ static char *prv_dump_ld_reg_off_ind(const specasm_line_t *line, char *buf) buf[0] = byte_regs[(line->data.op_code[1] >> 3) & 7]; buf[1] = ','; buf[2] = ' '; - return prv_dump_index(line->data.op_code, buf + 3, + return specasm_dump_index(line->data.op_code, buf + 3, specasm_line_get_format(line)); } static char *prv_dump_ld_ind_off_reg(const specasm_line_t *line, char *buf) { - buf = prv_dump_index(line->data.op_code, buf, + buf = specasm_dump_index(line->data.op_code, buf, specasm_line_get_format(line)); buf[0] = ','; buf[1] = ' '; @@ -760,12 +766,12 @@ static char *prv_dump_ld_ind_off_reg(const specasm_line_t *line, char *buf) static char *prv_dump_ld_ind_off_imm(const specasm_line_t *line, char *buf) { - buf = prv_dump_index(line->data.op_code, buf, + buf = specasm_dump_index(line->data.op_code, buf, specasm_line_get_format(line)); buf[0] = ','; buf[1] = ' '; buf += 2; - buf += prv_dump_byte(buf, line->data.op_code[3], + buf += specasm_dump_byte(buf, line->data.op_code[3], specasm_line_get_format2(line)); return buf; @@ -803,7 +809,11 @@ static char *prv_dump_ld_special_e(const specasm_line_t *line, char *buf) static uint8_t prv_dump_ld_sp(uint8_t opcode0, char *buf) { +#ifndef SPECASM_NEXT_BANKED static const char *com[] = { +#else + const char *com[] = { +#endif "\xf9" "sp, hl", "\xdd" @@ -930,7 +940,7 @@ static uint8_t prv_dump_ld_e(const specasm_line_t *line, char *buf) if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) buf = prv_dump_exp_e(line, buf, line->data.op_code[1]); else - buf += prv_dump_byte(buf, line->data.op_code[1], + buf += specasm_dump_byte(buf, line->data.op_code[1], specasm_line_get_format(line)); return buf - start; } @@ -983,6 +993,50 @@ static uint8_t prv_dump_ld_imm_8_sub_e(const specasm_line_t *line, char *buf) return prv_dump_subtraction_e(line, buf, 1) + (buf - start); } +#ifdef SPECASM_TARGET_NEXT_OPCODES +static uint8_t prv_dump_mirror_e(const specasm_line_t *line, char *buf) +{ + buf[0] = 'a'; + + return 1; +} + +static uint8_t prv_dump_mul_e(const specasm_line_t *line, char *buf) +{ + buf[0] = 'd'; + buf[1] = ','; + buf[2] = ' '; + buf[3] = 'e'; + + return 4; +} + +static uint8_t prv_dump_nextreg_e(const specasm_line_t *line, char *buf) +{ + char *start = buf; + + if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) + buf = prv_dump_exp_e(line, buf, line->data.op_code[2]); + else + buf += specasm_dump_byte(buf, line->data.op_code[2], + specasm_line_get_format(line)); + + buf[0] = ','; + buf[1] = ' '; + buf += 2; + + if (line->data.op_code[1] == 0x92) { + buf[0] = 'a'; + buf++; + } else { + buf += specasm_dump_byte(buf, line->data.op_code[3], + specasm_line_get_format2(line)); + } + + return buf - start; +} +#endif + static uint8_t prv_dump_or_e(const specasm_line_t *line, char *buf) { char *start = buf; @@ -1001,7 +1055,7 @@ static uint8_t prv_dump_out_e(const specasm_line_t *line, char *buf) *buf++ = '('; if (op_code[0] == 0xD3) - buf = prv_dump_byte_imm_ind_e(line, buf); + buf = specasm_dump_byte_imm_ind_e(line, buf, 1); else *buf++ = 'c'; buf[0] = ')'; @@ -1024,7 +1078,7 @@ static uint8_t prv_dump_rst_e(const specasm_line_t *line, char *buf) return buf - start; } - return prv_dump_byte(buf, val, specasm_line_get_format(line)); + return specasm_dump_byte(buf, val, specasm_line_get_format(line)); } static uint8_t prv_dump_sbc_e(const specasm_line_t *line, char *buf) @@ -1034,6 +1088,11 @@ static uint8_t prv_dump_sbc_e(const specasm_line_t *line, char *buf) static uint8_t prv_dump_stack(const specasm_line_t *line, char *buf) { +#ifdef SPECASM_TARGET_NEXT_OPCODES + uint16_t val; + char *start; + uint8_t val_bytes[2]; +#endif const uint8_t *op_code = line->data.op_code; char a = 'a'; char b = 'f'; @@ -1044,6 +1103,19 @@ static uint8_t prv_dump_stack(const specasm_line_t *line, char *buf) } else if (op_code[0] == 0xDD) { a = 'i'; b = 'x'; +#ifdef SPECASM_TARGET_NEXT_OPCODES + } else if (op_code[0] == 0xED) { + start = buf; + val_bytes[0] = op_code[3]; + val_bytes[1] = op_code[2]; + memcpy(&val, &val_bytes[0], 2); + if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) + buf = prv_dump_jump_label_e(line, buf, val); + else + buf += specasm_dump_word(buf, val, + specasm_line_get_format(line)); + return buf - start; +#endif } else { switch ((op_code[0] >> 4) & 3) { case SPECASM_BYTE_REG_BC - SPECASM_BYTE_REG_BC: @@ -1092,7 +1164,7 @@ static uint8_t prv_dump_shift_e(const specasm_line_t *line, char *buf) buf = prv_byte_and_hl_ind(op_code[1], buf); else if (op_code[0] == 0xDD || op_code[0] == 0xFD) buf = - prv_dump_index(op_code, buf, specasm_line_get_format(line)); + specasm_dump_index(op_code, buf, specasm_line_get_format(line)); else err_type = SPECASM_ERROR_BAD_REG; @@ -1106,6 +1178,17 @@ static uint8_t prv_dump_sub_e(const specasm_line_t *line, char *buf) return prv_dump_arith_e(line, buf, 0xD6, 0x90) - start; } +#ifdef SPECASM_TARGET_NEXT_OPCODES +static uint8_t prv_dump_test_e(const specasm_line_t *line, char *buf) +{ + char *start = buf; + + buf = specasm_dump_byte_imm_ind_e(line, buf, 2); + + return buf - start; +} +#endif + static uint8_t prv_dump_xor_e(const specasm_line_t *line, char *buf) { char *start = buf; @@ -1127,12 +1210,12 @@ static uint8_t prv_dump_db_e(const specasm_line_t *line, char *buf) } sz = specasm_line_get_size(line) + 1; - len = prv_dump_byte(buf, line->data.op_code[0], + len = specasm_dump_byte(buf, line->data.op_code[0], specasm_line_get_format(line)); for (i = 1; i < sz; i++) { ptr = &buf[len]; ptr[0] = ','; - len += prv_dump_byte(ptr + 1, line->data.op_code[i], + len += specasm_dump_byte(ptr + 1, line->data.op_code[i], specasm_line_get_format(line)); len++; } @@ -1155,12 +1238,12 @@ static uint8_t prv_dump_equw_e(const specasm_line_t *line, char *buf) } else { id = *((uint16_t *)&line->data.op_code[0]); str = - buf + prv_dump_word(buf, id, specasm_line_get_format(line)); + buf + specasm_dump_word(buf, id, specasm_line_get_format(line)); if (specasm_line_get_size(line) > 1) { str[0] = ','; str++; id = *((uint16_t *)&line->data.op_code[2]); - str += prv_dump_word(str, id, + str += specasm_dump_word(str, id, specasm_line_get_format(line)); } } @@ -1179,25 +1262,25 @@ static uint8_t prv_dump_ds_e(const specasm_line_t *line, char *buf) const uint8_t *op_code = line->data.op_code; count = (uint16_t *)&op_code[1]; - buf += prv_dump_word(buf, *count, specasm_line_get_format2(line)); + buf += specasm_dump_word(buf, *count, specasm_line_get_format2(line)); buf[0] = ','; buf[1] = ' '; buf += 2; - buf += prv_dump_byte(buf, op_code[0], specasm_line_get_format(line)); + buf += specasm_dump_byte(buf, op_code[0], specasm_line_get_format(line)); return buf - start; } static uint8_t prv_dump_org_e(const specasm_line_t *line, char *buf) { uint16_t addr = *((uint16_t *)&line->data.op_code[0]); - return prv_dump_word(buf, addr, specasm_line_get_format(line)); + return specasm_dump_word(buf, addr, specasm_line_get_format(line)); } static uint8_t prv_dump_align_e(const specasm_line_t *line, char *buf) { - return prv_dump_word(buf, 1 << line->data.op_code[0], + return specasm_dump_word(buf, 1 << line->data.op_code[0], specasm_line_get_format(line)); } @@ -1277,6 +1360,31 @@ static specasm_line_opcode_dump_t dump_opcodes[] = { { prv_dump_equws_e }, /* SPECASM_LINE_TYPE_DW_SUB */ { prv_dump_ld_imm_16_sub_e }, /* SPECASM_LINE_TYPE_LD_IMM_16_SUB */ { prv_dump_ld_imm_8_sub_e }, /* SPECASM_LINE_TYPE_LD_IMM_8_SUB */ +#ifdef SPECASM_TARGET_NEXT_OPCODES + { NULL }, /* Spare */ + { NULL }, /* Spare */ + { NULL }, /* SPECASM_LINE_TYPE_LDDRX */ + { NULL }, /* SPECASM_LINE_TYPE_LDDX */ + { NULL }, /* SPECASM_LINE_TYPE_LDIRX */ + { NULL }, /* SPECASM_LINE_TYPE_LDIX */ + { NULL }, /* SPECASM_LINE_TYPE_LDPIRX */ + { NULL }, /* SPECASM_LINE_TYPE_LDWS */ + { prv_dump_barrel_e }, /* SPECASM_LINE_TYPE_BRLC */ + { prv_dump_barrel_e }, /* SPECASM_LINE_TYPE_BSLA */ + { prv_dump_barrel_e }, /* SPECASM_LINE_TYPE_BSRA */ + { prv_dump_barrel_e }, /* SPECASM_LINE_TYPE_BSRF */ + { prv_dump_barrel_e }, /* SPECASM_LINE_TYPE_BSRL */ + { NULL }, /* SPECASM_LINE_TYPE_OUTINB */ + { NULL }, /* SPECASM_LINE_TYPE_SWAPNIB */ + { NULL }, /* SPECASM_LINE_TYPE_PIXELAD */ + { NULL }, /* SPECASM_LINE_TYPE_PIXELDN */ + { NULL }, /* SPECASM_LINE_TYPE_SETAE */ + { prv_dump_test_e }, /* SPECASM_LINE_TYPE_TEST */ + { prv_dump_mirror_e }, /* SPECASM_LINE_TYPE_MIRROR */ + { prv_dump_mul_e }, /* SPECASM_LINE_TYPE_MUL */ + { prv_dump_nextreg_e }, /* SPECASM_LINE_TYPE_NEXTREG */ + { NULL }, /* SPECASM_LINE_TYPE_NBRK */ +#endif { prv_dump_ds_e }, /* SPECASM_LINE_TYPE_REPB */ { prv_dump_org_e }, /* SPECASM_LINE_TYPE_ORG */ { NULL }, /* SPECASM_LINE_TYPE_MAP */ @@ -1285,7 +1393,11 @@ static specasm_line_opcode_dump_t dump_opcodes[] = { /* clang-format on */ +#ifdef SPECASM_NEXT_BANKED +void specasm_init_dump_table_banked(void) +#else void specasm_init_dump_table(void) +#endif { uint8_t line_type; @@ -1303,7 +1415,11 @@ void specasm_init_dump_table(void) } } +#ifdef SPECASM_NEXT_BANKED +uint8_t specasm_dump_opcode_banked_e(const specasm_line_t *line, char *buf) +#else uint8_t specasm_dump_opcode_e(const specasm_line_t *line, char *buf) +#endif { uint8_t index; const char *name; diff --git a/src/line_dump_common.c b/src/line_dump_common.c new file mode 100644 index 0000000..497040c --- /dev/null +++ b/src/line_dump_common.c @@ -0,0 +1,100 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include + +#include "line_common.h" +#include "peer.h" +#include "state.h" + +/* + * This file contains some functions from line_dump.c that are too big to + * fit into an 8KB page. They're stored in this file purely so they can be + * stored in the main segment on the Next. + */ + +uint8_t specasm_dump_byte(char *buf, uint8_t v, uint8_t flags) +{ + int iv; + unsigned char radix; + const char *start; + + if (flags == SPECASM_FLAGS_NUM_CHAR) { + buf[0] = '\''; + buf[1] = v; + buf[2] = '\''; + buf[3] = 0; + return 3; + } + + start = buf; + if (flags == SPECASM_FLAGS_NUM_HEX) { + *buf++ = '$'; + radix = 16; + iv = v; + } else { + radix = 10; + if (flags == SPECASM_FLAGS_NUM_SIGNED) + iv = (int8_t)v; + else + iv = v; + } + (void)itoa(iv, buf, radix); + return strlen(start); +} + +uint8_t specasm_dump_word(char *buf, uint16_t v, uint8_t flags) +{ + const char *start; + unsigned char radix; + + if (flags == SPECASM_FLAGS_NUM_CHAR) { + buf[0] = '\''; + buf[1] = (uint8_t)v; + buf[2] = '\''; + buf[3] = 0; + return 3; + } + + start = buf; + if (flags == SPECASM_FLAGS_NUM_SIGNED) { + (void)itoa(v, buf, 10); + } else { + if (flags == SPECASM_FLAGS_NUM_HEX) { + *buf++ = '$'; + radix = 16; + } else { + radix = 10; + } + + (void)utoa(v, buf, radix); + } + + return strlen(start); +} + +char *specasm_dump_index(const uint8_t *op_code, char *buf, uint8_t flags) +{ + buf[0] = '('; + buf[1] = 'i'; + buf[2] = op_code[0] == 0xDD ? 'x' : 'y'; + buf[3] = '+'; + buf += 4; + buf += specasm_dump_byte(buf, op_code[2], flags); + *buf++ = ')'; + return buf; +} diff --git a/src/line_parse.c b/src/line_parse.c index 29446cd..594d887 100644 --- a/src/line_parse.c +++ b/src/line_parse.c @@ -18,11 +18,14 @@ #include #include "line_common.h" +#include "line_parse_common.h" #include "peer.h" #include "state.h" typedef struct specasm_opcode_t_ specasm_opcode_t; +#include "ld_parse.h" + typedef uint8_t (*specasm_parse_fn_t)(const char *args, specasm_line_t *line, const specasm_opcode_t *op_code); @@ -35,404 +38,6 @@ struct specasm_opcode_t_ { uint8_t op_code[2]; }; -char *specasm_get_long_imm_e(const char *str, long *val, uint8_t *flags) -{ - int base = 10; - char *end_ptr; - - while (*str == ' ') - ++str; - - if (*str == '$') { - base = 16; - ++str; - } - - *val = strtol((char *)str, &end_ptr, base); - if (end_ptr == (char *)str) - goto on_error; - if (base == 16) { - if (*val < 0) - goto on_error; - *flags = SPECASM_FLAGS_NUM_HEX; - } else { - if (*val < 0) - *flags = SPECASM_FLAGS_NUM_SIGNED; - else - *flags = SPECASM_FLAGS_NUM_UNSIGNED; - } - - return end_ptr; - -on_error: - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; -} - -static char *prv_get_offset_imm_e(const char *str, uint8_t *val, uint8_t *flags) -{ - char *end_ptr; - long lval; - - end_ptr = specasm_get_long_imm_e(str, &lval, flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - if (*flags == SPECASM_FLAGS_NUM_HEX) { - if (lval > 255) - goto on_error; - } else { - if ((lval < -128) || (lval > 127)) - goto on_error; - } - *val = (uint8_t)lval; - return end_ptr; - -on_error: - err_type = SPECASM_ERROR_NUM_TOO_BIG; - return NULL; -} - -static const char *prv_get_char_imm_e(const char *str, uint8_t *val, - uint8_t *flags) -{ - while (*str == ' ') - ++str; - - if (*str == '\'') { - if (str[1] != '\'' && str[2] == '\'') { - *val = str[1]; - *flags = SPECASM_FLAGS_NUM_CHAR; - return (&str[3]); - } - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; - } - - return NULL; -} - -static const char *prv_get_byte_imm_e(const char *str, uint8_t *val, - uint8_t *flags) -{ - const char *end_ptr; - long lval; - - end_ptr = prv_get_char_imm_e(str, val, flags); - if (end_ptr || err_type != SPECASM_ERROR_OK) - return end_ptr; - - end_ptr = specasm_get_long_imm_e(str, &lval, flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - if (lval > 255 || lval < -128) - goto on_error; - *val = (uint8_t)lval; - return end_ptr; - -on_error: - err_type = SPECASM_ERROR_NUM_TOO_BIG; - return NULL; -} - -static const char *prv_get_word_imm_e(const char *str, uint16_t *val, - uint8_t *flags) -{ - const char *end_ptr; - long lval; - uint8_t bval; - - end_ptr = prv_get_char_imm_e(str, &bval, flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - - if (end_ptr) { - *val = (uint16_t)bval; - return end_ptr; - } - - end_ptr = specasm_get_long_imm_e(str, &lval, flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - if ((lval > 0xffff) || (lval < -32768)) - goto on_error; - *val = (uint16_t)lval; - return end_ptr; - -on_error: - err_type = SPECASM_ERROR_NUM_TOO_BIG; - return NULL; -} - -static char *prv_get_uword_imm_e(const char *str, uint16_t *val, uint8_t *flags) -{ - char *end_ptr; - long lval; - - end_ptr = specasm_get_long_imm_e(str, &lval, flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - if (*flags == SPECASM_FLAGS_NUM_SIGNED) { - err_type = SPECASM_ERROR_NUM_NEG; - return NULL; - } - if (lval > 0xffff) { - err_type = SPECASM_ERROR_NUM_TOO_BIG; - return NULL; - } - - *val = (uint16_t)lval; - return end_ptr; -} - -static const char *prv_get_word_imm_ind_e(const char *args, uint16_t *val, - uint8_t *flags) -{ - while (*args == ' ') - ++args; - - if (*args != '(') { - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; - } - args = prv_get_uword_imm_e(args + 1, val, flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - - while (*args == ' ') - ++args; - - if (*args != ')') { - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; - } - - return args + 1; -} - -static const char *prv_get_byte_imm_ind_e(specasm_line_t *line, - const char *args, uint8_t *val) -{ - long v; - uint8_t flags; - uint8_t label_type; - uint8_t label; - uint8_t read; - - while (*args == ' ') - ++args; - - if (*args != '(') { - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; - } - - ++args; - while (*args == ' ') - ++args; - - if (*args == '=') { - read = specasm_parse_exp_e(args + 1, &label, &label_type); - if (err_type != SPECASM_ERROR_OK) - return NULL; - specasm_line_set_addr_type(line, label_type); - args += read + 1; - line->type += SPECASM_LINE_TYPE_EXP_ADJ; - v = label; - } else { - args = specasm_get_long_imm_e(args, &v, &flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - if (flags == SPECASM_FLAGS_NUM_SIGNED) { - err_type = SPECASM_ERROR_NUM_NEG; - return NULL; - } - if (v > 0xff) { - err_type = SPECASM_ERROR_NUM_TOO_BIG; - return NULL; - } - specasm_line_set_format(line, flags); - } - - while (*args == ' ') - ++args; - - if (*args != ')') { - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; - } - *val = v; - - return args + 1; -} - -static uint8_t prv_parse_reg_e(const char *str, uint8_t *r, uint8_t *off, - uint8_t *flags) -{ - uint8_t reg; - uint8_t i; - const char *start = str; - - while (*str == ' ') - ++str; - - if (*str == 0) { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - - /* - * Check for indirect registers. - */ - - reg = *str++; - if (reg == '(') { - while (*str == ' ') - ++str; - if (!*str) { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - if (*str == 'h' && *(str + 1) == 'l') { - reg = SPECASM_BYTE_REG_HL_IND; - str += 2; - } else if (*str == 'b' && *(str + 1) == 'c') { - reg = SPECASM_BYTE_REG_BC_IND; - str += 2; - } else if (*str == 'd' && *(str + 1) == 'e') { - reg = SPECASM_BYTE_REG_DE_IND; - str += 2; - } else if (*str == 's' && *(str + 1) == 'p') { - reg = SPECASM_BYTE_REG_SP_IND; - str += 2; - } else if (*str == 'i') { - ++str; - if (*str == 'x') { - reg = SPECASM_BYTE_REG_IX_OFF; - } else if (*str == 'y') { - reg = SPECASM_BYTE_REG_IY_OFF; - } else { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - ++str; - while (*str == ' ') - ++str; - - if (*str == ')') { - reg += 2; - str++; - goto finish; - } - - if (*str++ != '+') { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - str = prv_get_offset_imm_e(str, off, flags); - if (err_type != SPECASM_ERROR_OK) - return 0; - } else if (str[1] == ' ' || str[1] == ')') { - for (i = 0; i < sizeof(byte_regs); i++) - if (byte_regs[i] == *str) - break; - if (i == sizeof(byte_regs)) { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - ++str; - reg = i + SPECASM_IND_MOD; - } - while (*str == ' ') - ++str; - if (*str++ != ')') { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - - goto finish; - } - - /* Check for byte reg */ - - if (*str == 0 || *str == ' ' || *str == ';' || *str == ',') { - for (i = 0; i < sizeof(byte_regs); i++) - if (byte_regs[i] == reg) - break; - - if (i < sizeof(byte_regs)) { - reg = i; - } else if (reg == 'i') { - reg = SPECASM_BYTE_REG_I; - } else if (reg == 'r') { - reg = SPECASM_BYTE_REG_R; - } else { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - goto finish; - } - - /* We must have a 16 bit reg */ - - if (reg == 'h' && *str == 'l') { - reg = SPECASM_BYTE_REG_HL; - } else if (reg == 'd' && *str == 'e') { - reg = SPECASM_BYTE_REG_DE; - } else if (reg == 'b' && *str == 'c') { - reg = SPECASM_BYTE_REG_BC; - } else if (reg == 's' && *str == 'p') { - reg = SPECASM_BYTE_REG_SP; - } else if (reg == 'a' && *str == 'f') { - if (str[1] == '\'') { - reg = SPECASM_BYTE_REG_AF_P; - str++; - } else { - reg = SPECASM_BYTE_REG_AF; - } - } else if (reg == 'i') { - if (*str == 'y') { - reg = SPECASM_BYTE_REG_IY; - } else if (*str == 'x') { - reg = SPECASM_BYTE_REG_IX; - } else { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - } else { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - str++; - if (*str != ' ' && *str != ',' && *str != 0 && *str != ';') { - err_type = SPECASM_ERROR_BAD_REG; - return 0; - } - -finish: - *r = reg; - return (uint8_t)(str - start); -} - -static const char *prv_parse_reg_comma_e(const char *args, uint8_t *r, - uint8_t *off, uint8_t *flags) -{ - const char *args2; - - args2 = args + prv_parse_reg_e(args, r, off, flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - - while (*args2 == ' ') - ++args2; - - if (*args2 != ',') { - err_type = SPECASM_ERROR_COMMA_EXPECTED; - return NULL; - } - - return args2 + 1; -} - static uint8_t prv_parse_16bit_unary_e(const char *args, specasm_line_t *line, const specasm_opcode_t *op_entry) { @@ -445,7 +50,7 @@ static uint8_t prv_parse_16bit_unary_e(const char *args, specasm_line_t *line, uint8_t rind = op_entry->op_code[0]; uint8_t hl_ind = op_entry->op_code[1]; - read = prv_parse_reg_e(args, ®, &off, &flags); + read = specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -502,115 +107,6 @@ static uint8_t prv_parse_16bit_unary_e(const char *args, specasm_line_t *line, return read; } -static const char *prv_parse_jump_label_e(const char *args, - specasm_line_t *line, uint8_t *label) -{ - uint8_t long_label; - uint8_t i = 0; - - i = 0; - while (*args != '-' && *args != ',' && *args != ' ' && *args != 0 && - *args != ';' && *args != ')') - scratch[i++] = *args++; - scratch[i] = 0; - specasm_state_check_label_e(scratch); - if (err_type != SPECASM_ERROR_OK) - return 0; - if (i >= SPECASM_MAX_SHORT_LEN) { - long_label = SPECASM_FLAGS_ADDR_LONG; - *label = specasm_state_add_long_e(scratch); - } else { - long_label = SPECASM_FLAGS_ADDR_SHORT; - *label = specasm_state_add_short_e(scratch); - } - if (err_type != SPECASM_ERROR_OK) - return 0; - - specasm_line_set_addr_type(line, long_label); - - return args; -} - -static const char * -prv_parse_label_or_exp_e(const char *args, specasm_line_t *line, uint8_t *label) -{ - const char *args2; - uint8_t read; - uint8_t label_type; - - if (*args == '=') { - read = specasm_parse_exp_e(args + 1, label, &label_type); - specasm_line_set_addr_type(line, label_type); - args2 = args + read + 1; - line->type += SPECASM_LINE_TYPE_EXP_ADJ; - } else { - args2 = prv_parse_jump_label_e(args, line, label); - } - - return args2; -} - -static const char *prv_parse_byte_imm_or_exp_e(const char *args, - specasm_line_t *line, - uint8_t *val, uint8_t *label) -{ - uint8_t flags; - uint8_t label_type; - uint8_t read; - - while (*args == ' ') - ++args; - - if (*args == '=') { - read = specasm_parse_exp_e(args + 1, label, &label_type); - if (err_type != SPECASM_ERROR_OK) - return NULL; - specasm_line_set_addr_type(line, label_type); - line->type += SPECASM_LINE_TYPE_EXP_ADJ; - args += read + 1; - if (val != label) - *val = 0; - return args; - } - - args = prv_get_byte_imm_e(args, val, &flags); - if (err_type != SPECASM_ERROR_OK) - return NULL; - - specasm_line_set_format(line, flags); - - return args; -} - -static const char *prv_get_label_ind_e(const char *args, specasm_line_t *line, - uint8_t *val) -{ - while (*args == ' ') - ++args; - - if (*args++ != '(') { - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; - } - - while (*args == ' ') - ++args; - - args = prv_parse_label_or_exp_e(args, line, val); - if (err_type != SPECASM_ERROR_OK) - return 0; - - while (*args == ' ') - ++args; - - if (*args != ')') { - err_type = SPECASM_ERROR_BAD_NUM; - return NULL; - } - - return args + 1; -} - static uint8_t prv_parse_arith_gen_e(const char *args, specasm_line_t *line, uint8_t areg, uint8_t hl_ind, uint8_t aimm) { @@ -628,7 +124,7 @@ static uint8_t prv_parse_arith_gen_e(const char *args, specasm_line_t *line, op_code = &line->data.op_code[0]; byte1 = &op_code[1]; - args2 = prv_parse_byte_imm_or_exp_e(args, line, byte1, byte1); + args2 = specasm_parse_byte_imm_or_exp_e(args, line, byte1, byte1); if (err_type == SPECASM_ERROR_OK) { op_code[0] = aimm; sz = 1; @@ -640,7 +136,7 @@ static uint8_t prv_parse_arith_gen_e(const char *args, specasm_line_t *line, } err_type = SPECASM_ERROR_OK; - read = prv_parse_reg_e(args, ®, &off, &flags); + read = specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; read += args - start; @@ -686,7 +182,7 @@ static uint8_t prv_parse_adc_sbc_e(const char *args, specasm_line_t *line, uint8_t hl_ind; const char *start = args; - args = prv_parse_reg_comma_e(args, ®, &off, &flags); + args = specasm_parse_reg_comma_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -696,7 +192,7 @@ static uint8_t prv_parse_adc_sbc_e(const char *args, specasm_line_t *line, areg = op_entry->op_code[1]; args += prv_parse_arith_gen_e(args, line, areg, hl_ind, aimm); } else if (reg == SPECASM_BYTE_REG_HL) { - args += prv_parse_reg_e(args, ®, &off, &flags); + args += specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; op_code = &line->data.op_code[0]; @@ -735,12 +231,17 @@ static uint8_t prv_parse_add_e(const char *args, specasm_line_t *line, uint8_t off; uint8_t i; uint8_t flags; +#ifdef SPECASM_TARGET_NEXT_OPCODES + uint16_t val; + const char *args2; + uint8_t *op_code; +#endif uint8_t opi = 0; const char *start = args; uint8_t sixbit_regs[] = {SPECASM_BYTE_REG_BC, SPECASM_BYTE_REG_DE, SPECASM_BYTE_REG_HL, SPECASM_BYTE_REG_SP}; - args = prv_parse_reg_comma_e(args, ®, &off, &flags); + args = specasm_parse_reg_comma_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -749,6 +250,10 @@ static uint8_t prv_parse_add_e(const char *args, specasm_line_t *line, args += prv_parse_arith_gen_e(args, line, 0x80, 0x86, 0xC6); return args - start; case SPECASM_BYTE_REG_HL: +#ifdef SPECASM_TARGET_NEXT_OPCODES + case SPECASM_BYTE_REG_BC: + case SPECASM_BYTE_REG_DE: +#endif break; case SPECASM_BYTE_REG_IY: sixbit_regs[2] = SPECASM_BYTE_REG_IY; @@ -763,9 +268,52 @@ static uint8_t prv_parse_add_e(const char *args, specasm_line_t *line, default: goto bad_reg; } - args += prv_parse_reg_e(args, ®2, &off, &flags); + +#ifdef SPECASM_TARGET_NEXT_OPCODES + args2 = args + specasm_parse_reg_e(args, ®2, &off, &flags); + if (err_type != SPECASM_ERROR_OK) { + /* + * 16 bit immediate cannot be added to ix and iy. + */ + + if (opi > 0) + return 0; + err_type = SPECASM_ERROR_OK; + args = specasm_parse_word_imm_or_exp_e(args, line, &val, + &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = 0x34 + (2 - (reg - SPECASM_BYTE_REG_BC)); + memcpy(&op_code[2], &val, 2); + specasm_line_set_size(line, 3); + specasm_line_set_format(line, flags); + return args - start; + } else { + /* + * a cannot be added to ix and add iy. + */ + + args = args2; + if (opi == 0) { + if (reg2 == SPECASM_BYTE_REG_A) { + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = + 0x31 + (2 - (reg - SPECASM_BYTE_REG_BC)); + specasm_line_set_size(line, 1); + return args - start; + } + if (reg != SPECASM_BYTE_REG_HL) + goto bad_reg; + } + } +#else + args += specasm_parse_reg_e(args, ®2, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; +#endif for (i = 0; i < 4; i++) if (sixbit_regs[i] == reg2) { @@ -788,6 +336,40 @@ static uint8_t prv_parse_arith_e(const char *args, specasm_line_t *line, return prv_parse_arith_gen_e(args, line, areg, areg | 6, aimm); } +#ifdef SPECASM_TARGET_NEXT_OPCODES +static uint8_t prv_parse_barrel_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) +{ + uint8_t reg; + uint8_t off; + uint8_t flags; + uint8_t *op_code; + const char *start = args; + + args = specasm_parse_reg_comma_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + if (reg != SPECASM_BYTE_REG_DE) { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + + args += specasm_parse_reg_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + if (reg != SPECASM_BYTE_REG_B) { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + + op_code = &line->data.op_code[0]; + op_code[0] = op_entry->op_code[0]; + op_code[1] = op_entry->op_code[1]; + specasm_line_set_size(line, 1); + return args - start; +} +#endif + static uint8_t prv_parse_org_e(const char *args, specasm_line_t *line, const specasm_opcode_t *op_entry) { @@ -795,7 +377,7 @@ static uint8_t prv_parse_org_e(const char *args, specasm_line_t *line, uint16_t val; const char *start = args; - args = prv_get_uword_imm_e(args, &val, &flags); + args = specasm_get_uword_imm_e(args, &val, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -812,8 +394,6 @@ static uint8_t prv_parse_bit_e(const char *args, specasm_line_t *line, uint8_t off; uint8_t val; uint8_t flags; - uint8_t read; - uint8_t label_type; uint8_t *op_code; uint8_t sz = 1; const char *start = args; @@ -825,15 +405,10 @@ static uint8_t prv_parse_bit_e(const char *args, specasm_line_t *line, if (*args == '=') { op_code = &line->data.op_code[0]; - read = specasm_parse_exp_e(args + 1, &op_code[2], &label_type); - if (err_type != SPECASM_ERROR_OK) - return 0; - specasm_line_set_addr_type(line, label_type); - line->type += SPECASM_LINE_TYPE_EXP_ADJ; - args += read + 1; + args = specasm_get_exp_e(line, args, &op_code[2]); val = 0; } else { - args = prv_get_byte_imm_e(args, &val, &flags); + args = specasm_get_byte_imm_e(args, &val, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -852,7 +427,7 @@ static uint8_t prv_parse_bit_e(const char *args, specasm_line_t *line, } args++; - args += prv_parse_reg_e(args, ®, &off, &flags); + args += specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -965,7 +540,7 @@ static uint8_t prv_parse_djnz_e(const char *args, specasm_line_t *line, while (*args == ' ') ++args; - args = prv_parse_jump_label_e(args, line, &line->data.op_code[1]); + args = specasm_parse_jump_label_e(args, line, &line->data.op_code[1]); if (err_type != SPECASM_ERROR_OK) return 0; line->data.op_code[0] = 0x10; @@ -983,17 +558,9 @@ static uint8_t prv_signed_ok(uint8_t flags, uint8_t val) static const char *prv_parse_data_exp_e(const char *args, specasm_line_t *line, uint8_t size) { - uint8_t read; - uint8_t label_type; - - read = specasm_parse_exp_e(args, &line->data.op_code[0], &label_type); - if (err_type != SPECASM_ERROR_OK) - return 0; - specasm_line_set_addr_type(line, label_type); + args = specasm_get_exp_e(line, args - 1, &line->data.op_code[0]); specasm_line_set_size(line, size); - line->type += SPECASM_LINE_TYPE_EXP_ADJ; - - return args + read; + return args; } static uint8_t prv_parse_db_e(const char *args, specasm_line_t *line, @@ -1015,7 +582,7 @@ static uint8_t prv_parse_db_e(const char *args, specasm_line_t *line, return args - start; } - args = prv_get_byte_imm_e(args, &val, &flags); + args = specasm_get_byte_imm_e(args, &val, &flags); if (err_type != SPECASM_ERROR_OK) return 0; line->data.op_code[0] = val; @@ -1027,7 +594,7 @@ static uint8_t prv_parse_db_e(const char *args, specasm_line_t *line, if (*args2 != ',') break; signed_ok = prv_signed_ok(flags, val); - args = prv_get_byte_imm_e(args2 + 1, &val, &flags2); + args = specasm_get_byte_imm_e(args2 + 1, &val, &flags2); if (err_type != SPECASM_ERROR_OK) return 0; if (flags2 != flags) { @@ -1073,7 +640,7 @@ static uint8_t prv_parse_dw_e(const char *args, specasm_line_t *line, return args - start; } - args = prv_get_word_imm_e(args, &val, &flags); + args = specasm_get_word_imm_e(args, &val, &flags); if (err_type == SPECASM_ERROR_NUM_TOO_BIG) return 0; if (err_type == SPECASM_ERROR_OK) { @@ -1083,7 +650,7 @@ static uint8_t prv_parse_dw_e(const char *args, specasm_line_t *line, ++args2; if (*args2 == ',') { signed_ok = prv_signed_ok_u16(flags, val); - args = prv_get_word_imm_e(args2 + 1, &val, &flags2); + args = specasm_get_word_imm_e(args2 + 1, &val, &flags2); if (err_type != SPECASM_ERROR_OK) return 0; if (flags2 != flags) { @@ -1105,7 +672,7 @@ static uint8_t prv_parse_dw_e(const char *args, specasm_line_t *line, op_code = &line->data.op_code[0]; while (*args2 == ' ') ++args2; - args = prv_parse_jump_label_e(args2, line, op_code); + args = specasm_parse_jump_label_e(args2, line, op_code); if (err_type != SPECASM_ERROR_OK) return 0; } @@ -1127,10 +694,10 @@ static uint8_t prv_parse_ex_e(const char *args, specasm_line_t *line, const char *args2; uint8_t sz = 0; - args2 = prv_parse_reg_comma_e(args, ®1, &off1, &flags); + args2 = specasm_parse_reg_comma_e(args, ®1, &off1, &flags); if (err_type != SPECASM_ERROR_OK) return 0; - args2 += prv_parse_reg_e(args2, ®2, &off2, &flags); + args2 += specasm_parse_reg_e(args2, ®2, &off2, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -1172,7 +739,7 @@ static uint8_t prv_parse_im_e(const char *args, specasm_line_t *line, uint8_t val; const char *args2; - args2 = prv_parse_byte_imm_or_exp_e(args, line, &val, + args2 = specasm_parse_byte_imm_or_exp_e(args, line, &val, &line->data.op_code[2]); if (err_type != SPECASM_ERROR_OK) return 0; @@ -1210,7 +777,7 @@ static uint8_t prv_parse_in_e(const char *args, specasm_line_t *line, uint8_t *op_code; const char *start = args; - args = prv_parse_reg_comma_e(args, ®, &off, &flags); + args = specasm_parse_reg_comma_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -1219,12 +786,12 @@ static uint8_t prv_parse_in_e(const char *args, specasm_line_t *line, return 0; } - args2 = args + prv_parse_reg_e(args, ®2, &off, &flags); + args2 = args + specasm_parse_reg_e(args, ®2, &off, &flags); if (err_type != SPECASM_ERROR_OK) { if (reg != SPECASM_BYTE_REG_A) return 0; err_type = SPECASM_ERROR_OK; - args = prv_get_byte_imm_ind_e(line, args, &val); + args = specasm_get_byte_imm_ind_e(line, args, &val); if (err_type != SPECASM_ERROR_OK) return 0; op_code = &line->data.op_code[0]; @@ -1262,7 +829,7 @@ static uint8_t prv_parse_jp_e(const char *args, specasm_line_t *line, uint8_t *op_code; const char *start = args; - read = prv_parse_reg_e(args, ®, &off, &flags); + read = specasm_parse_reg_e(args, ®, &off, &flags); if ((err_type == SPECASM_ERROR_OK) && (reg != SPECASM_BYTE_REG_C)) { sz = 1; switch (reg) { @@ -1278,6 +845,12 @@ static uint8_t prv_parse_jp_e(const char *args, specasm_line_t *line, op1 = 0xFD; op2 = 0xE9; break; +#ifdef SPECASM_TARGET_NEXT_OPCODES + case SPECASM_BYTE_REG_C_IND: + op1 = 0xED; + op2 = 0x98; + break; +#endif default: err_type = SPECASM_ERROR_BAD_REG; return 0; @@ -1311,7 +884,7 @@ static uint8_t prv_parse_jp_e(const char *args, specasm_line_t *line, while (*args == ' ') ++args; - args = prv_parse_label_or_exp_e(args, line, &line->data.op_code[1]); + args = specasm_parse_label_or_exp_e(args, line, &line->data.op_code[1]); if (err_type != SPECASM_ERROR_OK) return 0; @@ -1402,13 +975,13 @@ static uint8_t prv_parse_absolute_jmp_e(const char *args, specasm_line_t *line, uint8_t flags; len = prv_parse_relative_jmp_e(args, line, op_entry, max_cc, - prv_parse_label_or_exp_e); + specasm_parse_label_or_exp_e); if (err_type != SPECASM_ERROR_BAD_LABEL) return len; start = args; err_type = SPECASM_ERROR_OK; - args = prv_get_uword_imm_e(args + len, &val, &flags); + args = specasm_get_uword_imm_e(args + len, &val, &flags); if (err_type != SPECASM_ERROR_OK) { err_type = SPECASM_ERROR_BAD_LABEL; return 0; @@ -1432,433 +1005,126 @@ static uint8_t prv_parse_jr_e(const char *args, specasm_line_t *line, { specasm_line_set_size(line, 1); return prv_parse_relative_jmp_e(args, line, op_entry, SPECASM_CC_C, - prv_parse_jump_label_e); + specasm_parse_jump_label_e); } -static void prv_set_ld_imm_ind_e(specasm_line_t *line, uint16_t val, - uint8_t reg, uint8_t mod) +static uint8_t prv_parse_ld_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) { + return specasm_parse_ld_e(args, line, op_entry); +} - uint8_t opc1; - uint8_t opc2 = 0; - uint8_t lab_loc = 2; +#ifdef SPECASM_TARGET_NEXT_OPCODES +static uint8_t prv_parse_mirror_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) +{ + uint8_t reg; + uint8_t off; + uint8_t flags; uint8_t *op_code; + const char *start = args; - switch (reg) { - case SPECASM_BYTE_REG_A: - opc1 = 0x32; - lab_loc = 1; - break; - case SPECASM_BYTE_REG_BC: - opc1 = 0xED; - opc2 = 0x43; - break; - case SPECASM_BYTE_REG_DE: - opc1 = 0xED; - opc2 = 0x53; - break; - case SPECASM_BYTE_REG_HL: - opc1 = 0x22; - lab_loc = 1; - break; - case SPECASM_BYTE_REG_SP: - opc1 = 0xED; - opc2 = 0x73; - break; - case SPECASM_BYTE_REG_IX: - opc1 = 0xDD; - opc2 = 0x22; - break; - case SPECASM_BYTE_REG_IY: - opc1 = 0xFD; - opc2 = 0x22; - break; - default: + args += specasm_parse_reg_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + if (reg != SPECASM_BYTE_REG_A) { err_type = SPECASM_ERROR_BAD_REG; - return; + return 0; } - op_code = &line->data.op_code[0]; - op_code[0] = opc1; - op_code[1] = opc2; - op_code[lab_loc - 1] |= mod; - - *((uint16_t *)&op_code[lab_loc]) = val; - specasm_line_set_size(line, lab_loc + 1); + op_code[0] = 0xED; + op_code[1] = 0x24; + specasm_line_set_size(line, 1); + return args - start; } -static const char *prv_parse_st_ind_imm_e(const char *args, - specasm_line_t *line, uint16_t val) +static uint8_t prv_parse_mul_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) { uint8_t reg; uint8_t off; uint8_t flags; + uint8_t *op_code; + const char *start = args; - while (*args == ' ') - ++args; - - if (*args++ != ',') { - err_type = SPECASM_ERROR_COMMA_EXPECTED; + args = specasm_parse_reg_comma_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + if (reg != SPECASM_BYTE_REG_D) { + err_type = SPECASM_ERROR_BAD_REG; return 0; } - args += prv_parse_reg_e(args, ®, &off, &flags); + args += specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; - - prv_set_ld_imm_ind_e(line, val, reg, 0); - - return args; -} - -static void prv_ld_16bit_imm_e(specasm_line_t *line, uint8_t reg, uint8_t flags, - uint16_t val) -{ - uint8_t *op_code; - uint8_t rind; - uint8_t i = 0; - - op_code = &line->data.op_code[0]; - switch (reg) { - case SPECASM_BYTE_REG_BC: - rind = 0x1; - break; - case SPECASM_BYTE_REG_DE: - rind = 0x11; - break; - case SPECASM_BYTE_REG_HL: - rind = 0x21; - break; - case SPECASM_BYTE_REG_SP: - rind = 0x31; - break; - case SPECASM_BYTE_REG_IX: - op_code[0] = 0xDD; - rind = 0x21; - i++; - break; - case SPECASM_BYTE_REG_IY: - op_code[0] = 0xFD; - rind = 0x21; - i++; - break; - default: + if (reg != SPECASM_BYTE_REG_E) { err_type = SPECASM_ERROR_BAD_REG; - return; - } - - op_code[i++] = rind; - *((uint16_t *)&op_code[i]) = val; - specasm_line_set_size(line, i + 1); - specasm_line_set_format(line, flags); -} - -static void prv_ld_imm_e(specasm_line_t *line, uint8_t reg, uint8_t off, - uint8_t flags, uint8_t flags2, uint16_t val) -{ - uint8_t sz = 1; - int16_t sval; - uint8_t *op_code; - - if (reg <= SPECASM_BYTE_REG_A) { - op_code = &line->data.op_code[0]; - op_code[0] = 0x6 | (reg << 3); - op_code[1] = (uint8_t)val; - goto set_format; - } - - if (reg == SPECASM_BYTE_REG_HL_IND) { - op_code = &line->data.op_code[0]; - op_code[0] = 0x36; - op_code[1] = (uint8_t)val; - goto set_format; - } - - if ((reg == SPECASM_BYTE_REG_IX_OFF) || - (reg == SPECASM_BYTE_REG_IY_OFF)) { - op_code = &line->data.op_code[0]; - op_code[0] = (reg == SPECASM_BYTE_REG_IX_OFF) ? 0xDD : 0xFD; - op_code[1] = 0x36; - op_code[2] = off; - op_code[3] = (uint8_t)val; - sz = 3; - specasm_line_set_format(line, flags); - specasm_line_set_format2(line, flags2); - goto check_byte; - } - - prv_ld_16bit_imm_e(line, reg, flags2, val); - - return; - -set_format: - specasm_line_set_format(line, flags2); - -check_byte: - sval = (int16_t)val; - if ((sval > 255) || (sval < -128)) { - err_type = SPECASM_ERROR_NUM_TOO_BIG; - return; - } - - specasm_line_set_size(line, sz); -} - -static void prv_store_ind_reg_e(specasm_line_t *line, uint8_t reg, uint8_t reg2) -{ - uint8_t *op_code = &line->data.op_code[0]; - - /* - * Size is implicitly set to 0. - */ - - if (reg == SPECASM_BYTE_REG_HL_IND) { - if (reg2 > SPECASM_BYTE_REG_A) - goto fail; - op_code[0] = 0x70 | reg2; - return; + return 0; } - if (reg2 != SPECASM_BYTE_REG_A) - goto fail; - - if (reg == SPECASM_BYTE_REG_BC_IND) - op_code[0] = 0x2; - else if (reg == SPECASM_BYTE_REG_DE_IND) - op_code[0] = 0x12; - else - goto fail; - - return; - -fail: - err_type = SPECASM_ERROR_BAD_REG; + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = 0x30; + specasm_line_set_size(line, 1); + return args - start; } -static void prv_ld_reg_reg_e(specasm_line_t *line, uint8_t reg, uint8_t off, - uint8_t reg2, uint8_t off2, uint8_t flags) +static uint8_t prv_parse_nbrk_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) { - uint8_t *op_code; - uint8_t sz = 0; - uint8_t hl_ind[] = {0x46, 0x4E, 0x56, 0x5E, 0x66, 0x6E, 0, 0x7E}; - - if (reg & SPECASM_IND_MOD) { - prv_store_ind_reg_e(line, reg, reg2); - return; - } else if (reg <= SPECASM_BYTE_REG_A) { - if (reg2 <= SPECASM_BYTE_REG_A) { - op_code = &line->data.op_code[0]; - op_code[0] = 0x40 | reg2 | (reg << 3); - } else if ((reg2 == SPECASM_BYTE_REG_IX_OFF) || - (reg2 == SPECASM_BYTE_REG_IY_OFF)) { - op_code = &line->data.op_code[0]; - op_code[0] = - (reg2 == SPECASM_BYTE_REG_IX_OFF) ? 0xdd : 0xfd; - op_code[1] = 0x46 | (reg << 3); - op_code[2] = off2; - specasm_line_set_format(line, flags); - sz = 2; - } else if (reg2 == SPECASM_BYTE_REG_HL_IND) { - op_code = &line->data.op_code[0]; - op_code[0] = hl_ind[reg]; - } else if (reg == SPECASM_BYTE_REG_A) { - switch (reg2) { - case SPECASM_BYTE_REG_BC_IND: - op_code = &line->data.op_code[0]; - op_code[0] = 0xA; - break; - case SPECASM_BYTE_REG_DE_IND: - op_code = &line->data.op_code[0]; - op_code[0] = 0x1A; - break; - case SPECASM_BYTE_REG_I: - op_code = &line->data.op_code[0]; - op_code[0] = 0xED; - op_code[1] = 0x57; - sz = 1; - break; - case SPECASM_BYTE_REG_R: - op_code = &line->data.op_code[0]; - op_code[0] = 0xED; - op_code[1] = 0x5F; - sz = 1; - break; - default: - goto fail; - } - } else { - goto fail; - } - } else if ((reg == SPECASM_BYTE_REG_I) || (reg == SPECASM_BYTE_REG_R)) { - if (reg2 != SPECASM_BYTE_REG_A) - goto fail; - op_code = &line->data.op_code[0]; - op_code[0] = 0xED; - op_code[1] = reg == SPECASM_BYTE_REG_I ? 0x47 : 0x4F; - sz = 1; - } else if (reg == SPECASM_BYTE_REG_SP) { - switch (reg2) { - case SPECASM_BYTE_REG_HL: - op_code = &line->data.op_code[0]; - op_code[0] = 0xF9; - break; - case SPECASM_BYTE_REG_IX: - op_code = &line->data.op_code[0]; - op_code[0] = 0xDD; - op_code[1] = 0xF9; - sz = 1; - break; - case SPECASM_BYTE_REG_IY: - op_code = &line->data.op_code[0]; - op_code[0] = 0xFD; - op_code[1] = 0xF9; - sz = 1; - break; - default: - goto fail; - } - } else if (reg2 <= SPECASM_BYTE_REG_A) { - if ((reg == SPECASM_BYTE_REG_IX_OFF) || - (reg == SPECASM_BYTE_REG_IY_OFF)) { - op_code = &line->data.op_code[0]; - op_code[0] = - (reg == SPECASM_BYTE_REG_IX_OFF) ? 0xdd : 0xfd; - op_code[1] = 0x70 | reg2; - op_code[2] = off; - specasm_line_set_format(line, flags); - sz = 2; - } else { - goto fail; - } - } else { - goto fail; - } - - specasm_line_set_size(line, sz); - - return; - -fail: - err_type = SPECASM_ERROR_BAD_REG; + line->data.op_code[0] = 0xED; + line->data.op_code[1] = 0x91; + line->data.op_code[2] = 2; + line->data.op_code[3] = 8; + specasm_line_set_size(line, 3); + return 0; } -static uint8_t prv_parse_ld_e(const char *args, specasm_line_t *line, - const specasm_opcode_t *op_entry) +static uint8_t prv_parse_nextreg_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) { const char *args2; - uint16_t val; - uint8_t label; - uint8_t reg; uint8_t off; - uint8_t reg2; - uint8_t off2; uint8_t flags; - uint8_t flags2; - uint8_t read; - uint8_t label_type; - uint8_t v; + uint8_t reg; const char *start = args; + uint8_t *ptr = &line->data.op_code[2]; - args2 = prv_get_word_imm_ind_e(args, &val, &flags); - if (err_type == SPECASM_ERROR_OK) { - specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); - specasm_line_set_format(line, flags); - } else if ((err_type == SPECASM_ERROR_NUM_TOO_BIG) || - (err_type == SPECASM_ERROR_NUM_NEG)) { - return 0; - } else { - err_type = SPECASM_ERROR_OK; - args2 = prv_get_label_ind_e(args, line, &label); - if (err_type == SPECASM_ERROR_BAD_EXPRESSION) - return 0; - val = label; - } - if (err_type == SPECASM_ERROR_OK) { - args = prv_parse_st_ind_imm_e(args2, line, val); - return args - start; - } - - /* - For some reason using prv_parse_reg_comma_e here actually makes - the program bigger. - */ - - err_type = SPECASM_ERROR_OK; - args += prv_parse_reg_e(args, ®, &off, &flags); + args = specasm_parse_byte_imm_or_exp_e(args, line, ptr, ptr); if (err_type != SPECASM_ERROR_OK) return 0; while (*args == ' ') ++args; - if (*args++ != ',') { err_type = SPECASM_ERROR_COMMA_EXPECTED; return 0; } - args2 = prv_get_word_imm_ind_e(args, &val, &flags); - if ((err_type == SPECASM_ERROR_NUM_TOO_BIG) || - (err_type == SPECASM_ERROR_NUM_NEG)) { - return 0; - } else if (err_type == SPECASM_ERROR_OK) { - specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); - specasm_line_set_format(line, flags); - } else { + + args2 = args + specasm_parse_reg_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) { err_type = SPECASM_ERROR_OK; - args2 = prv_get_label_ind_e(args, line, &label); - if (err_type == SPECASM_ERROR_BAD_EXPRESSION) - return 0; - val = label; - } - if (err_type == SPECASM_ERROR_OK) { - prv_set_ld_imm_ind_e(line, val, reg, 0x8); - return args2 - start; - } - err_type = SPECASM_ERROR_OK; - while (*args == ' ') - ++args; - if (*args == '=') { - read = specasm_parse_exp_e(args + 1, &v, &label_type); + args = specasm_get_byte_imm_e(args, &line->data.op_code[3], &flags); if (err_type != SPECASM_ERROR_OK) return 0; - specasm_line_set_addr_type(line, label_type); - args2 = args + read + 1; - line->type += SPECASM_LINE_TYPE_EXP_ADJ; - flags2 = 0; - val = v; + specasm_line_set_format2(line, flags); + line->data.op_code[1] = 0x91; + specasm_line_set_size(line, 3); } else { - args2 = prv_get_word_imm_e(args, &val, &flags2); - } - if (err_type == SPECASM_ERROR_NUM_TOO_BIG) { - return 0; - } else if (err_type == SPECASM_ERROR_OK) { - prv_ld_imm_e(line, reg, off, flags, flags2, val); - return args2 - start; - } else if ((reg >= SPECASM_BYTE_REG_BC) && - (reg <= SPECASM_BYTE_REG_IY)) { - /* - * For 16 bit immediate loads we allow labels in place of - * the 16 bit value. - */ - - err_type = SPECASM_ERROR_OK; - args2 = prv_parse_jump_label_e(args, line, &v); - val = v; - if (err_type == SPECASM_ERROR_OK) { - prv_ld_16bit_imm_e(line, reg, - specasm_line_get_format(line), val); - return args2 - start; + if (reg != SPECASM_BYTE_REG_A) { + err_type = SPECASM_ERROR_BAD_REG; + return 0; } + args = args2; + line->data.op_code[1] = 0x92; + specasm_line_set_size(line, 2); } - - err_type = SPECASM_ERROR_OK; - args += prv_parse_reg_e(args, ®2, &off2, &flags); - if (err_type != SPECASM_ERROR_OK) - return 0; - - prv_ld_reg_reg_e(line, reg, off, reg2, off2, flags); - + line->data.op_code[0] = 0xED; + ; return args - start; } +#endif static uint8_t prv_parse_out_e(const char *args, specasm_line_t *line, const specasm_opcode_t *op_entry) @@ -1873,10 +1139,10 @@ static uint8_t prv_parse_out_e(const char *args, specasm_line_t *line, uint8_t val = 0; const char *start = args; - args2 = args + prv_parse_reg_e(args, ®, &off, &flags); + args2 = args + specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) { err_type = SPECASM_ERROR_OK; - args = prv_get_byte_imm_ind_e(line, args, &val); + args = specasm_get_byte_imm_ind_e(line, args, &val); if (err_type != SPECASM_ERROR_OK) return 0; n = 1; @@ -1894,7 +1160,7 @@ static uint8_t prv_parse_out_e(const char *args, specasm_line_t *line, } ++args; - args += prv_parse_reg_e(args, ®2, &off, &flags); + args += specasm_parse_reg_e(args, ®2, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -1928,13 +1194,40 @@ static uint8_t prv_parse_push_pop_e(const char *args, specasm_line_t *line, uint8_t off; uint8_t read; uint8_t flags; - uint8_t sz = 1; +#ifdef SPECASM_TARGET_NEXT_OPCODES + const char *start; + uint16_t val; + uint8_t val_bytes[2]; +#endif uint8_t *op_code; + uint8_t sz = 1; uint8_t ainstr = op_entry->op_code[0]; - read = prv_parse_reg_e(args, ®, &off, &flags); - if (err_type != SPECASM_ERROR_OK) + read = specasm_parse_reg_e(args, ®, &off, &flags); + if (err_type != SPECASM_ERROR_OK) { +#ifdef SPECASM_TARGET_NEXT_OPCODES + if (line->type == SPECASM_LINE_TYPE_PUSH) { + err_type = SPECASM_ERROR_OK; + start = args; + args = specasm_parse_word_imm_or_exp_e(args, line, &val, + &flags); + if (err_type == SPECASM_ERROR_BAD_NUM) + err_type = SPECASM_ERROR_BAD_REG; + if (err_type != SPECASM_ERROR_OK) + return 0; + memcpy(&val_bytes[0], &val, 2); + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = 0x8A; + op_code[2] = val_bytes[1]; + op_code[3] = val_bytes[0]; + specasm_line_set_size(line, 3); + specasm_line_set_format(line, flags); + return args - start; + } +#endif return 0; + } switch (reg) { case SPECASM_BYTE_REG_BC: @@ -1971,7 +1264,7 @@ static uint8_t prv_parse_rst_e(const char *args, specasm_line_t *line, uint8_t val; const char *start = args; - args = prv_parse_byte_imm_or_exp_e(args, line, &val, + args = specasm_parse_byte_imm_or_exp_e(args, line, &val, &line->data.op_code[1]); if (err_type != SPECASM_ERROR_OK) return 0; @@ -2006,7 +1299,7 @@ static uint8_t prv_parse_ds_e(const char *args, specasm_line_t *line, */ count = (uint16_t *)(&op_code[1]); - args2 = prv_get_uword_imm_e(args, count, &flags); + args2 = specasm_get_uword_imm_e(args, count, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -2024,7 +1317,7 @@ static uint8_t prv_parse_ds_e(const char *args, specasm_line_t *line, return 0; } - args2 = prv_get_byte_imm_e(args2 + 1, op_code, &flags); + args2 = specasm_get_byte_imm_e(args2 + 1, op_code, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -2069,7 +1362,7 @@ static uint8_t prv_parse_shift_e(const char *args, specasm_line_t *line, uint8_t sz = 1; uint8_t hl_ind = op_entry->op_code[0]; - read = prv_parse_reg_e(args, ®, &off, &flags); + read = specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) return 0; @@ -2110,6 +1403,24 @@ static uint8_t prv_parse_shift_e(const char *args, specasm_line_t *line, return read; } +#ifdef SPECASM_TARGET_NEXT_OPCODES +static uint8_t prv_parse_test_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) +{ + uint8_t *op_code; + const char *args2; + + op_code = &line->data.op_code[0]; + op_code[0] = 0xED; + op_code[1] = 0x27; + args2 = + specasm_parse_byte_imm_or_exp_e(args, line, &op_code[2], &op_code[2]); + specasm_line_set_size(line, 2); + + return args2 - args; +} +#endif + static uint8_t prv_parse_align_e(const char *args, specasm_line_t *line, const specasm_opcode_t *op_entry) { @@ -2145,12 +1456,19 @@ static uint8_t prv_parse_align_e(const char *args, specasm_line_t *line, /* clang-format off */ -const specasm_opcode_t opcode_table[] = { +static const specasm_opcode_t opcode_table[] = { { prv_parse_adc_sbc_e, {0x4A, 0x88} }, // SPECASM_LINE_TYPE_ADC { prv_parse_add_e, }, // SPECASM_LINE_TYPE_ADD { prv_parse_align_e, }, // SPECASM_LINE_TYPE_ALIGN { prv_parse_arith_e, {0xE6, 0xA0} }, // SPECASM_LINE_TYPE_AND { prv_parse_bit_e, { 0x40 } }, // SPECASM_LINE_TYPE_BIT +#ifdef SPECASM_TARGET_NEXT_OPCODES + { prv_parse_barrel_e, { 0xED, 0x2C } }, // SPECASM_LINE_TYPE_BRLC + { prv_parse_barrel_e, { 0xED, 0x28 } }, // SPECASM_LINE_TYPE_BSLA + { prv_parse_barrel_e, { 0xED, 0x29 } }, // SPECASM_LINE_TYPE_BSRA + { prv_parse_barrel_e, { 0xED, 0x2B } }, // SPECASM_LINE_TYPE_BSRF + { prv_parse_barrel_e, { 0xED, 0x2A } }, // SPECASM_LINE_TYPE_BSRL +#endif { prv_parse_call_e, {0xC4, 0xCD} }, // SPECASM_LINE_TYPE_CALL { NULL, {0x3F} }, // SPECASM_LINE_TYPE_CCF { prv_parse_arith_e, {0xFE, 0xB8} }, // SPECASM_LINE_TYPE_CP @@ -2182,10 +1500,28 @@ const specasm_opcode_t opcode_table[] = { { prv_parse_ld_e, }, // SPECASM_LINE_TYPE_LD { NULL, {0xED, 0xA8} }, // SPECASM_LINE_TYPE_LDD { NULL, {0xED, 0xB8} }, // SPECASM_LINE_TYPE_LDDR +#ifdef SPECASM_TARGET_NEXT_OPCODES + { NULL, {0xED, 0xBC} }, // SPECASM_LINE_TYPE_LDDRX + { NULL, {0xED, 0xAC} }, // SPECASM_LINE_TYPE_LDDX +#endif { NULL, {0xED, 0xA0} }, // SPECASM_LINE_TYPE_LDI { NULL, {0xED, 0xB0} }, // SPECASM_LINE_TYPE_LDIR +#ifdef SPECASM_TARGET_NEXT_OPCODES + { NULL, {0xED, 0xB4} }, // SPECASM_LINE_TYPE_LDIRX + { NULL, {0xED, 0xA4} }, // SPECASM_LINE_TYPE_LDIX + { NULL, {0xED, 0xB7} }, // SPECASM_LINE_TYPE_LDPIRX + { NULL, {0xED, 0xA5} }, // SPECASM_LINE_TYPE_LDWS +#endif { NULL, }, // SPECASM_LINE_TYPE_MAP +#ifdef SPECASM_TARGET_NEXT_OPCODES + { prv_parse_mirror_e, }, // SPECASM_LINE_TYPE_MIRROR + { prv_parse_mul_e, }, // SPECASM_LINE_TYPE_MUL + { prv_parse_nbrk_e, }, // SPECASM_LINE_TYPE_NBRK +#endif { NULL, {0xED, 0x44} }, // SPECASM_LINE_TYPE_NEG +#ifdef SPECASM_TARGET_NEXT_OPCODES + { prv_parse_nextreg_e, }, // SPECASM_LINE_TYPE_NEXTREG +#endif { NULL, }, // SPECASM_LINE_TYPE_NOP { prv_parse_arith_e, {0xF6, 0xB0} }, // SPECASM_LINE_TYPE_OR { prv_parse_org_e, }, // SPECASM_LINE_TYPE_ORG @@ -2194,6 +1530,11 @@ const specasm_opcode_t opcode_table[] = { { prv_parse_out_e, }, // SPECASM_LINE_TYPE_OUT { NULL, {0xED, 0xAB} }, // SPECASM_LINE_TYPE_OUTD { NULL, {0xED, 0xA3} }, // SPECASM_LINE_TYPE_OUTI +#ifdef SPECASM_TARGET_NEXT_OPCODES + { NULL, {0xED, 0x90} }, // SPECASM_LINE_TYPE_OUTINB + { NULL, {0xED, 0x94} }, // SPECASM_LINE_TYPE_PIXELAD + { NULL, {0xED, 0x93} }, // SPECASM_LINE_TYPE_PIXELDN +#endif { prv_parse_push_pop_e, {0x1} }, // SPECASM_LINE_TYPE_POP { prv_parse_push_pop_e, {0x5} }, // SPECASM_LINE_TYPE_PUSH { prv_parse_bit_e, {0x80} }, // SPECASM_LINE_TYPE_RES @@ -2214,20 +1555,32 @@ const specasm_opcode_t opcode_table[] = { { prv_parse_adc_sbc_e, {0x42, 0x98} }, // SPECASM_LINE_TYPE_SBC { NULL, { 0x37 } }, // SPECASM_LINE_TYPE_SCF { prv_parse_bit_e, { 0xC0 } }, // SPECASM_LINE_TYPE_SET +#ifdef SPECASM_TARGET_NEXT_OPCODES + { NULL, { 0xED, 0x95 } }, // SPECASM_LINE_TYPE_SETAE +#endif { prv_parse_shift_e, {0x26} }, // SPECASM_LINE_TYPE_SLA { prv_parse_shift_e, {0x2E} }, // SPECASM_LINE_TYPE_SRA { prv_parse_shift_e, {0x3E} }, // SPECASM_LINE_TYPE_SRL { prv_parse_arith_e, {0xD6, 0x90} }, // SPECASM_LINE_TYPE_SUB +#ifdef SPECASM_TARGET_NEXT_OPCODES + { NULL, {0xED, 0x23} }, // SPECASM_LINE_TYPE_SWAPNIB + { prv_parse_test_e, }, // SPECASM_LINE_TYPE_TEST +#endif { prv_parse_arith_e, {0xEE, 0xA8} }, // SPECASM_LINE_TYPE_XOR }; /* clang-format on */ -const uint8_t opcode_table_size = +static const uint8_t opcode_table_size = sizeof(opcode_table) / sizeof(specasm_opcode_t); +#ifdef SPECASM_NEXT_BANKED +uint8_t specasm_parse_exp_banked_e(const char *str, uint8_t *label1, + uint8_t *label1_type) +#else uint8_t specasm_parse_exp_e(const char *str, uint8_t *label1, uint8_t *label1_type) +#endif { uint8_t j; uint8_t len; @@ -2281,8 +1634,13 @@ uint8_t specasm_parse_exp_e(const char *str, uint8_t *label1, return end + 1; } +#ifdef SPECASM_NEXT_BANKED +uint8_t specasm_parse_mnemomic_banked_e(const char *str, uint8_t i, + specasm_line_t *line) +#else uint8_t specasm_parse_mnemomic_e(const char *str, uint8_t i, specasm_line_t *line) +#endif { uint8_t m; uint8_t l; diff --git a/src/line_parse_common.c b/src/line_parse_common.c new file mode 100644 index 0000000..90ab650 --- /dev/null +++ b/src/line_parse_common.c @@ -0,0 +1,502 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include + +#include "line.h" +#include "line_common.h" +#include "line_parse_common.h" +#include "state.h" + +const char *specasm_parse_jump_label_e(const char *args, + specasm_line_t *line, uint8_t *label) +{ + uint8_t long_label; + uint8_t i = 0; + + i = 0; + while (*args != '-' && *args != ',' && *args != ' ' && *args != 0 && + *args != ';' && *args != ')') + scratch[i++] = *args++; + scratch[i] = 0; + specasm_state_check_label_e(scratch); + if (err_type != SPECASM_ERROR_OK) + return 0; + if (i >= SPECASM_MAX_SHORT_LEN) { + long_label = SPECASM_FLAGS_ADDR_LONG; + *label = specasm_state_add_long_e(scratch); + } else { + long_label = SPECASM_FLAGS_ADDR_SHORT; + *label = specasm_state_add_short_e(scratch); + } + if (err_type != SPECASM_ERROR_OK) + return 0; + + specasm_line_set_addr_type(line, long_label); + + return args; +} + +const char *specasm_get_exp_e(specasm_line_t *line, const char *args, + uint8_t *val) +{ + uint8_t label_type; + uint8_t read; + + read = specasm_parse_exp_e(args + 1, val, &label_type); + if (err_type != SPECASM_ERROR_OK) + return NULL; + specasm_line_set_addr_type(line, label_type); + args += read + 1; + line->type += SPECASM_LINE_TYPE_EXP_ADJ; + return args; +} + +const char *specasm_get_char_imm_e(const char *str, uint8_t *val, + uint8_t *flags) +{ + while (*str == ' ') + ++str; + + if (*str == '\'') { + if (str[1] != '\'' && str[2] == '\'') { + *val = str[1]; + *flags = SPECASM_FLAGS_NUM_CHAR; + return (&str[3]); + } + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; + } + + return NULL; +} + +const char *specasm_get_word_imm_e(const char *str, uint16_t *val, + uint8_t *flags) +{ + const char *end_ptr; + long lval; + uint8_t bval; + + end_ptr = specasm_get_char_imm_e(str, &bval, flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + + if (end_ptr) { + *val = (uint16_t)bval; + return end_ptr; + } + + end_ptr = specasm_get_long_imm_e(str, &lval, flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + if ((lval > 0xffff) || (lval < -32768)) + goto on_error; + *val = (uint16_t)lval; + return end_ptr; + +on_error: + err_type = SPECASM_ERROR_NUM_TOO_BIG; + return NULL; +} + +const char *specasm_parse_word_imm_or_exp_e(const char *args, + specasm_line_t *line, + uint16_t *val, uint8_t *flags) +{ + uint8_t label; + + while (*args == ' ') + ++args; + + if (*args == '=') { + args = specasm_get_exp_e(line, args, &label); + if (err_type != SPECASM_ERROR_OK) + return NULL; + *val = label; + *flags = 0; + return args; + } + + args = specasm_get_word_imm_e(args, val, flags); + return args; +} + +const char * +specasm_parse_label_or_exp_e(const char *args, specasm_line_t *line, + uint8_t *label) +{ + const char *args2; + + if (*args == '=') + args2 = specasm_get_exp_e(line, args, label); + else + args2 = specasm_parse_jump_label_e(args, line, label); + + return args2; +} + +char *specasm_get_uword_imm_e(const char *str, uint16_t *val, uint8_t *flags) +{ + char *end_ptr; + long lval; + + end_ptr = specasm_get_long_imm_e(str, &lval, flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + if (*flags == SPECASM_FLAGS_NUM_SIGNED) { + err_type = SPECASM_ERROR_NUM_NEG; + return NULL; + } + if (lval > 0xffff) { + err_type = SPECASM_ERROR_NUM_TOO_BIG; + return NULL; + } + + *val = (uint16_t)lval; + return end_ptr; +} + +char *specasm_get_long_imm_e(const char *str, long *val, uint8_t *flags) +{ + int base = 10; + char *end_ptr; + + while (*str == ' ') + ++str; + + if (*str == '$') { + base = 16; + ++str; + } + + *val = strtol((char *)str, &end_ptr, base); + if (end_ptr == (char *)str) + goto on_error; + if (base == 16) { + if (*val < 0) + goto on_error; + *flags = SPECASM_FLAGS_NUM_HEX; + } else { + if (*val < 0) + *flags = SPECASM_FLAGS_NUM_SIGNED; + else + *flags = SPECASM_FLAGS_NUM_UNSIGNED; + } + + return end_ptr; + +on_error: + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; +} + +static char *prv_get_offset_imm_e(const char *str, uint8_t *val, uint8_t *flags) +{ + char *end_ptr; + long lval; + + end_ptr = specasm_get_long_imm_e(str, &lval, flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + if (*flags == SPECASM_FLAGS_NUM_HEX) { + if (lval > 255) + goto on_error; + } else { + if ((lval < -128) || (lval > 127)) + goto on_error; + } + *val = (uint8_t)lval; + return end_ptr; + +on_error: + err_type = SPECASM_ERROR_NUM_TOO_BIG; + return NULL; +} + +uint8_t specasm_parse_reg_e(const char *str, uint8_t *r, uint8_t *off, + uint8_t *flags) +{ + uint8_t reg; + uint8_t i; + const char *start = str; + + while (*str == ' ') + ++str; + + if (*str == 0) { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + + /* + * Check for indirect registers. + */ + + reg = *str++; + if (reg == '(') { + while (*str == ' ') + ++str; + if (!*str) { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + if (*str == 'h' && *(str + 1) == 'l') { + reg = SPECASM_BYTE_REG_HL_IND; + str += 2; + } else if (*str == 'b' && *(str + 1) == 'c') { + reg = SPECASM_BYTE_REG_BC_IND; + str += 2; + } else if (*str == 'd' && *(str + 1) == 'e') { + reg = SPECASM_BYTE_REG_DE_IND; + str += 2; + } else if (*str == 's' && *(str + 1) == 'p') { + reg = SPECASM_BYTE_REG_SP_IND; + str += 2; + } else if (*str == 'i') { + ++str; + if (*str == 'x') { + reg = SPECASM_BYTE_REG_IX_OFF; + } else if (*str == 'y') { + reg = SPECASM_BYTE_REG_IY_OFF; + } else { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + ++str; + while (*str == ' ') + ++str; + + if (*str == ')') { + reg += 2; + str++; + goto finish; + } + + if (*str++ != '+') { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + str = prv_get_offset_imm_e(str, off, flags); + if (err_type != SPECASM_ERROR_OK) + return 0; + } else if (str[1] == ' ' || str[1] == ')') { + for (i = 0; i < sizeof(byte_regs); i++) + if (byte_regs[i] == *str) + break; + if (i == sizeof(byte_regs)) { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + ++str; + reg = i + SPECASM_IND_MOD; + } + while (*str == ' ') + ++str; + if (*str++ != ')') { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + + goto finish; + } + + /* Check for byte reg */ + + if (*str == 0 || *str == ' ' || *str == ';' || *str == ',') { + for (i = 0; i < sizeof(byte_regs); i++) + if (byte_regs[i] == reg) + break; + + if (i < sizeof(byte_regs)) { + reg = i; + } else if (reg == 'i') { + reg = SPECASM_BYTE_REG_I; + } else if (reg == 'r') { + reg = SPECASM_BYTE_REG_R; + } else { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + goto finish; + } + + /* We must have a 16 bit reg */ + + if (reg == 'h' && *str == 'l') { + reg = SPECASM_BYTE_REG_HL; + } else if (reg == 'd' && *str == 'e') { + reg = SPECASM_BYTE_REG_DE; + } else if (reg == 'b' && *str == 'c') { + reg = SPECASM_BYTE_REG_BC; + } else if (reg == 's' && *str == 'p') { + reg = SPECASM_BYTE_REG_SP; + } else if (reg == 'a' && *str == 'f') { + if (str[1] == '\'') { + reg = SPECASM_BYTE_REG_AF_P; + str++; + } else { + reg = SPECASM_BYTE_REG_AF; + } + } else if (reg == 'i') { + if (*str == 'y') { + reg = SPECASM_BYTE_REG_IY; + } else if (*str == 'x') { + reg = SPECASM_BYTE_REG_IX; + } else { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + } else { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + str++; + if (*str != ' ' && *str != ',' && *str != 0 && *str != ';') { + err_type = SPECASM_ERROR_BAD_REG; + return 0; + } + +finish: + *r = reg; + return (uint8_t)(str - start); +} + +/* + * The following functions are only used by line_parse.c but don't fit + * in the 8Kb page. + */ + +const char *specasm_get_byte_imm_e(const char *str, uint8_t *val, + uint8_t *flags) +{ + const char *end_ptr; + long lval; + + end_ptr = specasm_get_char_imm_e(str, val, flags); + if (end_ptr || err_type != SPECASM_ERROR_OK) + return end_ptr; + + end_ptr = specasm_get_long_imm_e(str, &lval, flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + if (lval > 255 || lval < -128) + goto on_error; + *val = (uint8_t)lval; + return end_ptr; + +on_error: + err_type = SPECASM_ERROR_NUM_TOO_BIG; + return NULL; +} + +const char *specasm_get_byte_imm_ind_e(specasm_line_t *line, + const char *args, uint8_t *val) +{ + long v; + uint8_t flags; + uint8_t label; + + while (*args == ' ') + ++args; + + if (*args != '(') { + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; + } + + ++args; + while (*args == ' ') + ++args; + + if (*args == '=') { + args = specasm_get_exp_e(line, args, &label); + if (err_type != SPECASM_ERROR_OK) + return NULL; + v = label; + } else { + args = specasm_get_long_imm_e(args, &v, &flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + if (flags == SPECASM_FLAGS_NUM_SIGNED) { + err_type = SPECASM_ERROR_NUM_NEG; + return NULL; + } + if (v > 0xff) { + err_type = SPECASM_ERROR_NUM_TOO_BIG; + return NULL; + } + specasm_line_set_format(line, flags); + } + + while (*args == ' ') + ++args; + + if (*args != ')') { + err_type = SPECASM_ERROR_BAD_NUM; + return NULL; + } + *val = v; + + return args + 1; +} + +const char *specasm_parse_byte_imm_or_exp_e(const char *args, + specasm_line_t *line, + uint8_t *val, uint8_t *label) +{ + uint8_t flags; + + while (*args == ' ') + ++args; + + if (*args == '=') { + args = specasm_get_exp_e(line, args, label); + if (err_type != SPECASM_ERROR_OK) + return NULL; + if (val != label) + *val = 0; + return args; + } + + args = specasm_get_byte_imm_e(args, val, &flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + + specasm_line_set_format(line, flags); + + return args; +} + +const char *specasm_parse_reg_comma_e(const char *args, uint8_t *r, + uint8_t *off, uint8_t *flags) +{ + const char *args2; + + args2 = args + specasm_parse_reg_e(args, r, off, flags); + if (err_type != SPECASM_ERROR_OK) + return NULL; + + while (*args2 == ' ') + ++args2; + + if (*args2 != ',') { + err_type = SPECASM_ERROR_COMMA_EXPECTED; + return NULL; + } + + return args2 + 1; +} diff --git a/src/line_parse_common.h b/src/line_parse_common.h new file mode 100644 index 0000000..e0fd608 --- /dev/null +++ b/src/line_parse_common.h @@ -0,0 +1,49 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef LINE_PARSE_COMMON_H +#define LINE_PARSE_COMMON_H + +#include + +char *specasm_get_uword_imm_e(const char *str, uint16_t *val, uint8_t *flags); +char *specasm_get_long_imm_e(const char *str, long *val, uint8_t *flags); +uint8_t specasm_parse_reg_e(const char *str, uint8_t *r, uint8_t *off, + uint8_t *flags); +const char *specasm_parse_jump_label_e(const char *args, + specasm_line_t *line, uint8_t *label); +const char * +specasm_parse_label_or_exp_e(const char *args, specasm_line_t *line, + uint8_t *label); +const char *specasm_get_char_imm_e(const char *str, uint8_t *val, + uint8_t *flags); +const char *specasm_get_word_imm_e(const char *str, uint16_t *val, + uint8_t *flags); +const char *specasm_parse_word_imm_or_exp_e(const char *args, + specasm_line_t *line, + uint16_t *val, uint8_t *flags); +const char *specasm_get_exp_e(specasm_line_t *line, const char *args, + uint8_t *val); +const char *specasm_get_byte_imm_e(const char *str, uint8_t *val, + uint8_t *flags); +const char *specasm_get_byte_imm_ind_e(specasm_line_t *line, + const char *args, uint8_t *val); +const char *specasm_parse_byte_imm_or_exp_e(const char *args, + specasm_line_t *line, + uint8_t *val, uint8_t *label); +const char *specasm_parse_reg_comma_e(const char *args, uint8_t *r, + uint8_t *off, uint8_t *flags); +#endif diff --git a/src/link_obj.c b/src/link_obj.c new file mode 100644 index 0000000..28aba25 --- /dev/null +++ b/src/link_obj.c @@ -0,0 +1,1144 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include +#include + +#include "expression.h" +#include "map.h" +#include "peer.h" +#include "salink.h" +#include "state_base.h" + +static uint8_t queued_files; +static uint8_t got_org; +static uint8_t map_file; +static size_t label_count; +static size_t main_index = SIZE_MAX; +static uint16_t start_address = 0x8000; + +static specasm_dirent_t dirent; + +static int prv_check_file(const char *fname) +{ + char *period; + + period = strchr(fname, '.'); + + if (!period) + return 0; + + return ((period[1] == 'x' || period[1] == 'X') && period[2] == 0); +} + +static void prv_init_out_fnames(salink_obj_t *obj) +{ + unsigned int i; + + for (i = 0; i < MAX_FNAME; i++) { + if (obj->fname[i] == '.') + break; + image_name[i] = obj->fname[i]; + } + image_name[i] = 0; + + (void)specasm_text_print(image_name, SALINK_VAL_COL + 1, + SALINK_FIELD_NAME_ROW, SPECASM_CODE_COLOUR); + + strcpy(map_name, image_name); + if (i + 4 > MAX_FNAME) + i = MAX_FNAME - 4; + map_name[i] = '.'; + map_name[i + 1] = 'm'; + map_name[i + 2] = 'a'; + map_name[i + 3] = 'p'; +} + +static salink_label_t *prv_find_local_label(salink_obj_t *obj, uint8_t lng, + uint8_t id) +{ + unsigned int i; + salink_label_t *label; + + for (i = obj->label_start; i < obj->label_end; i++) { + label = &labels[i]; + if (lng != label->type) + continue; + if (id == label->id) + return label; + } + + return NULL; +} + +static salink_global_t *prv_find_global_label_e(salink_obj_t *obj, + unsigned int line_no, + uint8_t id, int8_t lng) +{ + const char *str; + salink_global_t *global; + unsigned int i; + + str = salink_get_label_str_e(id, lng); + if (err_type != SPECASM_ERROR_OK) + return NULL; + + for (i = 0; i < global_count; i++) { + global = &globals[i]; + if (!strcmp(str, global->name)) + return global; + } + + snprintf(error_buf, sizeof(error_buf), "%s:%d Unknown: %s", obj->fname, + line_no, str); + err_type = SALINK_ERROR_UNRESOLVED_LABEL; + + return NULL; +} + +static void prv_resolve_address_e(salink_obj_t *obj, specasm_line_t *line, + unsigned int line_no, uint8_t id, + uint16_t *addr, uint8_t lng) +{ + salink_label_t *label; + salink_global_t *global; + + label = prv_find_local_label(obj, lng, id); + if (!label) { + /* + * Couldn't find a local address. Let's check to see whether + * it's global. + */ + + global = prv_find_global_label_e(obj, line_no, id, lng); + if (err_type != SPECASM_ERROR_OK) + return; + label = &labels[global->label_index]; + } + + *addr = label->data.off; +} + +static int16_t prv_eval_exp_from_id_e(salink_obj_t *obj, unsigned int line_no, + uint8_t id, uint8_t lng) +{ + const char *str; + int16_t exp; + + str = salink_get_label_str_e(id, lng); + if (err_type != SPECASM_ERROR_OK) + return 0; + + exp = salink_equ_eval_e(obj, str, line_no); + + if ((err_type != SPECASM_ERROR_OK) && (err_type < SPECASM_MAX_ERRORS)) { + snprintf(error_buf, sizeof(error_buf), "%s:%d %s", obj->fname, + line_no, specasm_error_msg(err_type)); + err_type = SALINK_ERROR_BAD_EXP; + } + + return exp; +} + +static void prv_resolve_relative_address_e(salink_obj_t *obj, + specasm_line_t *line, unsigned int i, + uint16_t offset) +{ + salink_label_t *label; + int16_t diff; + uint8_t addr_type = specasm_line_get_addr_type(line); + uint8_t lng = addr_type == SPECASM_FLAGS_ADDR_LONG + ? SALINK_LABEL_TYPE_LNG + : SALINK_LABEL_TYPE_SHORT; + + label = prv_find_local_label(obj, lng, line->data.op_code[1]); + if (!label) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d Unresolved symbol", obj->fname, i); + err_type = SALINK_ERROR_UNRESOLVED_LABEL; + return; + } + + diff = label->data.off - offset; + if (diff < -126 || diff > 129) { + snprintf(error_buf, sizeof(error_buf), + "%s line %d label too far", obj->fname, i); + err_type = SALINK_ERROR_JUMP_TOO_FAR; + return; + } + line->data.op_code[1] = (int8_t)(diff - 2); +} + +static void prv_write_line_e(specasm_handle_t f, specasm_line_t *line, + uint16_t size) +{ + unsigned int i; + unsigned int to_set; + uint8_t id; + const char *data; + + if ((buf_count + size > MAX_BUFFER_SIZE) || + ((buf_count > 0) && (line->type == SPECASM_LINE_TYPE_DS))) { + specasm_file_write_e(f, buf.file_buf, buf_count); + if (err_type != SPECASM_ERROR_OK) + return; + bin_size += buf_count; + buf_count = 0; + } + + if (line->type <= SPECASM_LINE_TYPE_SIMPLE_MAX) { + for (i = 0; i < size; i++) + buf.file_buf[buf_count++] = line->data.op_code[i]; + return; + } + + if (line->type == SPECASM_LINE_TYPE_DS) { + to_set = size < MAX_BUFFER_SIZE ? size : MAX_BUFFER_SIZE; + memset(&buf.file_buf[0], line->data.op_code[0], to_set); + do { + size -= to_set; + if (size == 0) { + buf_count = to_set; + return; + } + specasm_file_write_e(f, buf.file_buf, MAX_BUFFER_SIZE); + if (err_type != SPECASM_ERROR_OK) + return; + bin_size += MAX_BUFFER_SIZE; + buf_count = 0; + to_set = + size < MAX_BUFFER_SIZE ? size : MAX_BUFFER_SIZE; + } while (1); + } + + if ((line->type >= SPECASM_LINE_TYPE_STR_HSH_SHORT) && + (line->type <= SPECASM_LINE_TYPE_STR_AMP_LONG)) { + size--; + buf.file_buf[buf_count++] = size; + } + + if ((line->type >= SPECASM_LINE_TYPE_STR_SIN_SHORT) && + (line->type <= SPECASM_LINE_TYPE_STR_AMP_LONG)) { + id = line->data.label; + data = salink_get_label_str_e(id, line->type); + memcpy(&buf.file_buf[buf_count], data, size); + buf_count += size; + } +} + +static void prv_label_subtraction_e(salink_obj_t *obj, specasm_line_t *line, + unsigned int l, uint8_t id_pos) +{ + uint16_t a; + uint16_t b; + uint8_t id1; + uint8_t id2; + uint8_t lng; + uint8_t *op_code = &line->data.op_code[id_pos]; + + id1 = op_code[0]; + id2 = op_code[1]; + lng = op_code[2] == SPECASM_FLAGS_ADDR_LONG ? SALINK_LABEL_TYPE_LNG + : SALINK_LABEL_TYPE_SHORT; + prv_resolve_address_e(obj, line, l, id1, &a, lng); + if (err_type != SPECASM_ERROR_OK) + return; + lng = + specasm_line_get_addr_type(line) == SPECASM_FLAGS_ADDR_LONG ? 1 : 0; + prv_resolve_address_e(obj, line, l, id2, &b, lng); + if (err_type != SPECASM_ERROR_OK) + return; + if (b > a) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d Negative difference", obj->fname, l); + err_type = SALINK_ERROR_NEGATIVE_SIZE; + return; + } + *((uint16_t *)&op_code[0]) = a - b; +} + +static void prv_label_subtraction_byte_e(salink_obj_t *obj, + specasm_line_t *line, unsigned int l, + uint8_t id_pos) +{ + uint16_t diff; + + prv_label_subtraction_e(obj, line, l, id_pos); + if (err_type != SPECASM_ERROR_OK) + return; + + diff = *((uint16_t *)&line->data.op_code[id_pos]); + + if (diff > 255) { + snprintf(error_buf, sizeof(error_buf), "%s:%d Too big:%d", + obj->fname, l, diff); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + } +} + +static uint16_t prv_align_e(specasm_handle_t f, uint16_t align) +{ + unsigned int mask = align - 1; + unsigned int adjust = (start_address + bin_size + buf_count) & mask; + + if (adjust == 0) + return 0; + + adjust = align - adjust; + + if (buf_count + adjust > MAX_BUFFER_SIZE) { + specasm_file_write_e(f, buf.file_buf, buf_count); + if (err_type != SPECASM_ERROR_OK) + return 0; + bin_size += buf_count; + buf_count = 0; + } + memset(&buf.file_buf[buf_count], 0, adjust); + buf_count += adjust; + return adjust; +} + +static int16_t prv_eval_equ_label(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no, uint8_t id) +{ + uint8_t label_type; + uint8_t lng; + + label_type = specasm_line_get_addr_type(line); + lng = label_type == SPECASM_FLAGS_ADDR_SHORT ? 0 : 1; + return prv_eval_exp_from_id_e(obj, line_no, id, lng); +} + +static void prv_eval_equ_8bit_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no, uint8_t loc) +{ + int16_t exp; + + exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); + if (err_type != SPECASM_ERROR_OK) + return; + + if ((exp > 255) || (exp < -128)) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d immediate too big :%d", obj->fname, line_no, + exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + line->data.op_code[loc] = exp; +} + +static void prv_eval_equ_16bit_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no, uint8_t loc) +{ + int16_t exp; + + exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); + if (err_type != SPECASM_ERROR_OK) + return; + + *((uint16_t *)&line->data.op_code[loc]) = exp; +} + +static void prv_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no) +{ + int16_t exp; + uint8_t opcode0; + + line->type -= SPECASM_LINE_TYPE_EXP_ADJ; + + switch (line->type) { + case SPECASM_LINE_TYPE_ADC: + case SPECASM_LINE_TYPE_ADD: + case SPECASM_LINE_TYPE_AND: + case SPECASM_LINE_TYPE_CP: + case SPECASM_LINE_TYPE_IN: + case SPECASM_LINE_TYPE_OUT: + case SPECASM_LINE_TYPE_OR: + case SPECASM_LINE_TYPE_SBC: + case SPECASM_LINE_TYPE_SUB: + case SPECASM_LINE_TYPE_XOR: +#ifdef SPECASM_TARGET_NEXT_OPCODES + if ((line->type == SPECASM_LINE_TYPE_ADD) && + (line->data.op_code[0] == 0xED)) + prv_eval_equ_16bit_e(line, obj, line_no, 2); + else + prv_eval_equ_8bit_e(line, obj, line_no, 1); +#else + prv_eval_equ_8bit_e(line, obj, line_no, 1); +#endif + break; + case SPECASM_LINE_TYPE_RST: + exp = prv_eval_equ_label(line, obj, line_no, + line->data.op_code[1]); + if (err_type != SPECASM_ERROR_OK) + return; + if ((exp > 0x38) || (exp & 7)) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d bad argument to rst :%d", obj->fname, + line_no, exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + line->data.op_code[0] |= exp; + break; + case SPECASM_LINE_TYPE_LD: + opcode0 = line->data.op_code[0]; + if ((opcode0 & 0xC7) == 0x6) { + prv_eval_equ_8bit_e(line, obj, line_no, 1); + break; + } + switch (opcode0) { + case 0x1: + case 0x11: + case 0x21: + case 0x31: + case 0x2a: + case 0x3a: + case 0x22: + case 0x32: + prv_eval_equ_16bit_e(line, obj, line_no, 1); + break; + case 0xDD: + case 0xED: + case 0xFD: + prv_eval_equ_16bit_e(line, obj, line_no, 2); + break; + } + break; + case SPECASM_LINE_TYPE_BIT: + case SPECASM_LINE_TYPE_RES: + case SPECASM_LINE_TYPE_SET: + exp = prv_eval_equ_label(line, obj, line_no, + line->data.op_code[2]); + if (err_type != SPECASM_ERROR_OK) + return; + if (exp > 7) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d bad bit position :%d", obj->fname, + line_no, exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + line->data.op_code[1] |= ((uint8_t)exp) << 3; + break; + case SPECASM_LINE_TYPE_IM: + exp = prv_eval_equ_label(line, obj, line_no, + line->data.op_code[2]); + if (err_type != SPECASM_ERROR_OK) + return; + if (exp > 2) { + snprintf(error_buf, sizeof(error_buf), + "%s:%d bad arg for im :%d", obj->fname, + line_no, exp); + err_type = SALINK_ERROR_SIZE_TOO_BIG; + return; + } + if (exp == 2) + line->data.op_code[1] = 0x5e; + else if (exp == 1) + line->data.op_code[1] = 0x56; + break; + case SPECASM_LINE_TYPE_CALL: + case SPECASM_LINE_TYPE_JP: + prv_eval_equ_16bit_e(line, obj, line_no, 1); + break; + case SPECASM_LINE_TYPE_DB: + prv_eval_equ_8bit_e(line, obj, line_no, 0); + break; + case SPECASM_LINE_TYPE_DW: + prv_eval_equ_16bit_e(line, obj, line_no, 0); + break; +#ifdef SPECASM_TARGET_NEXT_OPCODES + case SPECASM_LINE_TYPE_TEST: + case SPECASM_LINE_TYPE_NEXTREG: + prv_eval_equ_8bit_e(line, obj, line_no, 2); + break; + case SPECASM_LINE_TYPE_PUSH: + prv_eval_equ_16bit_e(line, obj, line_no, 2); + break; +#endif + default: + snprintf(error_buf, sizeof(error_buf), + "%s:%d unexpected expression", obj->fname, line_no); + err_type = SALINK_ERROR_UNEXPECTED_EXP; + break; + } + + specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); +} + + +static uint8_t prv_add_label_e(salink_obj_t *obj, uint16_t size, uint8_t type, + uint8_t id, uint16_t line_no) +{ + char ibuf[16]; + unsigned int i; + const char *str; + const char *str1; + salink_label_t *label; + salink_global_t *global; + uint8_t retval = 0; + + if (label_count == MAX_LABELS) { + snprintf(error_buf, sizeof(error_buf), + "Max label limit %d reached", MAX_LABELS); + err_type = SALINK_ERROR_TOO_MANY_LABELS; + return 0; + } + label = &labels[label_count]; + label->id = id; + label->type = type; + label->data.off = size; + str = salink_get_label_str_e(id, type); + if (err_type != SPECASM_ERROR_OK) + return 0; + + if (str[0] >= 'A' && str[0] <= 'Z') { + if (global_count == MAX_GLOBALS) { + snprintf(error_buf, sizeof(error_buf), + "Global limit %d reached", MAX_GLOBALS); + err_type = SALINK_ERROR_TOO_MANY_GLOBALS; + return 0; + } + for (i = 0; i < global_count; i++) { + if (!strcmp(str, globals[i].name)) { + snprintf(error_buf, sizeof(error_buf), + "%s defined in %s:%d and %s:%d", str, + obj->fname, line_no, + obj_files[globals[i].obj_index].fname, + globals[i].line_no); + err_type = SALINK_ERROR_MULTIPLE_DEFS; + return 0; + } + } + if (!strcmp(str, "Main")) { + main_index = obj_file_count; + prv_init_out_fnames(obj); + } + global = &globals[i]; + strcpy(global->name, str); + global->obj_index = obj_file_count; + global->label_index = label_count; + global->line_no = line_no; + global_count++; + itoa(global_count, ibuf, 10); + (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, + SALINK_FIELD_GLOBALS_ROW, + SPECASM_CODE_COLOUR); + retval = 1; + } else { + for (i = obj->label_start; i < label_count; i++) { + if ((labels[i].type == SALINK_LABEL_TYPE_ALIGN) || + (labels[i].type > SALINK_LABEL_TYPE_EQU_LONG)) + continue; + + str1 = salink_get_label_str_e(labels[i].id, + labels[i].type); + if (err_type != SPECASM_ERROR_OK) + return 0; + + if (!strcmp(str, str1)) { + snprintf(error_buf, sizeof(error_buf), + "%s multiply defined in %s:%d", str, + obj->fname, line_no); + err_type = SALINK_ERROR_MULTIPLE_DEFS; + return 0; + } + } + } + label_count++; + + return retval; +} + +static void prv_add_align_e(salink_obj_t *obj, specasm_line_t *line, + uint16_t size) +{ + salink_label_t *label; + + if (label_count == MAX_LABELS) { + snprintf(error_buf, sizeof(error_buf), + "Max label limit %d reached", MAX_LABELS); + err_type = SALINK_ERROR_TOO_MANY_LABELS; + return; + } + label = &labels[label_count++]; + label->type = SALINK_LABEL_TYPE_ALIGN; + label->id = line->data.op_code[0]; + label->data.off = size; +} + +static void prv_add_queued_file_e(const char *base, const char *prefix, + specasm_line_t *line) +{ + const char *str; + uint8_t id; + int space_needed; + int prefix_len; + int base_len; + char *ptr; + char *start; + char *slash; + + if (queued_files == MAX_PENDING_X_FILES) { + snprintf(error_buf, sizeof(error_buf), + "Pending file limit %d reached", MAX_PENDING_X_FILES); + err_type = SALINK_ERROR_TOO_MANY_FILES; + return; + } + + id = line->data.label; + str = salink_get_label_str_e(id, line->type); + + base_len = 0; + if (str[0] != '/') { + slash = strrchr(base, '/'); + if (slash) + base_len = (slash - base) + 1; + } + + prefix_len = strlen(prefix); + space_needed = strlen(str) + prefix_len + base_len; + if (space_needed > MAX_FNAME) { + err_type = SPECASM_ERROR_BAD_FNAME; + return; + } + + ptr = &buf.fname[queued_files++][0]; + start = ptr; + if (base_len) { + strncpy(ptr, base, base_len); + ptr += base_len; + } + if (prefix_len) { + strcpy(ptr, prefix); + ptr += prefix_len; + } + strcpy(ptr, str); + if (start[space_needed - 2] != '.' && + (start[space_needed - 1] | 32) != 'x') { + if (space_needed + 2 > MAX_FNAME) { + err_type = SPECASM_ERROR_BAD_FNAME; + return; + } + start[space_needed] = '.'; + start[space_needed + 1] = 'x'; + start[space_needed + 2] = 0; + } +} + +static void prv_add_equ_label_e(specasm_line_t *line, salink_obj_t *obj, + uint16_t line_no) +{ + uint8_t type; + uint8_t *op_code; + uint8_t is_global; + salink_global_t *global; + uint8_t id; + const char *str; + salink_label_t *label; + + /* + * With local equ expressions we can just store the expression + * information in the label itself for later use. For global + * expressions we need to store it in the label name itself, + * as a second string. There's guaranteed to be enough room + * for this as the sum of the label and the expression cannot + * exceed the max line length. + */ + + op_code = &line->data.op_code[0]; + type = op_code[0] == SPECASM_LINE_TYPE_SL ? SALINK_LABEL_TYPE_EQU_SHORT + : SALINK_LABEL_TYPE_EQU_LONG; + is_global = prv_add_label_e(obj, 0, type, op_code[1], line_no); + if (err_type != SPECASM_ERROR_OK) + return; + label = &labels[label_count - 1]; + if (!is_global) { + label->data.equ[0] = op_code[2]; + label->data.equ[1] = op_code[3]; + return; + } + + global = &globals[global_count - 1]; + id = op_code[3]; + str = salink_get_label_str_e(id, op_code[2]); + if (err_type != SPECASM_ERROR_OK) + return; + strcpy(global->name + strlen(global->name) + 1, str); + label->type = SALINK_LABEL_TYPE_EQU_GLOBAL; +} + +static void prv_parse_obj_e(const char *fname) +{ + uint16_t i; + specasm_line_t *line; + salink_obj_t *obj; + char ibuf[16]; + uint8_t type; + uint16_t size = 0; + + if (obj_file_count == MAX_FILES) { + snprintf(error_buf, sizeof(error_buf), + "Max file limit %d reached", MAX_FILES); + err_type = SALINK_ERROR_TOO_MANY_FILES; + return; + } + + obj = &obj_files[obj_file_count]; + + if (strlen(fname) > MAX_FNAME) { + err_type = SPECASM_ERROR_STRING_TOO_LONG; + return; + } + strcpy(obj->fname, fname); + obj->label_start = label_count; + + specasm_load_e(fname); + if (err_type != SPECASM_ERROR_OK) { + snprintf(error_buf, sizeof(error_buf), "Can't open %s", fname); + err_type = SALINK_ERROR_CANT_OPEN; + return; + } + + for (i = 0; i < state.lines.num_lines; i++) { + line = &state.lines.lines[i]; + if ((line->type == SPECASM_LINE_TYPE_LL) || + (line->type == SPECASM_LINE_TYPE_SL)) { + type = line->type == SPECASM_LINE_TYPE_LL + ? SALINK_LABEL_TYPE_LNG + : SALINK_LABEL_TYPE_SHORT; + (void)prv_add_label_e(obj, size, type, line->data.label, + i); + } else if (line->type == SPECASM_LINE_TYPE_EQU) { + prv_add_equ_label_e(line, obj, i); + } else if (line->type == SPECASM_LINE_TYPE_ORG) { + if (got_org) { + strcpy(error_buf, + "Only one org statement allowed"); + err_type = SALINK_ERROR_TOO_MANY_ORGS; + return; + } + got_org = 1; + start_address = *((uint16_t *)&line->data.op_code[0]); + } else if (line->type == SPECASM_LINE_TYPE_MAP) { + map_file = 1; + } else if (line->type == SPECASM_LINE_TYPE_ALIGN) { + prv_add_align_e(obj, line, size); + } else if ((line->type >= SPECASM_LINE_TYPE_INC_SHORT) && + (line->type <= SPECASM_LINE_TYPE_INC_LONG)) { + prv_add_queued_file_e(obj->fname, "", line); + } else if ((line->type >= SPECASM_LINE_TYPE_INC_SYS_SHORT) && + (line->type <= SPECASM_LINE_TYPE_INC_SYS_LONG)) { + prv_add_queued_file_e(fname, "/specasm/", line); + } else { + size += specasm_compute_line_size(line); + } + + if (err_type != SPECASM_ERROR_OK) + return; + } + + obj_file_count++; + obj->label_end = label_count; + obj->size = size; + + itoa(obj_file_count, ibuf, 10); + (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, + SALINK_FIELD_FILES_ROW, SPECASM_CODE_COLOUR); +} + +static void prv_process_queued_files_e(void) +{ + const char *path; + + while (queued_files > 0) { + --queued_files; + path = &buf.fname[queued_files][0]; + prv_parse_obj_e(path); + if (err_type != SPECASM_ERROR_OK) + return; + } +} + +static uint8_t prv_order_objects_e(void) +{ + salink_obj_t tmp; + uint8_t loaded; + uint8_t i; + salink_global_t *glob; + + /* + * We want to put the object file with the Main label + * first. + */ + + if (main_index == SIZE_MAX) { + strcpy(error_buf, "No Main label defined"); + err_type = SALINK_ERROR_NO_MAIN; + return 0; + } + + loaded = main_index == (obj_file_count - 1); + if (main_index > 0) { + memcpy(&tmp, &obj_files[0], sizeof(obj_files[0])); + memcpy(&obj_files[0], &obj_files[main_index], + sizeof(obj_files[0])); + memcpy(&obj_files[main_index], &tmp, sizeof(obj_files[0])); + for (i = 0; i < global_count; i++) { + glob = &globals[i]; + if (glob->obj_index == 0) + glob->obj_index = main_index; + else if (glob->obj_index == main_index) + glob->obj_index = 0; + } + main_index = 0; + } + + return loaded; +} + +static void prv_complete_absolutes_e(void) +{ + unsigned int i; + uint16_t j; + salink_obj_t *obj; + salink_label_t *label; + unsigned int mask; + unsigned int adjust; + uint16_t align; + uint16_t real_off = start_address; + + for (i = 0; i < obj_file_count; i++) { + obj = &obj_files[i]; + for (j = obj->label_start; j < obj->label_end; j++) { + label = &labels[j]; + + if (label->type > SALINK_LABEL_TYPE_ALIGN) + continue; + + if (label->type == SALINK_LABEL_TYPE_ALIGN) { + align = 1 << label->id; + mask = align - 1; + adjust = (real_off + label->data.off) & mask; + if (adjust > 0) + real_off += align - adjust; + continue; + } + if (label->data.off > 0xffff - real_off) { + snprintf(error_buf, sizeof(error_buf), + "%s past end of memory", obj->fname); + err_type = SALINK_ERROR_PROGRAM_TOO_BIG; + return; + } + label->data.off += real_off; + } + real_off += obj->size; + } +} + +static void prv_evaluate_global_equs_e(void) +{ + uint8_t i; + salink_obj_t *obj; + salink_label_t *label; + salink_global_t *global; + + for (i = 0; i < global_count; i++) { + global = &globals[i]; + label = &labels[global->label_index]; + obj = &obj_files[global->obj_index]; + if (label->type == SALINK_LABEL_TYPE_EQU_GLOBAL) { + salink_equ_eval_global_e(obj, global, label, 0); + if (err_type != SPECASM_ERROR_OK) + return; + } + } +} + +static uint16_t prv_link_obj_e(specasm_handle_t f, salink_obj_t *obj, + uint16_t offset) +{ + unsigned int i; + specasm_line_t *line; + uint16_t size; + uint8_t addr_type; + uint8_t id_pos; + uint8_t id; + uint8_t lng; + uint16_t *addr; + + for (i = 0; i < state.lines.num_lines; i++) { + id_pos = 1; + line = &state.lines.lines[i]; + + if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) + prv_apply_expressions_e(line, obj, i); + + switch (line->type) { + case SPECASM_LINE_TYPE_ALIGN: + offset += prv_align_e(f, 1 << line->data.op_code[0]); + break; + case SPECASM_LINE_TYPE_DW: + case SPECASM_LINE_TYPE_CALL: + case SPECASM_LINE_TYPE_JP: + case SPECASM_LINE_TYPE_LD: + addr_type = specasm_line_get_addr_type(line); + if ((addr_type == SPECASM_FLAGS_ADDR_SHORT) || + (addr_type == SPECASM_FLAGS_ADDR_LONG)) { + /* + * For all these instructions the id is + * in the penultimate byte. + */ + id_pos = specasm_line_get_size(line) - 1; + id = line->data.op_code[id_pos]; + addr = (uint16_t *)&line->data.op_code[id_pos]; + lng = addr_type == SPECASM_FLAGS_ADDR_LONG ? 1 + : 0; + prv_resolve_address_e(obj, line, i, id, addr, + lng); + } + break; + case SPECASM_LINE_TYPE_DW_SUB: + id_pos = 0; + case SPECASM_LINE_TYPE_LD_IMM_16_SUB: + prv_label_subtraction_e(obj, line, i, id_pos); + break; + case SPECASM_LINE_TYPE_DB_SUB: + id_pos = 0; + case SPECASM_LINE_TYPE_LD_IMM_8_SUB: + prv_label_subtraction_byte_e(obj, line, i, id_pos); + break; + case SPECASM_LINE_TYPE_JR: + case SPECASM_LINE_TYPE_DJNZ: + prv_resolve_relative_address_e(obj, line, i, offset); + break; + default: + break; + } + if (err_type != SPECASM_ERROR_OK) + return 0; + + size = specasm_compute_line_size(line); + if (size > 0) { + prv_write_line_e(f, line, size); + if (err_type != SPECASM_ERROR_OK) + return 0; + offset += size; + } + } + + return offset; +} + +static void prv_link_e(uint8_t main_loaded) +{ + char ibuf[16]; + unsigned int i; + specasm_handle_t f; + uint16_t offset = start_address; + salink_obj_t *obj = &obj_files[0]; + + f = specasm_file_wopen_e(image_name); + if (err_type != SPECASM_ERROR_OK) + return; + for (i = 0; i < obj_file_count; i++) { + obj = &obj_files[i]; + if (i > 0 || !main_loaded) { + specasm_load_e(obj->fname); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + } + offset = prv_link_obj_e(f, obj, offset); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + } + + if (buf_count > 0) { + specasm_file_write_e(f, buf.file_buf, buf_count); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + bin_size += buf_count; + } + + if (bin_size > 0xffff - start_address) { + snprintf(error_buf, sizeof(error_buf), "%s past end of memory", + obj->fname); + err_type = SALINK_ERROR_PROGRAM_TOO_BIG; + goto on_error; + } + + itoa(bin_size, ibuf, 10); + (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, + SALINK_FIELD_SIZE_ROW, SPECASM_CODE_COLOUR); + + specasm_file_close_e(f); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + return; + +on_error: + + specasm_file_close_e(f); + specasm_remove_file(image_name); + + return; +} + +static void prv_salink_e(void) +{ + uint8_t main_loaded; + char ibuf[16]; + + specasm_dir_t dir = specasm_opendir_e("."); + if (err_type != SPECASM_ERROR_OK) { + strcpy(error_buf, "Failed to read directory"); + err_type = SALINK_ERROR_READDIR; + return; + } + while (specasm_readdir(dir, &dirent)) { + if (!prv_check_file(specasm_getdirname(dirent))) + continue; + prv_parse_obj_e(specasm_getdirname(dirent)); + if (err_type != SPECASM_ERROR_OK) { + specasm_closedir(dir); + return; + } + } + specasm_closedir(dir); + + prv_process_queued_files_e(); + if (err_type != SPECASM_ERROR_OK) + return; + + if (obj_file_count == 0) + return; + + itoa(start_address, ibuf, 16); + (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, + SALINK_FIELD_STARTADDR_ROW, + SPECASM_CODE_COLOUR); + + main_loaded = prv_order_objects_e(); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_complete_absolutes_e(); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_evaluate_global_equs_e(); + if (err_type != SPECASM_ERROR_OK) + return; + + specasm_remove_file(image_name); + specasm_remove_file(map_name); + + prv_link_e(main_loaded); + if (err_type != SPECASM_ERROR_OK) + return; + + if (map_file) { + (void)specasm_text_print(map_name, SALINK_VAL_COL, + SALINK_FIELD_MAP_ROW, + SPECASM_CODE_COLOUR); + specasm_write_map_e(); + } else { + (void)specasm_text_print("None", SALINK_VAL_COL, + SALINK_FIELD_MAP_ROW, + SPECASM_CODE_COLOUR); + } +} + +static void prv_setup_screen(void) +{ + unsigned int i; + + specasm_cls(SPECASM_SALINK_BACKGROUND); + specasm_border(SPECASM_SALINK_BORDER); + +#ifdef SPECASM_TARGET_NEXT_OPCODES + (void)specasm_text_print(" SALINK " SPECASM_VERSION_STR +#else + (void)specasm_text_print(" SALINK " SPECASM_VERSION_STR +#endif + " ", + 0, 0, SPECASM_HEADER_COLOUR); + + (void)specasm_text_print("Name", SALINK_FIELD_COL, + SALINK_FIELD_NAME_ROW, SPECASM_STATUS_COLOUR); + (void)specasm_text_print("Start Address", SALINK_FIELD_COL, + SALINK_FIELD_STARTADDR_ROW, + SPECASM_STATUS_COLOUR); + (void)specasm_text_print("Source Files", SALINK_FIELD_COL, + SALINK_FIELD_FILES_ROW, SPECASM_STATUS_COLOUR); + (void)specasm_text_print("Globals", SALINK_FIELD_COL, + SALINK_FIELD_GLOBALS_ROW, + SPECASM_STATUS_COLOUR); + (void)specasm_text_print("Size", SALINK_FIELD_COL, + SALINK_FIELD_SIZE_ROW, SPECASM_STATUS_COLOUR); + (void)specasm_text_print("Map file", SALINK_FIELD_COL, + SALINK_FIELD_MAP_ROW, SPECASM_STATUS_COLOUR); + + for (i = SALINK_FIELD_NAME_ROW; i <= SALINK_FIELD_MAX_ROW; i += 2) { + (void)specasm_text_print(" ", SALINK_VAL_COL, i, + SPECASM_CODE_COLOUR); + } +} + +#ifdef SPECASM_NEXT_BANKED +int salink_link_banked_e(void) +#else +int salink_link_e(void) +#endif +{ + const char *err_str; + int err_buf_len; + uint16_t last_line = SALINK_STATUS_ROW; + int retval = 0; + + prv_setup_screen(); + + prv_salink_e(); + if (err_type != SPECASM_ERROR_OK) { + if (err_type >= SPECASM_MAX_ERRORS) + err_str = error_buf; + else + err_str = specasm_error_msg(err_type); + err_buf_len = strlen(err_str); + do { + (void)specasm_text_print(err_str, 0, last_line, + SPECASM_ERROR_COLOUR); + if (err_buf_len < SPECASM_LINE_MAX_LEN) + break; + ++last_line; + err_str = &err_str[SPECASM_LINE_MAX_LEN]; + err_buf_len -= SPECASM_LINE_MAX_LEN; + } while (1); + retval = 1; + } else { + (void)specasm_text_print("Link succeeded", 0, SALINK_STATUS_ROW, + SPECASM_SUCCESS_COLOUR); + ++last_line; + } + specasm_screen_flush(last_line + 2); + + return retval; +} diff --git a/src/link_obj.h b/src/link_obj.h new file mode 100644 index 0000000..7c699f7 --- /dev/null +++ b/src/link_obj.h @@ -0,0 +1,26 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef LINK_OBJ_H +#define LINK_OBJ_H + +#include + +#include "salink.h" + +int salink_link_e(void); + +#endif diff --git a/src/map.c b/src/map.c new file mode 100644 index 0000000..3e0724e --- /dev/null +++ b/src/map.c @@ -0,0 +1,179 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include + +#include "map.h" +#include "peer.h" +#include "salink.h" +#include "state_base.h" + +static void prv_write_buffered_e(specasm_handle_t f, const char *str) +{ + int len = strlen(str); + + if (len + buf_count > MAX_BUFFER_SIZE) { + specasm_file_write_e(f, buf.file_buf, buf_count); + if (err_type != SPECASM_ERROR_OK) + return; + buf_count = 0; + } + memcpy(&buf.file_buf[buf_count], str, len); + buf_count += len; +} + +static void prv_dump_globals_e(specasm_handle_t f, salink_global_t *glob, + salink_obj_t *obj) +{ + char ibuf[16]; + uint8_t type = labels[glob->label_index].type; + + if (type > SALINK_LABEL_TYPE_LNG) + return; + + ibuf[0] = '$'; + itoa(labels[glob->label_index].data.off, &ibuf[1], 16); + prv_write_buffered_e(f, ibuf); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_write_buffered_e(f, " - "); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_write_buffered_e(f, obj->fname); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_write_buffered_e(f, ":"); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_write_buffered_e(f, glob->name); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_write_buffered_e(f, "\n"); +} + +#ifdef SPECASM_NEXT_BANKED +void specasm_write_map_banked_e(void) +#else +void specasm_write_map_e(void) +#endif +{ + unsigned int of; + unsigned int i; + specasm_handle_t f; + salink_global_t *glob; + salink_obj_t *obj; + salink_label_t *label; + const char *str; + char ibuf[16]; + + ibuf[0] = '$'; + + (void)specasm_text_print(" ", SALINK_VAL_COL + 1, + SALINK_FIELD_FILES_ROW, SPECASM_CODE_COLOUR); + + f = specasm_file_wopen_e(map_name); + if (err_type != SPECASM_ERROR_OK) + return; + + buf_count = 0; + prv_write_buffered_e(f, "Globals\n-------\n"); + for (i = 0; i < global_count; i++) { + glob = &globals[i]; + if (glob->obj_index != 0) + continue; + obj = &obj_files[glob->obj_index]; + prv_dump_globals_e(f, glob, obj); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + } + for (i = 0; i < global_count; i++) { + glob = &globals[i]; + if (glob->obj_index == 0) + continue; + obj = &obj_files[glob->obj_index]; + prv_dump_globals_e(f, glob, obj); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + } + + for (of = 0; of < obj_file_count; of++) { + itoa(of + 1, &ibuf[1], 10); + (void)specasm_text_print(&ibuf[1], SALINK_VAL_COL + 1, + SALINK_FIELD_FILES_ROW, + SPECASM_CODE_COLOUR); + + obj = &obj_files[of]; + specasm_load_e(obj->fname); + if (err_type != SPECASM_ERROR_OK) + return; + + prv_write_buffered_e(f, "\n"); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + prv_write_buffered_e(f, obj->fname); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + prv_write_buffered_e(f, "\n-------\n"); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + for (i = obj->label_start; i < obj->label_end; i++) { + label = &labels[i]; + if (label->type > SALINK_LABEL_TYPE_LNG) + continue; + str = salink_get_label_str_e(label->id, label->type); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + itoa(label->data.off, &ibuf[1], 16); + prv_write_buffered_e(f, ibuf); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + prv_write_buffered_e(f, " - "); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + prv_write_buffered_e(f, str); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + prv_write_buffered_e(f, "\n"); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + } + } + + if (buf_count > 0) { + specasm_file_write_e(f, buf.file_buf, buf_count); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + } + + specasm_file_close_e(f); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + return; + +on_error: + + specasm_file_close_e(f); + specasm_remove_file(image_name); + + return; +} diff --git a/src/map.h b/src/map.h new file mode 100644 index 0000000..15641aa --- /dev/null +++ b/src/map.h @@ -0,0 +1,22 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef SPECASM_WRITE_MAP_H +#define SPECASM_WRITE_MAP_H + +void specasm_write_map_e(void); + +#endif diff --git a/src/peer.h b/src/peer.h index 17e131f..2040c57 100644 --- a/src/peer.h +++ b/src/peer.h @@ -25,6 +25,8 @@ #include "peer_unit.h" #elif defined(SPECTRUM) #include "peer_zx.h" +#elif defined(__ZXNEXT) +#include "peer_zx.h" #endif void specasm_peer_write_state_e(const char *fname, uint16_t checksum); @@ -35,7 +37,7 @@ void specasm_text_set_flash(uint8_t x, uint8_t y, uint8_t attr); void specasm_text_printch(char ch, uint8_t x, uint8_t y, uint8_t attr); -#ifndef SPECTRUM +#if !defined(SPECTRUM) && !defined(__ZXNEXT) int itoa(int n, char *s, unsigned char radix); int utoa(int n, char *s, unsigned char radix); #endif diff --git a/src/peer_file.h b/src/peer_file.h index 54d0c18..c3f6b21 100644 --- a/src/peer_file.h +++ b/src/peer_file.h @@ -17,10 +17,11 @@ #ifndef SPECASM_PEER_FILE_H #define SPECASM_PEER_FILE_H -#ifdef SPECTRUM -#include +#if defined(SPECTRUM) || defined(__ZXNEXT) typedef unsigned char specasm_handle_t; typedef unsigned char specasm_dir_t; +#ifdef SPECTRUM +#include typedef struct esxdos_dirent specasm_dirent_t; #define SPECASM_PATH_MAX ESXDOS_PATH_MAX #define specasm_readdir(dir, drent) esxdos_f_readdir(dir, drent) @@ -28,6 +29,17 @@ typedef struct esxdos_dirent specasm_dirent_t; #define specasm_getdirname(d) (&d.dir[1]) #define specasm_remove_file(f) (void)esxdos_f_unlink(f) #else +#include +typedef struct esxdos_dirent specasm_dirent_t; +#define SPECASM_PATH_MAX ESX_PATHNAME_MAX +#define specasm_readdir(dir, drent) esx_f_readdir(dir, drent) +#define specasm_closedir(dir) esx_f_close(dir) +#define specasm_getdirname(d) (&d.dir[1]) +#define specasm_remove_file(f) (void)esx_f_unlink(f) +#endif + +#else + #include #include #include diff --git a/src/peer_file_next.c b/src/peer_file_next.c new file mode 100644 index 0000000..a256534 --- /dev/null +++ b/src/peer_file_next.c @@ -0,0 +1,67 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "peer_file.h" +#include "error.h" + +specasm_handle_t specasm_file_wopen_e(const char *fname) +{ + specasm_handle_t f; + + f = esx_f_open(fname, ESX_MODE_W | ESX_MODE_OPEN_CREAT_TRUNC); + if (!f) + err_type = SPECASM_ERROR_OPEN; + + return f; +} + +specasm_handle_t specasm_file_ropen_e(const char *fname) +{ + specasm_handle_t f; + + f = esx_f_open(fname, ESX_MODE_R); + if (!f) + err_type = SPECASM_ERROR_OPEN; + + return f; +} + +void specasm_file_write_e(specasm_handle_t f, const void *data, size_t size) +{ + if (esx_f_write(f, (void *)data, size) == 0) + err_type = SPECASM_ERROR_WRITE; +} + +size_t specasm_file_read_e(specasm_handle_t f, void *data, size_t size) +{ + /* + * I don't see any way to distinguish between an error and an + * EOF, so for now this function just returns the bytes read. + */ + + return esx_f_read(f, data, size); +} + +void specasm_file_close_e(specasm_handle_t f) { (void)esxdos_f_close(f); } + +specasm_dir_t specasm_opendir_e(const char *fname) +{ + specasm_dir_t d = esx_f_opendir(fname); + if (!d) + err_type = SPECASM_ERROR_OPEN; + + return d; +} diff --git a/src/peer_file_zx.c b/src/peer_file_zx.c index 3977cb9..d02d449 100644 --- a/src/peer_file_zx.c +++ b/src/peer_file_zx.c @@ -21,7 +21,7 @@ specasm_handle_t specasm_file_wopen_e(const char *fname) { specasm_handle_t f; - f = esxdos_f_open(fname, ESXDOS_MODE_W | ESXDOS_MODE_OC); + f = esxdos_f_open(fname, ESXDOS_MODE_W | ESXDOS_MODE_CT); if (!f) err_type = SPECASM_ERROR_OPEN; diff --git a/src/peer_next.c b/src/peer_next.c new file mode 100644 index 0000000..0cdc1b9 --- /dev/null +++ b/src/peer_next.c @@ -0,0 +1,70 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "peer.h" +#include "state.h" + +#include +#include + +void specasm_peer_write_state_e(const char *fname, uint16_t checksum) +{ + unsigned char f; + + f = esx_f_open(fname, ESX_MODE_W | ESX_MODE_OPEN_CREAT); + if (!f) { + err_type = SPECASM_ERROR_OPEN; + return; + } + + if (esx_f_write(f, &state, sizeof(state)) == 0) { + err_type = SPECASM_ERROR_WRITE; + goto cleanup; + } + + if (esx_f_write(f, &checksum, sizeof(checksum)) == 0) + err_type = SPECASM_ERROR_WRITE; + +cleanup: + esx_f_close(f); +} + +uint16_t specasm_peer_read_state_e(const char *fname) +{ + unsigned char f; + uint16_t checksum = 0; + + f = esx_f_open(fname, ESX_MODE_R); + if (!f) { + err_type = SPECASM_ERROR_OPEN; + return 0; + } + + if (esx_f_read(f, &state, sizeof(state)) < sizeof(state)) { + err_type = SPECASM_ERROR_READ; + goto on_error; + } + + if (esx_f_read(f, &checksum, sizeof(checksum)) < sizeof(checksum)) { + err_type = SPECASM_ERROR_READ; + goto on_error; + } + +on_error: + esx_f_close(f); + + return checksum; +} diff --git a/src/peer_zx.h b/src/peer_zx.h index 6d5f166..c69fba3 100644 --- a/src/peer_zx.h +++ b/src/peer_zx.h @@ -17,7 +17,14 @@ #ifndef SPECASM_ZX_PEER_H #define SPECASM_ZX_PEER_H +#ifdef SPECTRUM #include +#define specasm_sleep_ms(a) z80_delay_ms(a) +#else +#include +#define specasm_sleep_ms(a) \ + z80_delay_ms((1 << (ZXN_READ_REG(REG_TURBO_MODE) & 3))*(a)) +#endif #include #include "util_print_zx.h" @@ -40,7 +47,6 @@ #define SPECASM_FLASH FLASH #define specasm_cls(a) zx_cls(a) #define specasm_border(a) zx_border(a) -#define specasm_sleep_ms(a) z80_delay_ms(a) #define specasm_text_print specasm_util_print #define specasm_text_clear specasm_util_clear diff --git a/src/salink.c b/src/salink.c index 8ff9d05..3976931 100644 --- a/src/salink.c +++ b/src/salink.c @@ -18,84 +18,41 @@ #include #include +#ifdef SPECASM_TARGET_NEXT +#include +#endif + #include "expression.h" +#include "link_obj.h" +#include "map.h" #include "peer.h" -#include "peer_file.h" #include "salink.h" #include "state_base.h" -#define MAX_PENDING_X_FILES (MAX_BUFFER_SIZE / (MAX_FNAME + 1)) - -#define SALINK_FIELD_COL 1 -#define SALINK_VAL_COL 15 -#define SALINK_FIELD_NAME_ROW 2 -#define SALINK_FIELD_STARTADDR_ROW 4 -#define SALINK_FIELD_FILES_ROW 6 -#define SALINK_FIELD_GLOBALS_ROW 8 -#define SALINK_FIELD_SIZE_ROW 10 -#define SALINK_FIELD_MAP_ROW 12 -#define SALINK_FIELD_MAX_ROW SALINK_FIELD_MAP_ROW -#define SALINK_STATUS_ROW SALINK_FIELD_MAX_ROW + 2 - char scratch[SPECASM_MAX_SCRATCH]; char error_buf[(SPECASM_LINE_MAX_LEN * 3) + 1]; -static union { - char file_buf[MAX_BUFFER_SIZE]; - char fname[MAX_PENDING_X_FILES][MAX_FNAME + 1]; -} buf; -static uint8_t queued_files; -static unsigned int buf_count; -static unsigned int bin_size; -static char image_name[MAX_FNAME + 1]; -static char map_name[MAX_FNAME + 1]; -static uint16_t start_address = 0x8000; -static uint8_t got_org; -static uint8_t map_file; +salink_buf_t buf; +unsigned int buf_count; +unsigned int bin_size; +char image_name[MAX_FNAME + 1]; +char map_name[MAX_FNAME + 1]; salink_label_t labels[MAX_LABELS]; -static size_t label_count; -static salink_obj_t obj_files[MAX_FILES]; -static unsigned int obj_file_count; +salink_obj_t obj_files[MAX_FILES]; +unsigned int obj_file_count; salink_global_t globals[MAX_GLOBALS]; -static unsigned int global_count; +unsigned int global_count; -size_t main_index = SIZE_MAX; - -static specasm_dirent_t dirent; - -static int prv_check_file(const char *fname) -{ - char *period; - period = strchr(fname, '.'); +#ifdef SPECASM_TARGET_NEXT +void specasm_peer_next_copy_chars(void); +#endif - if (!period) - return 0; - - return ((period[1] == 'x' || period[1] == 'X') && period[2] == 0); -} - -static void prv_init_out_fnames(salink_obj_t *obj) +static void prv_unknown_error_label_e(salink_obj_t *obj, const char *str) { - unsigned int i; - - for (i = 0; i < MAX_FNAME; i++) { - if (obj->fname[i] == '.') - break; - image_name[i] = obj->fname[i]; - } - image_name[i] = 0; - - (void)specasm_text_print(image_name, SALINK_VAL_COL + 1, - SALINK_FIELD_NAME_ROW, SPECASM_CODE_COLOUR); - - strcpy(map_name, image_name); - if (i + 4 > MAX_FNAME) - i = MAX_FNAME - 4; - map_name[i] = '.'; - map_name[i + 1] = 'm'; - map_name[i + 2] = 'a'; - map_name[i + 3] = 'p'; + snprintf(error_buf, sizeof(error_buf), "%s:Unknown label in equ:%s", + obj->fname, str); + err_type = SALINK_ERROR_UNRESOLVED_LABEL; } /* @@ -113,418 +70,6 @@ const char *salink_get_label_str_e(uint8_t id, uint8_t label_type) return specasm_state_get_short_e(id); } -static uint8_t prv_add_label_e(salink_obj_t *obj, uint16_t size, uint8_t type, - uint8_t id, uint16_t line_no) -{ - char ibuf[16]; - unsigned int i; - const char *str; - const char *str1; - salink_label_t *label; - salink_global_t *global; - uint8_t retval = 0; - - if (label_count == MAX_LABELS) { - snprintf(error_buf, sizeof(error_buf), - "Max label limit %d reached", MAX_LABELS); - err_type = SALINK_ERROR_TOO_MANY_LABELS; - return 0; - } - label = &labels[label_count]; - label->id = id; - label->type = type; - label->data.off = size; - str = salink_get_label_str_e(id, type); - if (err_type != SPECASM_ERROR_OK) - return 0; - - if (str[0] >= 'A' && str[0] <= 'Z') { - if (global_count == MAX_GLOBALS) { - snprintf(error_buf, sizeof(error_buf), - "Global limit %d reached", MAX_GLOBALS); - err_type = SALINK_ERROR_TOO_MANY_GLOBALS; - return 0; - } - for (i = 0; i < global_count; i++) { - if (!strcmp(str, globals[i].name)) { - snprintf(error_buf, sizeof(error_buf), - "%s defined in %s:%d and %s:%d", str, - obj->fname, line_no, - obj_files[globals[i].obj_index].fname, - globals[i].line_no); - err_type = SALINK_ERROR_MULTIPLE_DEFS; - return 0; - } - } - if (!strcmp(str, "Main")) { - main_index = obj_file_count; - prv_init_out_fnames(obj); - } - global = &globals[i]; - strcpy(global->name, str); - global->obj_index = obj_file_count; - global->label_index = label_count; - global->line_no = line_no; - global_count++; - itoa(global_count, ibuf, 10); - (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, - SALINK_FIELD_GLOBALS_ROW, - SPECASM_CODE_COLOUR); - retval = 1; - } else { - for (i = obj->label_start; i < label_count; i++) { - if ((labels[i].type == SALINK_LABEL_TYPE_ALIGN) || - (labels[i].type > SALINK_LABEL_TYPE_EQU_LONG)) - continue; - - str1 = salink_get_label_str_e(labels[i].id, - labels[i].type); - if (err_type != SPECASM_ERROR_OK) - return 0; - - if (!strcmp(str, str1)) { - snprintf(error_buf, sizeof(error_buf), - "%s multiply defined in %s:%d", str, - obj->fname, line_no); - err_type = SALINK_ERROR_MULTIPLE_DEFS; - return 0; - } - } - } - label_count++; - - return retval; -} - -static void prv_add_align_e(salink_obj_t *obj, specasm_line_t *line, - uint16_t size) -{ - salink_label_t *label; - - if (label_count == MAX_LABELS) { - snprintf(error_buf, sizeof(error_buf), - "Max label limit %d reached", MAX_LABELS); - err_type = SALINK_ERROR_TOO_MANY_LABELS; - return; - } - label = &labels[label_count++]; - label->type = SALINK_LABEL_TYPE_ALIGN; - label->id = line->data.op_code[0]; - label->data.off = size; -} - -static void prv_add_queued_file_e(const char *base, const char *prefix, - specasm_line_t *line) -{ - const char *str; - uint8_t id; - int space_needed; - int prefix_len; - int base_len; - char *ptr; - char *start; - char *slash; - - if (queued_files == MAX_PENDING_X_FILES) { - snprintf(error_buf, sizeof(error_buf), - "Pending file limit %d reached", MAX_PENDING_X_FILES); - err_type = SALINK_ERROR_TOO_MANY_FILES; - return; - } - - id = line->data.label; - str = salink_get_label_str_e(id, line->type); - - base_len = 0; - if (str[0] != '/') { - slash = strrchr(base, '/'); - if (slash) - base_len = (slash - base) + 1; - } - - prefix_len = strlen(prefix); - space_needed = strlen(str) + prefix_len + base_len; - if (space_needed > MAX_FNAME) { - err_type = SPECASM_ERROR_BAD_FNAME; - return; - } - - ptr = &buf.fname[queued_files++][0]; - start = ptr; - if (base_len) { - strncpy(ptr, base, base_len); - ptr += base_len; - } - if (prefix_len) { - strcpy(ptr, prefix); - ptr += prefix_len; - } - strcpy(ptr, str); - if (start[space_needed - 2] != '.' && - (start[space_needed - 1] | 32) != 'x') { - if (space_needed + 2 > MAX_FNAME) { - err_type = SPECASM_ERROR_BAD_FNAME; - return; - } - start[space_needed] = '.'; - start[space_needed + 1] = 'x'; - start[space_needed + 2] = 0; - } -} - -static void prv_add_equ_label_e(specasm_line_t *line, salink_obj_t *obj, - uint16_t line_no) -{ - uint8_t type; - uint8_t *op_code; - uint8_t is_global; - salink_global_t *global; - uint8_t id; - const char *str; - salink_label_t *label; - - /* - * With local equ expressions we can just store the expression - * information in the label itself for later use. For global - * expressions we need to store it in the label name itself, - * as a second string. There's guaranteed to be enough room - * for this as the sum of the label and the expression cannot - * exceed the max line length. - */ - - op_code = &line->data.op_code[0]; - type = op_code[0] == SPECASM_LINE_TYPE_SL ? SALINK_LABEL_TYPE_EQU_SHORT - : SALINK_LABEL_TYPE_EQU_LONG; - is_global = prv_add_label_e(obj, 0, type, op_code[1], line_no); - if (err_type != SPECASM_ERROR_OK) - return; - label = &labels[label_count - 1]; - if (!is_global) { - label->data.equ[0] = op_code[2]; - label->data.equ[1] = op_code[3]; - return; - } - - global = &globals[global_count - 1]; - id = op_code[3]; - str = salink_get_label_str_e(id, op_code[2]); - if (err_type != SPECASM_ERROR_OK) - return; - strcpy(global->name + strlen(global->name) + 1, str); - label->type = SALINK_LABEL_TYPE_EQU_GLOBAL; -} - -static void prv_parse_obj_e(const char *fname) -{ - uint16_t i; - specasm_line_t *line; - salink_obj_t *obj; - char ibuf[16]; - uint8_t type; - uint16_t size = 0; - - if (obj_file_count == MAX_FILES) { - snprintf(error_buf, sizeof(error_buf), - "Max file limit %d reached", MAX_FILES); - err_type = SALINK_ERROR_TOO_MANY_FILES; - return; - } - - obj = &obj_files[obj_file_count]; - - if (strlen(fname) > MAX_FNAME) { - err_type = SPECASM_ERROR_STRING_TOO_LONG; - return; - } - strcpy(obj->fname, fname); - obj->label_start = label_count; - - specasm_load_e(fname); - if (err_type != SPECASM_ERROR_OK) { - snprintf(error_buf, sizeof(error_buf), "Can't open %s", fname); - err_type = SALINK_ERROR_CANT_OPEN; - return; - } - - for (i = 0; i < state.lines.num_lines; i++) { - line = &state.lines.lines[i]; - if ((line->type == SPECASM_LINE_TYPE_LL) || - (line->type == SPECASM_LINE_TYPE_SL)) { - type = line->type == SPECASM_LINE_TYPE_LL - ? SALINK_LABEL_TYPE_LNG - : SALINK_LABEL_TYPE_SHORT; - (void)prv_add_label_e(obj, size, type, line->data.label, - i); - } else if (line->type == SPECASM_LINE_TYPE_EQU) { - prv_add_equ_label_e(line, obj, i); - } else if (line->type == SPECASM_LINE_TYPE_ORG) { - if (got_org) { - strcpy(error_buf, - "Only one org statement allowed"); - err_type = SALINK_ERROR_TOO_MANY_ORGS; - return; - } - got_org = 1; - start_address = *((uint16_t *)&line->data.op_code[0]); - } else if (line->type == SPECASM_LINE_TYPE_MAP) { - map_file = 1; - } else if (line->type == SPECASM_LINE_TYPE_ALIGN) { - prv_add_align_e(obj, line, size); - } else if ((line->type >= SPECASM_LINE_TYPE_INC_SHORT) && - (line->type <= SPECASM_LINE_TYPE_INC_LONG)) { - prv_add_queued_file_e(obj->fname, "", line); - } else if ((line->type >= SPECASM_LINE_TYPE_INC_SYS_SHORT) && - (line->type <= SPECASM_LINE_TYPE_INC_SYS_LONG)) { - prv_add_queued_file_e(fname, "/specasm/", line); - } else { - size += specasm_compute_line_size(line); - } - - if (err_type != SPECASM_ERROR_OK) - return; - } - - obj_file_count++; - obj->label_end = label_count; - obj->size = size; - - itoa(obj_file_count, ibuf, 10); - (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, - SALINK_FIELD_FILES_ROW, SPECASM_CODE_COLOUR); -} - -static void prv_process_queued_files_e(void) -{ - const char *path; - - while (queued_files > 0) { - --queued_files; - path = &buf.fname[queued_files][0]; - prv_parse_obj_e(path); - if (err_type != SPECASM_ERROR_OK) - return; - } -} - -static uint8_t prv_order_objects_e(void) -{ - salink_obj_t tmp; - uint8_t loaded; - uint8_t i; - salink_global_t *glob; - - /* - * We want to put the object file with the Main label - * first. - */ - - if (main_index == SIZE_MAX) { - strcpy(error_buf, "No Main label defined"); - err_type = SALINK_ERROR_NO_MAIN; - return 0; - } - - loaded = main_index == (obj_file_count - 1); - if (main_index > 0) { - memcpy(&tmp, &obj_files[0], sizeof(obj_files[0])); - memcpy(&obj_files[0], &obj_files[main_index], - sizeof(obj_files[0])); - memcpy(&obj_files[main_index], &tmp, sizeof(obj_files[0])); - for (i = 0; i < global_count; i++) { - glob = &globals[i]; - if (glob->obj_index == 0) - glob->obj_index = main_index; - else if (glob->obj_index == main_index) - glob->obj_index = 0; - } - main_index = 0; - } - - return loaded; -} - -static void prv_complete_absolutes_e(void) -{ - unsigned int i; - uint16_t j; - salink_obj_t *obj; - salink_label_t *label; - unsigned int mask; - unsigned int adjust; - uint16_t align; - uint16_t real_off = start_address; - - for (i = 0; i < obj_file_count; i++) { - obj = &obj_files[i]; - for (j = obj->label_start; j < obj->label_end; j++) { - label = &labels[j]; - - if (label->type > SALINK_LABEL_TYPE_ALIGN) - continue; - - if (label->type == SALINK_LABEL_TYPE_ALIGN) { - align = 1 << label->id; - mask = align - 1; - adjust = (real_off + label->data.off) & mask; - if (adjust > 0) - real_off += align - adjust; - continue; - } - if (label->data.off > 0xffff - real_off) { - snprintf(error_buf, sizeof(error_buf), - "%s past end of memory", obj->fname); - err_type = SALINK_ERROR_PROGRAM_TOO_BIG; - return; - } - label->data.off += real_off; - } - real_off += obj->size; - } -} - -static void prv_evaluate_global_equs_e(void) -{ - uint8_t i; - salink_obj_t *obj; - salink_label_t *label; - salink_global_t *global; - - for (i = 0; i < global_count; i++) { - global = &globals[i]; - label = &labels[global->label_index]; - obj = &obj_files[global->obj_index]; - if (label->type == SALINK_LABEL_TYPE_EQU_GLOBAL) { - salink_equ_eval_global_e(obj, global, label, 0); - if (err_type != SPECASM_ERROR_OK) - return; - } - } -} - -static salink_label_t *prv_find_local_label(salink_obj_t *obj, uint8_t lng, - uint8_t id) -{ - unsigned int i; - salink_label_t *label; - - for (i = obj->label_start; i < obj->label_end; i++) { - label = &labels[i]; - if (lng != label->type) - continue; - if (id == label->id) - return label; - } - - return NULL; -} - -static void prv_unknown_error_label_e(salink_obj_t *obj, const char *str) -{ - snprintf(error_buf, sizeof(error_buf), "%s:Unknown label in equ:%s", - obj->fname, str); - err_type = SALINK_ERROR_UNRESOLVED_LABEL; -} - /* * Find a label id given a string. We need this when evaluating expressions. * Labels within expressions are encoded as strings rather than as ids. @@ -582,797 +127,32 @@ unsigned int salink_find_global_label_e(const char *str, salink_obj_t *obj) return 0; } -static salink_global_t *prv_find_global_label_e(salink_obj_t *obj, - unsigned int line_no, - uint8_t id, int8_t lng) -{ - const char *str; - salink_global_t *global; - unsigned int i; - - str = salink_get_label_str_e(id, lng); - if (err_type != SPECASM_ERROR_OK) - return NULL; - - for (i = 0; i < global_count; i++) { - global = &globals[i]; - if (!strcmp(str, global->name)) - return global; - } - - snprintf(error_buf, sizeof(error_buf), "%s:%d Unknown: %s", obj->fname, - line_no, str); - err_type = SALINK_ERROR_UNRESOLVED_LABEL; - - return NULL; -} - -static void prv_resolve_address_e(salink_obj_t *obj, specasm_line_t *line, - unsigned int line_no, uint8_t id, - uint16_t *addr, uint8_t lng) -{ - salink_label_t *label; - salink_global_t *global; - - label = prv_find_local_label(obj, lng, id); - if (!label) { - /* - * Couldn't find a local address. Let's check to see whether - * it's global. - */ - - global = prv_find_global_label_e(obj, line_no, id, lng); - if (err_type != SPECASM_ERROR_OK) - return; - label = &labels[global->label_index]; - } - - *addr = label->data.off; -} - -static int16_t prv_eval_exp_from_id_e(salink_obj_t *obj, unsigned int line_no, - uint8_t id, uint8_t lng) -{ - const char *str; - int16_t exp; - - str = salink_get_label_str_e(id, lng); - if (err_type != SPECASM_ERROR_OK) - return 0; - - exp = salink_equ_eval_e(obj, str, line_no); - - if ((err_type != SPECASM_ERROR_OK) && (err_type < SPECASM_MAX_ERRORS)) { - snprintf(error_buf, sizeof(error_buf), "%s:%d %s", obj->fname, - line_no, specasm_error_msg(err_type)); - err_type = SALINK_ERROR_BAD_EXP; - } - - return exp; -} - -static void prv_resolve_relative_address_e(salink_obj_t *obj, - specasm_line_t *line, unsigned int i, - uint16_t offset) -{ - salink_label_t *label; - int16_t diff; - uint8_t addr_type = specasm_line_get_addr_type(line); - uint8_t lng = addr_type == SPECASM_FLAGS_ADDR_LONG - ? SALINK_LABEL_TYPE_LNG - : SALINK_LABEL_TYPE_SHORT; - - label = prv_find_local_label(obj, lng, line->data.op_code[1]); - if (!label) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d Unresolved symbol", obj->fname, i); - err_type = SALINK_ERROR_UNRESOLVED_LABEL; - return; - } - - diff = label->data.off - offset; - if (diff < -126 || diff > 129) { - snprintf(error_buf, sizeof(error_buf), - "%s line %d label too far", obj->fname, i); - err_type = SALINK_ERROR_JUMP_TOO_FAR; - return; - } - line->data.op_code[1] = (int8_t)(diff - 2); -} - -static void prv_write_line_e(specasm_handle_t f, specasm_line_t *line, - uint16_t size) -{ - unsigned int i; - unsigned int to_set; - uint8_t id; - const char *data; - - if ((buf_count + size > MAX_BUFFER_SIZE) || - ((buf_count > 0) && (line->type == SPECASM_LINE_TYPE_DS))) { - specasm_file_write_e(f, buf.file_buf, buf_count); - if (err_type != SPECASM_ERROR_OK) - return; - bin_size += buf_count; - buf_count = 0; - } - - if (line->type <= SPECASM_LINE_TYPE_SIMPLE_MAX) { - for (i = 0; i < size; i++) - buf.file_buf[buf_count++] = line->data.op_code[i]; - return; - } - - if (line->type == SPECASM_LINE_TYPE_DS) { - to_set = size < MAX_BUFFER_SIZE ? size : MAX_BUFFER_SIZE; - memset(&buf.file_buf[0], line->data.op_code[0], to_set); - do { - size -= to_set; - if (size == 0) { - buf_count = to_set; - return; - } - specasm_file_write_e(f, buf.file_buf, MAX_BUFFER_SIZE); - if (err_type != SPECASM_ERROR_OK) - return; - bin_size += MAX_BUFFER_SIZE; - buf_count = 0; - to_set = - size < MAX_BUFFER_SIZE ? size : MAX_BUFFER_SIZE; - } while (1); - } - - if ((line->type >= SPECASM_LINE_TYPE_STR_HSH_SHORT) && - (line->type <= SPECASM_LINE_TYPE_STR_AMP_LONG)) { - size--; - buf.file_buf[buf_count++] = size; - } - - if ((line->type >= SPECASM_LINE_TYPE_STR_SIN_SHORT) && - (line->type <= SPECASM_LINE_TYPE_STR_AMP_LONG)) { - id = line->data.label; - data = salink_get_label_str_e(id, line->type); - memcpy(&buf.file_buf[buf_count], data, size); - buf_count += size; - } -} - -static void prv_label_subtraction_e(salink_obj_t *obj, specasm_line_t *line, - unsigned int l, uint8_t id_pos) -{ - uint16_t a; - uint16_t b; - uint8_t id1; - uint8_t id2; - uint8_t lng; - uint8_t *op_code = &line->data.op_code[id_pos]; - - id1 = op_code[0]; - id2 = op_code[1]; - lng = op_code[2] == SPECASM_FLAGS_ADDR_LONG ? SALINK_LABEL_TYPE_LNG - : SALINK_LABEL_TYPE_SHORT; - prv_resolve_address_e(obj, line, l, id1, &a, lng); - if (err_type != SPECASM_ERROR_OK) - return; - lng = - specasm_line_get_addr_type(line) == SPECASM_FLAGS_ADDR_LONG ? 1 : 0; - prv_resolve_address_e(obj, line, l, id2, &b, lng); - if (err_type != SPECASM_ERROR_OK) - return; - if (b > a) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d Negative difference", obj->fname, l); - err_type = SALINK_ERROR_NEGATIVE_SIZE; - return; - } - *((uint16_t *)&op_code[0]) = a - b; -} - -static void prv_label_subtraction_byte_e(salink_obj_t *obj, - specasm_line_t *line, unsigned int l, - uint8_t id_pos) -{ - uint16_t diff; - - prv_label_subtraction_e(obj, line, l, id_pos); - if (err_type != SPECASM_ERROR_OK) - return; - - diff = *((uint16_t *)&line->data.op_code[id_pos]); - - if (diff > 255) { - snprintf(error_buf, sizeof(error_buf), "%s:%d Too big:%d", - obj->fname, l, diff); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - } -} - -static uint16_t prv_align_e(specasm_handle_t f, uint16_t align) -{ - unsigned int mask = align - 1; - unsigned int adjust = (start_address + bin_size + buf_count) & mask; - - if (adjust == 0) - return 0; - - adjust = align - adjust; - - if (buf_count + adjust > MAX_BUFFER_SIZE) { - specasm_file_write_e(f, buf.file_buf, buf_count); - if (err_type != SPECASM_ERROR_OK) - return 0; - bin_size += buf_count; - buf_count = 0; - } - memset(&buf.file_buf[buf_count], 0, adjust); - buf_count += adjust; - return adjust; -} - -static int16_t prv_eval_equ_label(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no, uint8_t id) -{ - uint8_t label_type; - uint8_t lng; - - label_type = specasm_line_get_addr_type(line); - lng = label_type == SPECASM_FLAGS_ADDR_SHORT ? 0 : 1; - return prv_eval_exp_from_id_e(obj, line_no, id, lng); -} - -static void prv_eval_equ_8bit_e(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no, uint8_t loc) -{ - int16_t exp; - - exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); - if (err_type != SPECASM_ERROR_OK) - return; - - if ((exp > 255) || (exp < -128)) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d immediate too big :%d", obj->fname, line_no, - exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - line->data.op_code[loc] = exp; -} - -static void prv_eval_equ_16bit_e(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no, uint8_t loc) -{ - int16_t exp; - - exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[loc]); - if (err_type != SPECASM_ERROR_OK) - return; - - *((uint16_t *)&line->data.op_code[loc]) = exp; -} - -static void prv_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, - unsigned int line_no) -{ - int16_t exp; - uint8_t opcode0; - - line->type -= SPECASM_LINE_TYPE_EXP_ADJ; - - switch (line->type) { - case SPECASM_LINE_TYPE_ADC: - case SPECASM_LINE_TYPE_ADD: - case SPECASM_LINE_TYPE_AND: - case SPECASM_LINE_TYPE_CP: - case SPECASM_LINE_TYPE_IN: - case SPECASM_LINE_TYPE_OUT: - case SPECASM_LINE_TYPE_OR: - case SPECASM_LINE_TYPE_SBC: - case SPECASM_LINE_TYPE_SUB: - case SPECASM_LINE_TYPE_XOR: - prv_eval_equ_8bit_e(line, obj, line_no, 1); - break; - case SPECASM_LINE_TYPE_RST: - exp = prv_eval_equ_label(line, obj, line_no, - line->data.op_code[1]); - if (err_type != SPECASM_ERROR_OK) - return; - if ((exp > 0x38) || (exp & 7)) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d bad argument to rst :%d", obj->fname, - line_no, exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - line->data.op_code[0] |= exp; - break; - case SPECASM_LINE_TYPE_LD: - opcode0 = line->data.op_code[0]; - if ((opcode0 & 0xC7) == 0x6) { - prv_eval_equ_8bit_e(line, obj, line_no, 1); - break; - } - switch (opcode0) { - case 0x1: - case 0x11: - case 0x21: - case 0x31: - case 0x2a: - case 0x3a: - case 0x22: - case 0x32: - prv_eval_equ_16bit_e(line, obj, line_no, 1); - break; - case 0xDD: - case 0xED: - case 0xFD: - prv_eval_equ_16bit_e(line, obj, line_no, 2); - break; - } - break; - case SPECASM_LINE_TYPE_BIT: - case SPECASM_LINE_TYPE_RES: - case SPECASM_LINE_TYPE_SET: - exp = prv_eval_equ_label(line, obj, line_no, - line->data.op_code[2]); - if (err_type != SPECASM_ERROR_OK) - return; - if (exp > 7) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d bad bit position :%d", obj->fname, - line_no, exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - line->data.op_code[1] |= ((uint8_t)exp) << 3; - break; - case SPECASM_LINE_TYPE_IM: - exp = prv_eval_equ_label(line, obj, line_no, - line->data.op_code[2]); - if (err_type != SPECASM_ERROR_OK) - return; - if (exp > 2) { - snprintf(error_buf, sizeof(error_buf), - "%s:%d bad arg for im :%d", obj->fname, - line_no, exp); - err_type = SALINK_ERROR_SIZE_TOO_BIG; - return; - } - if (exp == 2) - line->data.op_code[1] = 0x5e; - else if (exp == 1) - line->data.op_code[1] = 0x56; - break; - case SPECASM_LINE_TYPE_CALL: - case SPECASM_LINE_TYPE_JP: - prv_eval_equ_16bit_e(line, obj, line_no, 1); - break; - case SPECASM_LINE_TYPE_DB: - prv_eval_equ_8bit_e(line, obj, line_no, 0); - break; - case SPECASM_LINE_TYPE_DW: - prv_eval_equ_16bit_e(line, obj, line_no, 0); - break; - default: - snprintf(error_buf, sizeof(error_buf), - "%s:%d unexpected expression", obj->fname, line_no); - err_type = SALINK_ERROR_UNEXPECTED_EXP; - break; - } - - specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); -} - -static uint16_t prv_link_obj_e(specasm_handle_t f, salink_obj_t *obj, - uint16_t offset) -{ - unsigned int i; - specasm_line_t *line; - uint16_t size; - uint8_t addr_type; - uint8_t id_pos; - uint8_t id; - uint8_t lng; - uint16_t *addr; - - for (i = 0; i < state.lines.num_lines; i++) { - id_pos = 1; - line = &state.lines.lines[i]; - - if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) - prv_apply_expressions_e(line, obj, i); - - switch (line->type) { - case SPECASM_LINE_TYPE_ALIGN: - offset += prv_align_e(f, 1 << line->data.op_code[0]); - break; - case SPECASM_LINE_TYPE_DW: - case SPECASM_LINE_TYPE_CALL: - case SPECASM_LINE_TYPE_JP: - case SPECASM_LINE_TYPE_LD: - addr_type = specasm_line_get_addr_type(line); - if ((addr_type == SPECASM_FLAGS_ADDR_SHORT) || - (addr_type == SPECASM_FLAGS_ADDR_LONG)) { - /* - * For all these instructions the id is - * in the penultimate byte. - */ - id_pos = specasm_line_get_size(line) - 1; - id = line->data.op_code[id_pos]; - addr = (uint16_t *)&line->data.op_code[id_pos]; - lng = addr_type == SPECASM_FLAGS_ADDR_LONG ? 1 - : 0; - prv_resolve_address_e(obj, line, i, id, addr, - lng); - } - break; - case SPECASM_LINE_TYPE_DW_SUB: - id_pos = 0; - case SPECASM_LINE_TYPE_LD_IMM_16_SUB: - prv_label_subtraction_e(obj, line, i, id_pos); - break; - case SPECASM_LINE_TYPE_DB_SUB: - id_pos = 0; - case SPECASM_LINE_TYPE_LD_IMM_8_SUB: - prv_label_subtraction_byte_e(obj, line, i, id_pos); - break; - case SPECASM_LINE_TYPE_JR: - case SPECASM_LINE_TYPE_DJNZ: - prv_resolve_relative_address_e(obj, line, i, offset); - break; - default: - break; - } - if (err_type != SPECASM_ERROR_OK) - return 0; - - size = specasm_compute_line_size(line); - if (size > 0) { - prv_write_line_e(f, line, size); - if (err_type != SPECASM_ERROR_OK) - return 0; - offset += size; - } - } - - return offset; -} - -static void prv_link_e(uint8_t main_loaded) -{ - char ibuf[16]; - unsigned int i; - specasm_handle_t f; - uint16_t offset = start_address; - salink_obj_t *obj = &obj_files[0]; - - f = specasm_file_wopen_e(image_name); - if (err_type != SPECASM_ERROR_OK) - return; - for (i = 0; i < obj_file_count; i++) { - obj = &obj_files[i]; - if (i > 0 || !main_loaded) { - specasm_load_e(obj->fname); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - } - offset = prv_link_obj_e(f, obj, offset); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - } - - if (buf_count > 0) { - specasm_file_write_e(f, buf.file_buf, buf_count); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - bin_size += buf_count; - } - - if (bin_size > 0xffff - start_address) { - snprintf(error_buf, sizeof(error_buf), "%s past end of memory", - obj->fname); - err_type = SALINK_ERROR_PROGRAM_TOO_BIG; - goto on_error; - } - - itoa(bin_size, ibuf, 10); - (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, - SALINK_FIELD_SIZE_ROW, SPECASM_CODE_COLOUR); - - specasm_file_close_e(f); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - - return; - -on_error: - - specasm_file_close_e(f); - specasm_remove_file(image_name); - - return; -} - -static void prv_write_buffered_e(specasm_handle_t f, const char *str) -{ - int len = strlen(str); - - if (len + buf_count > MAX_BUFFER_SIZE) { - specasm_file_write_e(f, buf.file_buf, buf_count); - if (err_type != SPECASM_ERROR_OK) - return; - buf_count = 0; - } - memcpy(&buf.file_buf[buf_count], str, len); - buf_count += len; -} - -static void prv_dump_globals_e(specasm_handle_t f, salink_global_t *glob, - salink_obj_t *obj) -{ - char ibuf[16]; - uint8_t type = labels[glob->label_index].type; - - if (type > SALINK_LABEL_TYPE_LNG) - return; - - ibuf[0] = '$'; - itoa(labels[glob->label_index].data.off, &ibuf[1], 16); - prv_write_buffered_e(f, ibuf); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_write_buffered_e(f, " - "); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_write_buffered_e(f, obj->fname); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_write_buffered_e(f, ":"); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_write_buffered_e(f, glob->name); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_write_buffered_e(f, "\n"); -} - -static void prv_write_map_e(void) -{ - unsigned int of; - unsigned int i; - specasm_handle_t f; - salink_global_t *glob; - salink_obj_t *obj; - salink_label_t *label; - const char *str; - char ibuf[16]; - - ibuf[0] = '$'; - - (void)specasm_text_print(" ", SALINK_VAL_COL + 1, - SALINK_FIELD_FILES_ROW, SPECASM_CODE_COLOUR); - - f = specasm_file_wopen_e(map_name); - if (err_type != SPECASM_ERROR_OK) - return; - - buf_count = 0; - prv_write_buffered_e(f, "Globals\n-------\n"); - for (i = 0; i < global_count; i++) { - glob = &globals[i]; - if (glob->obj_index != 0) - continue; - obj = &obj_files[glob->obj_index]; - prv_dump_globals_e(f, glob, obj); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - } - for (i = 0; i < global_count; i++) { - glob = &globals[i]; - if (glob->obj_index == 0) - continue; - obj = &obj_files[glob->obj_index]; - prv_dump_globals_e(f, glob, obj); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - } - - for (of = 0; of < obj_file_count; of++) { - itoa(of + 1, &ibuf[1], 10); - (void)specasm_text_print(&ibuf[1], SALINK_VAL_COL + 1, - SALINK_FIELD_FILES_ROW, - SPECASM_CODE_COLOUR); - - obj = &obj_files[of]; - specasm_load_e(obj->fname); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_write_buffered_e(f, "\n"); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - prv_write_buffered_e(f, obj->fname); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - prv_write_buffered_e(f, "\n-------\n"); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - for (i = obj->label_start; i < obj->label_end; i++) { - label = &labels[i]; - if (label->type > SALINK_LABEL_TYPE_LNG) - continue; - str = salink_get_label_str_e(label->id, label->type); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - itoa(label->data.off, &ibuf[1], 16); - prv_write_buffered_e(f, ibuf); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - prv_write_buffered_e(f, " - "); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - prv_write_buffered_e(f, str); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - prv_write_buffered_e(f, "\n"); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - } - } - - if (buf_count > 0) { - specasm_file_write_e(f, buf.file_buf, buf_count); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - } - - specasm_file_close_e(f); - if (err_type != SPECASM_ERROR_OK) - goto on_error; - - return; - -on_error: - - specasm_file_close_e(f); - specasm_remove_file(image_name); - - return; -} - -static void prv_salink_e(void) -{ - uint8_t main_loaded; - char ibuf[16]; - - specasm_dir_t dir = specasm_opendir_e("."); - if (err_type != SPECASM_ERROR_OK) { - strcpy(error_buf, "Failed to read directory"); - err_type = SALINK_ERROR_READDIR; - return; - } - while (specasm_readdir(dir, &dirent)) { - if (!prv_check_file(specasm_getdirname(dirent))) - continue; - prv_parse_obj_e(specasm_getdirname(dirent)); - if (err_type != SPECASM_ERROR_OK) { - specasm_closedir(dir); - return; - } - } - specasm_closedir(dir); - - prv_process_queued_files_e(); - if (err_type != SPECASM_ERROR_OK) - return; - - if (obj_file_count == 0) - return; - - itoa(start_address, ibuf, 16); - (void)specasm_text_print(ibuf, SALINK_VAL_COL + 1, - SALINK_FIELD_STARTADDR_ROW, - SPECASM_CODE_COLOUR); - - main_loaded = prv_order_objects_e(); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_complete_absolutes_e(); - if (err_type != SPECASM_ERROR_OK) - return; - - prv_evaluate_global_equs_e(); - if (err_type != SPECASM_ERROR_OK) - return; - - specasm_remove_file(image_name); - specasm_remove_file(map_name); - - prv_link_e(main_loaded); - if (err_type != SPECASM_ERROR_OK) - return; - - if (map_file) { - (void)specasm_text_print(map_name, SALINK_VAL_COL, - SALINK_FIELD_MAP_ROW, - SPECASM_CODE_COLOUR); - prv_write_map_e(); - } else { - (void)specasm_text_print("None", SALINK_VAL_COL, - SALINK_FIELD_MAP_ROW, - SPECASM_CODE_COLOUR); - } -} - -static void prv_setup_screen(void) +int main(int argc, char *argv[]) { - unsigned int i; - - specasm_cls(SPECASM_SALINK_BACKGROUND); - specasm_border(SPECASM_SALINK_BORDER); - (void)specasm_text_print(" SALINK " SPECASM_VERSION_STR - " ", - 0, 0, SPECASM_HEADER_COLOUR); + int ret; - (void)specasm_text_print("Name", SALINK_FIELD_COL, - SALINK_FIELD_NAME_ROW, SPECASM_STATUS_COLOUR); - (void)specasm_text_print("Start Address", SALINK_FIELD_COL, - SALINK_FIELD_STARTADDR_ROW, - SPECASM_STATUS_COLOUR); - (void)specasm_text_print("Source Files", SALINK_FIELD_COL, - SALINK_FIELD_FILES_ROW, SPECASM_STATUS_COLOUR); - (void)specasm_text_print("Globals", SALINK_FIELD_COL, - SALINK_FIELD_GLOBALS_ROW, - SPECASM_STATUS_COLOUR); - (void)specasm_text_print("Size", SALINK_FIELD_COL, - SALINK_FIELD_SIZE_ROW, SPECASM_STATUS_COLOUR); - (void)specasm_text_print("Map file", SALINK_FIELD_COL, - SALINK_FIELD_MAP_ROW, SPECASM_STATUS_COLOUR); +#ifdef SPECASM_TARGET_NEXT + uint8_t turbo; + struct esx_mode mode; - for (i = SALINK_FIELD_NAME_ROW; i <= SALINK_FIELD_MAX_ROW; i += 2) { - (void)specasm_text_print(" ", SALINK_VAL_COL, i, - SPECASM_CODE_COLOUR); - } -} + memset(&mode, 0, sizeof(mode)); + (void) esx_ide_mode_set(&mode); -int main(int argc, char *argv[]) -{ - const char *err_str; - int err_buf_len; - uint16_t last_line = SALINK_STATUS_ROW; - int retval = 0; + /* + * Link at top speed on a spectrum Next. + */ - prv_setup_screen(); + turbo = ZXN_READ_REG(REG_TURBO_MODE); + ZXN_WRITE_REG(REG_TURBO_MODE, turbo | 3); + zx_cls(PAPER_WHITE | INK_WHITE); + specasm_peer_next_copy_chars(); + zx_cls(PAPER_WHITE | INK_BLACK); +#endif - prv_salink_e(); - if (err_type != SPECASM_ERROR_OK) { - if (err_type >= SPECASM_MAX_ERRORS) - err_str = error_buf; - else - err_str = specasm_error_msg(err_type); - err_buf_len = strlen(err_str); - do { - (void)specasm_text_print(err_str, 0, last_line, - SPECASM_ERROR_COLOUR); - if (err_buf_len < SPECASM_LINE_MAX_LEN) - break; - ++last_line; - err_str = &err_str[SPECASM_LINE_MAX_LEN]; - err_buf_len -= SPECASM_LINE_MAX_LEN; - } while (1); - retval = 1; - } else { - (void)specasm_text_print("Link succeeded", 0, SALINK_STATUS_ROW, - SPECASM_SUCCESS_COLOUR); - ++last_line; - } - specasm_screen_flush(last_line + 2); + ret = salink_link_e(); - return retval; +#ifdef SPECASM_TARGET_NEXT + ZXN_WRITE_REG(REG_TURBO_MODE, turbo); +#endif + return ret; } diff --git a/src/salink.h b/src/salink.h index 1e22ef3..ec59366 100644 --- a/src/salink.h +++ b/src/salink.h @@ -20,6 +20,7 @@ #include #include "line.h" +#include "peer_file.h" #define MAX_FILES 64 #define MAX_GLOBALS 128 @@ -111,4 +112,30 @@ unsigned int salink_find_local_label_e(const char *str, int len, unsigned int salink_find_global_label_e(const char *str, salink_obj_t *obj); const char *salink_get_label_str_e(uint8_t id, uint8_t label_type); +extern unsigned int buf_count; + +#define MAX_PENDING_X_FILES (MAX_BUFFER_SIZE / (MAX_FNAME + 1)) +#define SALINK_FIELD_COL 1 +#define SALINK_VAL_COL 15 +#define SALINK_FIELD_NAME_ROW 2 +#define SALINK_FIELD_STARTADDR_ROW 4 +#define SALINK_FIELD_FILES_ROW 6 +#define SALINK_FIELD_GLOBALS_ROW 8 +#define SALINK_FIELD_SIZE_ROW 10 +#define SALINK_FIELD_MAP_ROW 12 +#define SALINK_FIELD_MAX_ROW SALINK_FIELD_MAP_ROW +#define SALINK_STATUS_ROW SALINK_FIELD_MAX_ROW + 2 + +typedef union { + char file_buf[MAX_BUFFER_SIZE]; + char fname[MAX_PENDING_X_FILES][MAX_FNAME + 1]; +} salink_buf_t; + +extern salink_buf_t buf; +extern char map_name[MAX_FNAME + 1]; +extern unsigned int global_count; +extern salink_obj_t obj_files[MAX_FILES]; +extern unsigned int obj_file_count; +extern char image_name[MAX_FNAME + 1]; +extern unsigned int bin_size; #endif diff --git a/src/salink_trampolines.c b/src/salink_trampolines.c new file mode 100644 index 0000000..2a0f4b0 --- /dev/null +++ b/src/salink_trampolines.c @@ -0,0 +1,81 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include + +#include "salink.h" + +#define SALINK_NEXT_EXPRESSION_BANK ((43<<1)+1) +#define SALINK_NEXT_MAP_BANK ((44<<1)+1) +#define SALINK_NEXT_LINK_OBJ_BANK ((45<<1)+1) + +extern unsigned char _z_page_table[]; + +int16_t salink_equ_eval_banked_e(salink_obj_t *obj, const char *str, + uint16_t line_no); +void salink_equ_eval_global_banked_e(salink_obj_t *obj, salink_global_t *global, + salink_label_t *label, uint8_t depth); +void specasm_write_map_banked_e(void); +int salink_link_banked_e(void); + +int salink_link_e(void) +{ + ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_LINK_OBJ_BANK]); + return salink_link_banked_e(); +} + +void specasm_write_map_e(void) +{ + /* + * Only called from the link_obj page, so we map it back + * in once our call has finished. + */ + + ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_MAP_BANK]); + specasm_write_map_banked_e(); + ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_LINK_OBJ_BANK]); +} + +int16_t salink_equ_eval_e(salink_obj_t *obj, const char *str, uint16_t line_no) +{ + int16_t v; + + /* + * Only called from the link_obj page, so we map it back + * in once our call has finished. + */ + + ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_EXPRESSION_BANK]); + v = salink_equ_eval_banked_e(obj, str, line_no); + ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_LINK_OBJ_BANK]); + + return v; +} + +void salink_equ_eval_global_e(salink_obj_t *obj, salink_global_t *global, + salink_label_t *label, uint8_t depth) +{ + /* + * Only called from the link_obj page, so we map it back + * in once our call has finished. + */ + + ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_EXPRESSION_BANK]); + salink_equ_eval_global_banked_e(obj, global, label, depth); + ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_LINK_OBJ_BANK]); +} + diff --git a/src/specasm_next.c b/src/specasm_next.c new file mode 100644 index 0000000..a29925d --- /dev/null +++ b/src/specasm_next.c @@ -0,0 +1,94 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include + +#include +#include +#include +#include + +#include "editor.h" +#include "line.h" +#include "state.h" + +#define SPECASM_KEY_CALIBRATION 13 + +void specasm_peer_next_copy_chars(void); + +int main(void) +{ + uint8_t k; + uint16_t delay = ((200 / 11) * 11) / 10; + uint8_t new_key; + uint16_t i; + uint8_t turbo; + struct esx_mode mode; + + /* + * The editor is nicer to use at 28Mhz. We're running on + * a Next so we might as well use all that power! + */ + + turbo = ZXN_READ_REG(REG_TURBO_MODE); + ZXN_WRITE_REG(REG_TURBO_MODE, turbo | 3); + + specasm_init_dump_table(); + + memset(&mode, 0, sizeof(mode)); + (void) esx_ide_mode_set(&mode); + + zx_border(SPECASM_LABEL_BORDER); + zx_cls(SPECASM_CODE_COLOUR | SPECASM_LABEL_BACKGROUND); + specasm_peer_next_copy_chars(); + zx_cls(SPECASM_CODE_COLOUR | SPECASM_LABEL_BACKGROUND); + + err_type = SPECASM_ERROR_OK; + specasm_editor_reset(); + + specasm_draw_status(); + + // Make cursor flash + specasm_text_set_flash(col, line, FLASH); + + do { + in_wait_key(); + k = in_inkey(); + if (!k) + continue; + do { + if (k == SPECASM_KEY_COMMAND) { + in_wait_nokey(); + new_key = in_inkey(); + } else { + for (i = 0; i < SPECASM_KEY_CALIBRATION; i++) { + specasm_sleep_ms(delay); + new_key = in_inkey(); + if (k != new_key) + break; + } + } + specasm_handle_key_press(k); + k = new_key; + } while (k); + } while (!quitting); + + zx_cls(PAPER_WHITE | INK_WHITE); + ZXN_WRITE_REG(REG_TURBO_MODE, turbo); + + return 0; +} diff --git a/src/specasm_trampolines.c b/src/specasm_trampolines.c new file mode 100644 index 0000000..799660e --- /dev/null +++ b/src/specasm_trampolines.c @@ -0,0 +1,104 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include + +#include "line.h" + +#define SPECASM_NEXT_PARSE_BANK ((43<<1)+1) +#define SPECASM_NEXT_DUMP_BANK ((44<<1)+1) +#define SPECASM_NEXT_PARSE_LD_BANK ((45<<1)+1) + +extern unsigned char _z_page_table[]; + +typedef struct specasm_opcode_t_ specasm_opcode_t; + +uint8_t specasm_parse_exp_banked_e(const char *str, uint8_t *label1, + uint8_t *label1_type); +uint8_t specasm_parse_mnemomic_banked_e(const char *str, uint8_t i, + specasm_line_t *line); +void specasm_init_dump_table_banked(void); +uint8_t specasm_dump_opcode_banked_e(const specasm_line_t *line, char *buf); +uint8_t specasm_parse_ld_banked_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry); + +uint8_t specasm_parse_exp_e(const char *str, uint8_t *label1, + uint8_t *label1_type) +{ + uint8_t e; + unsigned char page = ZXN_READ_MMU7(); + + /* + * parse_exp_e is called from the main area, but can + * indirectly be called from the PARSE_BANK or LD_BANK, + * which is messy. Maybe we can clean this up but for + * the time being map back in the old page when the + * function call has finished. + */ + + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK]); + e = specasm_parse_exp_banked_e(str, label1, label1_type); + ZXN_WRITE_MMU7(page); + + return e; +} + +uint8_t specasm_parse_mnemomic_e(const char *str, uint8_t i, + specasm_line_t *line) +{ + uint8_t e; + + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK]); + e = specasm_parse_mnemomic_banked_e(str, i, line); + + return e; +} + +void specasm_init_dump_table(void) +{ + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK]); + specasm_init_dump_table_banked(); +} + +uint8_t specasm_dump_opcode_e(const specasm_line_t *line, char *buf) +{ + uint8_t e; + + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK]); + e = specasm_dump_opcode_banked_e(line, buf); + + return e; +} + +uint8_t specasm_parse_ld_e(const char *args, specasm_line_t *line, + const specasm_opcode_t *op_entry) +{ + uint8_t e; + + /* + * This one is a little tricky. It's actually called not + * from the main program but from another bank. So we need to + * switch back to the parse bank when we've finished. + */ + + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_LD_BANK]); + e = specasm_parse_ld_banked_e(args, line, op_entry); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK]); + + return e; +} + diff --git a/src/test_content.c b/src/test_content.c index 458b11f..dc32ac3 100644 --- a/src/test_content.c +++ b/src/test_content.c @@ -117,6 +117,17 @@ const test_t opcode_tests[] = { {"add iy, sp", "add iy, sp", 2, { 0xFD, 0x39 }}, {"add iy, sp", "add iy, sp", 2, { 0xFD, 0x39 }}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"add hl, a", "add hl, a", 2, { 0xED, 0x31 }}, + {"add de, a", "add de, a", 2, { 0xED, 0x32 }}, + {"add bc, a", "add bc, a", 2, { 0xED, 0x33 }}, + {"add hl, 257", "add hl, 257", 4, { 0xED, 0x34, 0x1, 0x1 }}, + {"add de, 257", "add de, 257", 4, { 0xED, 0x35, 0x1, 0x1 }}, + {"add bc, 257", "add bc, 257", 4, { 0xED, 0x36, 0x1, 0x1 }}, + {"add bc , $101", "add bc, $101", 4, { 0xED, 0x36, 0x1, 0x1 }}, + {"add de , '!'", "add de, '!'", 4, { 0xED, 0x35, 0x21, 0x0 }}, + {"add hl , =label", "add hl, =label", 4, { 0xED, 0x34, 0x0, 0x0 }}, +#endif {"add a , =label", "add a, =label", 2, { 0xC6, 0x00 }}, {"add a , =no", "add a, =no", 2, { 0xC6, 0x01 }}, {"add a , =verylonglabel", "add a, =verylonglabel", 2, { 0xC6, 0x00 }}, @@ -368,6 +379,14 @@ const test_t opcode_tests[] = { {"bit 7, l", "bit 7, l", 2, { 0xCB, 0X7D }}, {"bit $7, l", "bit 7, l", 2, { 0xCB, 0X7D }}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"bsla de, b", "bsla de, b", 2, { 0xED, 0x28 }}, + {"bsra de, b", "bsra de, b", 2, { 0xED, 0x29 }}, + {"bsrl de, b", "bsrl de, b", 2, { 0xED, 0x2A }}, + {"bsrf de, b", "bsrf de, b", 2, { 0xED, 0x2B }}, + {"brlc de, b", "brlc de, b", 2, { 0xED, 0x2C }}, +#endif + {"call c, label", "call c, label", 3, {0xDC, 0x00, 0x00}}, {"call c,label", "call c, label", 3, {0xDC, 0x00, 0x00}}, {"call m, label", "call m, label", 3, {0xFC, 0x00, 0x00}}, @@ -620,6 +639,10 @@ const test_t opcode_tests[] = { {"jp no", "jp no", 3, {0xC3, 0x01, 0x00}}, {"jp no", "jp no", 3, {0xC3, 0x01, 0x00}}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"jp (c)", "jp (c)", 2, {0xED, 0x98}}, + {"jp ( c )", "jp (c)", 2, {0xED, 0x98}}, +#endif {"jp =clabel", "jp =clabel", 3, {0xc3, 0x03, 0x00}}, {"jp = clabel", "jp =clabel", 3, {0xc3, 0x03, 0x00}}, {"jp nc,=label", "jp nc, =label", 3, {0xd2, 0x00, 0x00}}, @@ -1320,7 +1343,41 @@ const test_t opcode_tests[] = { {"ldd", "ldd", 2, {0xED, 0xA8}}, {"lddr", "lddr", 2, {0xED, 0xB8}}, + +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"lddrx", "lddrx", 2, {0xED, 0xBC}}, + {"lddx", "lddx", 2, {0xED, 0xAC}}, + {"ldirx", "ldirx", 2, {0xED, 0xB4}}, + {"ldix", "ldix", 2, {0xED, 0xA4}}, + {"ldpirx", "ldpirx", 2, {0xED, 0xB7}}, + {"ldws", "ldws", 2, {0xED, 0xA5}}, +#endif + {"ldi", "ldi", 2, {0xED, 0xA0}}, + +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"mirror a", "mirror a", 2, {0xED, 0x24}}, + {"mirror a", "mirror a", 2, {0xED, 0x24}}, + {"mul d, e", "mul d, e", 2, {0xED, 0x30}}, + {"mul d , e", "mul d, e", 2, {0xED, 0x30}}, + {"nbrk", "nbrk", 4, {0xED, 0x91, 0x2, 0x8}}, + {"nextreg 10, a", "nextreg 10, a", 3, {0xED, 0x92, 0xA }}, + {"nextreg 10 , a", "nextreg 10, a", 3, {0xED, 0x92, 0xA }}, + {"nextreg =label , a", "nextreg =label, a", 3, {0xED, 0x92, 0x0 }}, + {"nextreg = label1 , a", "nextreg =label1, a", 3, + {0xED, 0x92, 0x5 }}, + {"nextreg $10, a", "nextreg $10, a", 3, {0xED, 0x92, 0x10 }}, + {"nextreg 10, 11", "nextreg 10, 11", 4, {0xED, 0x91, 0xA, 0xB }}, + {"nextreg $10, 11", "nextreg $10, 11", 4, {0xED, 0x91, 0x10, 0xB }}, + {"nextreg 10, $11", "nextreg 10, $11", 4, {0xED, 0x91, 0xA, 0x11 }}, + {"nextreg $aa, $bb", "nextreg $AA, $BB", 4, {0xED, 0x91, 0xAA, 0xBB }}, + {"nextreg -1, -1", "nextreg -1, -1", 4, {0xED, 0x91, 0xFF, 0xFF }}, + {"nextreg =label , $10", "nextreg =label, $10", 4, + {0xED, 0x91, 0x0, 0x10 }}, + {"nextreg =label1 , -1", "nextreg =label1, -1", 4, + {0xED, 0x91, 0x5, 0xFF}}, +#endif + {"map", "map", 1, {0x0}}, {"neg", "neg", 2, {0xED, 0x44}}, {"nop", "nop", 1, {0x0}}, @@ -1401,6 +1458,12 @@ const test_t opcode_tests[] = { {"outd", "outd", 2, {0xED, 0xAB}}, {"outi", "outi", 2, {0xED, 0xA3}}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"outinb", "outinb", 2, {0xED, 0x90}}, + {"pixelad", "pixelad", 2, {0xED, 0x94}}, + {"pixeldn", "pixeldn", 2, {0xED, 0x93}}, +#endif + {"pop af", "pop af", 1, {0xF1}}, {"pop af", "pop af", 1, {0xF1}}, {"pop bc", "pop bc", 1, {0xC1}}, @@ -1427,6 +1490,14 @@ const test_t opcode_tests[] = { {"push iy", "push iy", 2, {0xFD, 0xE5}}, {"push iy", "push iy", 2, {0xFD, 0xE5}}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"push $102", "push $102", 4, {0xED, 0x8A, 0x1, 0x2}}, + {"push 258", "push 258", 4, {0xED, 0x8A, 0x1, 0x2}}, + {"push -2", "push -2", 4, {0xED, 0x8A, 0xFF, 0xFE}}, + {"push 16", "push 16", 4, {0xED, 0x8A, 0x0, 0x10}}, + {"push =label", "push =label", 4, {0xED, 0x8A, 0x00, 0x0}}, + {"push = label1", "push =label1", 4, {0xED, 0x8A, 0x0, 0x05}}, +#endif {"res 0, (hl)", "res 0, (hl)", 2, { 0xCB, 0x86 }}, {"res 0, ( hl )", "res 0, (hl)", 2, { 0xCB, 0x86 }}, {"res 0, (ix + 127)", "res 0, (ix+127)", 4, { 0xDD, 0xCB, 0x7f, 0x86 }}, @@ -2038,6 +2109,10 @@ const test_t opcode_tests[] = { {"set =label,( hl )", "set =label, (hl)", 2, { 0xCB, 0xC6 }}, {"set =verylonglabel,( hl )", "set =verylonglabel, (hl)", 2, { 0xCB, 0XC6 }}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"setae", "setae", 2, { 0xED, 0x95 }}, +#endif + {"sla (hl)", "sla (hl)", 2, { 0xCB, 0x26 }}, {"sla ( hl )", "sla (hl)", 2, { 0xCB, 0x26 }}, {"sla (ix+$FF)", "sla (ix+$FF)", 4, { 0xDD, 0xCB, 0xff, 0x26 }}, @@ -2140,6 +2215,17 @@ const test_t opcode_tests[] = { {"sub =label1", "sub =label1", 2, { 0xD6, 0x5 }}, {"sub = label", "sub =label", 2, { 0xD6, 0x0 }}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"swapnib", "swapnib", 2, { 0xED, 0x23 }}, + + {"test 40", "test 40", 3, { 0xED, 0x27, 0x28 }}, + {"test 40", "test 40", 3, { 0xED, 0x27, 0x28 }}, + {"test -1", "test -1", 3, { 0xED, 0x27, 0xff }}, + {"test $ff", "test $FF", 3, { 0xED, 0x27, 0xff }}, + {"test =label", "test =label", 3, { 0xED, 0x27, 0x0 }}, + {"test =label1", "test =label1", 3, { 0xED, 0x27, 0x5 }}, +#endif + {"xor (hl)", "xor (hl)", 1, { 0xAE }}, {"xor ( hl )", "xor (hl)", 1, { 0xAE }}, {"xor (ix+$FF)", "xor (ix+$FF)", 3, { 0xDD, 0xAE, 0xff }}, @@ -2387,15 +2473,22 @@ const bad_test_t bad_tests[] = { { "add a, 329", SPECASM_ERROR_NUM_TOO_BIG }, { "add 10, b", SPECASM_ERROR_BAD_REG }, { "add bc, hl", SPECASM_ERROR_BAD_REG }, +#ifdef SPECASM_NO_NEXT { "add hl, $101", SPECASM_ERROR_BAD_REG }, +#endif + { "add ix, a", SPECASM_ERROR_BAD_REG }, + { "add iy, a", SPECASM_ERROR_BAD_REG }, + { "add ix, 256", SPECASM_ERROR_BAD_REG }, + { "add iy, 256", SPECASM_ERROR_BAD_REG }, { "add ix, hl", SPECASM_ERROR_BAD_REG }, { "add iy, hl", SPECASM_ERROR_BAD_REG }, { "add sp, hl", SPECASM_ERROR_BAD_REG }, { "add", SPECASM_ERROR_BAD_REG }, { "add a, ", SPECASM_ERROR_BAD_REG }, { "add , 10", SPECASM_ERROR_BAD_REG }, +#ifdef SPECASM_NO_NEXT { "add hl, =label", SPECASM_ERROR_BAD_REG }, - +#endif {"align 512", SPECASM_ERROR_BAD_NUM }, {"align 1", SPECASM_ERROR_BAD_NUM }, {"align 3", SPECASM_ERROR_BAD_NUM }, @@ -2437,6 +2530,14 @@ const bad_test_t bad_tests[] = { {"bit =label, (iy + 10)", SPECASM_ERROR_BAD_EXPRESSION }, {"bit 0, (ix + =label)", SPECASM_ERROR_BAD_NUM }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"bsla hl, b", SPECASM_ERROR_BAD_REG }, + {"bsra de, a", SPECASM_ERROR_BAD_REG }, + {"bsrl hl,", SPECASM_ERROR_BAD_REG }, + {"bsrf de", SPECASM_ERROR_COMMA_EXPECTED }, + {"brlc", SPECASM_ERROR_BAD_REG }, +#endif + {"call", SPECASM_ERROR_BAD_LABEL }, {"call nzc, label", SPECASM_ERROR_CONDITION_CODE }, {"call nzc , label", SPECASM_ERROR_CONDITION_CODE }, @@ -2625,6 +2726,26 @@ const bad_test_t bad_tests[] = { {"ld a, =", SPECASM_ERROR_BAD_EXPRESSION }, {"ld hl, (=(label1+1", SPECASM_ERROR_BAD_EXPRESSION }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"mirror", SPECASM_ERROR_BAD_REG }, + {"mirror b", SPECASM_ERROR_BAD_REG }, + {"mirror hl", SPECASM_ERROR_BAD_REG }, + {"mirror 1", SPECASM_ERROR_BAD_REG }, + + {"mul", SPECASM_ERROR_BAD_REG }, + {"mul d", SPECASM_ERROR_COMMA_EXPECTED }, + {"mul d,", SPECASM_ERROR_BAD_REG }, + {"mul d, a", SPECASM_ERROR_BAD_REG }, + {"mul a, e", SPECASM_ERROR_BAD_REG }, + {"mul e, 10", SPECASM_ERROR_BAD_REG }, + + {"nextreg a, 10", SPECASM_ERROR_BAD_NUM }, + {"nextreg 10, b", SPECASM_ERROR_BAD_REG }, + {"nextreg 10", SPECASM_ERROR_COMMA_EXPECTED }, + {"nextreg 10, ", SPECASM_ERROR_BAD_NUM }, + {"nextreg =label1, =label2 ", SPECASM_ERROR_BAD_NUM }, +#endif + {"or", SPECASM_ERROR_BAD_REG }, {"or (bc)", SPECASM_ERROR_BAD_REG }, {"or (ix)", SPECASM_ERROR_BAD_REG }, @@ -2679,7 +2800,9 @@ const bad_test_t bad_tests[] = { {"push a", SPECASM_ERROR_BAD_REG }, {"push b", SPECASM_ERROR_BAD_REG }, {"push bb", SPECASM_ERROR_BAD_REG }, +#ifdef SPECASM_NO_NEXT {"push 10", SPECASM_ERROR_BAD_REG }, +#endif {"push (bc)", SPECASM_ERROR_BAD_REG }, {"push (ix+100)", SPECASM_ERROR_BAD_REG }, @@ -2870,6 +2993,12 @@ const bad_test_t bad_tests[] = { {"sub hl", SPECASM_ERROR_BAD_REG }, {"sub f", SPECASM_ERROR_BAD_REG }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + {"test", SPECASM_ERROR_BAD_NUM }, + {"test a", SPECASM_ERROR_BAD_NUM }, + {"test $ffff", SPECASM_ERROR_NUM_TOO_BIG }, +#endif + {"xor", SPECASM_ERROR_BAD_REG }, {"xor (bc)", SPECASM_ERROR_BAD_REG }, {"xor (ix)", SPECASM_ERROR_BAD_REG }, diff --git a/src/unittests_zx.c b/src/unittests_zx.c index 6ff2328..f3f7c00 100644 --- a/src/unittests_zx.c +++ b/src/unittests_zx.c @@ -17,8 +17,6 @@ #include #include -#include - #include "error.h" #include "peer.h" #include "peer_file.h" @@ -27,6 +25,13 @@ #include "test_content_zx.h" #include "util_print_zx.h" +#ifdef SPECASM_TARGET_NEXT +#include +void specasm_peer_next_copy_chars(void); +#else +#include +#endif + #define UNITTEST_ZX_TEST_LINE 0 #define UNITTEST_ZX_FORMAT_LINE 1 #define UNITTEST_ZX_FORMAT_LINE2 2 @@ -312,6 +317,20 @@ static int prv_test_old_version(void) int main(int argc, char *argv[]) { +#ifdef SPECASM_TARGET_NEXT + uint8_t turbo; + + /* + * Run tests at top speed on a spectrum Next. + */ + + turbo = ZXN_READ_REG(REG_TURBO_MODE); + ZXN_WRITE_REG(REG_TURBO_MODE, turbo | 3); + zx_cls(PAPER_WHITE | INK_WHITE); + specasm_peer_next_copy_chars(); + zx_cls(PAPER_WHITE | INK_BLACK); +#endif + specasm_init_dump_table(); specasm_error_init(err_type); @@ -322,5 +341,9 @@ int main(int argc, char *argv[]) prv_test_old_version()) return 1; +#ifdef SPECASM_TARGET_NEXT + ZXN_WRITE_REG(REG_TURBO_MODE, turbo); +#endif + return 0; } diff --git a/src/util_print_next.c b/src/util_print_next.c new file mode 100644 index 0000000..9604f61 --- /dev/null +++ b/src/util_print_next.c @@ -0,0 +1,181 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include + +#include "util_print_zx.h" + +#define SPECASM_NEXT_NUM_CHARS 112 + +/* + * Reserve some space for the spectrum font copied from the + * character set rendered to the screen during the dotn startup. + * We can't acccess the character set directly as it's in the ROM + * which is mapped out. + */ + +static uint8_t specasm_font[SPECASM_NEXT_NUM_CHARS * 8]; + +static void prv_write_chars(void) __naked +{ +__asm + push af + push bc + push hl + + ld hl, 0x5800 + + ld a, 22 + rst 16 + ld a, 0 + rst 16 + ld a, 0 + rst 16 + ld a, 32 + ld b, SPECASM_NEXT_NUM_CHARS +CHAR_LOOP: + push af + push hl + push bc + rst 16 + pop bc + pop hl + xor a + ld (hl), a + inc l + pop af + inc a + djnz CHAR_LOOP + + pop hl + pop bc + pop af + ret +__endasm; +} + +void specasm_peer_next_copy_chars(void) +{ + uint8_t i; + uint8_t j; + const uint8_t *new_sptr; + const uint8_t *sptr = zx_cxy2saddr(0, 0); + uint8_t *byte = &specasm_font[0]; + + prv_write_chars(); + + for (i = 0; i < SPECASM_NEXT_NUM_CHARS; i++) { + new_sptr = sptr; + for (j = 0; j < 8; j++) { + *byte = *new_sptr; + byte++; + new_sptr += 256; + } + sptr++; + } +} + +uint8_t specasm_util_print(const char *str, uint8_t x, uint8_t y, uint8_t attr) +{ + uint8_t *cptr; + uint8_t *aptr = zx_cxy2aaddr(x, y); + uint8_t *bsptr; + uint8_t *sptr; + uint8_t i; + uint8_t ch; + uint8_t old_x = x; + + bsptr = zx_cxy2saddr(x, y); + while (*str) { + *aptr++ = attr; + ch = *((uint8_t*) str); + if (ch >= 32 && ch <= 143) { + cptr = &specasm_font[(ch - 32) * 8]; + sptr = bsptr; + i = 8; + do { + *sptr = *cptr++; + sptr += 256; + i--; + } while (i != 0); + } + str++; + x++; + bsptr++; + } + + return x - old_x; +} + +void specasm_util_clear(uint8_t x, uint8_t y, uint8_t l, uint8_t attr) +{ + uint8_t *aptr = zx_cxy2aaddr(x, y); + uint8_t *sptr; + uint8_t *bsptr; + uint8_t i; + + bsptr = zx_cxy2saddr(x, y); + while (l > 0) { + *aptr++ = attr; + sptr = bsptr; + i = 8; + do { + *sptr = 0; + sptr += 256; + i--; + } while (i != 0); + x++; + l--; + bsptr++; + } +} + +void specasm_text_set_flash(uint8_t x, uint8_t y, uint8_t attr) +{ + uint8_t *aptr = zx_cxy2aaddr(x, y); + + *aptr &= ~((uint8_t)FLASH); + *aptr |= attr; +} + +void specasm_text_printch(char ch, uint8_t x, uint8_t y, uint8_t attr) +{ + uint8_t *cptr; + uint8_t *aptr = zx_cxy2aaddr(x, y); + uint8_t *sptr = zx_cxy2saddr(x, y); + uint8_t i; + + if (ch < 32 || ch > 143) + return; + + *aptr = attr; + cptr = &specasm_font[(((uint8_t)ch) - 32) * 8]; + i = 8; + do { + *sptr = *cptr++; + sptr += 256; + i--; + } while (i != 0); +} + +void specasm_screen_flush(uint16_t peer_last_row) +{ + uint8_t *posn_x = (uint8_t *)23688; + uint8_t *posn_y = (uint8_t *)23689; + + *posn_x = 33; + *posn_y = 24 - peer_last_row; +} diff --git a/tests/test_equ_next/equ.s b/tests/test_equ_next/equ.s new file mode 100644 index 0000000..0a1337e --- /dev/null +++ b/tests/test_equ_next/equ.s @@ -0,0 +1,11 @@ +org $8000 +.Main +.Size equ 16 + +test =Size +nextreg =Size, a +nextreg =Size, 10 +add hl, =(Size * 16) + 2 +add bc, =(Size * 16) + 3 +add de, =(Size * 32) + 4 +push =(Size*17) diff --git a/tests/test_equ_next/test.sh b/tests/test_equ_next/test.sh new file mode 100755 index 0000000..6f09a93 --- /dev/null +++ b/tests/test_equ_next/test.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +if [ -z "${SPECASM_TARGET_NEXT_OPCODES}" ]; then + exit 0 +fi + +set -e +rm equ 2>/dev/null 1>&2 || true +rm *.x 2>/dev/null 1>&2 || true + +../../saimport *.s +../../salink 2>/dev/null 1>&2 + +# test =Size +word=`od -An -j2 -tx1 -N1 equ | xargs` +if [ "$word" != "10" ]; then + echo "test =Size" + exit 1 +fi + +word=`od -An -j5 -tx1 -N1 equ | xargs` +if [ "$word" != "10" ]; then + echo "nextreg =Size, a" + exit 1 +fi + +word=`od -An -j8 -tx1 -N1 equ | xargs` +if [ "$word" != "10" ]; then + echo "nextreg =Size, 10" + exit 1 +fi + +word=`od -An -j12 -tx1 -N2 equ | xargs` +if [ "$word" != "02 01" ]; then + echo "add hl, =(Size * 16) + 2" + exit 1 +fi + +word=`od -An -j16 -tx1 -N2 equ | xargs` +if [ "$word" != "03 01" ]; then + echo "add bc, =(Size * 16) + 2" + exit 1 +fi + +word=`od -An -j20 -tx1 -N2 equ | xargs` +if [ "$word" != "04 02" ]; then + echo "add de, =(Size * 32) + 4" + exit 1 +fi + +word=`od -An -j24 -tx1 -N2 equ | xargs` +if [ "$word" != "10 01" ]; then + echo "push =(Size*17)" + exit 1 +fi + + +rm equ +rm *.x + diff --git a/tests/test_sub_negative/test.sh b/tests/test_sub_negative/test.sh index 47cd273..6e42e00 100755 --- a/tests/test_sub_negative/test.sh +++ b/tests/test_sub_negative/test.sh @@ -1,6 +1,11 @@ #!/bin/bash set -e + +if [ -z "${SPECASM_TARGET_NEXT_OPCODES}" ]; then + exit 0 +fi + rm subtraction 2>/dev/null 1>&2 || true if ../../salink 2>/dev/null 1>&2 ; then diff --git a/tests/test_sub_too_big/test.sh b/tests/test_sub_too_big/test.sh index 6847fd9..620b7ba 100755 --- a/tests/test_sub_too_big/test.sh +++ b/tests/test_sub_too_big/test.sh @@ -1,6 +1,11 @@ #!/bin/bash set -e + +if [ -z "${SPECASM_TARGET_NEXT_OPCODES}" ]; then + exit 0 +fi + rm subtraction 2>/dev/null 1>&2 || true if ../../salink 2>/dev/null 1>&2 ; then diff --git a/tests/test_subtraction/test.sh b/tests/test_subtraction/test.sh index f6e2e9b..a53742e 100755 --- a/tests/test_subtraction/test.sh +++ b/tests/test_subtraction/test.sh @@ -1,6 +1,11 @@ #!/bin/bash set -e + +if [ -z "${SPECASM_TARGET_NEXT_OPCODES}" ]; then + exit 0 +fi + rm subtraction 2>/dev/null 1>&2 || true ../../salink 2>/dev/null 1>&2 From 9d7a846cb5f8cc9a70700c854d2c9ce13d6967c2 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 13 Jan 2024 19:39:46 +0100 Subject: [PATCH 05/16] Don't link text routines to saimport/export These routines are not needed as saimport and saexport use the C runtime print routines. This saves 300 bytes or so. Signed-off-by: Mark Ryan --- build/48/specasm/Makefile | 2 -- build/next/specasm/Makefile | 2 -- src/peer_zx.c | 34 ---------------------------------- src/util_print_zx.c | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 38 deletions(-) diff --git a/build/48/specasm/Makefile b/build/48/specasm/Makefile index e006e4e..e6d2195 100644 --- a/build/48/specasm/Makefile +++ b/build/48/specasm/Makefile @@ -43,7 +43,6 @@ SAEXPORT = \ line_dump_common.o \ state_dump.o \ peer_file_zx.o \ - util_print_zx.o \ peer_zx.o \ saexport.o @@ -56,7 +55,6 @@ SAIMPORT = \ line_parse_common.o \ state_parse.o \ peer_file_zx.o \ - util_print_zx.o \ peer_zx.o \ saimport.o diff --git a/build/next/specasm/Makefile b/build/next/specasm/Makefile index 7e068dd..0acad47 100644 --- a/build/next/specasm/Makefile +++ b/build/next/specasm/Makefile @@ -64,7 +64,6 @@ SAEXPORT = \ line_dump_common.o \ state_dump.o \ peer_file_next.o \ - util_print_zx.o \ peer_next.o \ saexport.o @@ -77,7 +76,6 @@ SAIMPORT = \ line_parse_common.o \ state_parse.o \ peer_file_next.o \ - util_print_zx.o \ peer_next.o \ saimport.o diff --git a/src/peer_zx.c b/src/peer_zx.c index 291f07a..2543573 100644 --- a/src/peer_zx.c +++ b/src/peer_zx.c @@ -68,37 +68,3 @@ uint16_t specasm_peer_read_state_e(const char *fname) return checksum; } - -void specasm_text_set_flash(uint8_t x, uint8_t y, uint8_t attr) -{ - uint8_t *aptr = zx_cxy2aaddr(x, y); - - *aptr &= ~((uint8_t)FLASH); - *aptr |= attr; -} - -void specasm_text_printch(char ch, uint8_t x, uint8_t y, uint8_t attr) -{ - uint8_t *cptr; - uint8_t *aptr = zx_cxy2aaddr(x, y); - uint8_t *sptr = zx_cxy2saddr(x, y); - uint8_t i; - - *aptr = attr; - cptr = (uint8_t *)15360 + (((uint8_t)ch) * 8); - i = 8; - do { - *sptr = *cptr++; - sptr += 256; - i--; - } while (i != 0); -} - -void specasm_screen_flush(uint16_t peer_last_row) -{ - uint8_t *posn_x = (uint8_t *)23688; - uint8_t *posn_y = (uint8_t *)23689; - - *posn_x = 33; - *posn_y = 24 - peer_last_row; -} diff --git a/src/util_print_zx.c b/src/util_print_zx.c index 40c8fca..0f52bef 100644 --- a/src/util_print_zx.c +++ b/src/util_print_zx.c @@ -68,3 +68,37 @@ void specasm_util_clear(uint8_t x, uint8_t y, uint8_t l, uint8_t attr) bsptr++; } } + +void specasm_text_set_flash(uint8_t x, uint8_t y, uint8_t attr) +{ + uint8_t *aptr = zx_cxy2aaddr(x, y); + + *aptr &= ~((uint8_t)FLASH); + *aptr |= attr; +} + +void specasm_text_printch(char ch, uint8_t x, uint8_t y, uint8_t attr) +{ + uint8_t *cptr; + uint8_t *aptr = zx_cxy2aaddr(x, y); + uint8_t *sptr = zx_cxy2saddr(x, y); + uint8_t i; + + *aptr = attr; + cptr = (uint8_t *)15360 + (((uint8_t)ch) * 8); + i = 8; + do { + *sptr = *cptr++; + sptr += 256; + i--; + } while (i != 0); +} + +void specasm_screen_flush(uint16_t peer_last_row) +{ + uint8_t *posn_x = (uint8_t *)23688; + uint8_t *posn_y = (uint8_t *)23689; + + *posn_x = 33; + *posn_y = 24 - peer_last_row; +} From b52d21874396dada1ec5373a3233b3450029eed6 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 31 Dec 2023 19:55:37 +0100 Subject: [PATCH 06/16] Add samake A tool for automatically creating loaders, .bas or .tap, for binaries created by salink. Signed-off-by: Mark Ryan --- .gitignore | 4 + Makefile | 11 +- build/48/specasm/Make.include | 2 + build/48/specasm/Makefile | 17 +- build/next/specasm/Make.include | 2 + build/next/specasm/Makefile | 14 +- src/peer_file.h | 10 + src/peer_file_next.c | 6 + src/peer_file_posix.c | 6 + src/peer_file_zx.c | 7 + src/samake.c | 558 ++++++++++++++++++++++++++++++++ 11 files changed, 630 insertions(+), 7 deletions(-) create mode 100644 src/samake.c diff --git a/.gitignore b/.gitignore index 0dfca08..9342d13 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,10 @@ SAIMPORT.X SAEXPORT.X UNITNEXT UNITZX +SAIMPORT.X +SAEXPORT.X +SAMAKE +SAMAKE.X build.sh compile.sh build/48/specasm/release diff --git a/Makefile b/Makefile index bd14ec1..0e96dda 100644 --- a/Makefile +++ b/Makefile @@ -49,13 +49,16 @@ SALINK =\ salink.c \ expression.c +SAMAKE =\ + samake.c + TEST_CONTENT_ZX =\ test_content.c \ test_content_zx.c CFLAGS += -Wall -MMD -DUNITTESTS -Isrc -all: unittests saimport saexport salink +all: unittests saimport saexport salink samake unittests: $(BASE:%.c=%.o) $(COMMON:%.c=%.o) $(SRCS:%.c=%.o) $(CC) $(CFLAGS) -o $@ $^ @@ -72,8 +75,11 @@ salink: $(BASE:%.c=%.o) $(POSIX:%.c=%.o) $(SALINK:%.c=%.o) test_content_zx: $(TEST_CONTENT_ZX:%.c=%.o) $(CC) $(CFLAGS) -o $@ $^ +samake: $(BASE:%.c=%.o) $(POSIX:%.c=%.o) $(SAMAKE:%.c=%.o) + $(CC) $(CFLAGS) -o $@ $^ + clean: - - rm *.d *.o unittests saimport saexport salink + - rm *.d *.o unittests saimport saexport salink samake -include $(BASE:%.c=%.d) -include $(COMMON:%.c=%.d) @@ -82,4 +88,5 @@ clean: -include $(SAIMPORT:%.c=%.d) -include $(SAEXPORT:%.c=%.d) -include $(SALINK:%.c=%.d) +-include $(SAMAKE:%.c=%.d) -include $(TEST_CONTENT_ZX:%.c=%.d) diff --git a/build/48/specasm/Make.include b/build/48/specasm/Make.include index 788de1f..3d32774 100644 --- a/build/48/specasm/Make.include +++ b/build/48/specasm/Make.include @@ -47,6 +47,8 @@ peer_zx.o: peer_zx.c peer.h state.h state_base.h line.h \ strings.h error.h util_print_zx.h peer_file_zx.o: peer_file_zx.c error.h peer_file.h util_print_zx.o: util_print_zx.h +samake.o: samake.c peer.h error.h peer_zx.h line.h \ + salink.h peer_file.h state_base.h strings.h %.o: %.c zcc $(CFLAGS) -o $@ -c $< diff --git a/build/48/specasm/Makefile b/build/48/specasm/Makefile index e6d2195..373a71e 100644 --- a/build/48/specasm/Makefile +++ b/build/48/specasm/Makefile @@ -1,7 +1,7 @@ VPATH=../../../src .PHONY: all -all: specasm.tap salink.tap SAEXPORT SAIMPORT +all: specasm.tap salink.tap SAEXPORT SAIMPORT SAMAKE CC=zcc CFLAGS=+zx -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warning 85" -clib=sdcc_iy @@ -58,6 +58,14 @@ SAIMPORT = \ peer_zx.o \ saimport.o +SAMAKE =\ + samake.o \ + error.o \ + state_base.o \ + peer_file_zx.o \ + peer_zx.o + + specasm.tap: $(SPECASM) $(CC) $(CFLAGS) -zorg=24310 -startup=31 -o specasm-bare $^ -create-app cat ../../../bas/SPECLD.TAP specasm-bare.tap > specasm.tap @@ -71,9 +79,12 @@ SAEXPORT: $(SAEXPORT) SAIMPORT: $(SAIMPORT) $(CC) $(CFLAGS) -startup=30 -o $@ $(SAIMPORT) -subtype=dotx -Cz"--clean" -create-app +SAMAKE: $(SAMAKE) + $(CC) $(CFLAGS) -startup=30 -o $@ $(SAMAKE) -subtype=dotx -Cz"--clean" -create-app + clean: - rm -rf specasm *.zip -rf unitzx - - rm *.X *.o *.bin *.tap SAIMPORT SAEXPORT + - rm *.X *.o *.bin *.tap SAIMPORT SAEXPORT SAMAKE .PHONY: release release: @@ -81,7 +92,7 @@ release: mkdir -p release/specasm cp specasm.tap salink.tap release/specasm cp ../../../COPYING release/specasm - cp SAIMPORT SAEXPORT *.X release/specasm + cp SAMAKE SAIMPORT SAEXPORT *.X release/specasm cp ../../../bas/INSTALL release/specasm cp ../../../bas/REMOVE release/specasm cd release && zip -r specasm48.zip specasm diff --git a/build/next/specasm/Make.include b/build/next/specasm/Make.include index e41e9e3..d104414 100644 --- a/build/next/specasm/Make.include +++ b/build/next/specasm/Make.include @@ -54,6 +54,8 @@ peer_next.o: peer_next.c peer.h state.h state_base.h line.h \ strings.h error.h util_print_zx.h peer_file_next.o: peer_file_next.c error.h peer_file.h util_print_next.o: util_print_next.c util_print_zx.h +samake.o: samake.c peer.h error.h peer_zx.h line.h \ + salink.h peer_file.h state_base.h strings.h %.o: %.c zcc $(CFLAGS) -o $@ -c $< diff --git a/build/next/specasm/Makefile b/build/next/specasm/Makefile index 0acad47..077fe96 100644 --- a/build/next/specasm/Makefile +++ b/build/next/specasm/Makefile @@ -1,7 +1,7 @@ VPATH=../../../src .PHONY: all -all: SPECASM SALINK SAEXPORT SAIMPORT +all: SPECASM SALINK SAEXPORT SAIMPORT SAMAKE CC=zcc CFLAGS=+zxn -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warning 85" -clib=sdcc_iy -DSPECASM_TARGET_NEXT -DSPECASM_TARGET_NEXT_OPCODES @@ -79,6 +79,13 @@ SAIMPORT = \ peer_next.o \ saimport.o +SAMAKE =\ + samake.o \ + error.o \ + state_base.o \ + peer_file_next.o \ + peer_next.o + SPECASM: $(SPECASM) $(CC) $(CFLAGS) -m -startup=31 -o $@ $^ -pragma-include:zpragma.inc -subtype=dotn $(CZFLAGS) -create-app @@ -91,9 +98,12 @@ SAEXPORT: $(SAEXPORT) SAIMPORT: $(SAIMPORT) $(CC) $(CFLAGS) -startup=30 -o $@ $(SAIMPORT) -subtype=dotn -Cz"--clean" -create-app +SAMAKE: $(SAMAKE) + $(CC) $(CFLAGS) -startup=30 -o $@ $(SAMAKE) -subtype=dotn -Cz"--clean" -create-app + clean: - rm -rf specasm *.zip - - rm *.X *.o *.bin SPECASM SALINK SAIMPORT SAEXPORT + - rm *.X *.o *.bin SPECASM SALINK SAIMPORT SAEXPORT SAMAKE .PHONY: release release: diff --git a/src/peer_file.h b/src/peer_file.h index c3f6b21..af87da7 100644 --- a/src/peer_file.h +++ b/src/peer_file.h @@ -23,19 +23,23 @@ typedef unsigned char specasm_dir_t; #ifdef SPECTRUM #include typedef struct esxdos_dirent specasm_dirent_t; +typedef struct esxdos_stat specasm_stat_t; #define SPECASM_PATH_MAX ESXDOS_PATH_MAX #define specasm_readdir(dir, drent) esxdos_f_readdir(dir, drent) #define specasm_closedir(dir) esxdos_f_close(dir) #define specasm_getdirname(d) (&d.dir[1]) #define specasm_remove_file(f) (void)esxdos_f_unlink(f) +#define specasm_get_file_size(stat_buf) (stat_buf)->size #else #include typedef struct esxdos_dirent specasm_dirent_t; +typedef struct esx_stat specasm_stat_t; #define SPECASM_PATH_MAX ESX_PATHNAME_MAX #define specasm_readdir(dir, drent) esx_f_readdir(dir, drent) #define specasm_closedir(dir) esx_f_close(dir) #define specasm_getdirname(d) (&d.dir[1]) #define specasm_remove_file(f) (void)esx_f_unlink(f) +#define specasm_get_file_size(stat_buf) (stat_buf)->size #endif #else @@ -44,14 +48,19 @@ typedef struct esxdos_dirent specasm_dirent_t; #include #include #include +#include + #define SPECASM_PATH_MAX PATH_MAX typedef FILE *specasm_handle_t; typedef DIR *specasm_dir_t; typedef struct dirent specasm_dirent_t; +typedef struct stat specasm_stat_t; uint8_t specasm_readdir(specasm_dir_t dir, specasm_dirent_t *dirent); #define specasm_closedir(dir) (void)closedir(dir) #define specasm_getdirname(d) d.d_name #define specasm_remove_file(f) (void)remove(f) +#define specasm_f_stat(f, stat_buf) fstat(f, stat_buf) +#define specasm_get_file_size(stat_buf) (stat_buf)->st_size #endif specasm_handle_t specasm_file_wopen_e(const char *fname); @@ -60,5 +69,6 @@ void specasm_file_write_e(specasm_handle_t f, const void *data, size_t size); size_t specasm_file_read_e(specasm_handle_t f, void *data, size_t size); void specasm_file_close_e(specasm_handle_t f); specasm_dir_t specasm_opendir_e(const char *fname); +void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf); #endif diff --git a/src/peer_file_next.c b/src/peer_file_next.c index a256534..5a74233 100644 --- a/src/peer_file_next.c +++ b/src/peer_file_next.c @@ -65,3 +65,9 @@ specasm_dir_t specasm_opendir_e(const char *fname) return d; } + +void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf) +{ + if (esx_f_fstat(f, buf)) + err_type = SPECASM_ERROR_READ; +} diff --git a/src/peer_file_posix.c b/src/peer_file_posix.c index 6e35eac..d6e9784 100644 --- a/src/peer_file_posix.c +++ b/src/peer_file_posix.c @@ -81,3 +81,9 @@ uint8_t specasm_readdir(specasm_dir_t dir, specasm_dirent_t *dirent) return 1; } + +void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf) +{ + if (fstat(fileno(f), buf)) + err_type = SPECASM_ERROR_READ; +} diff --git a/src/peer_file_zx.c b/src/peer_file_zx.c index d02d449..53cf5a7 100644 --- a/src/peer_file_zx.c +++ b/src/peer_file_zx.c @@ -65,3 +65,10 @@ specasm_dir_t specasm_opendir_e(const char *fname) return d; } + +void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf) +{ + if (esxdos_f_fstat(f, buf) < 0) + err_type = SPECASM_ERROR_READ; +} + diff --git a/src/samake.c b/src/samake.c new file mode 100644 index 0000000..11ec510 --- /dev/null +++ b/src/samake.c @@ -0,0 +1,558 @@ +/* + * Copyright contributors to Specasm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include +#include +#include +#include +#include + +#include "peer.h" +#include "salink.h" +#include "state_base.h" + +#define SAMAKE_TARGET_TYPE_BAS 1 +#define SAMAKE_TARGET_TYPE_TAP 2 + +#define SAMAKE_CODE_BUF_SIZE 1024 + +static char bin_name[MAX_FNAME + 1]; +static char app_name[MAX_FNAME + 1]; +static uint8_t bin_name_len; +static char start_address[6] = "32768"; +static char clear_address[6] = "32767"; +static uint16_t org_address = 0x8000; +static uint16_t basic_prog_len; +static uint8_t got_org; + +#define SAMAKE_ERROR_NO_MAIN SPECASM_MAX_ERRORS +#define SAMAKE_ERROR_USAGE (SPECASM_MAX_ERRORS + 1) +#define SAMAKE_ERROR_READDIR (SPECASM_MAX_ERRORS + 2) +#define SAMAKE_ERROR_BIN_TOO_BIG (SPECASM_MAX_ERRORS + 3) + +/* + * Buffer for the BASIC loader. This is going to be 42 + MAX_FNAME + * bytes. We'll add a few extra bytes just to be on the safe side. + * If we need to add a screen loader this will need to be increased. + */ + +static uint8_t basic_buf[64 + MAX_FNAME]; +static union { + uint8_t three_dos_buf[128]; + uint8_t tap_block[24]; + uint8_t code_buf[SAMAKE_CODE_BUF_SIZE]; +} container; + +static specasm_dirent_t dirent; + +static uint8_t prv_parse_obj_e(const char *fname) +{ + uint16_t i; + specasm_line_t *line; + const char *str; + uint16_t sa; + char *dst; + + specasm_load_e(fname); + if (err_type != SPECASM_ERROR_OK) + return 0; + + for (i = 0; i < state.lines.num_lines; i++) { + line = &state.lines.lines[i]; + if (!bin_name[0] && ((line->type == SPECASM_LINE_TYPE_LL) || + (line->type == SPECASM_LINE_TYPE_SL))) { + if (line->type == SPECASM_LINE_TYPE_LL) + str = specasm_state_get_long_e( + line->data.label); + else + str = specasm_state_get_short_e( + line->data.label); + if (err_type != SPECASM_ERROR_OK) + return 0; + if (strcmp(str, "Main")) + continue; + dst = &bin_name[0]; + while (*fname != '.') + *dst++ = *fname++; + *dst = 0; + bin_name_len = dst - &bin_name[0]; + if (got_org) + return 1; + } else if (!got_org && (line->type == SPECASM_LINE_TYPE_ORG)) { + sa = *((uint16_t *)&line->data.op_code[0]); + org_address = sa; + got_org = 1; + (void) utoa(sa, start_address, 10); + sa--; + (void) utoa(sa, clear_address, 10); + if (bin_name[0]) + return 1; + } + } + + return 0; +} + +static uint8_t prv_check_file(const char *fname) +{ + char *period; + + period = strchr(fname, '.'); + + if (!period) + return 0; + + return ((period[1] == 'x' || period[1] == 'X') && period[2] == 0); +} + +static void prv_find_bin_name_e(const char *dirname) +{ + uint8_t done = 0; + specasm_dir_t dir = specasm_opendir_e(dirname); + + if (err_type != SPECASM_ERROR_OK) { + printf("Failed to read directory: %s", dirname); + err_type = SAMAKE_ERROR_READDIR; + return; + } + + while (!done && specasm_readdir(dir, &dirent)) { + if (!prv_check_file(specasm_getdirname(dirent))) + continue; + + done = prv_parse_obj_e(specasm_getdirname(dirent)); + if (err_type != SPECASM_ERROR_OK) + goto finish; + } + + if (!bin_name[0]) { + err_type = SAMAKE_ERROR_NO_MAIN; + printf("Unable to find 'Main' label in %s\n", dirname); + } + + /* + * TODO, this check isn't really correct. Ideally we'd add the loading + * address of BASIC program and the size of the BASIC program and check + * that the resulting value isn't greater than org_address, but I can't + * figure out whether there's a fixed starting address for BASIC + * programs, so for now let's just print a warning. It's mainly there + * to stop people creating a loader for a dot program. + */ + + if (org_address < 24000) + printf("Warning: org %" PRIu16 " is very low\n", org_address); + +finish: + specasm_closedir(dir); +} + +static void prv_make_app_name(const char *type) +{ + uint8_t ext_pos; + uint8_t bin_name_len_plus_ext; + + bin_name_len_plus_ext = bin_name_len + strlen(type) + 1; + if (bin_name_len_plus_ext > MAX_FNAME) + ext_pos = bin_name_len - (bin_name_len_plus_ext - MAX_FNAME); + else + ext_pos = bin_name_len; + strcpy(app_name, bin_name); + app_name[ext_pos] = '.'; + strcpy(&app_name[ext_pos + 1], type); +} + +static uint8_t *prv_write_address(uint8_t *ptr, const char *address) +{ + *ptr++ = 0xb0; /* VAL */ + *ptr++ = '"'; + memcpy(ptr, address, 5); + ptr += 5; + *ptr++ = '"';; + + return ptr; +} + +static void prv_make_basic_file(uint8_t star, const char* code_name, + uint8_t code_name_len) +{ + uint16_t line_len; + uint8_t *ptr; + + basic_buf[1] = 0xa; + basic_buf[4] = 0xfd; /* CLEAR */ + (void) prv_write_address(&basic_buf[5], clear_address); + basic_buf[13] = ':'; + basic_buf[14] = 0xef; /* LOAD */ + ptr = &basic_buf[15]; + if (star) + *ptr++ = '*'; + *ptr++ = '"'; + if (code_name_len > 0) { + memcpy(ptr, code_name, code_name_len); + ptr += code_name_len; + } + *ptr++ = '"'; + *ptr++ = 0xaf; /* CODE */ + if (code_name_len > 0) + ptr = prv_write_address(ptr, start_address); + *ptr++ = ':'; + *ptr++ = 0xf9; /* RANDOMIZE */ + *ptr++ = 0xc0; /* USR */ + ptr = prv_write_address(ptr, start_address); + *ptr++ = 0x0d; + + /* + * Write line length. + */ + + line_len = ptr - &basic_buf[4]; + memcpy(&basic_buf[2], &line_len, sizeof(uint16_t)); + basic_prog_len = (uint16_t) (ptr - basic_buf); +} + +static void prv_make_3dos_header(void) +{ + uint8_t i; + uint32_t file_len = basic_prog_len + 128; + uint8_t checksum = 0; + + memcpy(container.three_dos_buf, "PLUS3DOS", 8); + container.three_dos_buf[8] = 0x1a; + container.three_dos_buf[9] = 1; + memcpy(&container.three_dos_buf[11], &file_len, sizeof(uint32_t)); + memcpy(&container.three_dos_buf[16], &basic_prog_len, sizeof(uint16_t)); + container.three_dos_buf[18] = 10; /* LINE 10 */ + memcpy(&container.three_dos_buf[20], &basic_prog_len, sizeof(uint16_t)); + + for (i = 0; i < 127; i++) + checksum += container.three_dos_buf[i]; + container.three_dos_buf[127] = checksum; +} + +static specasm_handle_t prv_open_bin_e(uint16_t *bin_size) +{ + specasm_handle_t in_f; + uint32_t real_bin_size; + specasm_stat_t stat_buf; + + in_f = specasm_file_ropen_e(bin_name); + if (err_type != SPECASM_ERROR_OK) + return 0; + + specasm_file_stat_e(in_f, &stat_buf); + if (err_type != SPECASM_ERROR_OK) + goto close_in_f; + + real_bin_size = specasm_get_file_size(&stat_buf); + + if (real_bin_size > 0xffff) { + err_type = SAMAKE_ERROR_BIN_TOO_BIG; + printf("%s too big (%" PRIu32 " bytes)\n", bin_name, + real_bin_size); + goto close_in_f; + } + + if (real_bin_size + (uint32_t) org_address > 0xffff) { + err_type = SAMAKE_ERROR_BIN_TOO_BIG; + printf("%s too big (%" PRIu32 " bytes) for org %" PRIu16 "\n", + bin_name, real_bin_size, org_address); + goto close_in_f; + } + + *bin_size = (uint16_t) real_bin_size; + + return in_f; + +close_in_f: + specasm_file_close_e(in_f); + + return 0; +} + +static void prv_make_bas_e(void) +{ + specasm_handle_t f; + uint16_t bin_size; + + /* Check bin file exists and is not too big. */ + + f = prv_open_bin_e(&bin_size); + if (err_type != SPECASM_ERROR_OK) + return; + specasm_file_close_e(f); + err_type = SPECASM_ERROR_OK; + + prv_make_app_name("bas"); +#ifdef SPECASM_TARGET_NEXT + prv_make_basic_file(0, bin_name, bin_name_len); +#else + prv_make_basic_file(1, bin_name, bin_name_len); +#endif + prv_make_3dos_header(); + + f = specasm_file_wopen_e(app_name); + if (err_type != SPECASM_ERROR_OK) + return; + + specasm_file_write_e(f, container.three_dos_buf, 128); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_file_write_e(f, basic_buf, basic_prog_len); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_file_close_e(f); + return; + +on_error: + specasm_file_close_e(f); + specasm_remove_file(app_name); +} + +static void prv_make_basic_header(void) +{ + uint8_t i; + uint16_t block_len = basic_prog_len + 2; + uint8_t name_len = bin_name_len; + + if (name_len > 10) + name_len = 10; + + container.tap_block[0] = 0x13; /* Header len. */ + container.tap_block[2] = 0; /* Flag byte, 0 = header */ + container.tap_block[3] = 0; /* Type byte, 0 = program */ + + /* Copy the name of the BASIC file, we'll just use the bin file */ + + memset(&container.tap_block[4], ' ', 10); + memcpy(&container.tap_block[4], bin_name, name_len); + memcpy(&container.tap_block[14], &basic_prog_len, + sizeof(basic_prog_len)); + + container.tap_block[16] = 10; /* Autostart at line 10 */ + memcpy(&container.tap_block[18], &basic_prog_len, + sizeof(basic_prog_len)); + container.tap_block[20] = 0; + + for (i = 2; i < 20; i++) + container.tap_block[20] ^= container.tap_block[i]; + + /* + * Set len of second block. + */ + + memcpy(&container.tap_block[21], &block_len, sizeof(block_len)); + container.tap_block[23] = 0xff; +} + +static void prv_make_code_header(uint16_t bin_size) +{ + uint8_t i; + uint16_t block_len = bin_size + 2; + uint8_t name_len = bin_name_len; + + if (name_len > 10) + name_len = 10; + + container.tap_block[0] = 0x13; /* Header len. */ + container.tap_block[1] = 0; + container.tap_block[2] = 0; /* Flag byte, 0 = header */ + container.tap_block[3] = 3; /* Type byte, 0 = program */ + + /* Copy the name of the BASIC file, we'll just use the bin file */ + + memset(&container.tap_block[4], ' ', 10); + memcpy(&container.tap_block[4], bin_name, name_len); + memcpy(&container.tap_block[14], &bin_size, sizeof(bin_size)); + + /* Start of Code block */ + memcpy(&container.tap_block[16], &org_address, sizeof(org_address)); + + /* 32768 */ + container.tap_block[18] = 0; + container.tap_block[19] = 0x80; + container.tap_block[20] = 0; + + for (i = 2; i < 20; i++) + container.tap_block[20] ^= container.tap_block[i]; + + /* + * Set len of second block. + */ + + memcpy(&container.tap_block[21], &block_len, sizeof(block_len)); + container.tap_block[23] = 0xff; +} + +static uint8_t prv_write_code_e(specasm_handle_t in_f, specasm_handle_t out_f) +{ + uint16_t i; + uint8_t checksum = 0xff; + size_t read; + + for (;;) { + read = specasm_file_read_e(in_f, &container.code_buf[0], + SAMAKE_CODE_BUF_SIZE); + if (err_type != SPECASM_ERROR_OK) + return 0; + if (!read) + break; + for (i = 0; i < read; i++) + checksum ^= container.code_buf[i]; + specasm_file_write_e(out_f, &container.code_buf[0], read); + if (err_type != SPECASM_ERROR_OK) + return 0; + } + + return checksum; +} + +static void prv_make_tap_e(void) +{ + specasm_handle_t out_f; + specasm_handle_t in_f; + uint16_t bin_size; + uint16_t i; + uint8_t checksum = 0xff; + + prv_make_app_name("tap"); + prv_make_basic_file(0, "", 0); + + prv_make_basic_header(); + + in_f = prv_open_bin_e(&bin_size); + if (err_type != SPECASM_ERROR_OK) + return; + + /* + * That's the header. Now we can write our BASIC program followed + * by the checksum. + */ + + out_f = specasm_file_wopen_e(app_name); + if (err_type != SPECASM_ERROR_OK) { + specasm_file_close_e(in_f); + return; + } + + specasm_file_write_e(out_f, &container.tap_block[0], 24); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_file_write_e(out_f, basic_buf, basic_prog_len); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + for (i = 0; i < basic_prog_len; i++) + checksum ^= basic_buf[i]; + + specasm_file_write_e(out_f, &checksum, 1); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + /* + * Let's write the header for the code block. + */ + + prv_make_code_header(bin_size); + + specasm_file_write_e(out_f, &container.tap_block[0], 24); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + /* + * Now write out code file and compute the checksum. + */ + + checksum = prv_write_code_e(in_f, out_f); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_file_write_e(out_f, &checksum, 1); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_file_close_e(in_f); + specasm_file_close_e(out_f); + return; + +on_error: + specasm_file_close_e(in_f); + specasm_file_close_e(out_f); + specasm_remove_file(app_name); +} + +static void prv_make_e(const char *dir, uint8_t target_type) +{ + prv_find_bin_name_e(dir); + if (err_type != SPECASM_ERROR_OK) + return; + if (target_type == SAMAKE_TARGET_TYPE_BAS) + prv_make_bas_e(); + else + prv_make_tap_e(); +} + +int main(int argc, char *argv[]) +{ + const char* dir = "."; + uint8_t target_type = SAMAKE_TARGET_TYPE_BAS; + int ret = 0; + +#ifdef SPECASM_TARGET_NEXT + uint8_t turbo; + + /* + * Create loader at top speed on a spectrum Next. + */ + + turbo = ZXN_READ_REG(REG_TURBO_MODE); + ZXN_WRITE_REG(REG_TURBO_MODE, turbo | 3); +#endif + + if (argc >= 2) { + if (!strcmp(argv[1], "tap")) { + target_type = SAMAKE_TARGET_TYPE_TAP; + } else if (strcmp(argv[1], "bas")) { + err_type = SAMAKE_ERROR_USAGE; + goto on_error; + } + if (argc == 3) + dir = argv[2]; + else if (argc > 3) { + err_type = SAMAKE_ERROR_USAGE; + goto on_error; + } + } + prv_make_e(dir, target_type); + +on_error: + if (err_type != SPECASM_ERROR_OK) { + if (err_type < SPECASM_MAX_ERRORS) + printf("%s\n", specasm_error_msg(err_type)); + else if (err_type == SAMAKE_ERROR_USAGE) + printf("Usage: samake (bas|tap) [dir]\n"); + ret = 1; + } else { + printf("Created %s\n", app_name); + } + +#ifdef SPECASM_TARGET_NEXT + ZXN_WRITE_REG(REG_TURBO_MODE, turbo); +#endif + return ret; +} From 53e37011bcca7be114162abcf0e1273b983e2715 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 13 Jan 2024 19:50:10 +0100 Subject: [PATCH 07/16] Add hello example Might as well have one example. cd examples/hello ./make.sh fuse -g3x hello.tap Signed-off-by: Mark Ryan --- examples/hello/hello.s | 16 ++++++++++++++++ examples/hello/make.sh | 6 ++++++ 2 files changed, 22 insertions(+) create mode 100644 examples/hello/hello.s create mode 100755 examples/hello/make.sh diff --git a/examples/hello/hello.s b/examples/hello/hello.s new file mode 100644 index 0000000..db96483 --- /dev/null +++ b/examples/hello/hello.s @@ -0,0 +1,16 @@ +.openCh equ $1601 +.printS equ 8252 + +.Main + ld a, 2 + call =openCh + ld de, data + ld bc, (len) + call =printS + ret +.data +db 22, 10, 13 +"SPECASM !!!" +; Store string len in 2 bytes +.len +dw =len-data diff --git a/examples/hello/make.sh b/examples/hello/make.sh new file mode 100755 index 0000000..7e24c6b --- /dev/null +++ b/examples/hello/make.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +../../saimport *.s +../../salink +../../samake tap + From 395731df1c1ac63cb17e3c63cc08eefef7156a3d Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 21 Jan 2024 01:01:01 +0100 Subject: [PATCH 08/16] Fix EQU selection bug There was no visual feedback when a block of text containing equ statements was selected. This is now fixed. Signed-off-by: Mark Ryan --- src/editor.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/editor.c b/src/editor.c index ce446fc..f0d8e0e 100644 --- a/src/editor.c +++ b/src/editor.c @@ -51,6 +51,7 @@ static void specasm_dump_line_e(unsigned int l, uint8_t r, uint8_t inv) uint8_t label_col; uint8_t com_col; uint8_t data_col; + uint8_t equ_col; uint8_t type; const specasm_line_t *line; @@ -59,11 +60,13 @@ static void specasm_dump_line_e(unsigned int l, uint8_t r, uint8_t inv) label_col = SPECASM_SELECT_COLOUR; com_col = SPECASM_SELECT_COLOUR; data_col = SPECASM_SELECT_COLOUR; + equ_col = SPECASM_SELECT_COLOUR; } else { code_col = SPECASM_CODE_COLOUR; label_col = SPECASM_LABEL_COLOUR; com_col = SPECASM_COMMENT_COLOUR; data_col = SPECASM_DATA_COLOUR; + equ_col = SPECASM_EQU_COLOUR; } line = &state.lines.lines[l]; @@ -78,7 +81,7 @@ static void specasm_dump_line_e(unsigned int l, uint8_t r, uint8_t inv) return; if (type == SPECASM_LINE_TYPE_EQU) { - col = SPECASM_EQU_COLOUR; + col = equ_col; } else if ((type == SPECASM_LINE_TYPE_LL) || (type == SPECASM_LINE_TYPE_SL)) { col = label_col; From 08b7b4f38abeb7f99c913b29b07cbe3da8f5c092 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Thu, 25 Jan 2024 22:45:08 +0100 Subject: [PATCH 09/16] Update the documentation for Specasm v7 And update the relevant issues. --- README.md | 92 +++++++++++++++++++++++++++++++++++++++-------- docs/install.png | Bin 63095 -> 49692 bytes docs/salink.png | Bin 62811 -> 42376 bytes docs/specasm.md | 52 +++++++++++++++++++++++---- 4 files changed, 123 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 6b7db43..15c90f6 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,27 @@ # Specasm -Specasm is a Z80 assembler designed to run on the 48k and 128k ZX Spectrum. It requires an SD card solution running ESXDOS 0.87 or greater to function. For detailed information about how Specasm works, please see the [documentation](https://github.com/markdryan/specasm/blob/master/docs/specasm.md). To get started, carry on reading. +Specasm is a Z80 assembler designed to run on the 48k and 128k ZX Spectrum and the ZX Spectrum Next. It requires an SD card solution running ESXDOS 0.87 or greater to function on the 48Kb and 128Kb ZX Spectrum. For detailed information about how Specasm works, please see the [documentation](https://github.com/markdryan/specasm/blob/master/docs/specasm.md). To get started, carry on reading. ## Getting Started -Download the latest release of Specasm, and unzip the contents of the file into the root directory of your SD card. You should now have a folder in your root directory called SPECASM that contains some .tap and some BASIC files. It should look something like this. +[Download](https://github.com/markdryan/specasm/releases) the latest release of Specasm appropriate for your Spectrum and unzip the contents of the file into the root directory of your SD card. + +> [!TIP] +> There are two different Zip files available, specasm48.zip for the 48kb and 128kb Spectrum, and specasmnext.zip for the ZX Spectrum Next. You must download the appropriate version for your machine. + +You should now have a folder in your root directory called SPECASM. It should look something like this if you downloaded specasm48.zip ![Installing](/docs/install.png) -Now navigate to the INSTALL file, which is a BASIC program, and press **ENTER** to execute it. This will use ESXDOS's **.launcher** command to set up some command line short cuts for the tap files in the SPECASM directory. +Now navigate to the INSTALL file, which is a BASIC program, and press **ENTER** to execute it. On the 48kb or 128kb Spectrum this will use ESXDOS's **.launcher** command to set up some command line short cuts for the tap files in the SPECASM directory. It will also copy some executables to the /bin folder. On the ZX Spectrum Next it will copy the various executable programs that compose Specasm to the /dot folder. ## Reinstalling Specasm To upgrade to a new version of Specasm perform the following steps. -1. Manually remove the old /Specasm directory on your SD card, e.g., rm -rf /Specasm -2. Download the latest release and unzip its contents to /Specasm on the SD card -3. Execute the REMOVE BASIC program. This can be done from the ESXDOS file browser or by loading it from the BASIC prompt. This will remove the old version of Specasm stored under the /bin folder, and will also de-register the old **.launcher** shortcuts. +1. Execute the REMOVE BASIC program. This can be done from the ESXDOS file browser or by loading it from the BASIC prompt. This will remove the old version of Specasm stored under the /bin or the /dot folders, and will also de-register the old **.launcher** shortcuts. +2. Manually remove the old /Specasm directory on your SD card, e.g., rm -rf /Specasm +3. Download the latest release and unzip its contents to /Specasm on the SD card 4. Run the INSTALL BASIC program. This will install the new version of Specasm. ## Assembling your First Program @@ -25,8 +30,10 @@ Now, create a new directory somewhere on your SD card and cd into it and type .s ``` -.mkdir /asm/demo -.cd /asm/demo +.mkdir /asm +.cd /asm +.mkdir demo +.cd demo .specasm ``` @@ -56,12 +63,36 @@ You should see something like this appear on your screen ![Hello Specasm](/docs/salink.png) -Once the linker has finished a binary file will be created. The name of the file will be reported by the linker. In the example above its 'hello'. To execute the program, enter the BASIC commands at the bottom of the image above and press **ENTER**. You should see. +Once the linker has finished a binary file will be created. The name of the file will be reported by the linker. In the example above its 'hello'. We need to create a BASIC loader before we can execute the program. To do this type + +``` +CLEAR 32767 +.samake +``` + +> [!TIP] +> Note the CLEAR statement is not needed on the ZX Spectrum Next. + +This should create a file called hello.BAS. To run your program on a 48Kb or 128Kb Spectrum type + +``` +LOAD * "hello.bas" +``` + +On the ZX Spectrum Next simply type + +``` +LOAD "hello.bas" +``` + +You should see. ![Hello Specasm](/docs/hello.png) ## Building Specasm +### For the 48kb and 128kb Spectrums + Specasm is built with [z88dk](https://github.com/z88dk/z88dk) and GNU Make. To build Specasm for the 48k Spectrum clone the repoistory and type ``` @@ -77,9 +108,21 @@ To create a zip file with all the files that need to be copied onto the spectrum make release ``` -from the same directory. The specasm.zip file can be found in the build/release folder. +from the same directory. The specasm48.zip file can be found in the build/release folder. + +### For the Spectrum Next + +``` +cd build/next/specasm +make -j +make release +``` + +This will create a zip file called specasmnext.zip. + +### On Linux or macOS -The saexport, saimport and salink commands can be built and run on POSIX compatible systems. Simply type make from the main Specasm directory. The saimport is essentially the assembler without the editor, so can be used in conjunction with salink to assemble and build Spectrum programs directly on a modern machine, but where's the fun in that? +The samake, saexport, saimport and salink commands can be built and run on POSIX compatible systems. Simply type make from the main Specasm directory. The saimport is essentially the assembler without the editor, so can be used in conjunction with salink to assemble and build Spectrum programs directly on a modern machine, but where's the fun in that? ## Tests @@ -92,6 +135,13 @@ make from the project's top level folder. +To run the tests extra instructions supported by the Spectrum Next type + +``` +CFLAGS="-DSPECASM_TARGET_NEXT_OPCODES" make -j +./unittests +``` + To run the linker tests perform the following steps ``` @@ -100,16 +150,28 @@ cd tests ./tests.sh ``` -A large proportion (but not all) of the unit tests can be run on the spectrum itself. To build these tests type +To run the linker tests with the Next Opcodes enabled, type ``` -cd unitzx +cd tests && SPECASM_TARGET_NEXT_OPCODES=1 ./tests.sh +``` + +A large proportion (but not all) of the unit tests can be run on the Spectrums themselves. To build these tests for the 48kb and 128kb Spectrums type + +``` +build/48/unit make make tests ``` -This will create a folder called tests in the unitzx folder. Inside this folder are 3 files that need to be copied to the same directory on your spectrum. Run the unizx.tap file to run the tests. - +This will create a folder called tests in the unit folder. Inside this folder are 3 files that need to be copied to the same directory on your spectrum. Run the unitzx.tap file to run the tests. +To build for the ZX Spectrum Next, type +``` +build/next/unit +make -j +make tests +``` +This will create a folder called tests in the unit folder. Inside this folder are 3 files that need to be copied to the same directory on your spectrum. The unitnext file is a dotn file which needs to be copied to the /dot directory. Then make sure you are in the folder containing the test_bad and test_op files and type .unitnext. diff --git a/docs/install.png b/docs/install.png index 46f0bf404a14334a6a04ea230409cf6a26096bc4..fef694eea3d5b5398d54011c46cd23ec8c357783 100644 GIT binary patch literal 49692 zcmeFacU;rg{y(l%TWVVsEh5TLQE5R%L`E11tq4^VL>VGWK|w?a5ClmGky{l9!%<`> zR*ImEsK_RSTY)eW**ig2SV@FHLdg1_1hCpm@9pRJ$M3KH(MMA<-shaxe!iZs)7vMk zEWTN@WzC8eE514MliA4?E55k4V#O*0SsCybub1}Uf`6?M}oI;ZZ(X5ADFa@{OXJ5+iAPBzFL3XZ|7qIBf4|lq4#>HKg;6%&L_?%KUq~?8WZ`UMPM2lx`hzp(fAGp}qdqK??nyU!q(hvH8ixv2&cs zE($?jkBZ;R#k&%^d55m8SShngcKtTfPhS*lD(WB+rRp?*QbyA@bH_mS67-`w>z=idSutXm+98|(;}C?d1zJHHI?5rzFU4h zS64C=|Gd40Z$@mFaW!3cPX3qW&!FrY=j;DC80j}F*00oh9Qk6+(dBpIYKr=NX>FgL z!r5Eb6y6>>y?WR3JJEV<^4X_fSbD&Ivx=`qw`c!r!?HU8KZ;#`^XoV7=wCgv-u{O# zm)(g(jLUESA29;m_(zQYSmSbb^N%(D<5d4~s>@l1>9v1i<3B0RKPk@g`JK!^nd(28 z>OTd?@}lw;$3Nx&|F7kL|7sZt*kWLHmPk|8fBW4>$P3x3-`^m`g}h-mNa?

*%^%D-SuQa5^OhG|(oO%&H^#|}^{IsI2 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�>beZF*sA{(d3l#vC5}tVN+O}5{ zszt%^p6Tx~fPskIWjC??Y7W2miLY3K$*`w1KFLYACh&{yEfBG;MfY`th!P!Sd}WsC zJiSMI8dg?R%O}`%8ZW+0D6a7S#G`eiPbG*Dz@dab>hUl2+Q`Ti8o!j9XsUSS}CXfomv{SdP)|`1x zc@Cym0tDj8y%|$ENkhVSP2bC&9(P{<;GV0qje@uzyd`^#43K~k=PUhU`0G3GWrr`c ze{(kQCm+q*o@DcE@1&!g z=c!uLX$U?Tf)BbW5QgA`A^0$v$_mibe_!xnkpBBVIB{q24^S1I82?)5_Hx~@XD9)a$@h~pD}viCSo*fx=*@Q;oCzm^ z8b7rMw|H+O_co?7FDXHzUDw54d|!?jVe9g4Nz?j+rBb zTOhaHaM>yzQ`g_-EtYV(-1?n)STt5#|3uM|Za1~ahHM6M>}=ng`mvUl&$7Vc#Mf55 z5B`10gr@3HZf6k0aVSA^bNpP~Neo+==_Dx)8lm58dN?HychCj=G(=b+(w{p8Za_6w zZjUg7%lG%&k?`ZeyzL&BTGpQ=`V5XG-1ni^&`91a(N1+~qwdcB-6G-ISti^fOQ}3f zd=8q0?7**0-%u5x>Uy1($)kXai%8xd>(4IT3DWq9_roZkz(v3x$ppjI1^x|JiZ3+a zxYw$K7FKhH5E#P4K--t)vl?!$-4FP(W4ozd=AJhW7sf+LT`*1kE!xDwr)l?S2{EG) z-8{!(0*w(L+f`J4!B;S)e*Hg~>WV=jom7K$Bb`CI)*bF>XD55`=qQq4DYKd#<*)J| zF6PSuBP}NRTRZ8yfX71mJu&*5!sUYm&)}XVe2`APb*8n_8#l6+HJCuCz}%J_|!mWo|%y-1O1XeFFmjjkTvcn&yi{s4^r}SY7nay*zT+;d8D{{rHH|8Y~OD!niUegCN>a0!@ApFH?8Ltvb$vZ(V zGsD}r{X{kxV0;N+GP4t`_d zqHH5VD(;rL2JSUl!e0l!HLC8&_~O*7$?1&J3Nl!0?jfJNqx0FTSDd%lo)kRr#S(DG z+MCbG;UJqdq}y7gzZ?1kP)EBKzhu7tD%*7+UAHJIEmm)9RdNq_dck%6O#NMAJ_ISQ zn(Ssh)2fXIPahzX|M4cBz$cM&K)Pu-H7Dp5SLhHd6bYVc>Lm3%FFOJ9g#TTQrO8ON z&*DY;b_lM|G@kfSc-ZO{ZFT0i#}{Pu8n#zMxo3v$)nO8Y^y)~WL3(wyut7QNs8EA) z*0s2+Z`S9hxI>Tx0 v{otZ!NvgIT`hEZZn*~pO?f=IjKf0q>^0?vM4|jo0HQM~SuUFw`q2K>6KI95B literal 63095 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
  • w>Zjht6i0XAFMwdW>Y*<|H~7-~8xm7Ig`Me(#9TNG$kHsqCuM4Niqi%EaO9B!CUw!SByY9nH+P~7N?-!otg;;eA z*S=8OeDViSWe@#^5b_K~Tz%Q3gl;m;4x zgM`XTpMflbu)y)R`*!~Bn+nfp|MiB>0pNH6Cz2&|APa
  • t&|k|^RAkL&Ntp^ES- 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 Bm7V|q diff --git a/docs/salink.png b/docs/salink.png index f6427a0475b8c7abe1eb1aee5b0821121e61f70f..47196e0b173b6cb7b1e5242d42c6680303bc2889 100644 GIT binary patch literal 42376 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 literal 62811 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 [!TIP] +> Note the CLEAR statement is not needed on the ZX Spectrum Next as .saexport and .saimport are implemented as dotn files. + A future 128kb version of Specasm will incorporate the import and export commands directly in the editor and will also contain a 'gc' command to allow the garbage collection to be performed in one simple step. ### Source files -Specasm edits .x files, which are not text files. They're annotated object files, in which all the instructions are pre-assembled. This approach has some advantages and some disadvantages. The advantages are fast load and save times as there's no parsing required, and fast build times as the build, performed by the .salink command, consists of only one stage, the linker. The disadvantages are that it's more cumbersome to submit source files to source control as the source files are not text files. Separate tools need to be run, saimport and saexport that convert between .x files to .s files, before source code and be moved to and from source control. The other disadvantage is that there's a limited amount of memory per line (4 bytes). This means that it's not easy to support a complex expression syntax when specifying the arguments of instructions. In practice, this isn't much of an issue though as there isn't really enough memory to support such expressions anyway. +Specasm edits .x files, which are not text files. They're annotated object files, in which all the instructions are pre-assembled. This approach has some advantages and some disadvantages. The advantages are fast load and save times as there's no parsing required, and fast build times as the build, performed by the .salink command, consists of only one stage, the linker. The disadvantages are that it's more cumbersome to submit source files to source control as the source files are not text files. Separate tools need to be run, saimport and saexport that convert between .x files to .s files, before source code and be moved to and from source control. The other disadvantage is that there's a limited amount of memory per line (5 bytes) and it's not possible to encode all the formatting and expression information into just five bytes. For this reason Specasm places a restrictions on how some instructions and expressions can be written, e.g, expressions cannot be used as offsets to an index register. To convert a .s file, a text file containing specasm code, into a .x file you need to use the .saimport dotx command. It accepts one or more command line parameters, which must be the paths of .s files. It will convert each of these .s files to .x files. For example, to convert the file hello.s into hello.x copy hello.s to the spectrum and type @@ -563,7 +571,10 @@ CLEAR 32767 .saimport *.s ``` -The reverse process can be performed using the .saexport dotx command. Note the use of the CLEAR statement. This needs to be executed before the .saimport or .saexport commands as these are dotx commands and part of their code is loaded and executed from BASIC's memory. To ensure there's no interference between BASIC and the dotx commands, the CLEAR command must be issued before their use. If you're executing a sequence of .saimport/.saexport commands, You only need to issue the CLEAR command once. +> [!TIP] +> Note the CLEAR statement is not needed on the ZX Spectrum Next. + +The reverse process can be performed using the .saexport command. Note the use of the CLEAR statement. This needs to be executed on the 48Kb or 128Kb Spectrum before the .saimport or .saexport commands as these are dotx commands and part of their code is loaded and executed from BASIC's memory. To ensure there's no interference between BASIC and the dotx commands, the CLEAR command must be issued before their use. If you're executing a sequence of .saimport/.saexport commands, You only need to issue the CLEAR command once. The CLEAR statement is not needed at all on the ZX Spectrum Next which implements saexport and saimport using dotn commands. ## Program structure @@ -610,7 +621,7 @@ org 23760 will cause the linked program to be assembled at 23760. -The linker doesn't currently create a loader program or a tap file so this needs to be done manually. It can be scripted using BASIC and the relevant ESXDOS commands. +The linker doesn't currently create a loader program or a tap file. This can however be done using the samake command. ### Libraries @@ -633,7 +644,36 @@ Here are some examples of their use -lib/math.x ``` +## Creating Loaders with SAMAKE + +Salink generates a raw binary file. Before the binary can be executed, memory needs to be reserved, the binary needs to be loaded into memory at the address at which it was assembled and, finally, it needs to be inovked. Assuming that the binary was assembled to the default address, i.e., 32768, and is called "bin", this can all be achieved in BASIC as follows. + +``` +CLEAR 32767: LOAD * "bin" CODE 32768: RANDOMIZE USR 32768 +``` + +> [!TIP] +> Note LOAD * is simply LOAD on the ZX Spectrum Next. + +Typing this is a bit cumbersome, and also error prone. For this reason Specasm includes a tool called samake that can automatically create loaders. + +Currently, samake is capable of creating two types of loaders, BASIC loaders with a +3DOS header than can be loaded directly from the SD card, and tap loaders which bundle a BASIC loader and the binary file together in a tap file. + +Executing + +``` +CLEAR 32767 +.samake +``` + +> [!TIP] +> Note the CLEAR statement is not needed on the ZX Spectrum Next as .samakek is implemented as a dotn file. + +will automatically create a BASIC loader. The name of the loader will be derived from the .x file that contains the .Main label. The address at which the loader loads the code is defined by the org statement in the x files, or 32768 if no org statement is provided. +Samake takes two optional arguments. The first argument specifies the type of loader to be created, and can currently be set to either "bas" (the default) or "tap". The second argument specifies the directory containing the Specasm project. If the second argument is not specified, samake assumes the current directory. +## Versions, Binary and Source code Compatibility +There are two separate versions of Specasm, one for the 48kb and 128kb ZX Spectrums and one for the ZX Spectrum Next. The two versions are incompatible with each other. The 48kb version will not work on the Next and the Next version will not work on an original Spectrum. The two versions are also binary incompatible with each other. .x files created on the ZX Spectrum Next cannot be loaded by Specasm on the 48kb and vice-versa. Both versions of Specasm are source code compatible however, provided that the sources do not contain any of the Z80N instructions. The same .s file can be assembled by .saimport on both the Next and the 48kb Spectrum. From 7991bbe2fa5749d82384ef3776bfcb38af279120 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 21 Jan 2024 22:56:11 +0100 Subject: [PATCH 10/16] Create the specasmnext.zip file And reorganise the 48kb zip file and add update the 48 BASIC files to install and uninstall samake. Update .gitignore a little. Signed-off-by: Mark Ryan --- .gitignore | 11 ++++++++--- bas/48/INSTALL | Bin 0 -> 1125 bytes bas/48/REMOVE | Bin 0 -> 339 bytes bas/INSTALL | Bin 1041 -> 0 bytes bas/REMOVE | Bin 289 -> 0 bytes bas/SPECLD.TAP | Bin 316 -> 0 bytes bas/next/INSTALL | Bin 0 -> 233 bytes bas/next/REMOVE | Bin 0 -> 233 bytes build/48/specasm/Makefile | 6 +++--- build/next/specasm/Makefile | 12 +++++------- 10 files changed, 16 insertions(+), 13 deletions(-) create mode 100755 bas/48/INSTALL create mode 100755 bas/48/REMOVE delete mode 100755 bas/INSTALL delete mode 100755 bas/REMOVE delete mode 100755 bas/SPECLD.TAP create mode 100755 bas/next/INSTALL create mode 100755 bas/next/REMOVE diff --git a/.gitignore b/.gitignore index 9342d13..2896073 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,12 @@ SAMAKE SAMAKE.X build.sh compile.sh -build/48/specasm/release -build/48/unit/tests -build/next/unit/tests +!build/48/specasm/ +build/48/specasm/release/ +!build/next/specasm/ +build/next/specasm/release/ +build/48/unit/tests/ +build/next/unit/tests/ +examples/hello/hello +examples/hello/hello.x .DS_Store diff --git a/bas/48/INSTALL b/bas/48/INSTALL new file mode 100755 index 0000000000000000000000000000000000000000..2963671815b01c76813d29c2016e1c4acb6496eb GIT binary patch literal 1125 zcmcgr&ui2`6#n){D+tq574aZbP^jIQWG8911*@bs!qVH;ZTS z=*5d5Ui>TcC<^`s9z=T(Po;VhMBm$N>a<(0<}%-V?|a|-W-=45-HV)+_2_Z|y)uA< zQUPyD1pd$W1cfQcUyiDIwJG5DLkJWvKz>V?Y>Mf7?>Yo3phBq(`FTa4JPrAMmU|YM zvyV!8-Jtvg)sIqv$%~MOy}fjBf3WmgqY0e53i)$Xv1K;kow>c4q`T5ut~ae9Znu1a z^9H23)p^kGZuE9#CrRXBr`PQawxtz?!5u$p&P~+P5hOV7O0yoFCv(jnpJS%W_X3S2* zUk!5Cj0M8VnWWRIOlH4{1NlX{^Ykx%0ESYFgbs$NO6cQ>oTn&ti?2fe2DP~y&Jn|- zb&}T6l`$N?GBcFOfxwLi%(p&`7(asL63M))nmaIRB>6rCcxAQ zTDiGE>u9vV7iYO)w@sc0l(J=Q1OVPts literal 0 HcmV?d00001 diff --git a/bas/48/REMOVE b/bas/48/REMOVE new file mode 100755 index 0000000000000000000000000000000000000000..fa7c758cc73e98ce2b18e4e9d5c7af8ab67e3164 GIT binary patch literal 339 zcmWIW2@N)O@eh__WC&(tU|_h+zy-u~12qg>;tUFUIf##hxTIbLFM|=P pE>B;GE*lhG6%btzNsul#K_o+i9eo|WU3nP-B#{JRHi4voIso_!Iwb%A literal 0 HcmV?d00001 diff --git a/bas/INSTALL b/bas/INSTALL deleted file mode 100755 index 53727bae84ee0adb72971797e1b0a6ef6dc7f086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1041 zcmcgr&r2Io5dQK*3xy{Lwv-;^6)afY?rz?$8wIVbHHT1*A)CTpLpIPw6T~QZQ+n>D z9(w7eP`vn8^ja$T7xYlt9!gK5UWC%lWV80!wAb!szM1*vn|bs0Rac${&hlDtH-phU zfS0)p+BpLM=Q}`V1oDTYie7O9__+sx>@CRe@q$f0eaEgtU>cOj?=+ggjS0xhe#VwbgAZnVJ&L!awN$QHrLbP} z1#TLU=0;m-OX0B(QQjBsFoi4!D?#Fx?dF-n;PIZHsa2@M)z0Bl_v>% z{64Xpn|iVoR@Z9vWQM>`Jb_8;A4D_M#Qbl0aPmX~Qmh diff --git a/bas/SPECLD.TAP b/bas/SPECLD.TAP deleted file mode 100755 index 9f0d0527f664bc59bd37df331b5b45f8ef193048..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmYk2Jxc>Y6h-f95eV!WjCFijn^_ZdcE+f?klG)i1lGhtNFn%vg-iTLwqhYEV{50l zg^htAs9=O3GQJrQoa(*9oy(c20ysW8==OWWXM!>&D80g<9DsgwoFqvAm<#w=S_Hj? zAzf)DrDTenX>9>C8(O3o%~>>RfBtRDy`Kv-=bYKGaoM@8>$U0t>LtT)XAl@A+y-}eRWvoy#{*R z;;jUv specasm.tap + cat ../../../bas/48/SPECLD.TAP specasm-bare.tap > specasm.tap salink.tap: $(SALINK) $(CC) $(CFLAGS) -zorg=24000 -startup=31 -o salink $^ -create-app @@ -93,6 +93,6 @@ release: cp specasm.tap salink.tap release/specasm cp ../../../COPYING release/specasm cp SAMAKE SAIMPORT SAEXPORT *.X release/specasm - cp ../../../bas/INSTALL release/specasm - cp ../../../bas/REMOVE release/specasm + cp ../../../bas/48/INSTALL release/specasm + cp ../../../bas/48/REMOVE release/specasm cd release && zip -r specasm48.zip specasm diff --git a/build/next/specasm/Makefile b/build/next/specasm/Makefile index 077fe96..37421da 100644 --- a/build/next/specasm/Makefile +++ b/build/next/specasm/Makefile @@ -108,10 +108,8 @@ clean: .PHONY: release release: - rm -rf release - mkdir -p release/specasm - cp SPECASM SALINK release/specasm - cp ../../../COPYING release/specasm - cp SAIMPORT SAEXPORT *.X release/specasm - cp ../../../bas/INSTALL release/specasm - cp ../../../bas/REMOVE release/specasm - cd release && zip -r specasm48.zip specasm + mkdir -p release/specasmnext + cp SPECASM SALINK SAIMPORT SAEXPORT SAMAKE release/specasmnext + cp ../../../bas/next/INSTALL release/specasmnext + cp ../../../bas/next/REMOVE release/specasmnext + cd release && zip -r specasmnext.zip specasmnext From d801d7ee348572759b55e76275eaaebe3d483fd3 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Thu, 25 Jan 2024 22:42:43 +0100 Subject: [PATCH 11/16] Add a loader for the linker on the 48kb To clear the screen to PAPER 7: BORDER 7: INK 0. This was already done for Specasm on a previous release. There seems to be an issue with ESXDOS 0.89 which sets the screen to PAPER 0: BORDER 0: INK 7 after a .launcher tap file exits. --- .gitignore | 2 ++ bas/48/LINKLD.TAP | Bin 0 -> 87 bytes bas/48/SPECLD.TAP | Bin 0 -> 316 bytes build/48/specasm/Makefile | 3 ++- 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100755 bas/48/LINKLD.TAP create mode 100755 bas/48/SPECLD.TAP diff --git a/.gitignore b/.gitignore index 2896073..1bc4a53 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ SAMAKE.X build.sh compile.sh !build/48/specasm/ +!bas/48/*.TAP +!bas/*.TAP build/48/specasm/release/ !build/next/specasm/ build/next/specasm/release/ diff --git a/bas/48/LINKLD.TAP b/bas/48/LINKLD.TAP new file mode 100755 index 0000000000000000000000000000000000000000..d990d1d7bcde1c77736d7b88d7f95369e8669135 GIT binary patch literal 87 zcmWe;U|`5e$;r&iRsaG!1}+9W1||oF{|sDG41YH$85vtzS}Iw+S5jJU_45FbZ(?9z fpv21{BEz8Y+?K2S~(*P&}1iaG$cbpR3 literal 0 HcmV?d00001 diff --git a/bas/48/SPECLD.TAP b/bas/48/SPECLD.TAP new file mode 100755 index 0000000000000000000000000000000000000000..9f0d0527f664bc59bd37df331b5b45f8ef193048 GIT binary patch literal 316 zcmYk2Jxc>Y6h-f95eV!WjCFijn^_ZdcE+f?klG)i1lGhtNFn%vg-iTLwqhYEV{50l zg^htAs9=O3GQJrQoa(*9oy(c20ysW8==OWWXM!>&D80g<9DsgwoFqvAm<#w=S_Hj? zAzf)DrDTenX>9>C8(O3o%~>>RfBtRDy`Kv-=bYKGaoM@8>$U0t>LtT)XAl@A+y-}eRWvoy#{*R z;;jUv specasm.tap salink.tap: $(SALINK) - $(CC) $(CFLAGS) -zorg=24000 -startup=31 -o salink $^ -create-app + $(CC) $(CFLAGS) -zorg=24000 -startup=31 -o salink-bare $^ -Cz"--noloader" -create-app + cat ../../../bas/48/LINKLD.TAP salink-bare.tap > salink.tap SAEXPORT: $(SAEXPORT) $(CC) $(CFLAGS) -startup=30 -o $@ $(SAEXPORT) -subtype=dotx -Cz"--clean" -create-app From a50802e97a6b03c34302c1d38c78848cd57c1478 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 28 Jan 2024 17:29:20 +0100 Subject: [PATCH 12/16] Fix a bug in push =exp The linker was evaluating the wrong expression and encoded the wrong constant. Signed-off-by: Mark Ryan --- src/link_obj.c | 28 +++++++++++++++++++++++++++- tests/test_equ_next/test.sh | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/link_obj.c b/src/link_obj.c index 28aba25..e301144 100644 --- a/src/link_obj.c +++ b/src/link_obj.c @@ -354,6 +354,32 @@ static void prv_eval_equ_16bit_e(specasm_line_t *line, salink_obj_t *obj, *((uint16_t *)&line->data.op_code[loc]) = exp; } +#ifdef SPECASM_TARGET_NEXT_OPCODES +static void prv_eval_equ_push_imm_e(specasm_line_t *line, salink_obj_t *obj, + unsigned int line_no) +{ + int16_t exp; + uint8_t exp_rev[2]; + + /* + * The push imm instruction is weird in that the immediate is encoded + * in big endian format. If we're dealing with an expression this means + * that the expression id will be in byte 3 and not byte 2 as one might + * expect. + * + * We also need to make sure we reverse the bytes of the evaluated + * expression. + */ + + exp = prv_eval_equ_label(line, obj, line_no, line->data.op_code[3]); + if (err_type != SPECASM_ERROR_OK) + return; + memcpy(exp_rev, &exp, 2); + line->data.op_code[2] = exp_rev[1]; + line->data.op_code[3] = exp_rev[0]; +} +#endif + static void prv_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, unsigned int line_no) { @@ -470,7 +496,7 @@ static void prv_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, prv_eval_equ_8bit_e(line, obj, line_no, 2); break; case SPECASM_LINE_TYPE_PUSH: - prv_eval_equ_16bit_e(line, obj, line_no, 2); + prv_eval_equ_push_imm_e(line, obj, line_no); break; #endif default: diff --git a/tests/test_equ_next/test.sh b/tests/test_equ_next/test.sh index 6f09a93..1481708 100755 --- a/tests/test_equ_next/test.sh +++ b/tests/test_equ_next/test.sh @@ -49,7 +49,7 @@ if [ "$word" != "04 02" ]; then fi word=`od -An -j24 -tx1 -N2 equ | xargs` -if [ "$word" != "10 01" ]; then +if [ "$word" != "01 10" ]; then echo "push =(Size*17)" exit 1 fi From a500a0984a1c29ace8b9d5da7885a6838d599ab6 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 28 Jan 2024 17:30:27 +0100 Subject: [PATCH 13/16] Disable test_subtraction on the Next This feature was never enabled on the Next and the Next version of salink cannot process the binary file checked into the test folder. The test was mistakenly enabled on Next builds and disabled on 48kb builds. We want the opposite. Signed-off-by: Mark Ryan --- tests/test_subtraction/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_subtraction/test.sh b/tests/test_subtraction/test.sh index a53742e..f0602b4 100755 --- a/tests/test_subtraction/test.sh +++ b/tests/test_subtraction/test.sh @@ -2,7 +2,7 @@ set -e -if [ -z "${SPECASM_TARGET_NEXT_OPCODES}" ]; then +if [ ! -z "${SPECASM_TARGET_NEXT_OPCODES}" ]; then exit 0 fi From b802de1a5ce7b74d6f3871f3987b0819a4ad9c47 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 28 Jan 2024 17:34:19 +0100 Subject: [PATCH 14/16] Tidy up the CI - Rename the Next job - Make sure we run the sanatizer with the next instructions enabled. Signed-off-by: Mark Ryan --- .github/workflows/c-cpp.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index e713d88..5b66935 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -20,13 +20,14 @@ jobs: run: | ./unittests cd tests && ./tests.sh - - name: sanatizer + - name: next run: | make clean - CFLAGS="-g -fsanitize=address -fno-omit-frame-pointer -Wall -Werror" make + CFLAGS="-DSPECASM_TARGET_NEXT_OPCODES" make -j ./unittests - - name: no-next + cd tests && SPECASM_TARGET_NEXT_OPCODES=1 ./tests.sh + - name: sanatizer run: | make clean - CFLAGS="-DSPECASM_TARGET_NEXT_OPCODES" make -j - cd tests && SPECASM_TARGET_NEXT_OPCODES=1 ./tests.sh + CFLAGS="-DSPECASM_TARGET_NEXT_OPCODES -g -fsanitize=address -fno-omit-frame-pointer -Wall -Werror" make + ./unittests From 8dda9657e7ac12938f15f9061d2efec55bd10078 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 28 Jan 2024 17:46:14 +0100 Subject: [PATCH 15/16] Fix formatting a little Re-run all files through clang-format to tidy things up a bit. Signed-off-by: Mark Ryan --- src/line_dump.c | 176 +++++++++++++++++++------------------- src/line_parse.c | 15 ++-- src/line_parse_common.c | 21 +++-- src/line_parse_common.h | 21 +++-- src/link_obj.c | 1 - src/peer_file_zx.c | 1 - src/peer_zx.h | 4 +- src/salink.c | 5 +- src/salink_trampolines.c | 9 +- src/samake.c | 32 +++---- src/specasm_next.c | 2 +- src/specasm_trampolines.c | 9 +- src/util_print_next.c | 6 +- 13 files changed, 151 insertions(+), 151 deletions(-) diff --git a/src/line_dump.c b/src/line_dump.c index f89f7b5..da78e86 100644 --- a/src/line_dump.c +++ b/src/line_dump.c @@ -54,15 +54,15 @@ static char *prv_dump_arith_e(const specasm_line_t *line, char *buf, const uint8_t *op_code = line->data.op_code; if (op_code[0] == 0xDD || op_code[0] == 0xFD) { - buf = - specasm_dump_index(op_code, buf, specasm_line_get_format(line)); + buf = specasm_dump_index(op_code, buf, + specasm_line_get_format(line)); } else if (op_code[0] == imm) { if (line->type >= SPECASM_LINE_TYPE_EXP_ADJ) buf = prv_dump_exp_e(line, buf, op_code[1]); else buf += specasm_dump_byte(buf, op_code[1], - specasm_line_get_format(line)); + specasm_line_get_format(line)); } else if ((op_code[0] & reg) == reg) { buf = prv_byte_and_hl_ind(op_code[0], buf); } else { @@ -115,7 +115,7 @@ static char *prv_dump_16bit_unary_e(const specasm_line_t *line, char *buf, if (op_code[0] == 0xFD || op_code[0] == 0xDD) { if (op_code[1] == hl_ind) { buf = specasm_dump_index(op_code, buf, - specasm_line_get_format(line)); + specasm_line_get_format(line)); } else { *buf++ = 'i'; *buf++ = (op_code[0] == 0xFD) ? 'y' : 'x'; @@ -136,7 +136,8 @@ static char *prv_dump_jump_label_fmt_e(const specasm_line_t *line, char *buf, const char *label; if (fmt == SPECASM_FLAGS_ADDR_NUM) { - buf += specasm_dump_word(buf, id, specasm_line_get_format(line)); + buf += + specasm_dump_word(buf, id, specasm_line_get_format(line)); } else { if (fmt == SPECASM_FLAGS_ADDR_LONG) label = specasm_state_get_long_e((uint8_t)id); @@ -175,8 +176,7 @@ static char *prv_dump_cc(const uint8_t cc, char *buf) static const char *codes[] = {"nz", "z", "nc", "c", "po", "pe", "p", "m"}; #else - const char *codes[] = {"nz", "z", "nc", "c", - "po", "pe", "p", "m"}; + const char *codes[] = {"nz", "z", "nc", "c", "po", "pe", "p", "m"}; #endif const char *code = codes[cc & 7]; while (*code) @@ -316,8 +316,8 @@ static uint8_t prv_dump_bit_e(const specasm_line_t *line, char *buf) if (op_code[0] == 0xCB) buf = prv_byte_and_hl_ind(op_code[1], buf); else - buf = - specasm_dump_index(op_code, buf, specasm_line_get_format(line)); + buf = specasm_dump_index(op_code, buf, + specasm_line_get_format(line)); return buf - start; } @@ -352,7 +352,8 @@ static uint8_t prv_dump_jr_call_e(const specasm_line_t *line, char *buf, if (fmt == SPECASM_FLAGS_ADDR_NUM) { val = *((uint16_t *)&op_code[1]); - buf += specasm_dump_word(buf, val, specasm_line_get_format(line)); + buf += + specasm_dump_word(buf, val, specasm_line_get_format(line)); } else { buf = prv_dump_jump_label_e(line, buf, op_code[1]); } @@ -399,16 +400,16 @@ static uint8_t prv_dump_ex_e(const specasm_line_t *line, char *buf) #else const char *com[] = { #endif - "\x08" - "af, af'", - "\xeb" - "de, hl", - "\xe3" - "(sp), hl", - "\xdd" - "(sp), ix", - "\xfd" - "(sp), iy", + "\x08" + "af, af'", + "\xeb" + "de, hl", + "\xe3" + "(sp), hl", + "\xdd" + "(sp), ix", + "\xfd" + "(sp), iy", }; const uint8_t *op_code = line->data.op_code; @@ -448,7 +449,7 @@ static uint8_t prv_dump_im_e(const specasm_line_t *line, char *buf) } static char *specasm_dump_byte_imm_ind_e(const specasm_line_t *line, char *buf, - uint8_t index) + uint8_t index) { const uint8_t *op_code = line->data.op_code; @@ -456,7 +457,7 @@ static char *specasm_dump_byte_imm_ind_e(const specasm_line_t *line, char *buf, return prv_dump_exp_e(line, buf, op_code[index]); return buf + specasm_dump_byte(buf, op_code[index], - specasm_line_get_format(line)); + specasm_line_get_format(line)); } static uint8_t prv_dump_in_e(const specasm_line_t *line, char *buf) @@ -496,15 +497,15 @@ static uint8_t prv_dump_jp_e(const specasm_line_t *line, char *buf) #else const char *com[] = { #endif - "\xe9" - "(hl)", - "\xdd" - "(ix)", - "\xfd" - "(iy)", + "\xe9" + "(hl)", + "\xdd" + "(ix)", + "\xfd" + "(iy)", #ifdef SPECASM_TARGET_NEXT_OPCODES - "\xed" - "(c)", + "\xed" + "(c)", #endif }; const uint8_t *op_code = line->data.op_code; @@ -572,20 +573,20 @@ static char *prv_dump_label_ind_reg_e(const specasm_line_t *line, char *buf, #else const char *com[] = { #endif - "\x3a" - "a", - "\xed" - "bc", - "\xee" - "de", - "\xf0" - "sp", - "\x2a" - "hl", - "\xdd" - "ix", - "\xfd" - "iy", + "\x3a" + "a", + "\xed" + "bc", + "\xee" + "de", + "\xf0" + "sp", + "\x2a" + "hl", + "\xdd" + "ix", + "\xfd" + "iy", }; if (opcode0 == 0xED) @@ -633,8 +634,7 @@ static char *prv_dump_ld_16bit_imm_e(const specasm_line_t *line, char *buf, static const char *codes[] = {"bc", "de", "hl", "sp", "ix", "iy", "(hl)"}; #else - const char *codes[] = {"bc", "de", "hl", "sp", - "ix", "iy", "(hl)"}; + const char *codes[] = {"bc", "de", "hl", "sp", "ix", "iy", "(hl)"}; #endif code = codes[reg]; while (*code) @@ -647,7 +647,8 @@ static char *prv_dump_ld_16bit_imm_e(const specasm_line_t *line, char *buf, specasm_line_get_addr_type(line)) { buf = prv_dump_jump_label_e(line, buf, val); } else { - buf += specasm_dump_word(buf, val, specasm_line_get_format(line)); + buf += + specasm_dump_word(buf, val, specasm_line_get_format(line)); } return buf; @@ -715,20 +716,20 @@ static char *prv_dump_ld_hl_ind_e(uint8_t opcode0, char *buf) #else const char *com[] = { #endif - "\x46" - "b", - "\x4E" - "c", - "\x56" - "d", - "\x5E" - "e", - "\x66" - "h", - "\x6E" - "l", - "\x7E" - "a", + "\x46" + "b", + "\x4E" + "c", + "\x56" + "d", + "\x5E" + "e", + "\x66" + "h", + "\x6E" + "l", + "\x7E" + "a", }; buf += @@ -750,13 +751,13 @@ static char *prv_dump_ld_reg_off_ind(const specasm_line_t *line, char *buf) buf[1] = ','; buf[2] = ' '; return specasm_dump_index(line->data.op_code, buf + 3, - specasm_line_get_format(line)); + specasm_line_get_format(line)); } static char *prv_dump_ld_ind_off_reg(const specasm_line_t *line, char *buf) { buf = specasm_dump_index(line->data.op_code, buf, - specasm_line_get_format(line)); + specasm_line_get_format(line)); buf[0] = ','; buf[1] = ' '; buf[2] = byte_regs[line->data.op_code[1] & 7]; @@ -767,12 +768,12 @@ static char *prv_dump_ld_ind_off_reg(const specasm_line_t *line, char *buf) static char *prv_dump_ld_ind_off_imm(const specasm_line_t *line, char *buf) { buf = specasm_dump_index(line->data.op_code, buf, - specasm_line_get_format(line)); + specasm_line_get_format(line)); buf[0] = ','; buf[1] = ' '; buf += 2; buf += specasm_dump_byte(buf, line->data.op_code[3], - specasm_line_get_format2(line)); + specasm_line_get_format2(line)); return buf; } @@ -814,12 +815,12 @@ static uint8_t prv_dump_ld_sp(uint8_t opcode0, char *buf) #else const char *com[] = { #endif - "\xf9" - "sp, hl", - "\xdd" - "sp, ix", - "\xfd" - "sp, iy", + "\xf9" + "sp, hl", + "\xdd" + "sp, ix", + "\xfd" + "sp, iy", }; return prv_dump_fixed_e(opcode0, buf, com, sizeof(com) / sizeof(uint8_t *)); @@ -941,7 +942,7 @@ static uint8_t prv_dump_ld_e(const specasm_line_t *line, char *buf) buf = prv_dump_exp_e(line, buf, line->data.op_code[1]); else buf += specasm_dump_byte(buf, line->data.op_code[1], - specasm_line_get_format(line)); + specasm_line_get_format(line)); return buf - start; } @@ -957,9 +958,9 @@ static uint8_t prv_dump_subtraction_e(const specasm_line_t *line, char *buf, char *str; buf[0] = '='; - str = prv_dump_jump_label_fmt_e(line, buf + 1, - line->data.op_code[id_buf], - line->data.op_code[id_buf + 2]); + str = + prv_dump_jump_label_fmt_e(line, buf + 1, line->data.op_code[id_buf], + line->data.op_code[id_buf + 2]); if (err_type != SPECASM_ERROR_OK) return 0; str[0] = '-'; @@ -1019,7 +1020,7 @@ static uint8_t prv_dump_nextreg_e(const specasm_line_t *line, char *buf) buf = prv_dump_exp_e(line, buf, line->data.op_code[2]); else buf += specasm_dump_byte(buf, line->data.op_code[2], - specasm_line_get_format(line)); + specasm_line_get_format(line)); buf[0] = ','; buf[1] = ' '; @@ -1030,7 +1031,7 @@ static uint8_t prv_dump_nextreg_e(const specasm_line_t *line, char *buf) buf++; } else { buf += specasm_dump_byte(buf, line->data.op_code[3], - specasm_line_get_format2(line)); + specasm_line_get_format2(line)); } return buf - start; @@ -1113,7 +1114,7 @@ static uint8_t prv_dump_stack(const specasm_line_t *line, char *buf) buf = prv_dump_jump_label_e(line, buf, val); else buf += specasm_dump_word(buf, val, - specasm_line_get_format(line)); + specasm_line_get_format(line)); return buf - start; #endif } else { @@ -1163,8 +1164,8 @@ static uint8_t prv_dump_shift_e(const specasm_line_t *line, char *buf) if (op_code[0] == 0xCB) buf = prv_byte_and_hl_ind(op_code[1], buf); else if (op_code[0] == 0xDD || op_code[0] == 0xFD) - buf = - specasm_dump_index(op_code, buf, specasm_line_get_format(line)); + buf = specasm_dump_index(op_code, buf, + specasm_line_get_format(line)); else err_type = SPECASM_ERROR_BAD_REG; @@ -1211,12 +1212,12 @@ static uint8_t prv_dump_db_e(const specasm_line_t *line, char *buf) sz = specasm_line_get_size(line) + 1; len = specasm_dump_byte(buf, line->data.op_code[0], - specasm_line_get_format(line)); + specasm_line_get_format(line)); for (i = 1; i < sz; i++) { ptr = &buf[len]; ptr[0] = ','; len += specasm_dump_byte(ptr + 1, line->data.op_code[i], - specasm_line_get_format(line)); + specasm_line_get_format(line)); len++; } return len; @@ -1237,14 +1238,14 @@ static uint8_t prv_dump_equw_e(const specasm_line_t *line, char *buf) str = prv_dump_jump_label_e(line, buf, line->data.op_code[0]); } else { id = *((uint16_t *)&line->data.op_code[0]); - str = - buf + specasm_dump_word(buf, id, specasm_line_get_format(line)); + str = buf + + specasm_dump_word(buf, id, specasm_line_get_format(line)); if (specasm_line_get_size(line) > 1) { str[0] = ','; str++; id = *((uint16_t *)&line->data.op_code[2]); str += specasm_dump_word(str, id, - specasm_line_get_format(line)); + specasm_line_get_format(line)); } } return str - buf; @@ -1268,7 +1269,8 @@ static uint8_t prv_dump_ds_e(const specasm_line_t *line, char *buf) buf[1] = ' '; buf += 2; - buf += specasm_dump_byte(buf, op_code[0], specasm_line_get_format(line)); + buf += + specasm_dump_byte(buf, op_code[0], specasm_line_get_format(line)); return buf - start; } @@ -1281,7 +1283,7 @@ static uint8_t prv_dump_org_e(const specasm_line_t *line, char *buf) static uint8_t prv_dump_align_e(const specasm_line_t *line, char *buf) { return specasm_dump_word(buf, 1 << line->data.op_code[0], - specasm_line_get_format(line)); + specasm_line_get_format(line)); } /* clang-format off */ diff --git a/src/line_parse.c b/src/line_parse.c index 594d887..f62a725 100644 --- a/src/line_parse.c +++ b/src/line_parse.c @@ -279,8 +279,8 @@ static uint8_t prv_parse_add_e(const char *args, specasm_line_t *line, if (opi > 0) return 0; err_type = SPECASM_ERROR_OK; - args = specasm_parse_word_imm_or_exp_e(args, line, &val, - &flags); + args = + specasm_parse_word_imm_or_exp_e(args, line, &val, &flags); if (err_type != SPECASM_ERROR_OK) return 0; op_code = &line->data.op_code[0]; @@ -740,7 +740,7 @@ static uint8_t prv_parse_im_e(const char *args, specasm_line_t *line, const char *args2; args2 = specasm_parse_byte_imm_or_exp_e(args, line, &val, - &line->data.op_code[2]); + &line->data.op_code[2]); if (err_type != SPECASM_ERROR_OK) return 0; @@ -1105,7 +1105,8 @@ static uint8_t prv_parse_nextreg_e(const char *args, specasm_line_t *line, args2 = args + specasm_parse_reg_e(args, ®, &off, &flags); if (err_type != SPECASM_ERROR_OK) { err_type = SPECASM_ERROR_OK; - args = specasm_get_byte_imm_e(args, &line->data.op_code[3], &flags); + args = specasm_get_byte_imm_e(args, &line->data.op_code[3], + &flags); if (err_type != SPECASM_ERROR_OK) return 0; specasm_line_set_format2(line, flags); @@ -1265,7 +1266,7 @@ static uint8_t prv_parse_rst_e(const char *args, specasm_line_t *line, const char *start = args; args = specasm_parse_byte_imm_or_exp_e(args, line, &val, - &line->data.op_code[1]); + &line->data.op_code[1]); if (err_type != SPECASM_ERROR_OK) return 0; @@ -1413,8 +1414,8 @@ static uint8_t prv_parse_test_e(const char *args, specasm_line_t *line, op_code = &line->data.op_code[0]; op_code[0] = 0xED; op_code[1] = 0x27; - args2 = - specasm_parse_byte_imm_or_exp_e(args, line, &op_code[2], &op_code[2]); + args2 = specasm_parse_byte_imm_or_exp_e(args, line, &op_code[2], + &op_code[2]); specasm_line_set_size(line, 2); return args2 - args; diff --git a/src/line_parse_common.c b/src/line_parse_common.c index 90ab650..9ada4e0 100644 --- a/src/line_parse_common.c +++ b/src/line_parse_common.c @@ -21,8 +21,8 @@ #include "line_parse_common.h" #include "state.h" -const char *specasm_parse_jump_label_e(const char *args, - specasm_line_t *line, uint8_t *label) +const char *specasm_parse_jump_label_e(const char *args, specasm_line_t *line, + uint8_t *label) { uint8_t long_label; uint8_t i = 0; @@ -114,8 +114,8 @@ const char *specasm_get_word_imm_e(const char *str, uint16_t *val, } const char *specasm_parse_word_imm_or_exp_e(const char *args, - specasm_line_t *line, - uint16_t *val, uint8_t *flags) + specasm_line_t *line, uint16_t *val, + uint8_t *flags) { uint8_t label; @@ -135,9 +135,8 @@ const char *specasm_parse_word_imm_or_exp_e(const char *args, return args; } -const char * -specasm_parse_label_or_exp_e(const char *args, specasm_line_t *line, - uint8_t *label) +const char *specasm_parse_label_or_exp_e(const char *args, specasm_line_t *line, + uint8_t *label) { const char *args2; @@ -403,8 +402,8 @@ const char *specasm_get_byte_imm_e(const char *str, uint8_t *val, return NULL; } -const char *specasm_get_byte_imm_ind_e(specasm_line_t *line, - const char *args, uint8_t *val) +const char *specasm_get_byte_imm_ind_e(specasm_line_t *line, const char *args, + uint8_t *val) { long v; uint8_t flags; @@ -455,8 +454,8 @@ const char *specasm_get_byte_imm_ind_e(specasm_line_t *line, } const char *specasm_parse_byte_imm_or_exp_e(const char *args, - specasm_line_t *line, - uint8_t *val, uint8_t *label) + specasm_line_t *line, uint8_t *val, + uint8_t *label) { uint8_t flags; diff --git a/src/line_parse_common.h b/src/line_parse_common.h index e0fd608..536cf54 100644 --- a/src/line_parse_common.h +++ b/src/line_parse_common.h @@ -23,27 +23,26 @@ char *specasm_get_uword_imm_e(const char *str, uint16_t *val, uint8_t *flags); char *specasm_get_long_imm_e(const char *str, long *val, uint8_t *flags); uint8_t specasm_parse_reg_e(const char *str, uint8_t *r, uint8_t *off, uint8_t *flags); -const char *specasm_parse_jump_label_e(const char *args, - specasm_line_t *line, uint8_t *label); -const char * -specasm_parse_label_or_exp_e(const char *args, specasm_line_t *line, - uint8_t *label); +const char *specasm_parse_jump_label_e(const char *args, specasm_line_t *line, + uint8_t *label); +const char *specasm_parse_label_or_exp_e(const char *args, specasm_line_t *line, + uint8_t *label); const char *specasm_get_char_imm_e(const char *str, uint8_t *val, uint8_t *flags); const char *specasm_get_word_imm_e(const char *str, uint16_t *val, uint8_t *flags); const char *specasm_parse_word_imm_or_exp_e(const char *args, - specasm_line_t *line, - uint16_t *val, uint8_t *flags); + specasm_line_t *line, uint16_t *val, + uint8_t *flags); const char *specasm_get_exp_e(specasm_line_t *line, const char *args, uint8_t *val); const char *specasm_get_byte_imm_e(const char *str, uint8_t *val, uint8_t *flags); -const char *specasm_get_byte_imm_ind_e(specasm_line_t *line, - const char *args, uint8_t *val); +const char *specasm_get_byte_imm_ind_e(specasm_line_t *line, const char *args, + uint8_t *val); const char *specasm_parse_byte_imm_or_exp_e(const char *args, - specasm_line_t *line, - uint8_t *val, uint8_t *label); + specasm_line_t *line, uint8_t *val, + uint8_t *label); const char *specasm_parse_reg_comma_e(const char *args, uint8_t *r, uint8_t *off, uint8_t *flags); #endif diff --git a/src/link_obj.c b/src/link_obj.c index e301144..9ec71c8 100644 --- a/src/link_obj.c +++ b/src/link_obj.c @@ -509,7 +509,6 @@ static void prv_apply_expressions_e(specasm_line_t *line, salink_obj_t *obj, specasm_line_set_addr_type(line, SPECASM_FLAGS_ADDR_NUM); } - static uint8_t prv_add_label_e(salink_obj_t *obj, uint16_t size, uint8_t type, uint8_t id, uint16_t line_no) { diff --git a/src/peer_file_zx.c b/src/peer_file_zx.c index 53cf5a7..c3bd35e 100644 --- a/src/peer_file_zx.c +++ b/src/peer_file_zx.c @@ -71,4 +71,3 @@ void specasm_file_stat_e(specasm_handle_t f, specasm_stat_t *buf) if (esxdos_f_fstat(f, buf) < 0) err_type = SPECASM_ERROR_READ; } - diff --git a/src/peer_zx.h b/src/peer_zx.h index c69fba3..1ccc376 100644 --- a/src/peer_zx.h +++ b/src/peer_zx.h @@ -22,8 +22,8 @@ #define specasm_sleep_ms(a) z80_delay_ms(a) #else #include -#define specasm_sleep_ms(a) \ - z80_delay_ms((1 << (ZXN_READ_REG(REG_TURBO_MODE) & 3))*(a)) +#define specasm_sleep_ms(a) \ + z80_delay_ms((1 << (ZXN_READ_REG(REG_TURBO_MODE) & 3)) * (a)) #endif #include diff --git a/src/salink.c b/src/salink.c index 3976931..e074b44 100644 --- a/src/salink.c +++ b/src/salink.c @@ -43,7 +43,6 @@ unsigned int obj_file_count; salink_global_t globals[MAX_GLOBALS]; unsigned int global_count; - #ifdef SPECASM_TARGET_NEXT void specasm_peer_next_copy_chars(void); #endif @@ -136,7 +135,7 @@ int main(int argc, char *argv[]) struct esx_mode mode; memset(&mode, 0, sizeof(mode)); - (void) esx_ide_mode_set(&mode); + (void)esx_ide_mode_set(&mode); /* * Link at top speed on a spectrum Next. @@ -149,7 +148,7 @@ int main(int argc, char *argv[]) zx_cls(PAPER_WHITE | INK_BLACK); #endif - ret = salink_link_e(); + ret = salink_link_e(); #ifdef SPECASM_TARGET_NEXT ZXN_WRITE_REG(REG_TURBO_MODE, turbo); diff --git a/src/salink_trampolines.c b/src/salink_trampolines.c index 2a0f4b0..72c40ab 100644 --- a/src/salink_trampolines.c +++ b/src/salink_trampolines.c @@ -14,14 +14,14 @@ * limitations under the License. */ -#include #include +#include #include "salink.h" -#define SALINK_NEXT_EXPRESSION_BANK ((43<<1)+1) -#define SALINK_NEXT_MAP_BANK ((44<<1)+1) -#define SALINK_NEXT_LINK_OBJ_BANK ((45<<1)+1) +#define SALINK_NEXT_EXPRESSION_BANK ((43 << 1) + 1) +#define SALINK_NEXT_MAP_BANK ((44 << 1) + 1) +#define SALINK_NEXT_LINK_OBJ_BANK ((45 << 1) + 1) extern unsigned char _z_page_table[]; @@ -78,4 +78,3 @@ void salink_equ_eval_global_e(salink_obj_t *obj, salink_global_t *global, salink_equ_eval_global_banked_e(obj, global, label, depth); ZXN_WRITE_MMU7(_z_page_table[SALINK_NEXT_LINK_OBJ_BANK]); } - diff --git a/src/samake.c b/src/samake.c index 11ec510..fffc303 100644 --- a/src/samake.c +++ b/src/samake.c @@ -15,8 +15,8 @@ */ #include -#include #include +#include #include #include @@ -75,11 +75,11 @@ static uint8_t prv_parse_obj_e(const char *fname) if (!bin_name[0] && ((line->type == SPECASM_LINE_TYPE_LL) || (line->type == SPECASM_LINE_TYPE_SL))) { if (line->type == SPECASM_LINE_TYPE_LL) - str = specasm_state_get_long_e( - line->data.label); + str = + specasm_state_get_long_e(line->data.label); else - str = specasm_state_get_short_e( - line->data.label); + str = + specasm_state_get_short_e(line->data.label); if (err_type != SPECASM_ERROR_OK) return 0; if (strcmp(str, "Main")) @@ -95,9 +95,9 @@ static uint8_t prv_parse_obj_e(const char *fname) sa = *((uint16_t *)&line->data.op_code[0]); org_address = sa; got_org = 1; - (void) utoa(sa, start_address, 10); + (void)utoa(sa, start_address, 10); sa--; - (void) utoa(sa, clear_address, 10); + (void)utoa(sa, clear_address, 10); if (bin_name[0]) return 1; } @@ -180,12 +180,12 @@ static uint8_t *prv_write_address(uint8_t *ptr, const char *address) *ptr++ = '"'; memcpy(ptr, address, 5); ptr += 5; - *ptr++ = '"';; + *ptr++ = '"'; return ptr; } -static void prv_make_basic_file(uint8_t star, const char* code_name, +static void prv_make_basic_file(uint8_t star, const char *code_name, uint8_t code_name_len) { uint16_t line_len; @@ -193,7 +193,7 @@ static void prv_make_basic_file(uint8_t star, const char* code_name, basic_buf[1] = 0xa; basic_buf[4] = 0xfd; /* CLEAR */ - (void) prv_write_address(&basic_buf[5], clear_address); + (void)prv_write_address(&basic_buf[5], clear_address); basic_buf[13] = ':'; basic_buf[14] = 0xef; /* LOAD */ ptr = &basic_buf[15]; @@ -220,7 +220,7 @@ static void prv_make_basic_file(uint8_t star, const char* code_name, line_len = ptr - &basic_buf[4]; memcpy(&basic_buf[2], &line_len, sizeof(uint16_t)); - basic_prog_len = (uint16_t) (ptr - basic_buf); + basic_prog_len = (uint16_t)(ptr - basic_buf); } static void prv_make_3dos_header(void) @@ -265,14 +265,14 @@ static specasm_handle_t prv_open_bin_e(uint16_t *bin_size) goto close_in_f; } - if (real_bin_size + (uint32_t) org_address > 0xffff) { + if (real_bin_size + (uint32_t)org_address > 0xffff) { err_type = SAMAKE_ERROR_BIN_TOO_BIG; printf("%s too big (%" PRIu32 " bytes) for org %" PRIu16 "\n", bin_name, real_bin_size, org_address); goto close_in_f; } - *bin_size = (uint16_t) real_bin_size; + *bin_size = (uint16_t)real_bin_size; return in_f; @@ -370,8 +370,8 @@ static void prv_make_code_header(uint16_t bin_size) container.tap_block[0] = 0x13; /* Header len. */ container.tap_block[1] = 0; - container.tap_block[2] = 0; /* Flag byte, 0 = header */ - container.tap_block[3] = 3; /* Type byte, 0 = program */ + container.tap_block[2] = 0; /* Flag byte, 0 = header */ + container.tap_block[3] = 3; /* Type byte, 0 = program */ /* Copy the name of the BASIC file, we'll just use the bin file */ @@ -509,7 +509,7 @@ static void prv_make_e(const char *dir, uint8_t target_type) int main(int argc, char *argv[]) { - const char* dir = "."; + const char *dir = "."; uint8_t target_type = SAMAKE_TARGET_TYPE_BAS; int ret = 0; diff --git a/src/specasm_next.c b/src/specasm_next.c index a29925d..17277b1 100644 --- a/src/specasm_next.c +++ b/src/specasm_next.c @@ -50,7 +50,7 @@ int main(void) specasm_init_dump_table(); memset(&mode, 0, sizeof(mode)); - (void) esx_ide_mode_set(&mode); + (void)esx_ide_mode_set(&mode); zx_border(SPECASM_LABEL_BORDER); zx_cls(SPECASM_CODE_COLOUR | SPECASM_LABEL_BACKGROUND); diff --git a/src/specasm_trampolines.c b/src/specasm_trampolines.c index 799660e..232e55f 100644 --- a/src/specasm_trampolines.c +++ b/src/specasm_trampolines.c @@ -14,14 +14,14 @@ * limitations under the License. */ -#include #include +#include #include "line.h" -#define SPECASM_NEXT_PARSE_BANK ((43<<1)+1) -#define SPECASM_NEXT_DUMP_BANK ((44<<1)+1) -#define SPECASM_NEXT_PARSE_LD_BANK ((45<<1)+1) +#define SPECASM_NEXT_PARSE_BANK ((43 << 1) + 1) +#define SPECASM_NEXT_DUMP_BANK ((44 << 1) + 1) +#define SPECASM_NEXT_PARSE_LD_BANK ((45 << 1) + 1) extern unsigned char _z_page_table[]; @@ -101,4 +101,3 @@ uint8_t specasm_parse_ld_e(const char *args, specasm_line_t *line, return e; } - diff --git a/src/util_print_next.c b/src/util_print_next.c index 9604f61..5b69d2a 100644 --- a/src/util_print_next.c +++ b/src/util_print_next.c @@ -29,6 +29,8 @@ static uint8_t specasm_font[SPECASM_NEXT_NUM_CHARS * 8]; +/* clang-format off */ + static void prv_write_chars(void) __naked { __asm @@ -67,6 +69,8 @@ __asm __endasm; } +/* clang-format on */ + void specasm_peer_next_copy_chars(void) { uint8_t i; @@ -101,7 +105,7 @@ uint8_t specasm_util_print(const char *str, uint8_t x, uint8_t y, uint8_t attr) bsptr = zx_cxy2saddr(x, y); while (*str) { *aptr++ = attr; - ch = *((uint8_t*) str); + ch = *((uint8_t *)str); if (ch >= 32 && ch <= 143) { cptr = &specasm_font[(ch - 32) * 8]; sptr = bsptr; From be40ecaae6dc90b65d8a372fd09c34f4cf38cf70 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 20 Jan 2024 23:27:24 +0100 Subject: [PATCH 16/16] Update the version number to v7 and v7n And add a check to make sure that you can't load a 48k .x file on a Next and vice-versa. Signed-off-by: Mark Ryan --- src/state_base.c | 8 +++++++- src/state_base.h | 9 +++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/state_base.c b/src/state_base.c index d7e58dd..6792e44 100644 --- a/src/state_base.c +++ b/src/state_base.c @@ -112,7 +112,13 @@ void specasm_load_e(const char *fname) return; } - if (SPECASM_VERSION >= state.version) { + /* + * We use the top bit to distinguish between 48K and Next versions + * of the file format. + */ + + if (((SPECASM_VERSION & 0x8000) == (state.version & 0x8000)) && + (SPECASM_VERSION >= state.version)) { state.version = SPECASM_VERSION; return; } diff --git a/src/state_base.h b/src/state_base.h index 2678430..4a4d0bb 100644 --- a/src/state_base.h +++ b/src/state_base.h @@ -17,8 +17,13 @@ #ifndef SPECASM_STATE_READ_H #define SPECASM_STATE_READ_H -#define SPECASM_VERSION 6 -#define SPECASM_VERSION_STR "v6" +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_VERSION 0x8007 +#define SPECASM_VERSION_STR "v7n" +#else +#define SPECASM_VERSION 7 +#define SPECASM_VERSION_STR "v7" +#endif #include