From c757e1b061934958f2792c9bb15a1b301cfe473a Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Tue, 1 Oct 2024 08:15:48 +0200 Subject: [PATCH 01/24] specasm: add documentation A new command 'h' has been added that can be used to bring up some information about the z80 instructions and assembler directives. 'h' can be used with or without a parameter. Without a parameter it displays the help for the first instruction 'adc'. The parameter when specified is expected to by a z80 instruction or an assembler directive. The command will attempt to display help on the specified parameter, e.g., 'h in'. When in the help screen the arrow keys can be used to move left and right through the various help screens and any other key returns to the editor. Signed-off-by: Mark Ryan --- asm/sald128/sald128.s | 7 +- bas/128/specld.tap | Bin 478 -> 528 bytes build/128/specasm/Make.include | 1 + build/128/specasm/Makefile | 9 +- src/doc.c | 1486 ++++++++++++++++++++++++++++++++ src/doc.h | 22 + src/editor.c | 14 + src/specasm_trampolines_128.c | 15 +- 8 files changed, 1550 insertions(+), 4 deletions(-) create mode 100644 src/doc.c create mode 100644 src/doc.h diff --git a/asm/sald128/sald128.s b/asm/sald128/sald128.s index 524f1a0..23bbf0b 100644 --- a/asm/sald128/sald128.s +++ b/asm/sald128/sald128.s @@ -8,6 +8,7 @@ dw calib dw bank0 dw bank4 dw bank6 +dw bank3 dw bank1 .calib @@ -32,8 +33,10 @@ dw bank1 align 4 .bank128 db 16,20,22,17 +db 19 .bankPlus2a db 16,17,19,20 +db 22 .bank0 ld c, 0 @@ -44,10 +47,12 @@ db 16,17,19,20 .bank6 ld c, 2 jr switchBank +.bank3 + ld c, 4 + jr switchBank .bank1 ld c, 3 - .switchBank ld a, (=cpuid) cp 126 diff --git a/bas/128/specld.tap b/bas/128/specld.tap index ede33ec81d29215390fa876d41562b50ca7faf65..a1a56cc89605f9055b0f2af963e13b054c271300 100644 GIT binary patch delta 107 zcmcb|Jb^__n1O+zxF9t-u{c*jLE$?i7sED2!JiYwx`f?C7!*DxCFW%t+pb8_u;620 z;FvgjqCPi+f&jzM17HbWMjdtrh4)HI3hQ|p%>)_1EUTXfHYgbznVVTC@iMkec4VBx G*aZM{DIV1T delta 61 zcmbQha*tU|n1O+zxF9t-u{c*jLE$JP7efo9;PHuKT@!cohzc^iS5i_~Z}s!Q1|?%7 Rb2AGiUPhhC#f)x;H diff --git a/build/128/specasm/Make.include b/build/128/specasm/Make.include index 7d0991e..f783d34 100644 --- a/build/128/specasm/Make.include +++ b/build/128/specasm/Make.include @@ -58,6 +58,7 @@ editor_buffers.o: editor_buffers.c editor_buffers.h line.h \ error.h scratch.o: scratch.c scratch.h line.h error.h analysis_banked.o: analysis.c state.h state_base.h line_common.h line.h error.h strings.h +doc_banked.o: doc.h editor.h error.h line.h line_common.h peer.h peer_zx.h scratch.h util_print_zx.h %.o: %.c zcc $(CFLAGS) -o $@ -c $< diff --git a/build/128/specasm/Makefile b/build/128/specasm/Makefile index 7779962..b245915 100644 --- a/build/128/specasm/Makefile +++ b/build/128/specasm/Makefile @@ -37,6 +37,9 @@ editor_extra_banked.o: editor_extra.c analysis_banked.o: analysis.c $(CC) $(CFLAGS) -DSPECASM_128_BANKED -o $@ --codesegBANK_6 --constsegBANK_6 --datasegBANK_6 -c $< +doc_banked.o: doc.c + $(CC) $(CFLAGS) -DSPECASM_128_BANKED -o $@ --codesegBANK_3 --constsegBANK_3 --datasegBANK_3 -c $< + clipboard.o: clipboard.c $(CC) $(CFLAGS) -DSPECASM_128_BANKED -o $@ -c $< @@ -67,7 +70,8 @@ SPECASM = \ ld_parse_banked.o \ editor_banked.o \ editor_extra_banked.o \ - analysis_banked.o + analysis_banked.o \ + doc_banked.o specasm_bare.tap: $(SPECASM) $(CC) $(CFLAGS) -zorg=32768 -m -startup=31 -o $@ $^ -pragma-include:zpragma.inc $(CZFLAGS) @@ -80,8 +84,9 @@ specasm.tap: specasm_bare.tap sald128/sald128 z88dk-appmake +zx -b specasm_bare_BANK_0.bin -o bank0.tap --org 49152 --noloader z88dk-appmake +zx -b specasm_bare_BANK_4.bin -o bank4.tap --org 49152 --noloader z88dk-appmake +zx -b specasm_bare_BANK_6.bin -o bank6.tap --org 49152 --noloader + z88dk-appmake +zx -b specasm_bare_BANK_3.bin -o bank3.tap --org 49152 --noloader z88dk-appmake +zx -b sald128/sald128 -o sald128.tap --org 32768 --noloader - cat ../../../bas/128/specld.tap sald128.tap bank0.tap bank4.tap bank6.tap specasm_bare.tap > specasm.tap + cat ../../../bas/128/specld.tap sald128.tap bank0.tap bank4.tap bank6.tap bank3.tap specasm_bare.tap > specasm.tap clean: - rm -rf specasm *.zip -rf unitzx sald128 diff --git a/src/doc.c b/src/doc.c new file mode 100644 index 0000000..529b59d --- /dev/null +++ b/src/doc.c @@ -0,0 +1,1486 @@ +/* + * 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 "doc.h" +#include "editor.h" +#include "line_common.h" +#include "peer.h" +#include "scratch.h" + +#include + +#define SPECASM_DOC_ENCODING_X 16 +#define SPECASM_DOC_M_CYCLES_X 27 +#define SPECASM_DOC_T_STATES_X 30 + +struct specasm_ins_form_t_ { + const char *form; + const char *encoding; + uint8_t m_cycles; + uint8_t t_states; +}; + +typedef struct specasm_ins_form_t_ specasm_ins_form_t; + +#define SPECASM_DOC_MAX_REG_ENCODING (SPECASM_BYTE_REG_IY+1) + +struct specasm_ins_doc_t_ { + char name[8]; + specasm_ins_form_t forms[6]; + const uint8_t reg_encoding[SPECASM_DOC_MAX_REG_ENCODING]; + uint8_t bits; + uint8_t all_cc; + const char *description; + const char *flags; +}; + +typedef struct specasm_ins_doc_t_ specasm_ins_doc_t; + +static const char* const reg_names[] = { + "a", + "b", + "c", + "d", + "e", + "h", + "l", + "bc", + "de", + "hl", + "af", + "sp", + "ix", + "iy" +}; + +/* + * Must be in the same order as the opcode_table in line_parse.c. + */ + +const static char specasm_doc_add_16[] = + "The second argument is added to the contents of the " + "destination register. The carry flag is set according " + "to the result of the addition. H represents carry from " + "bit 11."; + +const static char specasm_doc_inc[] = + "The specified operand is incremented by 1."; + +const static specasm_ins_doc_t docs[] = { + { + "adc", + { + {"a,r", "88+r", 1, 4}, + {"a,n", "CE n", 2, 7}, + {"a,(hl)", "8E", 2, 7}, + {"a,(ix + d)", "DD 8E d", 5, 19 }, + {"a,(iy + d)", "FD 8E d", 5, 19 }, + {"hl,rr", "ED 4A+rr", 4, 15 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, 1, 17, 33, 0, 49, + }, + 0, + 0, + "The second argument and the carry flag are added to the " + "contents of the destination register, either a or hl.", + "XX X X0X", + }, + { + "add", + { + {"a,r", "80+r", 1, 4}, + {"a,n", "C6 n", 2, 7}, + {"a,(hl)", "86", 2, 7}, + {"a,(ix + d)", "DD 86 d", 5, 19 }, + {"a,(iy + d)", "FD 86 d", 5, 19 }, + }, + { + 8, 1, 2, 3, 4, 5, 6 + }, + 0, + 0, + "The second argument is added to the contents of the " + "destination register.", + "XX X X0X", + }, + { + "add", + { + {"hl,rr", "9+rr", 3, 11 }, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 0, 49, + }, + 0, + 0, + specasm_doc_add_16, + " X 0X", + }, + { + "add", + { + {"ix,rr", "DD 9+rr", 4, 15 }, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 17, 0, 0, 49, 33, + }, + 0, + 0, + specasm_doc_add_16, + " X 0X", + }, + { + "add", + { + {"iy,rr", "FD 9+rr", 4, 15 }, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 17, 0, 0, 49, 0, 33, + }, + 0, + 0, + specasm_doc_add_16, + " X 0X", + }, + { + "align", + { + {"n", "0", 1, 4 }, + }, + { + 0, + }, + 0, + 0, + "The align directive takes one immediate argument that must be " + "a power of 2, greater than or equal to 2 and less and or equal" + " to 256. It inserts null bytes into the binary until the " + "requested alignment is achieved. The number of t-states " + "consumed by an align directive is the number of bytes inserted" + " * 4", + NULL, + }, + { + "and", + { + {"r", "A0+r", 1, 4}, + {"n", "E6 n", 2, 7}, + {"(hl)", "A6", 2, 7}, + {"(ix + d)", "DD A6 d", 5, 19 }, + {"(iy + d)", "FD A6 d", 5, 19 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The result of a bitwise AND of the accumulator and the " + "argument is stored in the accumulator.", + "XX 1 X00", + }, + { + "bit", + { + {"n,r", "CB 40+b+r", 2, 8}, + {"n,(HL)", "CB 46+b", 3, 12}, + {"n,(IX + d)", "DDCBd46+b", 5, 20}, + {"n,(IY + d)", "FDCBd46+b", 5, 20}, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 8, + 0, + "Sets the zero flag to 1 if bit n of the 2nd operand " + "is 0, or to 0 if bit N is 1.", + "?X 1 ?0 ", + }, + { + "call", + { + {"nn", "CD n n", 5, 17}, + {"cc,nn", "C4+cc n n", 5, 17}, + {"cc,nn", "C4+cc n n", 3, 10}, + }, + { + 0, + }, + 0, + 8, + "Pushes the PC on the stack and jumps to nn. The conditional " + "version of the instruction takes fewer t-states to execute " + "if the call is not taken." + , + NULL, + }, + { + "ccf", + { + {"", "3F", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "Inverts the carry flag, setting it to 1 if it were previously " + "0 and 1 if it were previously 0.", + " ? 0X" + }, + { + "cp", + { + {"r", "B8+r", 1, 4}, + {"n", "FE n", 2, 7}, + {"(hl)", "BE", 2, 7}, + {"(ix + d)", "DD BE d", 5, 19 }, + {"(iy + d)", "FD BE d", 5, 19 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The operand is subtracted from the accumulator setting the " + "flags accordingly. The result of the subtraction is " + "discarded.", + "XX X X1X", + }, + { + "cpd", + { + {"", "ED A9", 4, 16}, + }, + { + 0 + }, + 0, + 0, + "The byte pointed to by the address in hl is subtracted from " + "the accumulator and the flags are set accordingly. The result " + "is discarded. bc and hl are decremented. The p flag is set " + "if bc != 0 after the instruction has finished and is " + "otherwise reset.", + "XX X X1 ", + }, + { + "cpdr", + { + {"", "ED B9", 4, 16}, + {"", "ED B9", 5, 21}, + }, + { + 0 + }, + 0, + 0, + "The byte pointed to by the address in hl is subtracted from " + "the accumulator and the flags are set accordingly. The result " + "is discarded. bc and hl are decremented. If bc > 0 and the " + "result of the subtraction is != 0 the instructon repeats. " + "The p flag is set if bc != 0 after the instruction has " + "finished and is otherwise reset. The slower timings apply " + "when the instruction repeats.", + "XX X X1 ", + }, + { + "cpi", + { + {"", "ED A1", 4, 16}, + }, + { + 0 + }, + 0, + 0, + "The byte pointed to by the address in hl is subtracted from " + "the accumulator and the flags are set accordingly. The result " + "is discarded. bc is decremented while hl is incremented. The " + "p flag is set if bc != 0 after the instruction has finished " + "and is otherwise reset.", + "XX X X1 ", + }, + { + "cpir", + { + {"", "ED B1", 4, 16}, + {"", "ED B1", 5, 21}, + }, + { + 0 + }, + 0, + 0, + "The byte pointed to by the address in hl is subtracted from " + "the accumulator and the flags are set accordingly. The result " + "is discarded. bc is decremented while hl is incremented. If " + "bc > 0 and the result of the subtraction is != 0 the " + "instructon repeats. The p flag is set if bc != 0 after the " + "instruction has finished and is otherwise reset. The slower " + "timings apply when the instruction repeats.", + "XX X X1 ", + }, + { + "cpl", + { + {"", "2F", 1, 4}, + }, + { + 0 + }, + 0, + 0, + "Invert the contents of the accumlator.", + " 1 1 ", + }, + { + "daa", + { + {"", "27", 1, 4}, + }, + { + 0 + }, + 0, + 0, + "Conditionally adjusts the accumulator for BCD addition " + "and subtraction.", + "XX X X X", + }, + { + "db", + { + {"n", "n", 0, 0}, + {"n,n", "n n", 0, 0}, + {"n,n,n", "n n n", 0, 0}, + {"n,n,n,n", "n n n n", 0, 0}, + }, + { + 0 + }, + 0, + 0, + "Stores up to 4 bytes in the program binary. All ns must be " + "formatted in the same way. Only one byte can be specified if " + "an expression is used.", + NULL, + }, + { + "dec", + { + {"r", "5+r", 1, 4}, + {"(hl)", "35", 3, 11}, + {"(ix + d)", "DD 35 d", 6, 23 }, + {"(iy + d)", "FD 35 d", 6, 23 }, + }, + { + 57, 1, 9, 17, 25, 33, 49, + }, + 0, + 0, + "The specified operand is decremented by 1.", + "XX X X1 ", + }, + { + "dec", + { + {"rr", "B+rr", 1, 6}, + {"ix", "DD 2B", 2, 10 }, + {"iy", "FD 2B", 2, 10 }, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 0, 49, + }, + 0, + 0, + "The specified operand is decremented by 1.", + NULL, + }, + { + "di", + { + {"", "F3", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "Disables maskable interrupts.", + NULL, + }, + { + "djnz", + { + {"n", "10 n", 2, 8}, + {"n", "10 n", 3, 13}, + }, + { + 0, + }, + 0, + 0, + "The b register is decremented by 1. If the result is > 0 " + "the cpu jumps to PC + 2 + n, where n is a signed 8 byte. The " + "instruction executes more quickly when the jump it not taken.", + NULL, + }, + { + "ds", + { + {"c n", "n c times", 0, 0}, + }, + { + 0 + }, + 0, + 0, + "Stores c copies of the byte n in the binary.", + NULL, + }, + { + "dw", + { + {"nn", "nn", 0, 0}, + {"nn,nn", "nn nn", 0, 0}, + }, + { + 0 + }, + 0, + 0, + "Stores up to 2 words in the program binary. All nns must be " + "formatted in the same way. Only one word can be specified if " + "an expression is used.", + NULL, + }, + { + "ei", + { + {"", "FB", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "Enables maskable interrupts.", + NULL, + }, + { + "ex", + { + {"af, af'", "08", 1, 4}, + {"de, hl", "EB", 1, 4}, + {"sp, (hl)", "E3", 5, 19}, + {"(sp), ix", "DD E3", 6, 23}, + {"(sp), iy", "FD E3", 6, 23}, + }, + { + 0, + }, + 0, + 0, + "The contents of the two operands are exchanged. " + "ex af, af' affects all the flags while the other forms of the " + "instruction have no effect on the flags.", + "XXXXXXXX", + }, + { + "exx", + { + {"", "D9", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "Exchange BC, DE and HL with BC', DE', HL'.", + NULL, + }, + { + "halt", + { + {"", "76", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "CPU execution is suspended until the next interrupt or reset.", + NULL, + }, + { + "im", + { + {"0", "ED 46", 2, 8}, + {"1", "ED 56", 2, 8}, + {"2", "ED 5E", 2, 8}, + }, + { + 0, + }, + 0, + 0, + "Sets the interrupt mode. With IM 2 the MSB of the vector " + "address is taken from the I register.", + NULL, + }, + { + "in", + { + {"r, (c)", "ED 40+r", 3, 12}, + }, + { + 57, 1, 9, 17, 25, 33, 49, + }, + 0, + 0, + "Reads a byte from the device at the adddress stored in the bc " + "register and stores it in r.", + "XX X X0 ", + }, + { + "in", + { + {"a, (n)", "DB n", 3, 11}, + }, + { + 0 + }, + 0, + 0, + "Reads a byte from the device at the adddress whose MSB is " + "taken from the accumulator and whose LSB is n. The byte is " + "stored in the accumulator.", + NULL, + }, + { + "inc", + { + {"r", "4+r", 1, 4}, + {"(hl)", "34", 3, 11}, + {"(ix + d)", "DD 34 d", 6, 23 }, + {"(iy + d)", "FD 34 d", 6, 23 }, + }, + { + 57, 1, 9, 17, 25, 33, 49, + }, + 0, + 0, + specasm_doc_inc, + "XX X X0 ", + }, + { + "inc", + { + {"rr", "3+rr", 1, 6}, + {"ix", "DD 23", 2, 10 }, + {"iy", "FD 23", 2, 10 }, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 0, 49, + }, + 0, + 0, + specasm_doc_inc, + NULL, + }, + { + "neg", + { + {"", "ED 44", 2, 8}, + }, + { + 0, + }, + 0, + 0, + "Let a = 0 - a.", + "XX X X1X", + }, + { + "nop", + { + {"", "0", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "CPU does nothing for 1 m-cycle.", + NULL, + }, + { + "or", + { + {"r", "B8+r", 1, 4}, + {"n", "F6 n", 2, 7}, + {"(hl)", "B6", 2, 7}, + {"(ix + d)", "DD B6 d", 5, 19 }, + {"(iy + d)", "FD B6 d", 5, 19 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The result of a bitwise OR of the accumulator and the " + "argument is stored in the accumulator.", + "XX 0 X00", + }, + { + "org", + { + {"nn","", 0, 0}, + }, + { + 0, + }, + 0, + 0, + "Assembler directive that sets the org address of the program, " + "i.e., the address the first byte in the .x or .t file that " + "contains the Main label is assembled at.", + NULL, + }, + { + "out", + { + {"(c), r", "ED 41+r", 3, 12}, + }, + { + 57, 1, 9, 17, 25, 33, 49, + }, + 0, + 0, + "Writes the register r to the device at the adddress stored " + "in the bc register.", + NULL, + }, + { + "outdr", + { + {"","ED BB", 4, 16}, + {"","ED BB", 5, 21}, + }, + { + 0, + }, + 0, + 0, + "b is decremented. The byte at the address in hl is written to " + "the port given by the address in bc, where b has already been " + "decremented. hl is decremented. The instruction repeats if " + "b!=0. The instruction consumes more t-states when it repeats.", + "?1 ? ?1 ", + }, + { + "outir", + { + {"","ED B3", 4, 16}, + {"","ED B3", 5, 21}, + }, + { + 0, + }, + 0, + 0, + "b is decremented. The byte at the address in hl is written to " + "the port given by the address in bc, where b has already been " + "decremented. hl is incremented. The instruction repeats if " + "b!=0. The instruction consumes more t-states when it repeats.", + "?1 ? ?1 ", + }, + { + "out", + { + {"(n), a", "D3 n", 3, 11}, + }, + { + 0 + }, + 0, + 0, + "Writes the accumulator to the device at the adddress whose MSB" + " is taken from the accumulator and whose LSB is n.", + NULL, + }, + { + "outd", + { + {"","ED AB", 4, 16}, + }, + { + 0, + }, + 0, + 0, + "b is decremented. The byte at the address in hl is written to " + "the port given by the address in bc, where b has already been " + "decremented. hl is decremented.", + "?X ? ?1 ", + }, + { + "outi", + { + {"","ED A3", 4, 16}, + }, + { + 0, + }, + 0, + 0, + "b is decremented. The byte at the address in hl is written to " + "the port given by the address in bc, where b has already been " + "decremented. hl is incremented.", + "?X ? ?1 ", + }, + { + "pop", + { + {"rr", "C1+rr", 3, 10}, + {"ix", "DD E1", 4, 14}, + {"iy", "FD E1", 4, 14}, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 49 + }, + 0, + 0, + "Pops 2 bytes off the stack into the operand.", + NULL, + }, + { + "push", + { + {"rr", "C5+rr", 3, 11}, + {"ix", "DD E5", 4, 15}, + {"iy", "FD E5", 4, 15}, + }, + { + 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 49 + }, + 0, + 0, + "Pushes the operand onto the stack.", + NULL, + }, + { + "res", + { + {"n,r", "CB 80+b+r", 2, 8}, + {"n,(HL)", "CB 86+b", 4, 15}, + {"n,(IX + d)", "DDCBd86+b", 6, 23}, + {"n,(IY + d)", "FDCBd86+b", 6, 23}, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 8, + 0, + "Resets bit n in the second operand.", + NULL, + }, + { + "ret", + { + {"", "C9", 3, 10}, + {"cc", "C0 + cc", 1, 5}, + {"cc", "C0 + cc", 3, 10}, + }, + { + 0, + }, + 0, + 8, + "If there is no condition or the condition is met value of " + "the stack is popped into the PC, from which program execution " + "continues. ret takes fewer cycles if the condition is not met" + ".", + NULL, + }, + { + "reti", + { + {"", "ED 4D", 4, 14}, + }, + { + 0, + }, + 0, + 0, + "Return from interrupt. The stack is popped into PC. An ei " + "instruction must be executed prior to the reti to renable " + "maskable interrupts.", + NULL, + }, + { + "retn", + { + {"", "ED 45", 4, 14}, + }, + { + 0, + }, + 0, + 0, + "Return from NMI. The stack is popped into PC and the " + "maskable interrupts are re-enabled if they were enabled " + "before the NMI.", + NULL, + }, + { + "rl", + { + {"r", "CB 10+r", 2, 8}, + {"(hl)", "CB 16", 4, 15}, + {"(ix + d)", "DD CB d 16", 6, 23 }, + {"(iy + d)", "FD CB d 16", 6, 23 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The operand is shifted left by 1 bit. The carry flag " + "is moved into bit 0 of the operand and the old bit 7 of " + "the operand is moved into the carry flag.", + "XX 0 X0X", + }, + { + "rla", + { + {"", "17", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "The accumulator is rotated left 1 bit. The carry flag is " + "moved to bit 0 of the accumulator and its old bit 7 is moved " + "to the carry flag.", + " 0 0X", + }, + { + "rlc", + { + {"r", "CB r", 2, 8}, + {"(hl)", "CB 06", 4, 15}, + {"(ix + d)", "DD CB d 06", 6, 23 }, + {"(iy + d)", "FD CB d 06", 6, 23 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The operand is rotated left 1 bit. The old bit 7 is moved to " + "both the carry flag and bit 0.", + "XX 0 X0X", + }, + { + "rlca", + { + {"", "07", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "The accumulator is rotated left 1 bit. The old bit 7 is moved " + "to both the carry flag and bit 0.", + " 0 0X", + }, + { + "rld", + { + {"", "ED 6F", 5, 18}, + }, + { + 0, + }, + 0, + 0, + "Let tmp = (hl) >> 4 " + "Let (hl) = ((hl) << 4)|(a & $f) " + "Let a = (a & $f0)|tmp", + "XX 0 X0 ", + }, + { + "rr", + { + {"r", "CB 18+r", 2, 8}, + {"(hl)", "CB 1E", 4, 15}, + {"(ix + d)", "DD CB d 1E", 6, 23 }, + {"(iy + d)", "FD CB d 1E", 6, 23 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The operand is shifted right by 1 bit. The carry flag " + "is moved into bit 7 of the operand and the old bit 0 of " + "the operand is moved into the carry flag.", + "XX 0 X0X", + }, + { + "rra", + { + {"", "1F", 1, 4}, + }, + { + 0 + }, + 0, + 0, + "The accumulator is shifted right 1 bit. The carry flag is " + "moved into bit 7 of the accumulator and the old contents of " + "the accumulator's bit 0 are moved into the carry flag." + " 0 0X", + }, + { + "rrc", + { + {"r", "CB 8+r", 2, 8}, + {"(hl)", "CB 0E", 4, 15}, + {"(ix + d)", "DD CB d 0E", 6, 23 }, + {"(iy + d)", "FD CB d 0E", 6, 23 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The second argument and the carry flag are subtracted from " + "the contents of the destination register, either a or hl.", + "XX 0 X0X", + }, + { + "rrca", + { + {"", "0F", 1, 4}, + }, + { + 0 + }, + 0, + 0, + "The accumulator is rotated right by 1 bit. The old bit 0 is " + "moved into both the carry flag and bit 7 of the accumulator.", + " 0 0X", + }, + { + "rrd", + { + {"", "ED 67", 5, 18}, + }, + { + 0, + }, + 0, + 0, + "Let tmp = a << 4 " + "Let a = (a & $f0)|((hl) & $f) " + "Let (hl) = tmp | ((hl) >> 4)", + "XX 0 X0 ", + }, + { + "rst", + { + {"n", "C3+n", 3, 11}, + }, + { + 0, + }, + 0, + 0, + "The PC is pushed to the stack and the CPU jumps to the " + "address n. Valid values of n are 0, 8, 16, 24, 32, 40, 48," + " and 56.", + NULL, + }, + { + "sbc", + { + {"a,r", "98+r", 1, 4}, + {"a,n", "DE n", 2, 7}, + {"a,(hl)", "9E", 2, 7}, + {"a,(ix + d)", "DD 9E d", 5, 19 }, + {"a,(iy + d)", "FD 9E d", 5, 19 }, + {"hl,rr", "ED 42+rr", 4, 15 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, 1, 17, 33, 0, 49, + }, + 0, + 0, + "The second argument and the carry flag are subtracted from " + "the contents of the destination register, either a or hl.", + "XX X X1X", + }, + { + "scf", + { + {"", "37", 1, 4}, + }, + { + 0, + }, + 0, + 0, + "Sets the carry flag.", + " 0 01" + }, + { + "set", + { + {"n,r", "CB C0+b+r", 2, 8}, + {"n,(HL)", "CB C6+b", 4, 15}, + {"n,(IX + d)", "DDCBdC6+b", 6, 23}, + {"n,(IY + d)", "FDCBdC6+b", 6, 23}, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 8, + 0, + "Sets bit n in the second operand.", + NULL, + }, + { + "sla", + { + {"r", "CB 20+r", 2, 8}, + {"(hl)", "CB 26", 4, 15}, + {"(ix + d)", "DD CB d 26", 6, 23 }, + {"(iy + d)", "FD CB d 26", 6, 23 }, + }, + { + 8, 1, 2, 3, 4, 5, 6 + }, + 0, + 0, + "The operand is shifted left by 1 bit. Bit 0 " + "is set to 0. Bit 7 is moved into the carry flag.", + "XX 0 X0X", + }, + { + "sra", + { + {"r", "CB 28+r", 2, 8}, + {"(hl)", "CB 2E", 4, 15}, + {"(ix + d)", "DD CB d 2E", 6, 23 }, + {"(iy + d)", "FD CB d 2E", 6, 23 }, + }, + { + 8, 1, 2, 3, 4, 5, 6 + }, + 0, + 0, + "The operand is arithmetically shifted right by 1 bit. Bit 7 " + "is set to 0. Bit 0 is moved into the carry flag.", + "XX 0 X0X", + }, + { + "srl", + { + {"r", "CB 38+r", 2, 8}, + {"(hl)", "CB 3E", 4, 15}, + {"(ix + d)", "DD CB d 3E", 6, 23 }, + {"(iy + d)", "FD CB d 3E", 6, 23 }, + }, + { + 8, 1, 2, 3, 4, 5, 6 + }, + 0, + 0, + "The operand is logically shifted right by 1 bit. Bit 7 is " + "unchanged. Bit 0 is moved into the carry flag.", + "XX 0 X0X", + }, + { + "sub", + { + {"r", "A0+r", 1, 4}, + {"n", "D6 n", 2, 7}, + {"(hl)", "96", 2, 7}, + {"(ix + d)", "DD 96 d", 5, 19 }, + {"(iy + d)", "FD 96 d", 5, 19 }, + }, + { + 8, 1, 2, 3, 4, 5, 6 + }, + 0, + 0, + "The operand is subracted from the accumlator.", + "XX X X1X", + }, + { + "xor", + { + {"r", "A8+r", 1, 4}, + {"n", "EE n", 2, 7}, + {"(hl)", "AE", 2, 7}, + {"(ix + d)", "DD AE d", 5, 19 }, + {"(iy + d)", "FD AE d", 5, 19 }, + }, + { + 8, 1, 2, 3, 4, 5, 6, + }, + 0, + 0, + "The result of a bitwise XOR of the accumulator and the " + "argument is stored in the accumulator.", + "XX 0 X00", + }, + { + "zx81", + { + {"", "", 0, 0 }, + }, + { + 0, + }, + 0, + 0, + "Linker directive that causes string and character literals to " + "be transliterated from ASCII to ZX81 encoding. It also sets " + "org address to 16514.", + NULL, + }, + +}; + +const uint8_t max_docs = sizeof(docs) / sizeof(specasm_ins_doc_t); + +static uint8_t prv_pretty_print(uint8_t y, const char * text) +{ + uint8_t i; + + while (*text) { + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + scratch[SPECASM_LINE_MAX_LEN] = 0; + for (i = 0; *text && i < SPECASM_LINE_MAX_LEN; i++) { + scratch[i] = *text++; + } + + if (i == SPECASM_LINE_MAX_LEN) { + for (i = i - 1; scratch[i] != ' '; i--) { + text--; + scratch[i] = ' '; + if (i == 0) + break; + } + } + + specasm_util_print(scratch, 0, y++, PAPER_BLACK | INK_WHITE); + } + + return y; +} + +static void prv_justify_num(uint8_t num) +{ + char *s = scratch; + + if (num < 10) { + s[0] = ' '; + s++; + } + itoa(num, s, 10); +} + +static uint8_t prv_print_register_encoding(const specasm_ins_doc_t *doc, + uint8_t y) +{ + uint8_t i; + uint8_t reg_encoding; + const char* reg_name; + char *s2; + char *rr_start = NULL; + char *s = scratch; + + for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) + if (doc->reg_encoding[i]) + break; + + if (i == SPECASM_DOC_MAX_REG_ENCODING) + return y; + + for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) { + reg_encoding = doc->reg_encoding[i]; + if (reg_encoding) { + if ((i > 6) && !rr_start) + rr_start = s; + s += 3; + } + } + + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + if (!rr_start) { + scratch[15] = 'r'; + } else if (rr_start == scratch) { + scratch[14] = 'r'; + scratch[15] = 'r'; + } else { + scratch[(rr_start - scratch) / 2] = 'r'; + s2 = SPECASM_LINE_MAX_LEN + scratch; + s = &rr_start[((s2 - rr_start) / 2)]; + s -= 3; + *s = 'r'; + s[1] = 'r'; + } + specasm_util_print(scratch, 0, y, PAPER_WHITE | INK_BLACK); + y++; + + s = scratch; + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) { + reg_encoding = doc->reg_encoding[i]; + if (reg_encoding) { + s2 = s; + reg_name = reg_names[i]; + if (!reg_name[1]) + s2++; + memcpy(s2, reg_name, strlen(reg_name)); + s +=3; + } + } + specasm_util_print(scratch, 0, y, PAPER_BLUE | INK_WHITE); + + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + s = scratch; + for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) { + reg_encoding = doc->reg_encoding[i]; + if (reg_encoding) { + reg_encoding--; + s2 = s; + if (reg_encoding < 16) + s2++; + itoa(reg_encoding, s2, 16); + s2[strlen(s2)] = ' '; + s +=3; + } + } + + specasm_util_print(scratch, 0, y + 1, PAPER_BLACK | INK_WHITE | 64); + + return y + 3; +} + +static void prv_print_flags(uint8_t y, const char *flags) +{ + if (!flags) + return; + + specasm_util_print("sz-h-pnc", 12, y, PAPER_BLUE | INK_WHITE); + y++; + specasm_util_print("Flags", 5, y, PAPER_BLACK | INK_WHITE); + specasm_util_print(flags, 12, y, PAPER_BLACK | INK_WHITE | 64); +} + +static uint8_t prv_print_bits_encoding(const specasm_ins_doc_t *doc, + uint8_t y) +{ + uint8_t i; + uint8_t x; + uint8_t num; + char *s; + + if (!doc->bits) + return y; + + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + scratch[15] = 'b'; + specasm_util_print(scratch, 0, y, PAPER_WHITE | INK_BLACK); + y++; + + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + s = scratch; + for (i = 0; i < 8; i++) { + s[2] = i + '0'; + s += 4; + } + specasm_util_print(scratch, 0, y, PAPER_BLUE | INK_WHITE); + y++; + + for (i = 0; i < 8; i++) { + num = doc->bits * i; + itoa(num, scratch, 16); + x = (i*4)+1; + if (num < 16) + x++; + specasm_util_print(scratch, x, y, PAPER_BLACK | INK_WHITE); + } + + return y + 2; +} + +static uint8_t prv_print_cc_encoding(const specasm_ins_doc_t *doc, + uint8_t y) +{ + uint8_t i; + uint8_t x; + uint8_t num; + char *s; + + if (!doc->all_cc) + return y; + + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + scratch[14] = 'c'; + scratch[15] = 'c'; + specasm_util_print(scratch, 0, y, PAPER_WHITE | INK_BLACK); + y++; + + specasm_util_print(" nz z nc c po pe p m ", 0, + y, PAPER_BLUE | INK_WHITE); + y++; + + for (i = 0; i < 8; i++) { + num = doc->all_cc * i; + itoa(num, scratch, 16); + x = (i*4)+1; + if (num < 16) + x++; + specasm_util_print(scratch, x, y, PAPER_BLACK | INK_WHITE); + } + + return y + 2; +} + +static void prv_draw_help(uint8_t ins_id) +{ + uint8_t x; + uint8_t i; + uint8_t y; + uint8_t col; + const specasm_ins_doc_t* doc = &docs[ins_id]; + const specasm_ins_form_t *form; + const char* ins_name = doc->name; + uint8_t ins_name_len = strlen(ins_name); + + specasm_cls(PAPER_BLACK | INK_WHITE); + x = (SPECASM_LINE_MAX_LEN / 2) - (strlen(ins_name) >> 1); + + memset(scratch, ' ', SPECASM_LINE_MAX_LEN); + scratch[SPECASM_LINE_MAX_LEN] = 0; + memcpy(&scratch[x], ins_name, ins_name_len); + + specasm_util_print(scratch, 0, 0, PAPER_BLUE | INK_WHITE | 64); + + y = 2; + memcpy(scratch, "Opcode Encoding M T", + SPECASM_LINE_MAX_LEN); + specasm_util_print(scratch, 0, y, PAPER_BLUE | INK_WHITE); + y++; + col = PAPER_BLACK | INK_WHITE; + for (i = 0; i < 6; i++) { + col ^= 1 << 6; + form = &doc->forms[i]; + if (!form->form) + break; + x = 0; + specasm_util_print(ins_name, x, y, col); + x += ins_name_len + 1; + specasm_util_print(form->form, x, y, col); + specasm_util_print(form->encoding, SPECASM_DOC_ENCODING_X, y, + col); + prv_justify_num(form->m_cycles); + specasm_util_print(scratch, SPECASM_DOC_M_CYCLES_X, y, col); + prv_justify_num(form->t_states); + specasm_util_print(scratch, SPECASM_DOC_T_STATES_X, y, col); + y++; + } + + y = prv_print_register_encoding(doc, y + 1); + y = prv_print_bits_encoding(doc, y); + y = prv_print_cc_encoding(doc, y); + y = prv_pretty_print(y, doc->description); + prv_print_flags(y + 1, doc->flags); +} + +static uint8_t prv_find_mnemomic(const char *ins_name) +{ + uint8_t m; + uint8_t l; + uint8_t r; + int res; + + if (strcmp(ins_name, docs[0].name) < 0) + return 0; + + l = 0; + r =max_docs - 1; + + while (l <= r) { + m = (l + r) >> 1; + res = strcmp(docs[m].name, ins_name); + if (res < 0) { + l = m + 1; + } else if (res > 0) { + if (m == 0) + break; + r = m - 1; + } else { + return m; + } + } + + /* + * Opcode not found. Let's return something close. + */ + + return r; +} + +void specasm_help_banked(const char *ins_name) +{ + uint8_t id; + uint8_t k; + + id = prv_find_mnemomic(ins_name); + do { + prv_draw_help(id); + do { + specasm_sleep_ms(25); + } while(!(k = in_inkey())); + + if (k == SPECASM_KEY_LEFT) { + if (id > 0) + id--; + } else if (k == SPECASM_KEY_RIGHT) { + if (id < max_docs - 1) + id++; + } else { + break; + } + } while(1); + + specasm_cls(PAPER_BLACK | INK_WHITE); +} diff --git a/src/doc.h b/src/doc.h new file mode 100644 index 0000000..5fc8d75 --- /dev/null +++ b/src/doc.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_DOC_H +#define SPECASM_DOC_H + +void specasm_help(const char *ins_name); + +#endif diff --git a/src/editor.c b/src/editor.c index bb5c841..26b94c9 100644 --- a/src/editor.c +++ b/src/editor.c @@ -17,6 +17,7 @@ #include "editor.h" #include "editor_buffers.h" #if defined(SPECASM_TARGET_NEXT_OPCODES) || defined(SPECASM_TARGET_128) +#include "doc.h" #include "editor_extra.h" #endif #include "line.h" @@ -561,6 +562,13 @@ static uint8_t prv_single_char_command_e(uint8_t ch) case 'v': specasm_selecting_clip_paste_e(); break; + case 'h': + specasm_text_set_flash(col, row, 0); + specasm_help("a"); + specasm_text_set_flash(col, row, SPECASM_FLASH); + specasm_draw_screen(line - row); + specasm_draw_status(); + break; #endif default: err_type = SPECASM_ERROR_BAD_COMMAND; @@ -687,6 +695,12 @@ static uint8_t prv_long_command_e(char *com, uint8_t len) return 0; } else if ((com[0] == 'f') && (com[1] == 'l') && !com[2]) { specasm_selecting_flags(); + } else if (com[0] == 'h' && com[1] == ' ') { + specasm_text_set_flash(col, row, 0); + specasm_help(&com[2]); + specasm_text_set_flash(col, row, SPECASM_FLASH); + specasm_draw_screen(line - row); + specasm_draw_status(); #endif } else { err_type = SPECASM_ERROR_BAD_COMMAND; diff --git a/src/specasm_trampolines_128.c b/src/specasm_trampolines_128.c index 9debf3c..09f778f 100644 --- a/src/specasm_trampolines_128.c +++ b/src/specasm_trampolines_128.c @@ -23,13 +23,15 @@ static uint8_t banks[] = { 0 + 16, 1 + 16, 4 + 16, - 6 + 16 + 6 + 16, + 3 + 16 }; #define SPECASM_128_PARSE_BANK 0 #define SPECASM_128_CLIP_BANK 1 #define SPECASM_128_DUMP_BANK 2 #define SPECASM_128_EDITOR_BANK 3 +#define SPECASM_128_HELP_BANK 4 #ifdef UNITTESTS #define SPECASM_128_UNIT_BANK 3 @@ -56,6 +58,7 @@ void specasm_format_line_banked_e(char *buf, unsigned int l); void specasm_draw_status_banked(void); void specasm_handle_key_press_banked(uint8_t k); void specasm_editor_reset_banked(void); +void specasm_help_banked(const char *ins_name); #ifdef UNITTESTS void specasm_peer_write_state_banked_e(const char *fname, uint16_t checksum); @@ -73,6 +76,7 @@ void specasm_trampolines_init(void) banks[SPECASM_128_CLIP_BANK] = 4 + 16; banks[SPECASM_128_DUMP_BANK] = 1 + 16; banks[SPECASM_128_EDITOR_BANK] = 3 + 16; + banks[SPECASM_128_HELP_BANK] = 6 + 16; } /* clang-format off */ @@ -270,6 +274,15 @@ void specasm_editor_reset(void) specasm_editor_reset_banked(); (void) prv_map_bank(bank); } + +void specasm_help(const char *ins_name) +{ + uint8_t bank; + + bank = prv_map_bank(banks[SPECASM_128_HELP_BANK]); + specasm_help_banked(ins_name); + (void) prv_map_bank(bank); +} #else void specasm_peer_write_state_e(const char *fname, uint16_t checksum) { From 405c1275697d4be83e96a8e4eaa3a6318b297e7d Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Tue, 1 Oct 2024 13:11:22 +0200 Subject: [PATCH 02/24] Abstract out the register encoding to save space Signed-off-by: Mark Ryan --- src/doc.c | 384 ++++++++++++++++++++++++------------------------------ 1 file changed, 169 insertions(+), 215 deletions(-) diff --git a/src/doc.c b/src/doc.c index 529b59d..4cf3078 100644 --- a/src/doc.c +++ b/src/doc.c @@ -44,7 +44,7 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; struct specasm_ins_doc_t_ { char name[8]; specasm_ins_form_t forms[6]; - const uint8_t reg_encoding[SPECASM_DOC_MAX_REG_ENCODING]; + uint8_t reg_encoding; uint8_t bits; uint8_t all_cc; const char *description; @@ -70,6 +70,16 @@ static const char* const reg_names[] = { "iy" }; +const uint8_t reg_encodings[][SPECASM_DOC_MAX_REG_ENCODING] = { + { 8, 1, 2, 3, 4, 5, 6, 1, 17, 33, 0, 49 }, + { 8, 1, 2, 3, 4, 5, 6 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 0, 49 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 17, 0, 0, 49, 33 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 17, 0, 0, 49, 0, 33}, + { 57, 1, 9, 17, 25, 33, 49}, + { 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 49 }, +}; + /* * Must be in the same order as the opcode_table in line_parse.c. */ @@ -94,9 +104,7 @@ const static specasm_ins_doc_t docs[] = { {"a,(iy + d)", "FD 8E d", 5, 19 }, {"hl,rr", "ED 4A+rr", 4, 15 }, }, - { - 8, 1, 2, 3, 4, 5, 6, 1, 17, 33, 0, 49, - }, + 1, 0, 0, "The second argument and the carry flag are added to the " @@ -112,9 +120,7 @@ const static specasm_ins_doc_t docs[] = { {"a,(ix + d)", "DD 86 d", 5, 19 }, {"a,(iy + d)", "FD 86 d", 5, 19 }, }, - { - 8, 1, 2, 3, 4, 5, 6 - }, + 2, 0, 0, "The second argument is added to the contents of the " @@ -126,9 +132,7 @@ const static specasm_ins_doc_t docs[] = { { {"hl,rr", "9+rr", 3, 11 }, }, - { - 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 0, 49, - }, + 3, 0, 0, specasm_doc_add_16, @@ -139,9 +143,7 @@ const static specasm_ins_doc_t docs[] = { { {"ix,rr", "DD 9+rr", 4, 15 }, }, - { - 0, 0, 0, 0, 0, 0, 0, 1, 17, 0, 0, 49, 33, - }, + 4, 0, 0, specasm_doc_add_16, @@ -152,9 +154,7 @@ const static specasm_ins_doc_t docs[] = { { {"iy,rr", "FD 9+rr", 4, 15 }, }, - { - 0, 0, 0, 0, 0, 0, 0, 1, 17, 0, 0, 49, 0, 33, - }, + 5, 0, 0, specasm_doc_add_16, @@ -165,9 +165,7 @@ const static specasm_ins_doc_t docs[] = { { {"n", "0", 1, 4 }, }, - { - 0, - }, + 0, 0, 0, "The align directive takes one immediate argument that must be " @@ -187,9 +185,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD A6 d", 5, 19 }, {"(iy + d)", "FD A6 d", 5, 19 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The result of a bitwise AND of the accumulator and the " @@ -204,9 +200,7 @@ const static specasm_ins_doc_t docs[] = { {"n,(IX + d)", "DDCBd46+b", 5, 20}, {"n,(IY + d)", "FDCBd46+b", 5, 20}, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 8, 0, "Sets the zero flag to 1 if bit n of the 2nd operand " @@ -220,9 +214,7 @@ const static specasm_ins_doc_t docs[] = { {"cc,nn", "C4+cc n n", 5, 17}, {"cc,nn", "C4+cc n n", 3, 10}, }, - { - 0, - }, + 0, 0, 8, "Pushes the PC on the stack and jumps to nn. The conditional " @@ -236,9 +228,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "3F", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "Inverts the carry flag, setting it to 1 if it were previously " @@ -254,9 +244,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD BE d", 5, 19 }, {"(iy + d)", "FD BE d", 5, 19 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The operand is subtracted from the accumulator setting the " @@ -269,9 +257,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "ED A9", 4, 16}, }, - { - 0 - }, + 0, 0, 0, "The byte pointed to by the address in hl is subtracted from " @@ -287,9 +273,7 @@ const static specasm_ins_doc_t docs[] = { {"", "ED B9", 4, 16}, {"", "ED B9", 5, 21}, }, - { - 0 - }, + 0, 0, 0, "The byte pointed to by the address in hl is subtracted from " @@ -306,9 +290,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "ED A1", 4, 16}, }, - { - 0 - }, + 0, 0, 0, "The byte pointed to by the address in hl is subtracted from " @@ -324,9 +306,7 @@ const static specasm_ins_doc_t docs[] = { {"", "ED B1", 4, 16}, {"", "ED B1", 5, 21}, }, - { - 0 - }, + 0, 0, 0, "The byte pointed to by the address in hl is subtracted from " @@ -343,9 +323,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "2F", 1, 4}, }, - { - 0 - }, + 0, 0, 0, "Invert the contents of the accumlator.", @@ -356,9 +334,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "27", 1, 4}, }, - { - 0 - }, + 0, 0, 0, "Conditionally adjusts the accumulator for BCD addition " @@ -373,9 +349,7 @@ const static specasm_ins_doc_t docs[] = { {"n,n,n", "n n n", 0, 0}, {"n,n,n,n", "n n n n", 0, 0}, }, - { - 0 - }, + 0, 0, 0, "Stores up to 4 bytes in the program binary. All ns must be " @@ -391,9 +365,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD 35 d", 6, 23 }, {"(iy + d)", "FD 35 d", 6, 23 }, }, - { - 57, 1, 9, 17, 25, 33, 49, - }, + 6, 0, 0, "The specified operand is decremented by 1.", @@ -406,9 +378,7 @@ const static specasm_ins_doc_t docs[] = { {"ix", "DD 2B", 2, 10 }, {"iy", "FD 2B", 2, 10 }, }, - { - 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 0, 49, - }, + 3, 0, 0, "The specified operand is decremented by 1.", @@ -419,9 +389,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "F3", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "Disables maskable interrupts.", @@ -433,9 +401,7 @@ const static specasm_ins_doc_t docs[] = { {"n", "10 n", 2, 8}, {"n", "10 n", 3, 13}, }, - { - 0, - }, + 0, 0, 0, "The b register is decremented by 1. If the result is > 0 " @@ -448,9 +414,7 @@ const static specasm_ins_doc_t docs[] = { { {"c n", "n c times", 0, 0}, }, - { - 0 - }, + 0, 0, 0, "Stores c copies of the byte n in the binary.", @@ -462,9 +426,7 @@ const static specasm_ins_doc_t docs[] = { {"nn", "nn", 0, 0}, {"nn,nn", "nn nn", 0, 0}, }, - { - 0 - }, + 0, 0, 0, "Stores up to 2 words in the program binary. All nns must be " @@ -477,9 +439,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "FB", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "Enables maskable interrupts.", @@ -494,9 +454,7 @@ const static specasm_ins_doc_t docs[] = { {"(sp), ix", "DD E3", 6, 23}, {"(sp), iy", "FD E3", 6, 23}, }, - { - 0, - }, + 0, 0, 0, "The contents of the two operands are exchanged. " @@ -509,9 +467,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "D9", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "Exchange BC, DE and HL with BC', DE', HL'.", @@ -522,9 +478,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "76", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "CPU execution is suspended until the next interrupt or reset.", @@ -537,9 +491,7 @@ const static specasm_ins_doc_t docs[] = { {"1", "ED 56", 2, 8}, {"2", "ED 5E", 2, 8}, }, - { - 0, - }, + 0, 0, 0, "Sets the interrupt mode. With IM 2 the MSB of the vector " @@ -551,9 +503,7 @@ const static specasm_ins_doc_t docs[] = { { {"r, (c)", "ED 40+r", 3, 12}, }, - { - 57, 1, 9, 17, 25, 33, 49, - }, + 6, 0, 0, "Reads a byte from the device at the adddress stored in the bc " @@ -565,9 +515,7 @@ const static specasm_ins_doc_t docs[] = { { {"a, (n)", "DB n", 3, 11}, }, - { - 0 - }, + 0, 0, 0, "Reads a byte from the device at the adddress whose MSB is " @@ -583,9 +531,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD 34 d", 6, 23 }, {"(iy + d)", "FD 34 d", 6, 23 }, }, - { - 57, 1, 9, 17, 25, 33, 49, - }, + 6, 0, 0, specasm_doc_inc, @@ -598,24 +544,103 @@ const static specasm_ins_doc_t docs[] = { {"ix", "DD 23", 2, 10 }, {"iy", "FD 23", 2, 10 }, }, + 3, + 0, + 0, + specasm_doc_inc, + NULL, + }, + { + "ldd", { - 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 0, 49, + {"", "ED A8", 4, 16}, }, 0, 0, - specasm_doc_inc, + 0, + "The byte pointed to by hl is loaded into the address in de. " + "hl, de and bc are decremented.", + " 0 X0 ", + }, + { + "lddr", + { + {"", "ED B8", 4, 16}, + {"", "ED B8", 5, 21}, + }, + 0, + 0, + 0, + "The byte pointed to by hl is loaded into the address in de. " + "hl, de and bc are decremented. If bc!=0 the instruction " + "repeats. The instruction consumes more t-states when it " + "repeats.", + " 0 00 ", + }, + { + "ldi", + { + {"", "ED A0", 4, 16}, + }, + 0, + 0, + 0, + "The byte pointed to by hl is loaded into the address in de. " + "Both hl and de are incremented while bc is decremented.", + " 0 X0 ", + }, + { + "ldir", + { + {"", "ED B0", 4, 16}, + {"", "ED B0", 5, 21}, + }, + 0, + 0, + 0, + "The byte pointed to by hl is loaded into the address in de. " + "Both hl and de are incremented while bc is decremented. If " + "bc!=0 the instruction repeats. The instruction consumes more " + "t-states when it repeats.", + " 0 00 ", + }, + { + "jp", + { + {"nn", "C3 n n", 3, 10}, + {"(hl)", "E9", 1, 4}, + {"(ix)", "DD E9", 2, 8}, + {"(iy)", "FD E9", 2, 8}, + {"cc,nn", "C2+cc n n", 3, 12}, + {"cc,nn", "C2+cc n n", 2, 7}, + }, + 0, + 0, + 8, + "Jump to the last operand if the condition is met or no " + "condition is supplied. Instruction consumes fewer t-states " + "if condition is not met.", NULL, }, { - "neg", + "map", { - {"", "ED 44", 2, 8}, + {"", "", 0, 0}, }, + 0, + 0, + 0, + "Instructs the linker to generate a map file.", + NULL, + }, + { + "neg", { - 0, + {"", "ED 44", 2, 8}, }, 0, 0, + 0, "Let a = 0 - a.", "XX X X1X", }, @@ -624,9 +649,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "0", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "CPU does nothing for 1 m-cycle.", @@ -641,9 +664,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD B6 d", 5, 19 }, {"(iy + d)", "FD B6 d", 5, 19 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The result of a bitwise OR of the accumulator and the " @@ -655,9 +676,7 @@ const static specasm_ins_doc_t docs[] = { { {"nn","", 0, 0}, }, - { - 0, - }, + 0, 0, 0, "Assembler directive that sets the org address of the program, " @@ -670,9 +689,7 @@ const static specasm_ins_doc_t docs[] = { { {"(c), r", "ED 41+r", 3, 12}, }, - { - 57, 1, 9, 17, 25, 33, 49, - }, + 6, 0, 0, "Writes the register r to the device at the adddress stored " @@ -685,9 +702,7 @@ const static specasm_ins_doc_t docs[] = { {"","ED BB", 4, 16}, {"","ED BB", 5, 21}, }, - { - 0, - }, + 0, 0, 0, "b is decremented. The byte at the address in hl is written to " @@ -702,9 +717,7 @@ const static specasm_ins_doc_t docs[] = { {"","ED B3", 4, 16}, {"","ED B3", 5, 21}, }, - { - 0, - }, + 0, 0, 0, "b is decremented. The byte at the address in hl is written to " @@ -718,9 +731,7 @@ const static specasm_ins_doc_t docs[] = { { {"(n), a", "D3 n", 3, 11}, }, - { - 0 - }, + 0, 0, 0, "Writes the accumulator to the device at the adddress whose MSB" @@ -732,9 +743,7 @@ const static specasm_ins_doc_t docs[] = { { {"","ED AB", 4, 16}, }, - { - 0, - }, + 0, 0, 0, "b is decremented. The byte at the address in hl is written to " @@ -747,9 +756,7 @@ const static specasm_ins_doc_t docs[] = { { {"","ED A3", 4, 16}, }, - { - 0, - }, + 0, 0, 0, "b is decremented. The byte at the address in hl is written to " @@ -764,9 +771,7 @@ const static specasm_ins_doc_t docs[] = { {"ix", "DD E1", 4, 14}, {"iy", "FD E1", 4, 14}, }, - { - 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 49 - }, + 7, 0, 0, "Pops 2 bytes off the stack into the operand.", @@ -779,9 +784,7 @@ const static specasm_ins_doc_t docs[] = { {"ix", "DD E5", 4, 15}, {"iy", "FD E5", 4, 15}, }, - { - 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 49 - }, + 7, 0, 0, "Pushes the operand onto the stack.", @@ -795,9 +798,7 @@ const static specasm_ins_doc_t docs[] = { {"n,(IX + d)", "DDCBd86+b", 6, 23}, {"n,(IY + d)", "FDCBd86+b", 6, 23}, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 8, 0, "Resets bit n in the second operand.", @@ -810,9 +811,7 @@ const static specasm_ins_doc_t docs[] = { {"cc", "C0 + cc", 1, 5}, {"cc", "C0 + cc", 3, 10}, }, - { - 0, - }, + 0, 0, 8, "If there is no condition or the condition is met value of " @@ -826,9 +825,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "ED 4D", 4, 14}, }, - { - 0, - }, + 0, 0, 0, "Return from interrupt. The stack is popped into PC. An ei " @@ -841,9 +838,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "ED 45", 4, 14}, }, - { - 0, - }, + 0, 0, 0, "Return from NMI. The stack is popped into PC and the " @@ -859,9 +854,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD CB d 16", 6, 23 }, {"(iy + d)", "FD CB d 16", 6, 23 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The operand is shifted left by 1 bit. The carry flag " @@ -874,9 +867,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "17", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "The accumulator is rotated left 1 bit. The carry flag is " @@ -892,9 +883,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD CB d 06", 6, 23 }, {"(iy + d)", "FD CB d 06", 6, 23 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The operand is rotated left 1 bit. The old bit 7 is moved to " @@ -906,9 +895,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "07", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "The accumulator is rotated left 1 bit. The old bit 7 is moved " @@ -920,9 +907,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "ED 6F", 5, 18}, }, - { - 0, - }, + 0, 0, 0, "Let tmp = (hl) >> 4 " @@ -938,9 +923,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD CB d 1E", 6, 23 }, {"(iy + d)", "FD CB d 1E", 6, 23 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The operand is shifted right by 1 bit. The carry flag " @@ -953,9 +936,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "1F", 1, 4}, }, - { - 0 - }, + 0, 0, 0, "The accumulator is shifted right 1 bit. The carry flag is " @@ -971,9 +952,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD CB d 0E", 6, 23 }, {"(iy + d)", "FD CB d 0E", 6, 23 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The second argument and the carry flag are subtracted from " @@ -985,9 +964,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "0F", 1, 4}, }, - { - 0 - }, + 0, 0, 0, "The accumulator is rotated right by 1 bit. The old bit 0 is " @@ -999,9 +976,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "ED 67", 5, 18}, }, - { - 0, - }, + 0, 0, 0, "Let tmp = a << 4 " @@ -1014,9 +989,7 @@ const static specasm_ins_doc_t docs[] = { { {"n", "C3+n", 3, 11}, }, - { - 0, - }, + 0, 0, 0, "The PC is pushed to the stack and the CPU jumps to the " @@ -1034,9 +1007,7 @@ const static specasm_ins_doc_t docs[] = { {"a,(iy + d)", "FD 9E d", 5, 19 }, {"hl,rr", "ED 42+rr", 4, 15 }, }, - { - 8, 1, 2, 3, 4, 5, 6, 1, 17, 33, 0, 49, - }, + 1, 0, 0, "The second argument and the carry flag are subtracted from " @@ -1048,9 +1019,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "37", 1, 4}, }, - { - 0, - }, + 0, 0, 0, "Sets the carry flag.", @@ -1064,9 +1033,7 @@ const static specasm_ins_doc_t docs[] = { {"n,(IX + d)", "DDCBdC6+b", 6, 23}, {"n,(IY + d)", "FDCBdC6+b", 6, 23}, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 8, 0, "Sets bit n in the second operand.", @@ -1080,9 +1047,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD CB d 26", 6, 23 }, {"(iy + d)", "FD CB d 26", 6, 23 }, }, - { - 8, 1, 2, 3, 4, 5, 6 - }, + 2, 0, 0, "The operand is shifted left by 1 bit. Bit 0 " @@ -1097,9 +1062,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD CB d 2E", 6, 23 }, {"(iy + d)", "FD CB d 2E", 6, 23 }, }, - { - 8, 1, 2, 3, 4, 5, 6 - }, + 2, 0, 0, "The operand is arithmetically shifted right by 1 bit. Bit 7 " @@ -1114,9 +1077,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD CB d 3E", 6, 23 }, {"(iy + d)", "FD CB d 3E", 6, 23 }, }, - { - 8, 1, 2, 3, 4, 5, 6 - }, + 2, 0, 0, "The operand is logically shifted right by 1 bit. Bit 7 is " @@ -1132,9 +1093,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD 96 d", 5, 19 }, {"(iy + d)", "FD 96 d", 5, 19 }, }, - { - 8, 1, 2, 3, 4, 5, 6 - }, + 2, 0, 0, "The operand is subracted from the accumlator.", @@ -1149,9 +1108,7 @@ const static specasm_ins_doc_t docs[] = { {"(ix + d)", "DD AE d", 5, 19 }, {"(iy + d)", "FD AE d", 5, 19 }, }, - { - 8, 1, 2, 3, 4, 5, 6, - }, + 2, 0, 0, "The result of a bitwise XOR of the accumulator and the " @@ -1163,9 +1120,7 @@ const static specasm_ins_doc_t docs[] = { { {"", "", 0, 0 }, }, - { - 0, - }, + 0, 0, 0, "Linker directive that causes string and character literals to " @@ -1223,17 +1178,16 @@ static uint8_t prv_print_register_encoding(const specasm_ins_doc_t *doc, const char* reg_name; char *s2; char *rr_start = NULL; + const uint8_t* enc; char *s = scratch; - for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) - if (doc->reg_encoding[i]) - break; - - if (i == SPECASM_DOC_MAX_REG_ENCODING) + if (!doc->reg_encoding) return y; + enc = (const uint8_t*) ®_encodings[doc->reg_encoding-1]; + for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) { - reg_encoding = doc->reg_encoding[i]; + reg_encoding = enc[i]; if (reg_encoding) { if ((i > 6) && !rr_start) rr_start = s; @@ -1261,7 +1215,7 @@ static uint8_t prv_print_register_encoding(const specasm_ins_doc_t *doc, s = scratch; memset(scratch, ' ', SPECASM_LINE_MAX_LEN); for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) { - reg_encoding = doc->reg_encoding[i]; + reg_encoding = enc[i]; if (reg_encoding) { s2 = s; reg_name = reg_names[i]; @@ -1276,7 +1230,7 @@ static uint8_t prv_print_register_encoding(const specasm_ins_doc_t *doc, memset(scratch, ' ', SPECASM_LINE_MAX_LEN); s = scratch; for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) { - reg_encoding = doc->reg_encoding[i]; + reg_encoding = enc[i]; if (reg_encoding) { reg_encoding--; s2 = s; From e8d0df15ebcace2a5d62068abcd7c10037cb35f9 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Tue, 1 Oct 2024 18:53:58 +0200 Subject: [PATCH 03/24] specasm: refactor and reword to safe space We're redone the instruction encodings placing them all into one large table, reducing wastage, and have reworded some of the instructions to make them shorter, saving over 2Kb. Signed-off-by: Mark Ryan --- src/doc.c | 1347 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 890 insertions(+), 457 deletions(-) diff --git a/src/doc.c b/src/doc.c index 4cf3078..ce7a84e 100644 --- a/src/doc.c +++ b/src/doc.c @@ -39,11 +39,589 @@ struct specasm_ins_form_t_ { typedef struct specasm_ins_form_t_ specasm_ins_form_t; +#define SPECASM_DOC_ADC_FORMS 0 +#define SPECASM_DOC_ADC_NUM_FORMS 6 +#define SPECASM_DOC_ADD1_FORMS \ + (SPECASM_DOC_ADC_FORMS + SPECASM_DOC_ADC_NUM_FORMS) +#define SPECASM_DOC_ADD1_NUM_FORMS 5 +#define SPECASM_DOC_ADD2_FORMS \ + (SPECASM_DOC_ADD1_FORMS + SPECASM_DOC_ADD1_NUM_FORMS) +#define SPECASM_DOC_ADD2_NUM_FORMS 1 +#define SPECASM_DOC_ADD3_FORMS \ + (SPECASM_DOC_ADD2_FORMS + SPECASM_DOC_ADD2_NUM_FORMS) +#define SPECASM_DOC_ADD3_NUM_FORMS 1 +#define SPECASM_DOC_ADD4_FORMS \ + (SPECASM_DOC_ADD3_FORMS + SPECASM_DOC_ADD3_NUM_FORMS) +#define SPECASM_DOC_ADD4_NUM_FORMS 1 +#define SPECASM_DOC_ALIGN_FORMS \ + (SPECASM_DOC_ADD4_FORMS + SPECASM_DOC_ADD4_NUM_FORMS) +#define SPECASM_DOC_ALIGN_NUM_FORMS 1 +#define SPECASM_DOC_AND_FORMS \ + (SPECASM_DOC_ALIGN_FORMS + SPECASM_DOC_ALIGN_NUM_FORMS) +#define SPECASM_DOC_AND_NUM_FORMS 5 +#define SPECASM_DOC_BIT_FORMS \ + (SPECASM_DOC_AND_FORMS + SPECASM_DOC_AND_NUM_FORMS) +#define SPECASM_DOC_BIT_NUM_FORMS 4 +#define SPECASM_DOC_CALL_FORMS \ + (SPECASM_DOC_BIT_FORMS + SPECASM_DOC_BIT_NUM_FORMS) +#define SPECASM_DOC_CALL_NUM_FORMS 3 +#define SPECASM_DOC_CCF_FORMS \ + (SPECASM_DOC_CALL_FORMS + SPECASM_DOC_CALL_NUM_FORMS) +#define SPECASM_DOC_CCF_NUM_FORMS 1 +#define SPECASM_DOC_CP_FORMS \ + (SPECASM_DOC_CCF_FORMS + SPECASM_DOC_CCF_NUM_FORMS) +#define SPECASM_DOC_CP_NUM_FORMS 5 +#define SPECASM_DOC_CPD_FORMS \ + (SPECASM_DOC_CP_FORMS + SPECASM_DOC_CP_NUM_FORMS) +#define SPECASM_DOC_CPD_NUM_FORMS 1 +#define SPECASM_DOC_CPDR_FORMS \ + (SPECASM_DOC_CPD_FORMS + SPECASM_DOC_CPD_NUM_FORMS) +#define SPECASM_DOC_CPDR_NUM_FORMS 2 +#define SPECASM_DOC_CPI_FORMS \ + (SPECASM_DOC_CPDR_FORMS + SPECASM_DOC_CPDR_NUM_FORMS) +#define SPECASM_DOC_CPI_NUM_FORMS 1 +#define SPECASM_DOC_CPIR_FORMS \ + (SPECASM_DOC_CPI_FORMS + SPECASM_DOC_CPI_NUM_FORMS) +#define SPECASM_DOC_CPIR_NUM_FORMS 2 +#define SPECASM_DOC_CPL_FORMS \ + (SPECASM_DOC_CPIR_FORMS + SPECASM_DOC_CPIR_NUM_FORMS) +#define SPECASM_DOC_CPL_NUM_FORMS 1 +#define SPECASM_DOC_DAA_FORMS \ + (SPECASM_DOC_CPL_FORMS + SPECASM_DOC_CPL_NUM_FORMS) +#define SPECASM_DOC_DAA_NUM_FORMS 1 +#define SPECASM_DOC_DB_FORMS \ + (SPECASM_DOC_DAA_FORMS + SPECASM_DOC_DAA_NUM_FORMS) +#define SPECASM_DOC_DB_NUM_FORMS 4 +#define SPECASM_DOC_DEC1_FORMS \ + (SPECASM_DOC_DB_FORMS + SPECASM_DOC_DB_NUM_FORMS) +#define SPECASM_DOC_DEC1_NUM_FORMS 4 +#define SPECASM_DOC_DEC2_FORMS \ + (SPECASM_DOC_DEC1_FORMS + SPECASM_DOC_DEC1_NUM_FORMS) +#define SPECASM_DOC_DEC2_NUM_FORMS 3 +#define SPECASM_DOC_DI_FORMS \ + (SPECASM_DOC_DEC2_FORMS + SPECASM_DOC_DEC2_NUM_FORMS) +#define SPECASM_DOC_DI_NUM_FORMS 1 +#define SPECASM_DOC_DJNZ_FORMS \ + (SPECASM_DOC_DI_FORMS + SPECASM_DOC_DI_NUM_FORMS) +#define SPECASM_DOC_DJNZ_NUM_FORMS 2 +#define SPECASM_DOC_DS_FORMS \ + (SPECASM_DOC_DJNZ_FORMS + SPECASM_DOC_DJNZ_NUM_FORMS) +#define SPECASM_DOC_DS_NUM_FORMS 1 +#define SPECASM_DOC_DW_FORMS \ + (SPECASM_DOC_DS_FORMS + SPECASM_DOC_DS_NUM_FORMS) +#define SPECASM_DOC_DW_NUM_FORMS 2 +#define SPECASM_DOC_EI_FORMS \ + (SPECASM_DOC_DW_FORMS + SPECASM_DOC_DW_NUM_FORMS) +#define SPECASM_DOC_EI_NUM_FORMS 1 +#define SPECASM_DOC_EX_FORMS \ + (SPECASM_DOC_EI_FORMS + SPECASM_DOC_EI_NUM_FORMS) +#define SPECASM_DOC_EX_NUM_FORMS 5 +#define SPECASM_DOC_EXX_FORMS \ + (SPECASM_DOC_EX_FORMS + SPECASM_DOC_EX_NUM_FORMS) +#define SPECASM_DOC_EXX_NUM_FORMS 1 +#define SPECASM_DOC_HALT_FORMS \ + (SPECASM_DOC_EXX_FORMS + SPECASM_DOC_EXX_NUM_FORMS) +#define SPECASM_DOC_HALT_NUM_FORMS 1 +#define SPECASM_DOC_IM_FORMS \ + (SPECASM_DOC_HALT_FORMS + SPECASM_DOC_HALT_NUM_FORMS) +#define SPECASM_DOC_IM_NUM_FORMS 3 +#define SPECASM_DOC_IN1_FORMS \ + (SPECASM_DOC_IM_FORMS + SPECASM_DOC_IM_NUM_FORMS) +#define SPECASM_DOC_IN1_NUM_FORMS 1 +#define SPECASM_DOC_IN2_FORMS \ + (SPECASM_DOC_IN1_FORMS + SPECASM_DOC_IN1_NUM_FORMS) +#define SPECASM_DOC_IN2_NUM_FORMS 1 +#define SPECASM_DOC_INC1_FORMS \ + (SPECASM_DOC_IN2_FORMS + SPECASM_DOC_IN2_NUM_FORMS) +#define SPECASM_DOC_INC1_NUM_FORMS 4 +#define SPECASM_DOC_INC2_FORMS \ + (SPECASM_DOC_INC1_FORMS + SPECASM_DOC_INC1_NUM_FORMS) +#define SPECASM_DOC_INC2_NUM_FORMS 3 +#define SPECASM_DOC_IND_FORMS \ + (SPECASM_DOC_INC2_FORMS + SPECASM_DOC_INC2_NUM_FORMS) +#define SPECASM_DOC_IND_NUM_FORMS 1 +#define SPECASM_DOC_INDR_FORMS \ + (SPECASM_DOC_IND_FORMS + SPECASM_DOC_IND_NUM_FORMS) +#define SPECASM_DOC_INDR_NUM_FORMS 2 +#define SPECASM_DOC_INI_FORMS \ + (SPECASM_DOC_INDR_FORMS + SPECASM_DOC_INDR_NUM_FORMS) +#define SPECASM_DOC_INI_NUM_FORMS 1 +#define SPECASM_DOC_INIR_FORMS \ + (SPECASM_DOC_INI_FORMS + SPECASM_DOC_INI_NUM_FORMS) +#define SPECASM_DOC_INIR_NUM_FORMS 2 +#define SPECASM_DOC_JP_FORMS \ + (SPECASM_DOC_INIR_FORMS + SPECASM_DOC_INIR_NUM_FORMS) +#define SPECASM_DOC_JP_NUM_FORMS 6 +#define SPECASM_DOC_LDD_FORMS \ + (SPECASM_DOC_JP_FORMS + SPECASM_DOC_JP_NUM_FORMS) +#define SPECASM_DOC_LDD_NUM_FORMS 1 +#define SPECASM_DOC_LDDR_FORMS \ + (SPECASM_DOC_LDD_FORMS + SPECASM_DOC_LDD_NUM_FORMS) +#define SPECASM_DOC_LDDR_NUM_FORMS 2 +#define SPECASM_DOC_LDI_FORMS \ + (SPECASM_DOC_LDDR_FORMS + SPECASM_DOC_LDDR_NUM_FORMS) +#define SPECASM_DOC_LDI_NUM_FORMS 1 +#define SPECASM_DOC_LDIR_FORMS \ + (SPECASM_DOC_LDI_FORMS + SPECASM_DOC_LDI_NUM_FORMS) +#define SPECASM_DOC_LDIR_NUM_FORMS 2 +#define SPECASM_DOC_MAP_FORMS \ + (SPECASM_DOC_LDIR_FORMS + SPECASM_DOC_LDIR_NUM_FORMS) +#define SPECASM_DOC_MAP_NUM_FORMS 1 +#define SPECASM_DOC_NEG_FORMS \ + (SPECASM_DOC_MAP_FORMS + SPECASM_DOC_MAP_NUM_FORMS) +#define SPECASM_DOC_NEG_NUM_FORMS 1 +#define SPECASM_DOC_NOP_FORMS \ + (SPECASM_DOC_NEG_FORMS + SPECASM_DOC_NEG_NUM_FORMS) +#define SPECASM_DOC_NOP_NUM_FORMS 1 +#define SPECASM_DOC_OR_FORMS \ + (SPECASM_DOC_NOP_FORMS + SPECASM_DOC_NOP_NUM_FORMS) +#define SPECASM_DOC_OR_NUM_FORMS 5 +#define SPECASM_DOC_ORG_FORMS \ + (SPECASM_DOC_OR_FORMS + SPECASM_DOC_OR_NUM_FORMS) +#define SPECASM_DOC_ORG_NUM_FORMS 1 +#define SPECASM_DOC_OUT1_FORMS \ + (SPECASM_DOC_ORG_FORMS + SPECASM_DOC_ORG_NUM_FORMS) +#define SPECASM_DOC_OUT1_NUM_FORMS 1 +#define SPECASM_DOC_OUT2_FORMS \ + (SPECASM_DOC_OUT1_FORMS + SPECASM_DOC_OUT1_NUM_FORMS) +#define SPECASM_DOC_OUT2_NUM_FORMS 1 +#define SPECASM_DOC_OUTD_FORMS \ + (SPECASM_DOC_OUT2_FORMS + SPECASM_DOC_OUT2_NUM_FORMS) +#define SPECASM_DOC_OUTD_NUM_FORMS 1 +#define SPECASM_DOC_OUTDR_FORMS \ + (SPECASM_DOC_OUTD_FORMS + SPECASM_DOC_OUTD_NUM_FORMS) +#define SPECASM_DOC_OUTDR_NUM_FORMS 2 +#define SPECASM_DOC_OUTI_FORMS \ + (SPECASM_DOC_OUTDR_FORMS + SPECASM_DOC_OUTDR_NUM_FORMS) +#define SPECASM_DOC_OUTI_NUM_FORMS 1 +#define SPECASM_DOC_OUTIR_FORMS \ + (SPECASM_DOC_OUTI_FORMS + SPECASM_DOC_OUTI_NUM_FORMS) +#define SPECASM_DOC_OUTIR_NUM_FORMS 2 +#define SPECASM_DOC_POP_FORMS \ + (SPECASM_DOC_OUTIR_FORMS + SPECASM_DOC_OUTIR_NUM_FORMS) +#define SPECASM_DOC_POP_NUM_FORMS 3 +#define SPECASM_DOC_PUSH_FORMS \ + (SPECASM_DOC_POP_FORMS + SPECASM_DOC_POP_NUM_FORMS) +#define SPECASM_DOC_PUSH_NUM_FORMS 3 +#define SPECASM_DOC_RES_FORMS \ + (SPECASM_DOC_PUSH_FORMS + SPECASM_DOC_PUSH_NUM_FORMS) +#define SPECASM_DOC_RES_NUM_FORMS 4 +#define SPECASM_DOC_RET_FORMS \ + (SPECASM_DOC_RES_FORMS + SPECASM_DOC_RES_NUM_FORMS) +#define SPECASM_DOC_RET_NUM_FORMS 3 +#define SPECASM_DOC_RETI_FORMS \ + (SPECASM_DOC_RET_FORMS + SPECASM_DOC_RET_NUM_FORMS) +#define SPECASM_DOC_RETI_NUM_FORMS 1 +#define SPECASM_DOC_RETN_FORMS \ + (SPECASM_DOC_RETI_FORMS + SPECASM_DOC_RETI_NUM_FORMS) +#define SPECASM_DOC_RETN_NUM_FORMS 1 +#define SPECASM_DOC_RL_FORMS \ + (SPECASM_DOC_RETN_FORMS + SPECASM_DOC_RETN_NUM_FORMS) +#define SPECASM_DOC_RL_NUM_FORMS 4 +#define SPECASM_DOC_RLA_FORMS \ + (SPECASM_DOC_RL_FORMS + SPECASM_DOC_RL_NUM_FORMS) +#define SPECASM_DOC_RLA_NUM_FORMS 1 +#define SPECASM_DOC_RLC_FORMS \ + (SPECASM_DOC_RLA_FORMS + SPECASM_DOC_RLA_NUM_FORMS) +#define SPECASM_DOC_RLC_NUM_FORMS 4 +#define SPECASM_DOC_RLCA_FORMS \ + (SPECASM_DOC_RLC_FORMS + SPECASM_DOC_RLC_NUM_FORMS) +#define SPECASM_DOC_RLCA_NUM_FORMS 1 +#define SPECASM_DOC_RLD_FORMS \ + (SPECASM_DOC_RLCA_FORMS + SPECASM_DOC_RLCA_NUM_FORMS) +#define SPECASM_DOC_RLD_NUM_FORMS 1 +#define SPECASM_DOC_RR_FORMS \ + (SPECASM_DOC_RLD_FORMS + SPECASM_DOC_RLD_NUM_FORMS) +#define SPECASM_DOC_RR_NUM_FORMS 4 +#define SPECASM_DOC_RRA_FORMS \ + (SPECASM_DOC_RR_FORMS + SPECASM_DOC_RR_NUM_FORMS) +#define SPECASM_DOC_RRA_NUM_FORMS 1 +#define SPECASM_DOC_RRC_FORMS \ + (SPECASM_DOC_RRA_FORMS + SPECASM_DOC_RRA_NUM_FORMS) +#define SPECASM_DOC_RRC_NUM_FORMS 4 +#define SPECASM_DOC_RRCA_FORMS \ + (SPECASM_DOC_RRC_FORMS + SPECASM_DOC_RRC_NUM_FORMS) +#define SPECASM_DOC_RRCA_NUM_FORMS 1 +#define SPECASM_DOC_RRD_FORMS \ + (SPECASM_DOC_RRCA_FORMS + SPECASM_DOC_RRCA_NUM_FORMS) +#define SPECASM_DOC_RRD_NUM_FORMS 1 +#define SPECASM_DOC_RST_FORMS \ + (SPECASM_DOC_RRD_FORMS + SPECASM_DOC_RRD_NUM_FORMS) +#define SPECASM_DOC_RST_NUM_FORMS 1 +#define SPECASM_DOC_SBC_FORMS \ + (SPECASM_DOC_RST_FORMS + SPECASM_DOC_RST_NUM_FORMS) +#define SPECASM_DOC_SBC_NUM_FORMS 6 +#define SPECASM_DOC_SCF_FORMS \ + (SPECASM_DOC_SBC_FORMS + SPECASM_DOC_SBC_NUM_FORMS) +#define SPECASM_DOC_SCF_NUM_FORMS 1 +#define SPECASM_DOC_SET_FORMS \ + (SPECASM_DOC_SCF_FORMS + SPECASM_DOC_SCF_NUM_FORMS) +#define SPECASM_DOC_SET_NUM_FORMS 4 +#define SPECASM_DOC_SLA_FORMS \ + (SPECASM_DOC_SET_FORMS + SPECASM_DOC_SET_NUM_FORMS) +#define SPECASM_DOC_SLA_NUM_FORMS 4 +#define SPECASM_DOC_SRA_FORMS \ + (SPECASM_DOC_SLA_FORMS + SPECASM_DOC_SLA_NUM_FORMS) +#define SPECASM_DOC_SRA_NUM_FORMS 4 +#define SPECASM_DOC_SRL_FORMS \ + (SPECASM_DOC_SRA_FORMS + SPECASM_DOC_SRA_NUM_FORMS) +#define SPECASM_DOC_SRL_NUM_FORMS 4 +#define SPECASM_DOC_SUB_FORMS \ + (SPECASM_DOC_SRL_FORMS + SPECASM_DOC_SRL_NUM_FORMS) +#define SPECASM_DOC_SUB_NUM_FORMS 5 +#define SPECASM_DOC_XOR_FORMS \ + (SPECASM_DOC_SUB_FORMS + SPECASM_DOC_SUB_NUM_FORMS) +#define SPECASM_DOC_XOR_NUM_FORMS 5 + + +static const specasm_ins_form_t specasm_forms[] = { + /* SPECASM_DOC_ADC_FORMS */ + {"a,r", "88+r", 1, 4}, + {"a,n", "CE n", 2, 7}, + {"a,(hl)", "8E", 2, 7}, + {"a,(ix+d)", "DD 8E d", 5, 19 }, + {"a,(iy+d)", "FD 8E d", 5, 19 }, + {"hl,rr", "ED 4A+rr", 4, 15 }, + + /* SPECASM_DOC_ADD1_FORMS */ + {"a,r", "80+r", 1, 4}, + {"a,n", "C6 n", 2, 7}, + {"a,(hl)", "86", 2, 7}, + {"a,(ix+d)", "DD 86 d", 5, 19 }, + {"a,(iy+d)", "FD 86 d", 5, 19 }, + + /* SPECASM_DOC_ADD2_FORMS */ + {"hl,rr", "9+rr", 3, 11 }, + + /* SPECASM_DOC_ADD3_FORMS */ + {"ix,rr", "DD 9+rr", 4, 15 }, + + /* SPECASM_DOC_ADD4_FORMS */ + {"iy,rr", "FD 9+rr", 4, 15 }, + + /* SPECASM_DOC_ALIGN_FORMS */ + {"n", "0", 1, 4 }, + + /* SPECASM_DOC_AND_FORMS */ + {"r", "A0+r", 1, 4}, + {"n", "E6 n", 2, 7}, + {"(hl)", "A6", 2, 7}, + {"(ix+d)", "DD A6 d", 5, 19 }, + {"(iy+d)", "FD A6 d", 5, 19 }, + + /* SPECASM_DOC_BIT_FORMS */ + {"n,r", "CB 40+b+r", 2, 8}, + {"n,(hl)", "CB 46+b", 3, 12}, + {"n,(ix+d)", "DDCBd46+b", 5, 20}, + {"n,(iy+d)", "FDCBd46+b", 5, 20}, + + /* SPECASM_DOC_CALL_FORMS */ + {"nn", "CD n n", 5, 17}, + {"cc,nn", "C4+cc n n", 5, 17}, + {"cc,nn", "C4+cc n n", 3, 10}, + + /* SPECASM_DOC_CCF_FORMS */ + {"", "3F", 1, 4}, + + /* SPECASM_DOC_CP_FORMS */ + {"r", "B8+r", 1, 4}, + {"n", "FE n", 2, 7}, + {"(hl)", "BE", 2, 7}, + {"(ix+d)", "DD BE d", 5, 19 }, + {"(iy+d)", "FD BE d", 5, 19 }, + + /* SPECASM_DOC_CPD_FORMS */ + {"", "ED A9", 4, 16}, + + /* SPECASM_DOC_CPDR_FORMS */ + {"", "ED B9", 4, 16}, + {"", "ED B9", 5, 21}, + + /* SPECASM_DOC_CPI_FORMS */ + {"", "ED A1", 4, 16}, + + /* SPECASM_DOC_CPIR_FORMS */ + {"", "ED B1", 4, 16}, + {"", "ED B1", 5, 21}, + + /* SPECASM_DOC_CPL_FORMS */ + {"", "2F", 1, 4}, + + /* SPECASM_DOC_DAA_FORMS */ + {"", "27", 1, 4}, + + /* SPECASM_DOC_DB_FORMS */ + {"n", "n", 0, 0}, + {"n,n", "n n", 0, 0}, + {"n,n,n", "n n n", 0, 0}, + {"n,n,n,n", "n n n n", 0, 0}, + + /* SPECASM_DOC_DEC1_FORMS */ + {"r", "5+r", 1, 4}, + {"(hl)", "35", 3, 11}, + {"(ix+d)", "DD 35 d", 6, 23 }, + {"(iy+d)", "FD 35 d", 6, 23 }, + + /* SPECASM_DOC_DEC2_FORMS */ + {"rr", "B+rr", 1, 6}, + {"ix", "DD 2B", 2, 10 }, + {"iy", "FD 2B", 2, 10 }, + + /* SPECASM_DOC_DI_FORMS */ + {"", "F3", 1, 4}, + + /* SPECASM_DOC_DJNZ_FORMS */ + {"n", "10 n", 2, 8}, + {"n", "10 n", 3, 13}, + + /* SPECASM_DOC_DS_FORMS */ + {"c n", "n c times", 0, 0}, + + /* SPECASM_DOC_DW_FORMS */ + {"nn", "nn", 0, 0}, + {"nn,nn", "nn nn", 0, 0}, + + /* SPECASM_DOC_EI_FORMS */ + {"", "FB", 1, 4}, + + /* SPECASM_DOC_EX_FORMS */ + {"af, af'", "08", 1, 4}, + {"de, hl", "EB", 1, 4}, + {"sp, (hl)", "E3", 5, 19}, + {"(sp), ix", "DD E3", 6, 23}, + {"(sp), iy", "FD E3", 6, 23}, + + /* SPECASM_DOC_EXX_FORMS */ + {"", "D9", 1, 4}, + + /* SPECASM_DOC_HALT_FORMS */ + {"", "76", 1, 4}, + + /* SPECASM_DOC_IM_FORMS */ + {"0", "ED 46", 2, 8}, + {"1", "ED 56", 2, 8}, + {"2", "ED 5E", 2, 8}, + + /* SPECASM_DOC_IN1_FORMS */ + {"r, (c)", "ED 40+r", 3, 12}, + + /* SPECASM_DOC_IN2_FORMS */ + {"a, (n)", "DB n", 3, 11}, + + /* SPECASM_DOC_INC1_FORMS */ + {"r", "4+r", 1, 4}, + {"(hl)", "34", 3, 11}, + {"(ix+d)", "DD 34 d", 6, 23 }, + {"(iy+d)", "FD 34 d", 6, 23 }, + + /* SPECASM_DOC_INC2_FORMS */ + {"rr", "3+rr", 1, 6}, + {"ix", "DD 23", 2, 10 }, + {"iy", "FD 23", 2, 10 }, + + /* SPECASM_DOC_IND_FORMS */ + {"", "ED AA", 4, 16}, + + /* SPECASM_DOC_INDR_FORMS */ + {"", "ED BA", 4, 16}, + {"", "ED BA", 5, 21}, + + /* SPECASM_DOC_INI_FORMS */ + {"", "ED A2", 4, 16}, + + /* SPECASM_DOC_INIR_FORMS */ + {"", "ED B2", 4, 16}, + {"", "ED B2", 5, 21}, + + /* SPECASM_DOC_JP_FORMS */ + {"nn", "C3 n n", 3, 10}, + {"(hl)", "E9", 1, 4}, + {"(ix)", "DD E9", 2, 8}, + {"(iy)", "FD E9", 2, 8}, + {"cc,nn", "C2+cc n n", 3, 12}, + {"cc,nn", "C2+cc n n", 2, 7}, + + /* SPECASM_DOC_LDD_FORMS */ + {"", "ED A8", 4, 16}, + + /* SPECASM_DOC_LDDR_FORMS */ + {"", "ED B8", 4, 16}, + {"", "ED B8", 5, 21}, + + /* SPECASM_DOC_LDI_FORMS */ + {"", "ED A0", 4, 16}, + + /* SPECASM_DOC_LDIR_FORMS */ + {"", "ED B0", 4, 16}, + {"", "ED B0", 5, 21}, + + /* SPECASM_DOC_MAP_FORMS */ + {"", "", 0, 0}, + + /* SPECASM_DOC_NEG_FORMS */ + {"", "ED 44", 2, 8}, + + /* SPECASM_DOC_NOP_FORMS */ + {"", "0", 1, 4}, + + /* SPECASM_DOC_OR_FORMS */ + {"r", "B8+r", 1, 4}, + {"n", "F6 n", 2, 7}, + {"(hl)", "B6", 2, 7}, + {"(ix+d)", "DD B6 d", 5, 19 }, + {"(iy+d)", "FD B6 d", 5, 19 }, + + /* SPECASM_DOC_ORG_FORMS */ + {"nn","", 0, 0}, + + /* SPECASM_DOC_OUT1_FORMS */ + {"(c), r", "ED 41+r", 3, 12}, + + /* SPECASM_DOC_OUT2_FORMS */ + {"(n), a", "D3 n", 3, 11}, + + /* SPECASM_DOC_OUTD_FORMS */ + {"","ED AB", 4, 16}, + + /* SPECASM_DOC_OUTDR_FORMS */ + {"","ED BB", 4, 16}, + {"","ED BB", 5, 21}, + + /* SPECASM_DOC_OUTI_FORMS */ + {"","ED A3", 4, 16}, + + /* SPECASM_DOC_OUTIR_FORMS */ + {"","ED B3", 4, 16}, + {"","ED B3", 5, 21}, + + /* SPECASM_DOC_POP_FORMS */ + {"rr", "C1+rr", 3, 10}, + {"ix", "DD E1", 4, 14}, + {"iy", "FD E1", 4, 14}, + + /* SPECASM_DOC_PUSH_FORMS */ + {"rr", "C5+rr", 3, 11}, + {"ix", "DD E5", 4, 15}, + {"iy", "FD E5", 4, 15}, + + /* SPECASM_DOC_RES_FORMS */ + {"n,r", "CB 80+b+r", 2, 8}, + {"n,(hl)", "CB 86+b", 4, 15}, + {"n,(ix+d)", "DDCBd86+b", 6, 23}, + {"n,(iy+d)", "FDCBd86+b", 6, 23}, + + /* SPECASM_DOC_RET_FORMS */ + {"", "C9", 3, 10}, + {"cc", "C0+cc", 1, 5}, + {"cc", "C0+cc", 3, 10}, + + /* SPECASM_DOC_RETI_FORMS */ + {"", "ED 4D", 4, 14}, + + /* SPECASM_DOC_RETN_FORMS */ + {"", "ED 45", 4, 14}, + + /* SPECASM_DOC_RL_FORMS */ + {"r", "CB 10+r", 2, 8}, + {"(hl)", "CB 16", 4, 15}, + {"(ix+d)", "DD CB d 16", 6, 23 }, + {"(iy+d)", "FD CB d 16", 6, 23 }, + + /* SPECASM_DOC_RLA_FORMS */ + {"", "17", 1, 4}, + + /* SPECASM_DOC_RLC_FORMS */ + {"r", "CB r", 2, 8}, + {"(hl)", "CB 06", 4, 15}, + {"(ix+d)", "DD CB d 06", 6, 23 }, + {"(iy+d)", "FD CB d 06", 6, 23 }, + + /* SPECASM_DOC_RLCA_FORMS */ + {"", "07", 1, 4}, + + /* SPECASM_DOC_RLD_FORMS */ + {"", "ED 6F", 5, 18}, + + /* SPECASM_DOC_RR_FORMS */ + {"r", "CB 18+r", 2, 8}, + {"(hl)", "CB 1E", 4, 15}, + {"(ix+d)", "DD CB d 1E", 6, 23 }, + {"(iy+d)", "FD CB d 1E", 6, 23 }, + + /* SPECASM_DOC_RRA_FORMS */ + {"", "1F", 1, 4}, + + /* SPECASM_DOC_RRC_FORMS */ + {"r", "CB 8+r", 2, 8}, + {"(hl)", "CB 0E", 4, 15}, + {"(ix+d)", "DD CB d 0E", 6, 23 }, + {"(iy+d)", "FD CB d 0E", 6, 23 }, + + /* SPECASM_DOC_RRCA_FORMS */ + {"", "0F", 1, 4}, + + /* SPECASM_DOC_RRD_FORMS */ + {"", "ED 67", 5, 18}, + + /* SPECASM_DOC_RST_FORMS */ + {"n", "C3+n", 3, 11}, + + /* SPECASM_DOC_SBC_FORMS */ + {"a,r", "98+r", 1, 4}, + {"a,n", "DE n", 2, 7}, + {"a,(hl)", "9E", 2, 7}, + {"a,(ix+d)", "DD 9E d", 5, 19 }, + {"a,(iy+d)", "FD 9E d", 5, 19 }, + {"hl,rr", "ED 42+rr", 4, 15 }, + + /* SPECASM_DOC_SCF_FORMS */ + {"", "37", 1, 4}, + + /* SPECASM_DOC_SET_FORMS */ + {"n,r", "CB C0+b+r", 2, 8}, + {"n,(hl)", "CB C6+b", 4, 15}, + {"n,(ix+d)", "DDCBdC6+b", 6, 23}, + {"n,(iy+d)", "FDCBdC6+b", 6, 23}, + + /* SPECASM_DOC_SLA_FORMS */ + {"r", "CB 20+r", 2, 8}, + {"(hl)", "CB 26", 4, 15}, + {"(ix+d)", "DD CB d 26", 6, 23 }, + {"(iy+d)", "FD CB d 26", 6, 23 }, + + /* SPECASM_DOC_SRA_FORMS */ + {"r", "CB 28+r", 2, 8}, + {"(hl)", "CB 2E", 4, 15}, + {"(ix+d)", "DD CB d 2E", 6, 23 }, + {"(iy+d)", "FD CB d 2E", 6, 23 }, + + /* SPECASM_DOC_SRL_FORMS */ + {"r", "CB 38+r", 2, 8}, + {"(hl)", "CB 3E", 4, 15}, + {"(ix+d)", "DD CB d 3E", 6, 23 }, + {"(iy+d)", "FD CB d 3E", 6, 23 }, + + /* SPECASM_DOC_SUB_FORMS */ + {"r", "A0+r", 1, 4}, + {"n", "D6 n", 2, 7}, + {"(hl)", "96", 2, 7}, + {"(ix+d)", "DD 96 d", 5, 19 }, + {"(iy+d)", "FD 96 d", 5, 19 }, + + /* SPECASM_DOC_XOR_FORMS */ + {"r", "A8+r", 1, 4}, + {"n", "EE n", 2, 7}, + {"(hl)", "AE", 2, 7}, + {"(ix+d)", "DD AE d", 5, 19 }, + {"(iy+d)", "FD AE d", 5, 19 }, +}; + #define SPECASM_DOC_MAX_REG_ENCODING (SPECASM_BYTE_REG_IY+1) struct specasm_ins_doc_t_ { char name[8]; - specasm_ins_form_t forms[6]; + uint8_t forms; + uint8_t num_forms; uint8_t reg_encoding; uint8_t bits; uint8_t all_cc; @@ -87,7 +665,7 @@ const uint8_t reg_encodings[][SPECASM_DOC_MAX_REG_ENCODING] = { const static char specasm_doc_add_16[] = "The second argument is added to the contents of the " "destination register. The carry flag is set according " - "to the result of the addition. H represents carry from " + "to the result of the addition. h represents carry from " "bit 11."; const static char specasm_doc_inc[] = @@ -96,30 +674,19 @@ const static char specasm_doc_inc[] = const static specasm_ins_doc_t docs[] = { { "adc", - { - {"a,r", "88+r", 1, 4}, - {"a,n", "CE n", 2, 7}, - {"a,(hl)", "8E", 2, 7}, - {"a,(ix + d)", "DD 8E d", 5, 19 }, - {"a,(iy + d)", "FD 8E d", 5, 19 }, - {"hl,rr", "ED 4A+rr", 4, 15 }, - }, + SPECASM_DOC_ADC_FORMS, + SPECASM_DOC_ADC_NUM_FORMS, 1, 0, 0, "The second argument and the carry flag are added to the " - "contents of the destination register, either a or hl.", + "contents of the destination register.", "XX X X0X", }, { "add", - { - {"a,r", "80+r", 1, 4}, - {"a,n", "C6 n", 2, 7}, - {"a,(hl)", "86", 2, 7}, - {"a,(ix + d)", "DD 86 d", 5, 19 }, - {"a,(iy + d)", "FD 86 d", 5, 19 }, - }, + SPECASM_DOC_ADD1_FORMS, + SPECASM_DOC_ADD1_NUM_FORMS, 2, 0, 0, @@ -129,9 +696,8 @@ const static specasm_ins_doc_t docs[] = { }, { "add", - { - {"hl,rr", "9+rr", 3, 11 }, - }, + SPECASM_DOC_ADD2_FORMS, + SPECASM_DOC_ADD2_NUM_FORMS, 3, 0, 0, @@ -140,9 +706,8 @@ const static specasm_ins_doc_t docs[] = { }, { "add", - { - {"ix,rr", "DD 9+rr", 4, 15 }, - }, + SPECASM_DOC_ADD3_FORMS, + SPECASM_DOC_ADD3_NUM_FORMS, 4, 0, 0, @@ -151,9 +716,8 @@ const static specasm_ins_doc_t docs[] = { }, { "add", - { - {"iy,rr", "FD 9+rr", 4, 15 }, - }, + SPECASM_DOC_ADD4_FORMS, + SPECASM_DOC_ADD4_NUM_FORMS, 5, 0, 0, @@ -162,44 +726,33 @@ const static specasm_ins_doc_t docs[] = { }, { "align", - { - {"n", "0", 1, 4 }, - }, + SPECASM_DOC_ALIGN_FORMS, + SPECASM_DOC_ALIGN_NUM_FORMS, 0, 0, 0, "The align directive takes one immediate argument that must be " - "a power of 2, greater than or equal to 2 and less and or equal" - " to 256. It inserts null bytes into the binary until the " - "requested alignment is achieved. The number of t-states " - "consumed by an align directive is the number of bytes inserted" - " * 4", + "a power of 2, >= 2 and <= 256. It inserts null bytes into " + "the binary until the requested alignment is achieved. The " + "number of t-states consumed by an align directive is the " + "number of bytes inserted * 4.", NULL, }, { "and", - { - {"r", "A0+r", 1, 4}, - {"n", "E6 n", 2, 7}, - {"(hl)", "A6", 2, 7}, - {"(ix + d)", "DD A6 d", 5, 19 }, - {"(iy + d)", "FD A6 d", 5, 19 }, - }, + SPECASM_DOC_AND_FORMS, + SPECASM_DOC_AND_NUM_FORMS, 2, 0, 0, - "The result of a bitwise AND of the accumulator and the " - "argument is stored in the accumulator.", + "The result of a bitwise AND of a and the " + "argument is stored in a.", "XX 1 X00", }, { "bit", - { - {"n,r", "CB 40+b+r", 2, 8}, - {"n,(HL)", "CB 46+b", 3, 12}, - {"n,(IX + d)", "DDCBd46+b", 5, 20}, - {"n,(IY + d)", "FDCBd46+b", 5, 20}, - }, + SPECASM_DOC_BIT_FORMS, + SPECASM_DOC_BIT_NUM_FORMS, 2, 8, 0, @@ -209,11 +762,8 @@ const static specasm_ins_doc_t docs[] = { }, { "call", - { - {"nn", "CD n n", 5, 17}, - {"cc,nn", "C4+cc n n", 5, 17}, - {"cc,nn", "C4+cc n n", 3, 10}, - }, + SPECASM_DOC_CALL_FORMS, + SPECASM_DOC_CALL_NUM_FORMS, 0, 0, 8, @@ -225,130 +775,106 @@ const static specasm_ins_doc_t docs[] = { }, { "ccf", - { - {"", "3F", 1, 4}, - }, + SPECASM_DOC_CCF_FORMS, + SPECASM_DOC_CCF_NUM_FORMS, 0, 0, 0, - "Inverts the carry flag, setting it to 1 if it were previously " - "0 and 1 if it were previously 0.", + "Inverts the carry flag.", " ? 0X" }, { "cp", - { - {"r", "B8+r", 1, 4}, - {"n", "FE n", 2, 7}, - {"(hl)", "BE", 2, 7}, - {"(ix + d)", "DD BE d", 5, 19 }, - {"(iy + d)", "FD BE d", 5, 19 }, - }, + SPECASM_DOC_CP_FORMS, + SPECASM_DOC_CP_NUM_FORMS, 2, 0, 0, - "The operand is subtracted from the accumulator setting the " + "The operand is subtracted from a setting the " "flags accordingly. The result of the subtraction is " "discarded.", "XX X X1X", }, { "cpd", - { - {"", "ED A9", 4, 16}, - }, + SPECASM_DOC_CPD_FORMS, + SPECASM_DOC_CPD_NUM_FORMS, 0, 0, 0, - "The byte pointed to by the address in hl is subtracted from " - "the accumulator and the flags are set accordingly. The result " - "is discarded. bc and hl are decremented. The p flag is set " - "if bc != 0 after the instruction has finished and is " - "otherwise reset.", + "(hl) is subtracted from a and the flags are set " + "accordingly. The result is discarded. bc and hl are " + "decremented. The p flag is set if bc != 0 after the " + "instruction has finished and is otherwise reset.", "XX X X1 ", }, { "cpdr", - { - {"", "ED B9", 4, 16}, - {"", "ED B9", 5, 21}, - }, + SPECASM_DOC_CPDR_FORMS, + SPECASM_DOC_CPDR_NUM_FORMS, 0, 0, 0, - "The byte pointed to by the address in hl is subtracted from " - "the accumulator and the flags are set accordingly. The result " - "is discarded. bc and hl are decremented. If bc > 0 and the " - "result of the subtraction is != 0 the instructon repeats. " - "The p flag is set if bc != 0 after the instruction has " + "(hl) is subtracted from a and the flags are set accordingly. " + "The result is discarded. bc and hl are decremented. If bc>0 " + "and the result of the subtraction is != 0 cpdr " + "repeats. The p flag is set if bc!=0 after cpdr has " "finished and is otherwise reset. The slower timings apply " - "when the instruction repeats.", + "when cpdr repeats.", "XX X X1 ", }, { "cpi", - { - {"", "ED A1", 4, 16}, - }, + SPECASM_DOC_CPI_FORMS, + SPECASM_DOC_CPI_NUM_FORMS, 0, 0, 0, - "The byte pointed to by the address in hl is subtracted from " - "the accumulator and the flags are set accordingly. The result " - "is discarded. bc is decremented while hl is incremented. The " - "p flag is set if bc != 0 after the instruction has finished " - "and is otherwise reset.", + "(hl) is subtracted from a and the flags are set accordingly. " + "The result is discarded. bc is decremented while hl is " + "incremented. The p flag is set if bc != 0 after the cpi has " + "finished and is otherwise reset.", "XX X X1 ", }, { "cpir", - { - {"", "ED B1", 4, 16}, - {"", "ED B1", 5, 21}, - }, + SPECASM_DOC_CPIR_FORMS, + SPECASM_DOC_CPIR_NUM_FORMS, 0, 0, 0, - "The byte pointed to by the address in hl is subtracted from " - "the accumulator and the flags are set accordingly. The result " - "is discarded. bc is decremented while hl is incremented. If " - "bc > 0 and the result of the subtraction is != 0 the " - "instructon repeats. The p flag is set if bc != 0 after the " - "instruction has finished and is otherwise reset. The slower " - "timings apply when the instruction repeats.", + "(hl) is subtracted from a and the flags are set accordingly. " + "The result is discarded. bc is decremented while hl is " + "incremented. If bc>0 and the result of the subtraction is != 0" + " cpir repeats. The p flag is set if bc!=0 after cpir " + "has finished and is otherwise reset. The slower " + "timings apply when the cpir repeats.", "XX X X1 ", }, { "cpl", - { - {"", "2F", 1, 4}, - }, + SPECASM_DOC_CPL_FORMS, + SPECASM_DOC_CPL_NUM_FORMS, 0, 0, 0, - "Invert the contents of the accumlator.", + "Invert a.", " 1 1 ", }, { "daa", - { - {"", "27", 1, 4}, - }, + SPECASM_DOC_DAA_FORMS, + SPECASM_DOC_DAA_NUM_FORMS, 0, 0, 0, - "Conditionally adjusts the accumulator for BCD addition " - "and subtraction.", + "Conditionally adjusts a for BCD addition and subtraction.", "XX X X X", }, { "db", - { - {"n", "n", 0, 0}, - {"n,n", "n n", 0, 0}, - {"n,n,n", "n n n", 0, 0}, - {"n,n,n,n", "n n n n", 0, 0}, - }, + SPECASM_DOC_DB_FORMS, + SPECASM_DOC_DB_NUM_FORMS, 0, 0, 0, @@ -359,12 +885,8 @@ const static specasm_ins_doc_t docs[] = { }, { "dec", - { - {"r", "5+r", 1, 4}, - {"(hl)", "35", 3, 11}, - {"(ix + d)", "DD 35 d", 6, 23 }, - {"(iy + d)", "FD 35 d", 6, 23 }, - }, + SPECASM_DOC_DEC1_FORMS, + SPECASM_DOC_DEC1_NUM_FORMS, 6, 0, 0, @@ -373,11 +895,8 @@ const static specasm_ins_doc_t docs[] = { }, { "dec", - { - {"rr", "B+rr", 1, 6}, - {"ix", "DD 2B", 2, 10 }, - {"iy", "FD 2B", 2, 10 }, - }, + SPECASM_DOC_DEC2_FORMS, + SPECASM_DOC_DEC2_NUM_FORMS, 3, 0, 0, @@ -386,9 +905,8 @@ const static specasm_ins_doc_t docs[] = { }, { "di", - { - {"", "F3", 1, 4}, - }, + SPECASM_DOC_DI_FORMS, + SPECASM_DOC_DI_NUM_FORMS, 0, 0, 0, @@ -397,23 +915,20 @@ const static specasm_ins_doc_t docs[] = { }, { "djnz", - { - {"n", "10 n", 2, 8}, - {"n", "10 n", 3, 13}, - }, + SPECASM_DOC_DJNZ_FORMS, + SPECASM_DOC_DJNZ_NUM_FORMS, 0, 0, 0, - "The b register is decremented by 1. If the result is > 0 " - "the cpu jumps to PC + 2 + n, where n is a signed 8 byte. The " - "instruction executes more quickly when the jump it not taken.", + "b is decremented by 1. If the result is>0 the cpu jumps to " + "PC+2+n, where n is a signed 8 byte. djnz executes " + "more quickly when the jump it not taken.", NULL, }, { "ds", - { - {"c n", "n c times", 0, 0}, - }, + SPECASM_DOC_DS_FORMS, + SPECASM_DOC_DS_NUM_FORMS, 0, 0, 0, @@ -422,10 +937,8 @@ const static specasm_ins_doc_t docs[] = { }, { "dw", - { - {"nn", "nn", 0, 0}, - {"nn,nn", "nn nn", 0, 0}, - }, + SPECASM_DOC_DW_FORMS, + SPECASM_DOC_DW_NUM_FORMS, 0, 0, 0, @@ -436,9 +949,8 @@ const static specasm_ins_doc_t docs[] = { }, { "ei", - { - {"", "FB", 1, 4}, - }, + SPECASM_DOC_EI_FORMS, + SPECASM_DOC_EI_NUM_FORMS, 0, 0, 0, @@ -447,13 +959,8 @@ const static specasm_ins_doc_t docs[] = { }, { "ex", - { - {"af, af'", "08", 1, 4}, - {"de, hl", "EB", 1, 4}, - {"sp, (hl)", "E3", 5, 19}, - {"(sp), ix", "DD E3", 6, 23}, - {"(sp), iy", "FD E3", 6, 23}, - }, + SPECASM_DOC_EX_FORMS, + SPECASM_DOC_EX_NUM_FORMS, 0, 0, 0, @@ -464,20 +971,18 @@ const static specasm_ins_doc_t docs[] = { }, { "exx", - { - {"", "D9", 1, 4}, - }, + SPECASM_DOC_EXX_FORMS, + SPECASM_DOC_EXX_NUM_FORMS, 0, 0, 0, - "Exchange BC, DE and HL with BC', DE', HL'.", + "Exchange bc, de and hl with bc', de', hl'.", NULL, }, { "halt", - { - {"", "76", 1, 4}, - }, + SPECASM_DOC_HALT_FORMS, + SPECASM_DOC_HALT_NUM_FORMS, 0, 0, 0, @@ -486,51 +991,42 @@ const static specasm_ins_doc_t docs[] = { }, { "im", - { - {"0", "ED 46", 2, 8}, - {"1", "ED 56", 2, 8}, - {"2", "ED 5E", 2, 8}, - }, + SPECASM_DOC_IM_FORMS, + SPECASM_DOC_IM_NUM_FORMS, 0, 0, 0, - "Sets the interrupt mode. With IM 2 the MSB of the vector " - "address is taken from the I register.", + "Sets the interrupt mode. With im 2 the MSB of the vector " + "address is taken from the i register.", NULL, }, { "in", - { - {"r, (c)", "ED 40+r", 3, 12}, - }, + SPECASM_DOC_IN1_FORMS, + SPECASM_DOC_IN1_NUM_FORMS, 6, 0, 0, - "Reads a byte from the device at the adddress stored in the bc " - "register and stores it in r.", + "Reads a byte from the device identified by bc and stores it " + "in r.", "XX X X0 ", }, { "in", - { - {"a, (n)", "DB n", 3, 11}, - }, + SPECASM_DOC_IN2_FORMS, + SPECASM_DOC_IN2_NUM_FORMS, 0, 0, 0, - "Reads a byte from the device at the adddress whose MSB is " - "taken from the accumulator and whose LSB is n. The byte is " - "stored in the accumulator.", + "Reads a byte from the device adddress whose MSB is " + "taken from a and whose LSB is n. The byte is " + "stored in a.", NULL, }, { "inc", - { - {"r", "4+r", 1, 4}, - {"(hl)", "34", 3, 11}, - {"(ix + d)", "DD 34 d", 6, 23 }, - {"(iy + d)", "FD 34 d", 6, 23 }, - }, + SPECASM_DOC_INC1_FORMS, + SPECASM_DOC_INC1_NUM_FORMS, 6, 0, 0, @@ -539,11 +1035,8 @@ const static specasm_ins_doc_t docs[] = { }, { "inc", - { - {"rr", "3+rr", 1, 6}, - {"ix", "DD 23", 2, 10 }, - {"iy", "FD 23", 2, 10 }, - }, + SPECASM_DOC_INC2_FORMS, + SPECASM_DOC_INC2_NUM_FORMS, 3, 0, 0, @@ -551,69 +1044,57 @@ const static specasm_ins_doc_t docs[] = { NULL, }, { - "ldd", - { - {"", "ED A8", 4, 16}, - }, + "ind", + SPECASM_DOC_IND_FORMS, + SPECASM_DOC_IND_NUM_FORMS, 0, 0, 0, - "The byte pointed to by hl is loaded into the address in de. " - "hl, de and bc are decremented.", - " 0 X0 ", + "A byte is read from the device identified by bc and stored " + "in (hl). b and hl are decremented.", + "?X ? ?1 ", }, { - "lddr", - { - {"", "ED B8", 4, 16}, - {"", "ED B8", 5, 21}, - }, + "indr", + SPECASM_DOC_INDR_FORMS, + SPECASM_DOC_INDR_NUM_FORMS, 0, 0, 0, - "The byte pointed to by hl is loaded into the address in de. " - "hl, de and bc are decremented. If bc!=0 the instruction " - "repeats. The instruction consumes more t-states when it " + "A byte is read from the device identified by bc and stored " + "(hl). b and hl are decremented. If bc!=0 indr " + "repeats. indr consumes more t-states when it " "repeats.", - " 0 00 ", + "?1 ? ?1 " }, { - "ldi", - { - {"", "ED A0", 4, 16}, - }, + "ini", + SPECASM_DOC_INI_FORMS, + SPECASM_DOC_INI_NUM_FORMS, 0, 0, 0, - "The byte pointed to by hl is loaded into the address in de. " - "Both hl and de are incremented while bc is decremented.", - " 0 X0 ", + "A byte is read from the device identified by bc " + "and stored in (hl). b is decremented and h is incremented.", + "?X ? ?1 ", }, { - "ldir", - { - {"", "ED B0", 4, 16}, - {"", "ED B0", 5, 21}, - }, + "inir", + SPECASM_DOC_INIR_FORMS, + SPECASM_DOC_INIR_NUM_FORMS, 0, 0, 0, - "The byte pointed to by hl is loaded into the address in de. " - "Both hl and de are incremented while bc is decremented. If " - "bc!=0 the instruction repeats. The instruction consumes more " - "t-states when it repeats.", - " 0 00 ", + "A byte is read from the device identified by bc " + "and stored in (hl). b is decremented and h is incremented. " + "If bc!=0 inir repeats. inir consumes more t-states when it " + "repeats.", + "?1 ? ?1 " }, { "jp", - { - {"nn", "C3 n n", 3, 10}, - {"(hl)", "E9", 1, 4}, - {"(ix)", "DD E9", 2, 8}, - {"(iy)", "FD E9", 2, 8}, - {"cc,nn", "C2+cc n n", 3, 12}, - {"cc,nn", "C2+cc n n", 2, 7}, - }, + SPECASM_DOC_JP_FORMS, + SPECASM_DOC_JP_NUM_FORMS, 0, 0, 8, @@ -622,11 +1103,55 @@ const static specasm_ins_doc_t docs[] = { "if condition is not met.", NULL, }, + { + "ldd", + SPECASM_DOC_LDD_FORMS, + SPECASM_DOC_LDD_NUM_FORMS, + 0, + 0, + 0, + "Load (hl) into (de). hl, de and bc are decremented.", + " 0 X0 ", + }, + { + "lddr", + SPECASM_DOC_LDDR_FORMS, + SPECASM_DOC_LDDR_NUM_FORMS, + 0, + 0, + 0, + "Load (hl) into (de). hl, de and bc are decremented. If " + "bc!=0 lddr repeats. lddr consumes more t-states when it " + "repeats.", + " 0 00 ", + }, + { + "ldi", + SPECASM_DOC_LDI_FORMS, + SPECASM_DOC_LDI_NUM_FORMS, + 0, + 0, + 0, + "Load (hl) into (de). Both hl and de are incremented while bc " + "is decremented.", + " 0 X0 ", + }, + { + "ldir", + SPECASM_DOC_LDIR_FORMS, + SPECASM_DOC_LDIR_NUM_FORMS, + 0, + 0, + 0, + "Load (hl) into (de). Both hl and de are incremented while bc " + "is decremented. If bc!=0 ldir repeats. ldir " + "consumes more t-states when it repeats.", + " 0 00 ", + }, { "map", - { - {"", "", 0, 0}, - }, + SPECASM_DOC_MAP_FORMS, + SPECASM_DOC_MAP_NUM_FORMS, 0, 0, 0, @@ -635,9 +1160,8 @@ const static specasm_ins_doc_t docs[] = { }, { "neg", - { - {"", "ED 44", 2, 8}, - }, + SPECASM_DOC_NEG_FORMS, + SPECASM_DOC_NEG_NUM_FORMS, 0, 0, 0, @@ -646,9 +1170,8 @@ const static specasm_ins_doc_t docs[] = { }, { "nop", - { - {"", "0", 1, 4}, - }, + SPECASM_DOC_NOP_FORMS, + SPECASM_DOC_NOP_NUM_FORMS, 0, 0, 0, @@ -657,25 +1180,19 @@ const static specasm_ins_doc_t docs[] = { }, { "or", - { - {"r", "B8+r", 1, 4}, - {"n", "F6 n", 2, 7}, - {"(hl)", "B6", 2, 7}, - {"(ix + d)", "DD B6 d", 5, 19 }, - {"(iy + d)", "FD B6 d", 5, 19 }, - }, + SPECASM_DOC_OR_FORMS, + SPECASM_DOC_OR_NUM_FORMS, 2, 0, 0, - "The result of a bitwise OR of the accumulator and the " - "argument is stored in the accumulator.", + "The result of a bitwise OR of a and the argument is stored " + "in a.", "XX 0 X00", }, { "org", - { - {"nn","", 0, 0}, - }, + SPECASM_DOC_ORG_FORMS, + SPECASM_DOC_ORG_NUM_FORMS, 0, 0, 0, @@ -686,91 +1203,78 @@ const static specasm_ins_doc_t docs[] = { }, { "out", - { - {"(c), r", "ED 41+r", 3, 12}, - }, + SPECASM_DOC_OUT1_FORMS, + SPECASM_DOC_OUT1_NUM_FORMS, 6, 0, 0, - "Writes the register r to the device at the adddress stored " - "in the bc register.", + "Writes the register r to the device identified by bc.", NULL, }, { - "outdr", - { - {"","ED BB", 4, 16}, - {"","ED BB", 5, 21}, - }, + "out", + SPECASM_DOC_OUT2_FORMS, + SPECASM_DOC_OUT2_NUM_FORMS, 0, 0, 0, - "b is decremented. The byte at the address in hl is written to " - "the port given by the address in bc, where b has already been " - "decremented. hl is decremented. The instruction repeats if " - "b!=0. The instruction consumes more t-states when it repeats.", - "?1 ? ?1 ", + "Writes a to the device at the adddress whose MSB " + "is taken from the a and whose LSB is n.", + NULL, }, { - "outir", - { - {"","ED B3", 4, 16}, - {"","ED B3", 5, 21}, - }, + "outd", + SPECASM_DOC_OUTD_FORMS, + SPECASM_DOC_OUTD_NUM_FORMS, 0, 0, 0, - "b is decremented. The byte at the address in hl is written to " - "the port given by the address in bc, where b has already been " - "decremented. hl is incremented. The instruction repeats if " - "b!=0. The instruction consumes more t-states when it repeats.", - "?1 ? ?1 ", + "b is decremented. (hl) is written to the device identified " + "by bc, where b has already been decremented. hl is " + "decremented.", + "?X ? ?1 ", }, { - "out", - { - {"(n), a", "D3 n", 3, 11}, - }, + "outdr", + SPECASM_DOC_OUTDR_FORMS, + SPECASM_DOC_OUTDR_NUM_FORMS, 0, 0, 0, - "Writes the accumulator to the device at the adddress whose MSB" - " is taken from the accumulator and whose LSB is n.", - NULL, + "b is decremented. (hl) is written to the device identified " + "by bc, where b has already been decremented. hl is " + "decremented. outdr repeats if b!=0. It consumes more t-states " + "when it repeats.", + "?1 ? ?1 ", }, { - "outd", - { - {"","ED AB", 4, 16}, - }, + "outi", + SPECASM_DOC_OUTI_FORMS, + SPECASM_DOC_OUTI_NUM_FORMS, 0, 0, 0, - "b is decremented. The byte at the address in hl is written to " - "the port given by the address in bc, where b has already been " - "decremented. hl is decremented.", + "b is decremented. (hl) is written to the device identified by " + "bc, where b has already been decremented. hl is incremented.", "?X ? ?1 ", }, { - "outi", - { - {"","ED A3", 4, 16}, - }, + "outir", + SPECASM_DOC_OUTIR_FORMS, + SPECASM_DOC_OUTIR_NUM_FORMS, 0, 0, 0, - "b is decremented. The byte at the address in hl is written to " - "the port given by the address in bc, where b has already been " - "decremented. hl is incremented.", - "?X ? ?1 ", + "b is decremented. (hl) is written to the device identified by " + "bc, where b has already been decremented. hl is incremented. " + "outir repeats if b!=0. It consumes more t-states " + "when it repeats.", + "?1 ? ?1 ", }, { "pop", - { - {"rr", "C1+rr", 3, 10}, - {"ix", "DD E1", 4, 14}, - {"iy", "FD E1", 4, 14}, - }, + SPECASM_DOC_POP_FORMS, + SPECASM_DOC_POP_NUM_FORMS, 7, 0, 0, @@ -779,11 +1283,8 @@ const static specasm_ins_doc_t docs[] = { }, { "push", - { - {"rr", "C5+rr", 3, 11}, - {"ix", "DD E5", 4, 15}, - {"iy", "FD E5", 4, 15}, - }, + SPECASM_DOC_PUSH_FORMS, + SPECASM_DOC_PUSH_NUM_FORMS, 7, 0, 0, @@ -792,12 +1293,8 @@ const static specasm_ins_doc_t docs[] = { }, { "res", - { - {"n,r", "CB 80+b+r", 2, 8}, - {"n,(HL)", "CB 86+b", 4, 15}, - {"n,(IX + d)", "DDCBd86+b", 6, 23}, - {"n,(IY + d)", "FDCBd86+b", 6, 23}, - }, + SPECASM_DOC_RES_FORMS, + SPECASM_DOC_RES_NUM_FORMS, 2, 8, 0, @@ -806,54 +1303,45 @@ const static specasm_ins_doc_t docs[] = { }, { "ret", - { - {"", "C9", 3, 10}, - {"cc", "C0 + cc", 1, 5}, - {"cc", "C0 + cc", 3, 10}, - }, + SPECASM_DOC_RET_FORMS, + SPECASM_DOC_RET_NUM_FORMS, 0, 0, 8, - "If there is no condition or the condition is met value of " - "the stack is popped into the PC, from which program execution " - "continues. ret takes fewer cycles if the condition is not met" - ".", + "If there is no condition or the condition is met, a word " + "is popped off the stack into the PC, from which program " + "execution continues. ret takes fewer cycles if the condition " + "is not met.", NULL, }, { "reti", - { - {"", "ED 4D", 4, 14}, - }, + SPECASM_DOC_RETI_FORMS, + SPECASM_DOC_RETI_NUM_FORMS, 0, 0, 0, - "Return from interrupt. The stack is popped into PC. An ei " - "instruction must be executed prior to the reti to renable " + "Return from interrupt. A word is popped off the stack into PC." + " An ei must be executed prior to the reti to renable " "maskable interrupts.", NULL, }, { "retn", - { - {"", "ED 45", 4, 14}, - }, + SPECASM_DOC_RETN_FORMS, + SPECASM_DOC_RETN_NUM_FORMS, 0, 0, 0, - "Return from NMI. The stack is popped into PC and the " - "maskable interrupts are re-enabled if they were enabled " + "Return from NMI. A word is popped off the stack into PC and " + "the maskable interrupts are re-enabled if they were enabled " "before the NMI.", NULL, }, { "rl", - { - {"r", "CB 10+r", 2, 8}, - {"(hl)", "CB 16", 4, 15}, - {"(ix + d)", "DD CB d 16", 6, 23 }, - {"(iy + d)", "FD CB d 16", 6, 23 }, - }, + SPECASM_DOC_RL_FORMS, + SPECASM_DOC_RL_NUM_FORMS, 2, 0, 0, @@ -864,49 +1352,41 @@ const static specasm_ins_doc_t docs[] = { }, { "rla", - { - {"", "17", 1, 4}, - }, + SPECASM_DOC_RLA_FORMS, + SPECASM_DOC_RLA_NUM_FORMS, 0, 0, 0, - "The accumulator is rotated left 1 bit. The carry flag is " - "moved to bit 0 of the accumulator and its old bit 7 is moved " - "to the carry flag.", + "a is rotated left 1 bit. The carry flag is moved to bit 0 of " + "a and its old bit 7 is moved to the carry flag.", " 0 0X", }, { "rlc", - { - {"r", "CB r", 2, 8}, - {"(hl)", "CB 06", 4, 15}, - {"(ix + d)", "DD CB d 06", 6, 23 }, - {"(iy + d)", "FD CB d 06", 6, 23 }, - }, + SPECASM_DOC_RLC_FORMS, + SPECASM_DOC_RLC_NUM_FORMS, 2, 0, 0, "The operand is rotated left 1 bit. The old bit 7 is moved to " - "both the carry flag and bit 0.", + "both the carry flag and bit 0 of the operand.", "XX 0 X0X", }, { "rlca", - { - {"", "07", 1, 4}, - }, + SPECASM_DOC_RLCA_FORMS, + SPECASM_DOC_RLCA_NUM_FORMS, 0, 0, 0, - "The accumulator is rotated left 1 bit. The old bit 7 is moved " - "to both the carry flag and bit 0.", + "a is rotated left 1 bit. The old bit 7 is moved " + "to both the carry flag and bit 0 of a.", " 0 0X", }, { "rld", - { - {"", "ED 6F", 5, 18}, - }, + SPECASM_DOC_RLD_FORMS, + SPECASM_DOC_RLD_NUM_FORMS, 0, 0, 0, @@ -917,12 +1397,8 @@ const static specasm_ins_doc_t docs[] = { }, { "rr", - { - {"r", "CB 18+r", 2, 8}, - {"(hl)", "CB 1E", 4, 15}, - {"(ix + d)", "DD CB d 1E", 6, 23 }, - {"(iy + d)", "FD CB d 1E", 6, 23 }, - }, + SPECASM_DOC_RR_FORMS, + SPECASM_DOC_RR_NUM_FORMS, 2, 0, 0, @@ -933,49 +1409,42 @@ const static specasm_ins_doc_t docs[] = { }, { "rra", - { - {"", "1F", 1, 4}, - }, + SPECASM_DOC_RRA_FORMS, + SPECASM_DOC_RRA_NUM_FORMS, 0, 0, 0, - "The accumulator is shifted right 1 bit. The carry flag is " - "moved into bit 7 of the accumulator and the old contents of " - "the accumulator's bit 0 are moved into the carry flag." + "a is shifted right 1 bit. The carry flag is " + "moved into bit 7 of a and the old contents of a's " + "bit 0 are moved into the carry flag.", " 0 0X", }, { "rrc", - { - {"r", "CB 8+r", 2, 8}, - {"(hl)", "CB 0E", 4, 15}, - {"(ix + d)", "DD CB d 0E", 6, 23 }, - {"(iy + d)", "FD CB d 0E", 6, 23 }, - }, + SPECASM_DOC_RRC_FORMS, + SPECASM_DOC_RRC_NUM_FORMS, 2, 0, 0, - "The second argument and the carry flag are subtracted from " - "the contents of the destination register, either a or hl.", + "The operand is rotated right by 1 bit. Its old bit 0 is " + "moved into both the carry flag and bit 7 of the operand.", "XX 0 X0X", }, { "rrca", - { - {"", "0F", 1, 4}, - }, + SPECASM_DOC_RRCA_FORMS, + SPECASM_DOC_RRCA_NUM_FORMS, 0, 0, 0, - "The accumulator is rotated right by 1 bit. The old bit 0 is " - "moved into both the carry flag and bit 7 of the accumulator.", + "a is rotated right by 1 bit. a's old bit 0 is " + "moved into both the carry flag and bit 7 of a.", " 0 0X", }, { "rrd", - { - {"", "ED 67", 5, 18}, - }, + SPECASM_DOC_RRD_FORMS, + SPECASM_DOC_RRD_NUM_FORMS, 0, 0, 0, @@ -986,9 +1455,8 @@ const static specasm_ins_doc_t docs[] = { }, { "rst", - { - {"n", "C3+n", 3, 11}, - }, + SPECASM_DOC_RST_FORMS, + SPECASM_DOC_RST_NUM_FORMS, 0, 0, 0, @@ -999,14 +1467,8 @@ const static specasm_ins_doc_t docs[] = { }, { "sbc", - { - {"a,r", "98+r", 1, 4}, - {"a,n", "DE n", 2, 7}, - {"a,(hl)", "9E", 2, 7}, - {"a,(ix + d)", "DD 9E d", 5, 19 }, - {"a,(iy + d)", "FD 9E d", 5, 19 }, - {"hl,rr", "ED 42+rr", 4, 15 }, - }, + SPECASM_DOC_SBC_FORMS, + SPECASM_DOC_SBC_NUM_FORMS, 1, 0, 0, @@ -1016,9 +1478,8 @@ const static specasm_ins_doc_t docs[] = { }, { "scf", - { - {"", "37", 1, 4}, - }, + SPECASM_DOC_SCF_FORMS, + SPECASM_DOC_SCF_NUM_FORMS, 0, 0, 0, @@ -1027,12 +1488,8 @@ const static specasm_ins_doc_t docs[] = { }, { "set", - { - {"n,r", "CB C0+b+r", 2, 8}, - {"n,(HL)", "CB C6+b", 4, 15}, - {"n,(IX + d)", "DDCBdC6+b", 6, 23}, - {"n,(IY + d)", "FDCBdC6+b", 6, 23}, - }, + SPECASM_DOC_SET_FORMS, + SPECASM_DOC_SET_NUM_FORMS, 2, 8, 0, @@ -1041,12 +1498,8 @@ const static specasm_ins_doc_t docs[] = { }, { "sla", - { - {"r", "CB 20+r", 2, 8}, - {"(hl)", "CB 26", 4, 15}, - {"(ix + d)", "DD CB d 26", 6, 23 }, - {"(iy + d)", "FD CB d 26", 6, 23 }, - }, + SPECASM_DOC_SLA_FORMS, + SPECASM_DOC_SLA_NUM_FORMS, 2, 0, 0, @@ -1056,70 +1509,51 @@ const static specasm_ins_doc_t docs[] = { }, { "sra", - { - {"r", "CB 28+r", 2, 8}, - {"(hl)", "CB 2E", 4, 15}, - {"(ix + d)", "DD CB d 2E", 6, 23 }, - {"(iy + d)", "FD CB d 2E", 6, 23 }, - }, + SPECASM_DOC_SRA_FORMS, + SPECASM_DOC_SRA_NUM_FORMS, 2, 0, 0, "The operand is arithmetically shifted right by 1 bit. Bit 7 " - "is set to 0. Bit 0 is moved into the carry flag.", + "remains unchanged. Bit 0 is moved into the carry flag.", "XX 0 X0X", }, { "srl", - { - {"r", "CB 38+r", 2, 8}, - {"(hl)", "CB 3E", 4, 15}, - {"(ix + d)", "DD CB d 3E", 6, 23 }, - {"(iy + d)", "FD CB d 3E", 6, 23 }, - }, + SPECASM_DOC_SRL_FORMS, + SPECASM_DOC_SRL_NUM_FORMS, 2, 0, 0, "The operand is logically shifted right by 1 bit. Bit 7 is " - "unchanged. Bit 0 is moved into the carry flag.", + "set to 0. Bit 0 is moved into the carry flag.", "XX 0 X0X", }, { "sub", - { - {"r", "A0+r", 1, 4}, - {"n", "D6 n", 2, 7}, - {"(hl)", "96", 2, 7}, - {"(ix + d)", "DD 96 d", 5, 19 }, - {"(iy + d)", "FD 96 d", 5, 19 }, - }, + SPECASM_DOC_SUB_FORMS, + SPECASM_DOC_SUB_NUM_FORMS, 2, 0, 0, - "The operand is subracted from the accumlator.", + "The operand is subracted from a.", "XX X X1X", }, { "xor", - { - {"r", "A8+r", 1, 4}, - {"n", "EE n", 2, 7}, - {"(hl)", "AE", 2, 7}, - {"(ix + d)", "DD AE d", 5, 19 }, - {"(iy + d)", "FD AE d", 5, 19 }, - }, + SPECASM_DOC_XOR_FORMS, + SPECASM_DOC_XOR_NUM_FORMS, 2, 0, 0, - "The result of a bitwise XOR of the accumulator and the " - "argument is stored in the accumulator.", + "The result of a bitwise XOR of a and the argument is stored " + "in a.", "XX 0 X00", }, { "zx81", - { - {"", "", 0, 0 }, - }, + SPECASM_DOC_MAP_FORMS, + SPECASM_DOC_MAP_NUM_FORMS, 0, 0, 0, @@ -1354,11 +1788,9 @@ static void prv_draw_help(uint8_t ins_id) specasm_util_print(scratch, 0, y, PAPER_BLUE | INK_WHITE); y++; col = PAPER_BLACK | INK_WHITE; - for (i = 0; i < 6; i++) { + form = &specasm_forms[doc->forms]; + for (i = 0; i < doc->num_forms; i++) { col ^= 1 << 6; - form = &doc->forms[i]; - if (!form->form) - break; x = 0; specasm_util_print(ins_name, x, y, col); x += ins_name_len + 1; @@ -1370,6 +1802,7 @@ static void prv_draw_help(uint8_t ins_id) prv_justify_num(form->t_states); specasm_util_print(scratch, SPECASM_DOC_T_STATES_X, y, col); y++; + form++; } y = prv_print_register_encoding(doc, y + 1); From 9522f8e71923dd552e7f7c08260058270b4bbe0c Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 16 Nov 2024 13:43:47 +0100 Subject: [PATCH 04/24] specasm: fix issues with the docs Signed-off-by: Mark Ryan --- src/doc.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 211 insertions(+), 6 deletions(-) diff --git a/src/doc.c b/src/doc.c index ce7a84e..e41821f 100644 --- a/src/doc.c +++ b/src/doc.c @@ -152,8 +152,35 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_JP_FORMS \ (SPECASM_DOC_INIR_FORMS + SPECASM_DOC_INIR_NUM_FORMS) #define SPECASM_DOC_JP_NUM_FORMS 6 -#define SPECASM_DOC_LDD_FORMS \ +#define SPECASM_DOC_JR_FORMS \ (SPECASM_DOC_JP_FORMS + SPECASM_DOC_JP_NUM_FORMS) +#define SPECASM_DOC_JR_NUM_FORMS 3 +#define SPECASM_DOC_LD1_FORMS \ + (SPECASM_DOC_JR_FORMS + SPECASM_DOC_JR_NUM_FORMS) +#define SPECASM_DOC_LD1_NUM_FORMS 7 +#define SPECASM_DOC_LD2_FORMS \ + (SPECASM_DOC_LD1_FORMS + SPECASM_DOC_LD1_NUM_FORMS) +#define SPECASM_DOC_LD2_NUM_FORMS 3 +#define SPECASM_DOC_LD3_FORMS \ + (SPECASM_DOC_LD2_FORMS + SPECASM_DOC_LD2_NUM_FORMS) +#define SPECASM_DOC_LD3_NUM_FORMS 2 +#define SPECASM_DOC_LD4_FORMS \ + (SPECASM_DOC_LD3_FORMS + SPECASM_DOC_LD3_NUM_FORMS) +#define SPECASM_DOC_LD4_NUM_FORMS 6 +#define SPECASM_DOC_LD5_FORMS \ + (SPECASM_DOC_LD4_FORMS + SPECASM_DOC_LD4_NUM_FORMS) +#define SPECASM_DOC_LD5_NUM_FORMS 4 +#define SPECASM_DOC_LD6_FORMS \ + (SPECASM_DOC_LD5_FORMS + SPECASM_DOC_LD5_NUM_FORMS) +#define SPECASM_DOC_LD6_NUM_FORMS 3 +#define SPECASM_DOC_LD7_FORMS \ + (SPECASM_DOC_LD6_FORMS + SPECASM_DOC_LD6_NUM_FORMS) +#define SPECASM_DOC_LD7_NUM_FORMS 5 +#define SPECASM_DOC_LD8_FORMS \ + (SPECASM_DOC_LD7_FORMS + SPECASM_DOC_LD7_NUM_FORMS) +#define SPECASM_DOC_LD8_NUM_FORMS 4 +#define SPECASM_DOC_LDD_FORMS \ + (SPECASM_DOC_LD8_FORMS + SPECASM_DOC_LD8_NUM_FORMS) #define SPECASM_DOC_LDD_NUM_FORMS 1 #define SPECASM_DOC_LDDR_FORMS \ (SPECASM_DOC_LDD_FORMS + SPECASM_DOC_LDD_NUM_FORMS) @@ -441,6 +468,61 @@ static const specasm_ins_form_t specasm_forms[] = { {"cc,nn", "C2+cc n n", 3, 12}, {"cc,nn", "C2+cc n n", 2, 7}, + /* SPECASM_DOC_JR_FORMS */ + {"n", "18 n", 3, 12}, + {"cc,n", "40+cc n", 3, 12}, + {"cc,n", "40+cc n", 2, 7}, + + /* SPECASM_DOC_LD1_FORMS */ + {"rr,nn", "1+rr n n", 3, 10}, + {"ix,nn", "DD 21 n n", 4, 14}, + {"iy,nn", "FD 21 n n", 4, 14}, + {"hl,(nn)","2A n n", 5, 16}, + {"rr,(nn)", "ED 4A+rr", 6, 20}, + {"ix,(nn)", "DD 2A n n", 6, 20}, + {"iy,(nn)", "FD 2A n n", 6, 20}, + + /* SPECASM_DOC_LD2_FORMS */ + {"sp,hl", "F9", 1, 6}, + {"sp,ix", "DD F9", 2, 10}, + {"sp,iy", "FD F9", 2, 10}, + + /* SPECASM_DOC_LD3_FORMS */ + {"r,n", "6+r n", 2, 7}, + {"r,r'", "40+r+(r'/8)", 1, 4}, + + /* SPECASM_DOC_LD4_FORMS */ + {"(bc),a", "02", 2, 7}, + {"(de),a", "12", 2, 7}, + {"(hl),r", "70+r", 2, 7}, + {"(ix+d),r", "DD 70+r d", 5, 19}, + {"(iy+d),r", "FD 70+r d", 5, 19}, + {"(nn),a", "32 n n", 4, 13}, + + /* SPECASM_DOC_LD5_FORMS */ + {"(nn), hl", "22 nn", 5, 16}, + {"(nn), rr", "ED 43+rr nn", 6, 20}, + {"(nn), ix", "DD 22 nn", 6, 20}, + {"(nn), iy", "FD 22 nn", 6, 20}, + + /* SPECASM_DOC_LD6_FORMS */ + {"(hl),n", "36 n", 3, 10}, + {"(ix+d),n", "DD 36 d n", 5, 19}, + {"(iy+d),n", "FD 36 d n", 5, 19}, + + /* SPECASM_DOC_LD7_FORMS */ + {"r,(hl)", "46+r", 2, 7}, + {"a,(bc)", "0A", 2, 7}, + {"a,(de)", "1A", 2, 7}, + {"r,(ix+d)","DD 46 d", 5, 19}, + {"r,(iy+d)","FD 46 d", 5, 19}, + + /* SPECASM_DOC_LD8_FORMS */ + {"a,i", "ED 57", 2, 9}, + {"a,r", "ED 5F", 2, 9}, + {"i,a", "ED 47", 2, 9}, + {"r,a", "ED 4F", 2, 9}, + /* SPECASM_DOC_LDD_FORMS */ {"", "ED A8", 4, 16}, @@ -1103,6 +1185,104 @@ const static specasm_ins_doc_t docs[] = { "if condition is not met.", NULL, }, + { + "jr", + SPECASM_DOC_JR_FORMS, + SPECASM_DOC_JR_NUM_FORMS, + 0, + 0, + 8, + "Jump to PC+2+n if the condition is met or no condition is " + "supplied. Range of jump is -126 + 129 bytes. Instruction " + "consumes fewer t-states if condition is not met.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD1_FORMS, + SPECASM_DOC_LD1_NUM_FORMS, + 3, + 0, + 0, + "Loads register pair from an immediate value or from an " + "absolute address.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD2_FORMS, + SPECASM_DOC_LD2_NUM_FORMS, + 0, + 0, + 0, + "Loads SP with the value of another 16 bit register pair.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD3_FORMS, + SPECASM_DOC_LD3_NUM_FORMS, + 6, + 0, + 0, + "Loads byte register an immediate value or another register.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD4_FORMS, + SPECASM_DOC_LD4_NUM_FORMS, + 2, + 0, + 0, + "Stores a byte register to the memory location provided " + "by the first operand.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD5_FORMS, + SPECASM_DOC_LD5_NUM_FORMS, + 3, + 0, + 0, + "Loads a 16 bit value from a register into an absolute " + "address.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD6_FORMS, + SPECASM_DOC_LD6_NUM_FORMS, + 0, + 0, + 0, + "Stores an 8 bit immediate to the memory location provided " + "by the first operand.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD7_FORMS, + SPECASM_DOC_LD7_NUM_FORMS, + 6, + 0, + 0, + "Indirectly loads a byte into register using the pointer given " + "in the second operator.", + NULL, + }, + { + "ld", + SPECASM_DOC_LD8_FORMS, + SPECASM_DOC_LD8_NUM_FORMS, + 0, + 0, + 0, + "Instructions to load and store the interrupt vector and " + "memory refresh registers to and from a.", + NULL, + }, { "ldd", SPECASM_DOC_LDD_FORMS, @@ -1735,6 +1915,7 @@ static uint8_t prv_print_cc_encoding(const specasm_ins_doc_t *doc, uint8_t i; uint8_t x; uint8_t num; + uint8_t limit; char *s; if (!doc->all_cc) @@ -1746,11 +1927,18 @@ static uint8_t prv_print_cc_encoding(const specasm_ins_doc_t *doc, specasm_util_print(scratch, 0, y, PAPER_WHITE | INK_BLACK); y++; - specasm_util_print(" nz z nc c po pe p m ", 0, - y, PAPER_BLUE | INK_WHITE); + if (strcmp(doc->name, "jr")) { + limit = 8; + specasm_util_print(" nz z nc c po pe p m ", 0, + y, PAPER_BLUE | INK_WHITE); + } else { + limit = 4; + specasm_util_print(" nz z nc c ", 0, + y, PAPER_BLUE | INK_WHITE); + } y++; - for (i = 0; i < 8; i++) { + for (i = 0; i < limit; i++) { num = doc->all_cc * i; itoa(num, scratch, 16); x = (i*4)+1; @@ -1835,6 +2023,7 @@ static uint8_t prv_find_mnemomic(const char *ins_name) break; r = m - 1; } else { + for (; m > 0 && !strcmp(docs[m-1].name, ins_name); m--); return m; } } @@ -1850,20 +2039,36 @@ void specasm_help_banked(const char *ins_name) { uint8_t id; uint8_t k; + char jmp[2]; + uint8_t redraw = 1; + + jmp[1] = 0; id = prv_find_mnemomic(ins_name); do { - prv_draw_help(id); + if (redraw) + prv_draw_help(id); do { specasm_sleep_ms(25); } while(!(k = in_inkey())); + redraw = 0; if (k == SPECASM_KEY_LEFT) { - if (id > 0) + if (id > 0) { id--; + redraw = 1; + } } else if (k == SPECASM_KEY_RIGHT) { + if (id < max_docs - 1) { + id++; + redraw = 1; + } + } else if (k >= 'a' && k <= 'z') { + jmp[0] = k; + id = prv_find_mnemomic(jmp); if (id < max_docs - 1) id++; + redraw = 1; } else { break; } From 56449a5ecc3043114c153fd4b494d9556d55bfd0 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Thu, 2 Jan 2025 14:13:52 +0100 Subject: [PATCH 05/24] SAMAC: add new binary for generating .x files SAMAC is a new binary which when loaded extends the Spectrum's BASIC interpreter with four new commands. These commands allow the user to construct a .x file using a BASIC program. The commands are: *new - starts a new specasm file, removing any existing statements. An implicit *new statement is executed when the SAMAC.bin file is first loaded and executed. *asm - assembles a specasm statement (passed via a single string argument) and appends it to the current Specasm file. *save - saves the current file to disk. The name of the file is provided in a string argument. *load - loads an existing .x file into memory. Any new statements assembled with *asm will be appended to the in memory copy of the loaded file. Signed-off-by: Mark Ryan --- build/128/specasm/Makefile | 1 + build/48/specasm/Make.include | 1 + build/48/specasm/Makefile | 30 ++- src/samac.c | 191 ++++++++++++++++++ src/samac_cmds.asm | 366 ++++++++++++++++++++++++++++++++++ 5 files changed, 586 insertions(+), 3 deletions(-) create mode 100644 src/samac.c create mode 100644 src/samac_cmds.asm diff --git a/build/128/specasm/Makefile b/build/128/specasm/Makefile index b245915..ab953ea 100644 --- a/build/128/specasm/Makefile +++ b/build/128/specasm/Makefile @@ -98,6 +98,7 @@ release: mkdir -p release/specasm cp specasm.tap release/specasm cp ../../48/specasm/salink.tap release/specasm + cp ../../48/specasm/SAMAC_CODE.bin release/specasm/SAMAC cp ../../../COPYING release/specasm sed 's/```//g' ../../../docs/specasm.md > release/specasm/specasm.txt cp ../../48/specasm/SAMAKE ../../48/specasm/SAIMPORT ../../48/specasm/SAEXPORT ../../48/specasm/*.X release/specasm diff --git a/build/48/specasm/Make.include b/build/48/specasm/Make.include index f5cadc9..4e93224 100644 --- a/build/48/specasm/Make.include +++ b/build/48/specasm/Make.include @@ -54,6 +54,7 @@ queued_files.o: queued_files.c peer.h error.h peer_zx.h \ editor_buffers.o: editor_buffers.c editor_buffers.h line.h \ error.h scratch.o: scratch.c scratch.h line.h error.h +samac.o: samac.c error.h line.h scratch.h state.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 de7d857..f0798a3 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 SAMAKE +all: specasm.tap salink.tap SAEXPORT SAIMPORT SAMAKE SAMAC CC=zcc CFLAGS=+zx -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warning 85" -clib=sdcc_iy @@ -9,6 +9,9 @@ CFLAGS=+zx -SO3 --opt-code-size --max-allocs-per-node200000 -Cs "--disable-warni util_print_acc_zx.o: util_print_acc_zx.asm $(CC) $(CFLAGS) -o $@ -c $< +samac_cmds.o: samac_cmds.asm + $(CC) $(CFLAGS) -o $@ -c $< + include Make.include SPECASM = \ @@ -75,9 +78,25 @@ SAMAKE =\ peer_file_zx.o \ peer_zx.o +SAMAC = \ + samac.o \ + samac_cmds.o \ + ld_parse.o \ + line_common.o \ + line_dump.o \ + line_dump_common.o \ + line_parse.o \ + line_parse_common.o \ + error.o \ + scratch.o \ + state_base.o \ + state_dump.o \ + state_parse.o \ + peer_zx.o + specasm.tap: $(SPECASM) - $(CC) $(CFLAGS) -zorg=24310 -startup=31 -o specasm-bare $^ -create-app + $(CC) $(CFLAGS) -m -zorg=24310 -startup=31 -o specasm-bare $^ -create-app cat ../../../bas/48/SPECLD.TAP specasm-bare.tap > specasm.tap salink.tap: $(SALINK) @@ -93,9 +112,13 @@ SAIMPORT: $(SAIMPORT) SAMAKE: $(SAMAKE) $(CC) $(CFLAGS) -startup=30 -o $@ $(SAMAKE) -subtype=dotx -Cz"--clean" -create-app +SAMAC: $(SAMAC) + $(CC) $(CFLAGS) -m -zorg=32768 -pragma-output="CLIB_MALLOC_HEAP_SIZE=0" -pragma-output="REGISTER_SP=-1" -startup=31 -o $@ $^ +# $(CC) $(CFLAGS) -m -zorg=26000 -startup=31 -o $@ $^ -Cz"--bin" -create-app -pragma-output="REGISTER_SP=-1" + clean: - rm -rf specasm *.zip -rf unitzx - - rm *.X *.o *.bin *.tap SAIMPORT SAEXPORT SAMAKE + - rm *.X *.o *.bin *.tap SAIMPORT SAEXPORT SAMAKE SAMAC .PHONY: release release: @@ -105,6 +128,7 @@ release: cp ../../../COPYING release/specasm sed 's/```//g' ../../../docs/specasm.md > release/specasm/specasm.txt cp SAMAKE SAIMPORT SAEXPORT *.X release/specasm + cp SAMAC_CODE.bin release/specasm/SAMAC cp ../../../bas/48/INSTALL.BAS release/specasm cp ../../../bas/48/REMOVE.BAS release/specasm ../../../buildlib.sh `realpath release` 48 specasm diff --git a/src/samac.c b/src/samac.c new file mode 100644 index 0000000..73760d7 --- /dev/null +++ b/src/samac.c @@ -0,0 +1,191 @@ +/* + * 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 "error.h" +#include "scratch.h" +#include "state.h" + +#include +static char samac_scratch[SPECASM_MAX_SCRATCH + 1]; + +extern void specasm_samac_init(void); +void specasm_samac_new(void) { specasm_state_reset(); } + +/* + * TODO: Copied from editor.c. Banking makes sharing of the function trickier, + * so it's better to factorize this code in it's own commit. Best not to + * duplicate it long term in case we add more file types. Note the code + * is a bit different as in the editor we subtract one from the length to + * get rid of the l or s and also I think the length is 1 less than it should + * be, so refactor with caution. + */ + +static char *prv_complete_filename_e(char *com, uint8_t len) +{ + char *com2; + + while (*com == ' ') { + ++com; + len--; + } + + /* + * Do we already have a valid extension? + */ + + com2 = com + (len - 1); + if ((len >= 3) && ((*com2 == 't') || (*com2 == 'x'))) { + com2--; + if (*com2 == '.') + return com; + } + + while ((com2 > com) && (*com2 != '.') && (*com2 != '/')) + com2--; + + /* + * We've got another extension where there shouldn't be one. + */ + + if (*com2 == '.') { + err_type = SPECASM_ERROR_BAD_FNAME; + return NULL; + } + + /* + * If we get here the last part of the file name does not contain an + * extension. There must be an extension so we'll add the default + * one before loading, '.x'. + */ + + if (len + 2 >= SPECASM_MAX_SCRATCH) { + err_type = SPECASM_ERROR_BAD_FNAME; + return NULL; + } + com2 = com + len; + com2[0] = '.'; + com2[1] = 'x'; + com2[2] = 0; + + return com; +} + +specasm_error_t specasm_samac_asm(const char *str, uint16_t len) +{ + uint8_t i; + specasm_error_t err; + char *ptr; + uint16_t cur_line = state.lines.num_lines; + + if (len > SPECASM_LINE_MAX_LEN) + return SPECASM_ERROR_NO_ROOM_IN_LINE; + + specasm_append_empty_line_e(); + if ((err_type != SPECASM_ERROR_OK) || (len == 0)) + return err_type; + + memset(samac_scratch, ' ', SPECASM_LINE_MAX_LEN); + + /* + * Similar to saimport, we'll ignore characters < ' ' + */ + ptr = samac_scratch; + for (i = 0; i < (uint8_t)len; i++) { + if (str[i] >= 32) + *ptr++ = str[i]; + } + samac_scratch[SPECASM_LINE_MAX_LEN] = 0; + + specasm_parse_line_e(cur_line, samac_scratch); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_format_line_e(samac_scratch, cur_line); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + return SPECASM_ERROR_OK; + +on_error: + err = err_type; + specasm_delete_lines(cur_line, cur_line + 1); + err_type = SPECASM_ERROR_OK; + return err; +} + +specasm_error_t specasm_samac_load(const char *str, uint16_t len) +{ + const char *fname; + specasm_error_t err; + + if (len >= SPECASM_MAX_SCRATCH) + return SPECASM_ERROR_BAD_FNAME; + + memcpy(samac_scratch, str, len); + samac_scratch[len] = 0; + + fname = prv_complete_filename_e(samac_scratch, len); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_load_e(fname); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + return SPECASM_ERROR_OK; + +on_error: + err = err_type; + if (err_type != SPECASM_ERROR_OPEN) + specasm_state_reset(); + err_type = SPECASM_ERROR_OK; + return err; +} + +specasm_error_t specasm_samac_save(const char *str, uint16_t len) +{ + const char *fname; + specasm_error_t err; + + if (len >= SPECASM_MAX_SCRATCH) + return SPECASM_ERROR_BAD_FNAME; + + memcpy(samac_scratch, str, len); + samac_scratch[len] = 0; + + fname = prv_complete_filename_e(samac_scratch, len); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + specasm_save_e(fname); + if (err_type != SPECASM_ERROR_OK) + goto on_error; + + return SPECASM_ERROR_OK; + +on_error: + err = err_type; + err_type = SPECASM_ERROR_OK; + return err; +} + +int main(int argc, char *argv[]) +{ + specasm_init_dump_table(); + specasm_state_reset(); + specasm_samac_init(); + return 0; +} diff --git a/src/samac_cmds.asm b/src/samac_cmds.asm new file mode 100644 index 0000000..bf49dd6 --- /dev/null +++ b/src/samac_cmds.asm @@ -0,0 +1,366 @@ +; 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. + +SECTION code_compiler + +PUBLIC _specasm_samac_init +EXTERN _specasm_samac_new +EXTERN _specasm_samac_asm +EXTERN _specasm_samac_load +EXTERN _specasm_samac_save + +; Some SPECASM errors map to BASIC errors. For everything else there's +; "Invalid Argument" + +defc SPECASM_ERROR_TOO_MANY_LINES = 2 +defc SPECASM_ERROR_NUM_TOO_BIG = 21 +defc SPECASM_ERROR_BAD_FNAME = 23 + +; Addresses of ROM routines and System variables we need to subvert the error +; handling code. + +defc CLEARSP=$1097 +defc CLSLWR=$d6e +defc COPYBUF=$ecd +defc EXPTSTG=$1c8c +defc INCCHAD=$0074 +defc OUTCODE=$15ef +defc OUTNUM=$1a1b +defc POMSGE=$c0a +defc SETMIN=$16b0 +defc STKFTCH=$2bf1 +defc STMRET=$1b76 + +defc CHADD=23645 +defc DEFAD=23563 +defc ERRNR=23610 +defc ERRSP=23613 +defc PPC=23621 +defc STRM6=23574 +defc XPTR=23647 + +_specasm_samac_init: + ld hl, (ERRSP) + ld de, extend + ld (hl), e + inc hl + ld (hl), d + ret + +synend: + pop hl + bit 7, (iy+1) + jr z, exit + jp (hl) + +exit: + ld (iy+0), 255 + ld hl, extend + push hl + jp STMRET + +error: + bit 7, (iy+1) + jr nz, runer + +syner: + ld hl, extend + push hl + ld hl, (CHADD) + ld (XPTR), hl + jp 4791 + +runer: + res 5, (iy+1) + bit 1, (iy+48) + call nz, COPYBUF + ld a, (ERRNR) + inc a + push af + ld hl, 0 + ld (DEFAD), hl + ld (iy+38), h + ld (iy+55), h + inc hl + ld (STRM6), hl + call SETMIN + call CLSLWR + set 5, (iy+2) + pop af + ld b, a + cp 10 + jr c, numcod + add a, 7 + +numcod: + call OUTCODE + ld a, 32 + rst 16 + ld a, b + ld de, 5009 + call POMSGE + xor a + ld de, 5430 + call POMSGE + ld bc, (PPC) + call OUTNUM + ld a, 58 + rst 16 + ld b, 0 + ld c, (iy+13) + call OUTNUM + call CLEARSP + ld a, (ERRNR) + ld (iy+0), 255 + ld hl, extend + push hl + jp 4968 + +fnameErr: + ld a, $e + jr exerr +numErr: + ld a, $a + jr exerr +noRoomErr: + ld a, $f + jr exerr +argErr: + ld a, 9 +exerr: + ld (ERRNR), a + jp error + +; Entry point to our added Specasm commands. +extend: +; error needs to be Nonsense in +; Basic and next char needs to +; be a '*' + ld a, (ERRNR) + cp 11 + jp nz, error + ld hl, (CHADD) + ld a, (hl) + cp '*' + jp nz, error + rst 24 + jp parse + +; Each command is 8 bytes +; length 1 byte +; name 3 to 4 bytes +; args 1 byte (0 or 1) +; handler address +; optional padding byte + +commands: +db 3, "asm" +db 1 +dw asmH +db 0 +db 3, "new" +db 0 +dw newH +db 0 +db 4, "load" +db 1 +dw loadH +db 4, "save" +db 1 +dw saveH +comEnd: + +defc comCnt=(comEnd-commands)>>3 + +; returns if no match +parse: + ld de, (CHADD) + ld (chaddSv), de + ld hl, commands + ld (comSave), hl + ld c, comCnt +findCom: + ld b, (hl) + inc hl +parseLoop: + push hl + rst 32 + pop hl + cp (hl) + jr z, checkNext + dec c + jr z, parseBad + ld de, (chaddSv) + ld (CHADD), de + ld hl, (comSave) + ld de, 8 + add hl, de + ld (comSave), hl + jr findCom +checkNext: + inc hl + djnz parseLoop + push hl + call INCCHAD + pop hl + ld b, a + xor a + cp (hl) + jr nz, parseEnd + ld a, b + cp 13 + jr z, parseEnd + cp ':' + jr z, parseEnd + jr parseBad +parseEnd: + ld a, b + inc hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + ; a has 1st ch of parameter + jp (hl) +parseBad: + ld de, (chaddSv) + ld (CHADD), de + jp error +comSave: +dw 0 +chaddSv: +dw 0 + +samac_error: + or a + ret z + pop hl + cp SPECASM_ERROR_BAD_FNAME + jp z, fnameErr + cp SPECASM_ERROR_NUM_TOO_BIG + jp z, numErr + cp SPECASM_ERROR_TOO_MANY_LINES + jp z, noRoomErr + jp argErr + +asmH: + call EXPTSTG + bit 6, (iy+1) + jp nz, argErr + call synend + call STKFTCH + + ; note as we're calling into C code here we + ; need to duplicate what the z88dk crt does and + ; preserve certain registers and disable interrupts. + ; Otherwise it all gets a bit crashy. + + di + push iy + exx + push hl + exx + + push bc + push de + call _specasm_samac_asm + ld a, l + pop de + pop bc + + exx + pop hl + exx + pop iy + ei + + call samac_error + jp exit + +newH: + call synend + + di + push iy + exx + push hl + exx + + call _specasm_samac_new + + exx + pop hl + exx + pop iy + ei + + jp exit + +loadH: + call EXPTSTG + bit 6, (iy+1) + jp nz, argErr + call synend + call STKFTCH + + di + push iy + exx + push hl + exx + + push bc + push de + call _specasm_samac_load + ld a, l + pop de + pop bc + + exx + pop hl + exx + pop iy + ei + + call samac_error + jp exit + +saveH: + call EXPTSTG + bit 6, (iy+1) + jp nz, argErr + call synend + call STKFTCH + + di + push iy + exx + push hl + exx + + push bc + push de + call _specasm_samac_save + ld a, l + pop de + pop bc + + exx + pop hl + exx + pop iy + ei + + call samac_error + jp exit From 9ac9dfbf32382eb2fae734f3d7a2c72902dd4ae3 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Wed, 8 Jan 2025 22:00:46 +0100 Subject: [PATCH 06/24] samake: add macro support samake mac will now generate a small BASIC file called mac.bas that loads Specasm's scriptable BASIC extension that allows the user to write Specasm macros in Sinclair BASIC. An optional 2nd parameter can be used to provide an alternative name for the macro file. Signed-off-by: Mark Ryan --- src/samake.c | 108 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/src/samake.c b/src/samake.c index 26227b9..1b8224d 100644 --- a/src/samake.c +++ b/src/samake.c @@ -12,7 +12,7 @@ * 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 @@ -29,6 +29,7 @@ #define SAMAKE_TARGET_TYPE_TAP 2 #define SAMAKE_TARGET_TYPE_P 3 #define SAMAKE_TARGET_TYPE_TST 4 +#define SAMAKE_TARGET_TYPE_MAC 5 #define SAMAKE_CODE_BUF_SIZE 1024 @@ -39,6 +40,7 @@ static char start_address[6] = "32768"; static char clear_address[6] = "32767"; static char fsize_file_address[6] = "60000"; static uint16_t org_address = 0x8000; +static const char *samac_name = "/specasm/SAMAC"; static uint16_t basic_prog_len; static uint8_t got_org; uint8_t got_zx81; @@ -173,21 +175,27 @@ static void prv_find_bin_name_e(const char *dirname, uint8_t target_type) specasm_closedir(dir); } -static void prv_make_app_name(const char *type) +static void prv_make_app_name_gen(const char *type, const char *name, + uint8_t len) { uint8_t ext_pos; - uint8_t bin_name_len_plus_ext; + uint8_t 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); + len_plus_ext = len + strlen(type) + 1; + if (len_plus_ext > MAX_FNAME) + ext_pos = len - (len_plus_ext - MAX_FNAME); else - ext_pos = bin_name_len; - strcpy(app_name, bin_name); + ext_pos = len; + strcpy(app_name, name); app_name[ext_pos] = '.'; strcpy(&app_name[ext_pos + 1], type); } +static void prv_make_app_name(const char *type) +{ + prv_make_app_name_gen(type, bin_name, bin_name_len); +} + static uint8_t *prv_write_address(uint8_t *ptr, const char *address) { *ptr++ = 0xb0; /* VAL */ @@ -296,20 +304,10 @@ static specasm_handle_t prv_open_bin_e(uint16_t *bin_size) return 0; } -static void prv_make_bas_e(void) +static void prv_create_bas_file_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 @@ -337,6 +335,49 @@ static void prv_make_bas_e(void) specasm_remove_file(app_name); } +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"); + prv_create_bas_file_e(); +} + +static void prv_make_mac_e(const char *apn) +{ + 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; + + if (!strchr(apn, '.')) { + prv_make_app_name_gen("bas", apn, strlen(apn)); + } else { + if (strlen(apn) > MAX_FNAME) { + err_type = SPECASM_ERROR_BAD_FNAME; + return; + } + strcpy(app_name, apn); + } + + prv_create_bas_file_e(); +} + static void prv_make_basic_header(void) { uint8_t i; @@ -599,7 +640,7 @@ static void prv_make_p_e(void) uint16_t i; const uint8_t loader[] = {118, 0, 2, 11, 0, 249, 212, 197, - 11, 29, 34, 33, 29, 32, 11, 118}; + 11, 29, 34, 33, 29, 32, 11, 118}; const uint8_t footer[] = {118, 128}; if (got_org && strcmp(start_address, "16514")) { @@ -926,9 +967,16 @@ static void prv_make_tst_e(void) static void prv_make_e(const char *dir, uint8_t target_type) { - prv_find_bin_name_e(dir, target_type); - if (err_type != SPECASM_ERROR_OK) - return; + const char *apn; + + if (target_type == SAMAKE_TARGET_TYPE_MAC) { + strcpy(bin_name, samac_name); + bin_name_len = strlen(samac_name); + } else { + prv_find_bin_name_e(dir, target_type); + if (err_type != SPECASM_ERROR_OK) + return; + } if (got_zx81 && (target_type != SAMAKE_TARGET_TYPE_P) && (target_type != SAMAKE_TARGET_TYPE_NONE)) { @@ -962,14 +1010,18 @@ static void prv_make_e(const char *dir, uint8_t target_type) if ((target_type != SAMAKE_TARGET_TYPE_P) && (org_address < 24000)) printf("Warning: org %" PRIu16 " is very low\n", org_address); - if (target_type == SAMAKE_TARGET_TYPE_BAS) + if (target_type == SAMAKE_TARGET_TYPE_BAS) { prv_make_bas_e(); - else if (target_type == SAMAKE_TARGET_TYPE_TAP) + } else if (target_type == SAMAKE_TARGET_TYPE_MAC) { + apn = strcmp(dir, ".") ? dir : "mac"; + prv_make_mac_e(apn); + } else if (target_type == SAMAKE_TARGET_TYPE_TAP) { prv_make_tap_e(); - else if (target_type == SAMAKE_TARGET_TYPE_P) + } else if (target_type == SAMAKE_TARGET_TYPE_P) { prv_make_p_e(); - else + } else { prv_make_tst_e(); + } } int main(int argc, char *argv[]) @@ -998,6 +1050,8 @@ int main(int argc, char *argv[]) target_type = SAMAKE_TARGET_TYPE_BAS; } else if (!strcmp(argv[1], "tst")) { target_type = SAMAKE_TARGET_TYPE_TST; + } else if (!strcmp(argv[1], "mac")) { + target_type = SAMAKE_TARGET_TYPE_MAC; } else { err_type = SAMAKE_ERROR_USAGE; goto on_error; From aa4d35705a22145564020e566ae6dc795e4fcb04 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 12 Jan 2025 16:01:53 +0100 Subject: [PATCH 07/24] samake: add 'ace' support Specifying 'ace' now allows the user to create a Jupiter ace tap file that loads the binary created by salink. The binary is, however, not auto run. To load 0 0 CALL bin.tap CALL This change allows Specasm users to cross compile for the Jupiter Ace. Signed-off-by: Mark Ryan --- src/samake.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/src/samake.c b/src/samake.c index 1b8224d..503bdaf 100644 --- a/src/samake.c +++ b/src/samake.c @@ -30,6 +30,7 @@ #define SAMAKE_TARGET_TYPE_P 3 #define SAMAKE_TARGET_TYPE_TST 4 #define SAMAKE_TARGET_TYPE_MAC 5 +#define SAMAKE_TARGET_TYPE_ACE 6 #define SAMAKE_CODE_BUF_SIZE 1024 @@ -453,10 +454,10 @@ static void prv_make_code_header(uint16_t bin_size) container.tap_block[23] = 0xff; } -static uint8_t prv_write_code_e(specasm_handle_t in_f, specasm_handle_t out_f) +static uint8_t prv_write_code_e(specasm_handle_t in_f, specasm_handle_t out_f, + uint8_t checksum) { uint16_t i; - uint8_t checksum = 0xff; size_t read; for (;;) { @@ -476,6 +477,71 @@ static uint8_t prv_write_code_e(specasm_handle_t in_f, specasm_handle_t out_f) return checksum; } +static void prv_make_ace_e(void) +{ + specasm_handle_t out_f; + specasm_handle_t in_f; + uint16_t bin_size; + uint16_t i; + uint8_t header[30]; + uint16_t data_block_size; + uint8_t checksum = 0; + uint8_t ace_name_len = bin_name_len; + + prv_make_app_name("tap"); + in_f = prv_open_bin_e(&bin_size); + if (err_type != SPECASM_ERROR_OK) + return; + + if (ace_name_len > 10) + ace_name_len = 10; + + memset(&header[3], 0x20, 24); + header[0] = 26; /* header block size LSB */ + header[1] = 0; /* header block size MSB */ + header[2] = 32; /* file type (byte) */ + memcpy(&header[3], bin_name, ace_name_len); + memcpy(&header[13], &bin_size, sizeof(bin_size)); + memcpy(&header[15], &org_address, sizeof(org_address)); + + for (i = 2; i < 27; i++) + checksum ^= header[i]; + header[27] = checksum; + data_block_size = bin_size + 1; + memcpy(&header[28], &data_block_size, sizeof(data_block_size)); + + 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, &header, sizeof(header)); + 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, 0); + 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_tap_e(void) { specasm_handle_t out_f; @@ -533,7 +599,7 @@ static void prv_make_tap_e(void) * Now write out code file and compute the checksum. */ - checksum = prv_write_code_e(in_f, out_f); + checksum = prv_write_code_e(in_f, out_f, 0xff); if (err_type != SPECASM_ERROR_OK) goto on_error; @@ -667,7 +733,7 @@ static void prv_make_p_e(void) if (err_type != SPECASM_ERROR_OK) goto on_error; - (void)prv_write_code_e(in_f, out_f); + (void)prv_write_code_e(in_f, out_f, 0xff); if (err_type != SPECASM_ERROR_OK) goto on_error; @@ -1007,10 +1073,13 @@ static void prv_make_e(const char *dir, uint8_t target_type) * to stop people creating a loader for a dot program. */ - if ((target_type != SAMAKE_TARGET_TYPE_P) && (org_address < 24000)) + if ((target_type != SAMAKE_TARGET_TYPE_P) && + (target_type != SAMAKE_TARGET_TYPE_ACE) && (org_address < 24000)) printf("Warning: org %" PRIu16 " is very low\n", org_address); - if (target_type == SAMAKE_TARGET_TYPE_BAS) { + if (target_type == SAMAKE_TARGET_TYPE_ACE) { + prv_make_ace_e(); + } else if (target_type == SAMAKE_TARGET_TYPE_BAS) { prv_make_bas_e(); } else if (target_type == SAMAKE_TARGET_TYPE_MAC) { apn = strcmp(dir, ".") ? dir : "mac"; @@ -1052,6 +1121,8 @@ int main(int argc, char *argv[]) target_type = SAMAKE_TARGET_TYPE_TST; } else if (!strcmp(argv[1], "mac")) { target_type = SAMAKE_TARGET_TYPE_MAC; + } else if (!strcmp(argv[1], "ace")) { + target_type = SAMAKE_TARGET_TYPE_ACE; } else { err_type = SAMAKE_ERROR_USAGE; goto on_error; From fc4fe99128f7144b28245de251c046c0f4117aaa Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Wed, 15 Jan 2025 22:36:30 +0100 Subject: [PATCH 08/24] samake: add autoace support autoace generates an autoload tap file for a salinked binary for the Jupiter ace. The org address needs to be set to $3c5b. The .tap file can be loaded and run on the ace with the following command: 0 0 bload autorun Signed-off-by: Mark Ryan --- src/samake.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 166 insertions(+), 16 deletions(-) diff --git a/src/samake.c b/src/samake.c index 503bdaf..00ed48c 100644 --- a/src/samake.c +++ b/src/samake.c @@ -31,6 +31,7 @@ #define SAMAKE_TARGET_TYPE_TST 4 #define SAMAKE_TARGET_TYPE_MAC 5 #define SAMAKE_TARGET_TYPE_ACE 6 +#define SAMAKE_TARGET_TYPE_AUTOACE 7 #define SAMAKE_CODE_BUF_SIZE 1024 @@ -483,7 +484,6 @@ static void prv_make_ace_e(void) specasm_handle_t in_f; uint16_t bin_size; uint16_t i; - uint8_t header[30]; uint16_t data_block_size; uint8_t checksum = 0; uint8_t ace_name_len = bin_name_len; @@ -496,19 +496,19 @@ static void prv_make_ace_e(void) if (ace_name_len > 10) ace_name_len = 10; - memset(&header[3], 0x20, 24); - header[0] = 26; /* header block size LSB */ - header[1] = 0; /* header block size MSB */ - header[2] = 32; /* file type (byte) */ - memcpy(&header[3], bin_name, ace_name_len); - memcpy(&header[13], &bin_size, sizeof(bin_size)); - memcpy(&header[15], &org_address, sizeof(org_address)); + memset(&basic_buf[3], 0x20, 24); + basic_buf[0] = 26; /* header block size LSB */ + basic_buf[1] = 0; /* header block size MSB */ + basic_buf[2] = 32; /* file type (byte) */ + memcpy(&basic_buf[3], bin_name, ace_name_len); + memcpy(&basic_buf[13], &bin_size, sizeof(bin_size)); + memcpy(&basic_buf[15], &org_address, sizeof(org_address)); for (i = 2; i < 27; i++) - checksum ^= header[i]; - header[27] = checksum; + checksum ^= basic_buf[i]; + basic_buf[27] = checksum; data_block_size = bin_size + 1; - memcpy(&header[28], &data_block_size, sizeof(data_block_size)); + memcpy(&basic_buf[28], &data_block_size, sizeof(data_block_size)); out_f = specasm_file_wopen_e(app_name); if (err_type != SPECASM_ERROR_OK) { @@ -516,7 +516,7 @@ static void prv_make_ace_e(void) return; } - specasm_file_write_e(out_f, &header, sizeof(header)); + specasm_file_write_e(out_f, basic_buf, 30); if (err_type != SPECASM_ERROR_OK) goto on_error; @@ -542,6 +542,143 @@ static void prv_make_ace_e(void) specasm_remove_file(app_name); } +/* + * Template based on: + * https://github.com/McKlaud76/JACE-TAP-templates/blob/main/asm/AUTORUN_template.asm + */ + +/* clang-format off */ + +static const uint8_t aceauto_template[] = { + /* TAP Header Block (0-27). All data is constant */ + 0x1a, 0x00, 0x20, 'a', 'u', 't', 'o', 'r', + 'u', 'n', 0x20, 0x20, 0x20, 0x1f, 0x00, 0xe0, + 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xbb, + + /* TAP Data Block (28-61). Binary file name and checksum are overwritten */ + 0x20, 0x00, 0x00, 'L', 'O', 'A', 'D', 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, /* Binary file name, offset 36 */ + 0x20, 'R', 'U', 'N', 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, /* checksum, offset 61 */ + + /* TAP Header Block (62-89). Binary file name, file length, STKBOT and checksum are overwritten */ + 0x1a, 0x00, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, /* Binary file name, offset 65 */ + 0x00, 0x00, /* File length, offset 75 */ + 0x51, 0x3c, 0x58, 0x3c, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, + 0x00, 0x00, /* STKBOT, offset 87 */ + 0x00, /* checksum, offset 89 */ + + /* TAP Data Block (90-101). Data block length and word length overwritten */ + + 0x00, 0x00, /* Data block length, offset 90 */ + 'R', 'U', 0xce, + 0x00, 0x00, /* Word length, offset 95 */ + 0x49, 0x3c, 0x03, 0x5b, 0x3c + + /* Code starts here followed by checksum of entire data block. */ + }; + +/* clang-format on */ + +static void prv_make_autoace_e(void) +{ + specasm_handle_t out_f; + specasm_handle_t in_f; + uint16_t bin_size; + uint16_t i; + uint16_t word_size; + uint16_t stkbot; + uint8_t checksum; + uint8_t ace_name_len; + + if (org_address != 15451) { + printf("Bad ORG address %s, want 15451\n", start_address); + err_type = SAMAKE_ERROR_BAD_ORG; + return; + } + + prv_make_app_name("tap"); + in_f = prv_open_bin_e(&bin_size); + if (err_type != SPECASM_ERROR_OK) + return; + + memcpy(basic_buf, aceauto_template, sizeof(aceauto_template)); + + ace_name_len = bin_name_len; + if (ace_name_len > 10) + ace_name_len = 10; + + memcpy(&basic_buf[36], bin_name, ace_name_len); + + checksum = 0; + for (i = 30; i < 61; i++) + checksum ^= basic_buf[i]; + basic_buf[61] = checksum; + + memcpy(&basic_buf[65], bin_name, ace_name_len); + + word_size = bin_size + 10; + memcpy(&basic_buf[75], &word_size, sizeof(word_size)); + + stkbot = org_address + bin_size; + memcpy(&basic_buf[87], &stkbot, sizeof(stkbot)); + checksum = 0; + for (i = 64; i < 89; i++) + checksum ^= basic_buf[i]; + basic_buf[89] = checksum; + + /* + * length of block containing RUN word and code + */ + word_size++; + memcpy(&basic_buf[90], &word_size, sizeof(word_size)); + + /* + * length of word and code + */ + word_size -= 4; + memcpy(&basic_buf[95], &word_size, sizeof(word_size)); + + checksum = 0; + for (i = 92; i < 102; i++) + checksum ^= basic_buf[i]; + + 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, basic_buf, 102); + 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, checksum); + 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_tap_e(void) { specasm_handle_t out_f; @@ -710,7 +847,7 @@ static void prv_make_p_e(void) const uint8_t footer[] = {118, 128}; if (got_org && strcmp(start_address, "16514")) { - printf("Bad ORG address %s, want 16514", start_address); + printf("Bad ORG address %s, want 16514\n", start_address); err_type = SAMAKE_ERROR_BAD_ORG; return; } @@ -1073,12 +1210,23 @@ static void prv_make_e(const char *dir, uint8_t target_type) * to stop people creating a loader for a dot program. */ - if ((target_type != SAMAKE_TARGET_TYPE_P) && - (target_type != SAMAKE_TARGET_TYPE_ACE) && (org_address < 24000)) - printf("Warning: org %" PRIu16 " is very low\n", org_address); + if (org_address < 24000) { + switch (target_type) { + case SAMAKE_TARGET_TYPE_BAS: + case SAMAKE_TARGET_TYPE_TAP: + case SAMAKE_TARGET_TYPE_TST: + printf("Warning: org %" PRIu16 " is very low\n", + org_address); + break; + default: + break; + } + } if (target_type == SAMAKE_TARGET_TYPE_ACE) { prv_make_ace_e(); + } else if (target_type == SAMAKE_TARGET_TYPE_AUTOACE) { + prv_make_autoace_e(); } else if (target_type == SAMAKE_TARGET_TYPE_BAS) { prv_make_bas_e(); } else if (target_type == SAMAKE_TARGET_TYPE_MAC) { @@ -1123,6 +1271,8 @@ int main(int argc, char *argv[]) target_type = SAMAKE_TARGET_TYPE_MAC; } else if (!strcmp(argv[1], "ace")) { target_type = SAMAKE_TARGET_TYPE_ACE; + } else if (!strcmp(argv[1], "autoace")) { + target_type = SAMAKE_TARGET_TYPE_AUTOACE; } else { err_type = SAMAKE_ERROR_USAGE; goto on_error; From 13037206739a4ca37a495abcee4c4f85f6f4ee65 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 19 Jan 2025 21:29:50 +0100 Subject: [PATCH 09/24] .gitignore: exclude a few extra files and directories. Signed-off-by: Mark Ryan --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 173ca5b..98d4dcf 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ SAIMPORT.X SAEXPORT.X SAMAKE SAMAKE.X +SAMAC build.sh compile.sh !build/48/specasm/ @@ -30,6 +31,7 @@ compile.sh !bas/128/*.TAP !bas/*.TAP build/48/specasm/release/ +build/128/specasm/release/ !build/next/specasm/ !build/128/specasm/ build/next/specasm/release/ @@ -40,3 +42,4 @@ build/next/unit/tests/ examples/hello/hello examples/hello/hello.x .DS_Store +.vscode \ No newline at end of file From 64ee8050aff304341d0d7b86ae003f38450eb11f Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 26 Jan 2025 16:07:53 +0100 Subject: [PATCH 10/24] specasm: add documentation for the Next The invovles splitting up the documentation strings into separate files so that they can be stored in separate pages in the Next. On the spectrum 128 the documentation code and all the instruction data is stored in a single 16kb bank. On the next the code and all the instruction data apart from the descriptions are stored in one 8kb bank. The descriptions are split over 2 8kb banks which are paged in as necessary. Signed-off-by: Mark Ryan --- build/128/specasm/Make.include | 4 +- build/128/specasm/Makefile | 10 +- build/next/specasm/Make.include | 3 + build/next/specasm/Makefile | 14 +- src/descra2l.c | 278 ++++++++++ src/descra2l.h | 99 ++++ src/descrm2z.c | 179 +++++++ src/descrm2z.h | 96 ++++ src/doc.c | 874 +++++++++++++++++++++++--------- src/specasm_trampolines_next.c | 66 ++- 10 files changed, 1362 insertions(+), 261 deletions(-) create mode 100644 src/descra2l.c create mode 100644 src/descra2l.h create mode 100644 src/descrm2z.c create mode 100644 src/descrm2z.h diff --git a/build/128/specasm/Make.include b/build/128/specasm/Make.include index f783d34..b8aba84 100644 --- a/build/128/specasm/Make.include +++ b/build/128/specasm/Make.include @@ -58,7 +58,9 @@ editor_buffers.o: editor_buffers.c editor_buffers.h line.h \ error.h scratch.o: scratch.c scratch.h line.h error.h analysis_banked.o: analysis.c state.h state_base.h line_common.h line.h error.h strings.h -doc_banked.o: doc.h editor.h error.h line.h line_common.h peer.h peer_zx.h scratch.h util_print_zx.h +doc_banked.o: doc.c doc.h editor.h error.h line.h line_common.h peer.h peer_zx.h scratch.h util_print_zx.h +descra2l_banked.o: descra2l.c descra2l.h +descrm2z_banked.o: descrm2z.c descrm2z.h %.o: %.c zcc $(CFLAGS) -o $@ -c $< diff --git a/build/128/specasm/Makefile b/build/128/specasm/Makefile index ab953ea..5535137 100644 --- a/build/128/specasm/Makefile +++ b/build/128/specasm/Makefile @@ -40,6 +40,12 @@ analysis_banked.o: analysis.c doc_banked.o: doc.c $(CC) $(CFLAGS) -DSPECASM_128_BANKED -o $@ --codesegBANK_3 --constsegBANK_3 --datasegBANK_3 -c $< +descra2l_banked.o: descra2l.c + $(CC) $(CFLAGS) -DSPECASM_128_BANKED -o $@ --codesegBANK_3 --constsegBANK_3 --datasegBANK_3 -c $< + +descrm2z_banked.o: descrm2z.c + $(CC) $(CFLAGS) -DSPECASM_128_BANKED -o $@ --codesegBANK_3 --constsegBANK_3 --datasegBANK_3 -c $< + clipboard.o: clipboard.c $(CC) $(CFLAGS) -DSPECASM_128_BANKED -o $@ -c $< @@ -71,7 +77,9 @@ SPECASM = \ editor_banked.o \ editor_extra_banked.o \ analysis_banked.o \ - doc_banked.o + doc_banked.o \ + descra2l_banked.o \ + descrm2z_banked.o specasm_bare.tap: $(SPECASM) $(CC) $(CFLAGS) -zorg=32768 -m -startup=31 -o $@ $^ -pragma-include:zpragma.inc $(CZFLAGS) diff --git a/build/next/specasm/Make.include b/build/next/specasm/Make.include index b0bfaed..cf32dc4 100644 --- a/build/next/specasm/Make.include +++ b/build/next/specasm/Make.include @@ -82,6 +82,9 @@ editor_buffers.o: editor_buffers.c editor_buffers.h line.h \ error.h scratch.o: scratch.c scratch.h line.h error.h analysis_banked.o: analysis.c state.h state_base.h line_common.h line.h error.h strings.h +doc_banked.o: doc.h editor.h error.h line.h line_common.h peer.h peer_zx.h scratch.h util_print_zx.h +descra2l_banked.o: descra2l.c descra2l.h +descrm2z_banked.o: descrm2z.c descrm2z.h %.o: %.c zcc $(CFLAGS) -o $@ -c $< diff --git a/build/next/specasm/Makefile b/build/next/specasm/Makefile index 87f8dfc..2333031 100644 --- a/build/next/specasm/Makefile +++ b/build/next/specasm/Makefile @@ -53,6 +53,15 @@ queued_files_banked.o: queued_files.c link_obj_banked.o: link_obj.c $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $< +doc_banked.o: doc.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_47_L --constsegBANK_47_L --datasegBANK_47_L -c $< + +descra2l_banked.o: descra2l.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_47_H --constsegBANK_47_H --datasegBANK_47_H -c $< + +descrm2z_banked.o: descrm2z.c + $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_48_H --constsegBANK_48_H --datasegBANK_48_H -c $< + clipboard.o: clipboard.c $(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ -c $< @@ -80,7 +89,10 @@ SPECASM = \ state_dump_banked.o \ editor_banked.o \ editor_extra_banked.o \ - analysis_banked.o + analysis_banked.o \ + doc_banked.o \ + descra2l_banked.o \ + descrm2z_banked.o SALINK = \ salink.o \ diff --git a/src/descra2l.c b/src/descra2l.c new file mode 100644 index 0000000..a6144e6 --- /dev/null +++ b/src/descra2l.c @@ -0,0 +1,278 @@ +/* + * 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 "descra2l.h" + +const char specasm_doc_adc[] = + "The second argument and the carry flag are added to the " + "contents of the destination register."; + +const char specasm_doc_add[] = + "The second argument is added to the contents of the " + "destination register."; + +const char specasm_doc_add_16[] = + "The second argument is added to the contents of the " + "destination register. The carry flag is set according " + "to the result of the addition. h represents carry from " + "bit 11."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_add_rra[] = + "A is added to the destination register. Carry flag undefined."; +const char specasm_doc_add_16imm[] = + "The 16 bit immediate in the second argument is added to the " + "destination register."; +#endif + +const char specasm_doc_align[] = + "The align directive takes one immediate argument that must be " + "a power of 2, >= 2 and <= 256. It inserts null bytes into " + "the binary until the requested alignment is achieved. The " + "number of t-states consumed by an align directive is the " + "number of bytes inserted * 4."; + +const char specasm_doc_and[] = "The result of a bitwise AND of a and the " + "argument is stored in a."; + +const char specasm_doc_bit[] = + "Sets the zero flag to 1 if bit n of the 2nd operand " + "is 0, or to 0 if bit N is 1."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_brlc[] = + "Rotates de left by the value in the lower nibble of b."; +const char specasm_doc_bsla[] = + "Shifts de left by the value in the bottom 5 bits of b."; +const char specasm_doc_bsra[] = + "Performs an arithmetic right shift of de by the value in the " + "bottom 5 bits of b."; +const char specasm_doc_bsrf[] = + "Shifts de right by the value in the bottom 5 bits of b. " + "The vacated bits are set to 1."; +const char specasm_doc_bsrl[] = + "Performs a logical right shift of de by the value in the " + "bottom 5 bits of b."; +#endif + +const char specasm_doc_call[] = + "Pushes the PC on the stack and jumps to nn. The conditional " + "version of the instruction takes fewer t-states to execute " + "if the call is not taken."; + +const char specasm_doc_ccf[] = "Inverts the carry flag."; + +const char specasm_doc_cp[] = + "The operand is subtracted from a setting the " + "flags accordingly. The result of the subtraction is " + "discarded."; + +const char specasm_doc_cpd[] = + "(hl) is subtracted from a and the flags are set " + "accordingly. The result is discarded. bc and hl are " + "decremented. The p flag is set if bc != 0 after the " + "instruction has finished and is otherwise reset."; + +const char specasm_doc_cpdr[] = + "(hl) is subtracted from a and the flags are set accordingly. " + "The result is discarded. bc and hl are decremented. If bc>0 " + "and the result of the subtraction is != 0 cpdr " + "repeats. The p flag is set if bc!=0 after cpdr has " + "finished and is otherwise reset. The slower timings apply " + "when cpdr repeats."; + +const char specasm_doc_cpi[] = + "(hl) is subtracted from a and the flags are set accordingly. " + "The result is discarded. bc is decremented while hl is " + "incremented. The p flag is set if bc != 0 after the cpi has " + "finished and is otherwise reset."; + +const char specasm_doc_cpir[] = + "(hl) is subtracted from a and the flags are set accordingly. " + "The result is discarded. bc is decremented while hl is " + "incremented. If bc>0 and the result of the subtraction is != 0" + " cpir repeats. The p flag is set if bc!=0 after cpir " + "has finished and is otherwise reset. The slower " + "timings apply when the cpir repeats."; + +const char specasm_doc_cpl[] = "Invert a."; + +const char specasm_doc_daa[] = + "Conditionally adjusts a for BCD addition and subtraction."; + +const char specasm_doc_db[] = + "Stores up to 4 bytes in the program binary. All ns must be " + "formatted in the same way. Only one byte can be specified if " + "an expression is used."; + +const char specasm_doc_dec[] = "The specified operand is decremented by 1."; + +const char specasm_doc_di[] = "Disables maskable interrupts."; + +const char specasm_doc_djnz[] = + "b is decremented by 1. If the result is>0 the cpu jumps to " + "PC+2+n, where n is a signed 8 byte. djnz executes " + "more quickly when the jump it not taken."; + +const char specasm_doc_ds[] = "Stores c copies of the byte n in the binary."; + +const char specasm_doc_dw[] = + "Stores up to 2 words in the program binary. All nns must be " + "formatted in the same way. Only one word can be specified if " + "an expression is used."; + +const char specasm_doc_ei[] = "Enables maskable interrupts."; + +const char specasm_doc_ex[] = + "The contents of the two operands are exchanged. " + "ex af, af' affects all the flags while the other forms of the " + "instruction have no effect on the flags."; + +const char specasm_doc_exx[] = "Exchange bc, de and hl with bc', de', hl'."; + +const char specasm_doc_halt[] = + "CPU execution is suspended until the next interrupt or reset."; + +const char specasm_doc_im[] = + "Sets the interrupt mode. With im 2 the MSB of the vector " + "address is taken from the i register."; + +const char specasm_doc_in[] = + "Reads a byte from the device identified by bc and stores it in r."; + +const char specasm_doc_in2[] = + "Reads a byte from the device adddress whose MSB is " + "taken from a and whose LSB is n. The byte is " + "stored in a."; + +const char specasm_doc_inc[] = "The specified operand is incremented by 1."; + +const char specasm_doc_ind[] = + "A byte is read from the device identified by bc and stored " + "in (hl). b and hl are decremented."; + +const char specasm_doc_indr[] = + "A byte is read from the device identified by bc and stored " + "(hl). b and hl are decremented. If bc!=0 indr " + "repeats. indr consumes more t-states when it " + "repeats."; + +const char specasm_doc_ini[] = + "A byte is read from the device identified by bc " + "and stored in (hl). b is decremented and h is incremented."; + +const char specasm_doc_inir[] = + "A byte is read from the device identified by bc " + "and stored in (hl). b is decremented and h is incremented. " + "If bc!=0 inir repeats. inir consumes more t-states when it " + "repeats."; + +const char specasm_doc_jp[] = + "Jump to the last operand if the condition is met or no " + "condition is supplied. Instruction consumes fewer t-states " + "if condition is not met."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_jp_c[] = + "Jumps to address formed from the address of the next instruction " + "and data read from an io port. Bit 14 and 15 are taken " + "from the PC of the next instruction. Bits 0-5 are 0. Bits " + "6-13 come from an implicit in (c)."; +#endif + +const char specasm_doc_jr[] = + "Jump to PC+2+n if the condition is met or no condition is " + "supplied. Range of jump is -126 + 129 bytes. Instruction " + "consumes fewer t-states if condition is not met."; + +const char specasm_doc_ld1[] = + "Loads register pair from an immediate value or from an " + "absolute address."; + +const char specasm_doc_ld2[] = + "Loads SP with the value of another 16 bit register pair."; + +const char specasm_doc_ld3[] = + "Loads byte register an immediate value or another register."; + +const char specasm_doc_ld4[] = + "Stores a byte register to the memory location provided " + "by the first operand."; + +const char specasm_doc_ld5[] = + "Loads a 16 bit value from a register into an absolute " + "address."; + +const char specasm_doc_ld6[] = + "Stores an 8 bit immediate to the memory location provided " + "by the first operand."; + +const char specasm_doc_ld7[] = + "Indirectly loads a byte into register using the pointer given " + "in the second operator."; + +const char specasm_doc_ld8[] = + "Instructions to load and store the interrupt vector and " + "memory refresh registers to and from a."; + +const char specasm_doc_ldd[] = + "Load (hl) into (de). hl, de and bc are decremented."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_lddrx[] = + "Load (hl) into (de) if (hl) != a. hl and bc are decremented while" + "de in incremented. If bc!=0 lddrx repeats. lddrx consumes more " + "t-states when it repeats."; +const char specasm_doc_lddx[] = + "Load (hl) into (de) if (hl) != a. de is incremented. bc and " + "hl are decremented."; +#endif + +const char specasm_doc_lddr[] = + "Load (hl) into (de). hl, de and bc are decremented. If " + "bc!=0 lddr repeats. lddr consumes more t-states when it " + "repeats."; + +const char specasm_doc_ldi[] = + "Load (hl) into (de). Both hl and de are incremented while bc " + "is decremented."; + +const char specasm_doc_ldir[] = + "Load (hl) into (de). Both hl and de are incremented while bc " + "is decremented. If bc!=0 ldir repeats. ldir " + "consumes more t-states when it repeats."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_ldirx[] = + "Load (hl) into (de) if (hl) != a. hl and de are incremented, while " + "bc is decremented. If bc != 0 ldirx repeats. ldirx consumes more " + "t-states when it repeats."; + +const char specasm_doc_ldix[] = + "Load (hl) into (de) if (hl) != a. Both hl and de " + "are incremented while bc is decremented."; + +const char specasm_doc_ldpirx[] = + "Load the byte whose address is formed from the top 13 bits of hl " + "and bottom 3 bits of de into (de) if the byte != a. de is incremented, " + "while bc is decremented. If bc != 0 ldpirx repeats. ldpirx consumes more " + "t-states when it repeats."; + +const char specasm_doc_ldws[] = + "Load (hl) into (de) and increment l and d. The v flag is set " + "if d was $7f before increment."; + +#endif \ No newline at end of file diff --git a/src/descra2l.h b/src/descra2l.h new file mode 100644 index 0000000..9ec01ff --- /dev/null +++ b/src/descra2l.h @@ -0,0 +1,99 @@ +/* + * 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 DESCRA2L_H +#define DESCRA2L_H + +extern const char specasm_doc_adc[]; +extern const char specasm_doc_add[]; +extern const char specasm_doc_add_16[]; +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_add_rra[]; +extern const char specasm_doc_add_16imm[]; +#endif +extern const char specasm_doc_align[]; +extern const char specasm_doc_and[]; +extern const char specasm_doc_bit[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_brlc[]; +extern const char specasm_doc_bsla[]; +extern const char specasm_doc_bsra[]; +extern const char specasm_doc_bsrf[]; +extern const char specasm_doc_bsrl[]; +#endif + +extern const char specasm_doc_call[]; +extern const char specasm_doc_ccf[]; +extern const char specasm_doc_cp[]; +extern const char specasm_doc_cpd[]; +extern const char specasm_doc_cpdr[]; +extern const char specasm_doc_cpi[]; +extern const char specasm_doc_cpir[]; +extern const char specasm_doc_cpl[]; +extern const char specasm_doc_daa[]; +extern const char specasm_doc_db[]; +extern const char specasm_doc_dec[]; +extern const char specasm_doc_di[]; +extern const char specasm_doc_djnz[]; +extern const char specasm_doc_ds[]; +extern const char specasm_doc_dw[]; +extern const char specasm_doc_ei[]; +extern const char specasm_doc_ex[]; +extern const char specasm_doc_exx[]; +extern const char specasm_doc_halt[]; +extern const char specasm_doc_im[]; +extern const char specasm_doc_in[]; +extern const char specasm_doc_in2[]; +extern const char specasm_doc_inc[]; +extern const char specasm_doc_ind[]; +extern const char specasm_doc_indr[]; +extern const char specasm_doc_ini[]; +extern const char specasm_doc_inir[]; +extern const char specasm_doc_jp[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_jp_c[]; +#endif + +extern const char specasm_doc_jr[]; +extern const char specasm_doc_ld1[]; +extern const char specasm_doc_ld2[]; +extern const char specasm_doc_ld3[]; +extern const char specasm_doc_ld4[]; +extern const char specasm_doc_ld5[]; +extern const char specasm_doc_ld6[]; +extern const char specasm_doc_ld7[]; +extern const char specasm_doc_ld8[]; +extern const char specasm_doc_ldd[]; +extern const char specasm_doc_lddr[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_lddrx[]; +extern const char specasm_doc_lddx[]; +#endif + +extern const char specasm_doc_ldi[]; +extern const char specasm_doc_ldir[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_ldirx[]; +extern const char specasm_doc_ldix[]; +extern const char specasm_doc_ldpirx[]; +extern const char specasm_doc_ldws[]; +#endif + +#endif \ No newline at end of file diff --git a/src/descrm2z.c b/src/descrm2z.c new file mode 100644 index 0000000..a846836 --- /dev/null +++ b/src/descrm2z.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 "descrm2z.h" + +const char specasm_doc_map[] = "Instructs the linker to generate a map file."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_mirror[] = "Reverses the order of the bits in a."; +const char specasm_doc_mul[] = "Let de = d * e."; +#endif + +const char specasm_doc_neg[] = "Let a = 0 - a."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_nextreg[] = + "Stores the 2nd operand in the Next register identified by the first " + "operand."; +#endif + +const char specasm_doc_nop[] = "CPU does nothing for 1 m-cycle."; +const char specasm_doc_or[] = + "The result of a bitwise OR of a and the argument is stored " + "in a."; +const char specasm_doc_org[] = + "Assembler directive that sets the org address of the program, " + "i.e., the address the first byte in the .x or .t file that " + "contains the Main label is assembled at."; +const char specasm_doc_out1[] = + "Writes the register r to the device identified by bc."; +const char specasm_doc_out2[] = + "Writes a to the device at the adddress whose MSB " + "is taken from the a and whose LSB is n."; +const char specasm_doc_outd[] = + "b is decremented. (hl) is written to the device identified " + "by bc, where b has already been decremented. hl is " + "decremented."; +const char specasm_doc_outdr[] = + "b is decremented. (hl) is written to the device identified " + "by bc, where b has already been decremented. hl is " + "decremented. outdr repeats if b!=0. It consumes more t-states " + "when it repeats."; +const char specasm_doc_outi[] = + "b is decremented. (hl) is written to the device identified by " + "bc, where b has already been decremented. hl is incremented."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_outinb[] = + "(hl) is written to the device identified by bc. hl is incremented."; +#endif + +const char specasm_doc_outir[] = + "b is decremented. (hl) is written to the device identified by " + "bc, where b has already been decremented. hl is incremented. " + "outir repeats if b!=0. It consumes more t-states " + "when it repeats."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_pixelad[] = + "Stores in hl the address of the byte in the Spectrum's display file " + "that contains the pixel addressed by the x and y coordinates in " + "the e and d registers, respectively."; +const char specasm_doc_pixeldn[] = + "On entry hl should point to the start of a 8 bit pixel block in the " + "Spectrum's display file. On exit hl points to the same pixel block " + "one line down."; +#endif + +const char specasm_doc_pop[] = "Pops 2 bytes off the stack into the operand."; +const char specasm_doc_push[] = "Pushes the operand onto the stack."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_push_imm[] = + "Pushes a 16 bit immediate onto the stack. The immediate is big endian " + "encoded."; +#endif + +const char specasm_doc_res[] = "Resets bit n in the second operand."; +const char specasm_doc_ret[] = + "If there is no condition or the condition is met, a word " + "is popped off the stack into the PC, from which program " + "execution continues. ret takes fewer cycles if the condition " + "is not met."; +const char specasm_doc_reti[] = + "Return from interrupt. A word is popped off the stack into PC." + " An ei must be executed prior to the reti to renable " + "maskable interrupts."; +const char specasm_doc_retn[] = + "Return from NMI. A word is popped off the stack into PC and " + "the maskable interrupts are re-enabled if they were enabled " + "before the NMI."; +const char specasm_doc_rl[] = + "The operand is shifted left by 1 bit. The carry flag " + "is moved into bit 0 of the operand and the old bit 7 of " + "the operand is moved into the carry flag."; +const char specasm_doc_rla[] = + "a is rotated left 1 bit. The carry flag is moved to bit 0 of " + "a and its old bit 7 is moved to the carry flag."; +const char specasm_doc_rlc[] = + "The operand is rotated left 1 bit. The old bit 7 is moved to " + "both the carry flag and bit 0 of the operand."; +const char specasm_doc_rlca[] = + "a is rotated left 1 bit. The old bit 7 is moved " + "to both the carry flag and bit 0 of a."; +const char specasm_doc_rld[] = "Let tmp = (hl) >> 4 " + "Let (hl) = ((hl) << 4)|(a & $f) " + "Let a = (a & $f0)|tmp"; +const char specasm_doc_rr[] = + "The operand is shifted right by 1 bit. The carry flag " + "is moved into bit 7 of the operand and the old bit 0 of " + "the operand is moved into the carry flag."; +const char specasm_doc_rra[] = + "a is shifted right 1 bit. The carry flag is " + "moved into bit 7 of a and the old contents of a's " + "bit 0 are moved into the carry flag."; +const char specasm_doc_rrc[] = + "The operand is rotated right by 1 bit. Its old bit 0 is " + "moved into both the carry flag and bit 7 of the operand."; +const char specasm_doc_rrca[] = + "a is rotated right by 1 bit. a's old bit 0 is " + "moved into both the carry flag and bit 7 of a."; +const char specasm_doc_rrd[] = "Let tmp = a << 4 " + "Let a = (a & $f0)|((hl) & $f) " + "Let (hl) = tmp | ((hl) >> 4)"; +const char specasm_doc_rst[] = + "The PC is pushed to the stack and the CPU jumps to the " + "address n. Valid values of n are 0, 8, 16, 24, 32, 40, 48," + " and 56."; +const char specasm_doc_sbc[] = + "The second argument and the carry flag are subtracted from " + "the contents of the destination register, either a or hl."; +const char specasm_doc_scf[] = "Sets the carry flag."; +const char specasm_doc_set[] = "Sets bit n in the second operand."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_setae[] = + "Let a = $80 >> (e & 7). " + "Used to set a bit corresponding to a pixel in the Spectrum's display " + "file, where the x coordinate of the pixel is in e."; +#endif + +const char specasm_doc_sla[] = + "The operand is shifted left by 1 bit. Bit 0 " + "is set to 0. Bit 7 is moved into the carry flag."; +const char specasm_doc_sra[] = + "The operand is arithmetically shifted right by 1 bit. Bit 7 " + "remains unchanged. Bit 0 is moved into the carry flag."; +const char specasm_doc_srl[] = + "The operand is logically shifted right by 1 bit. Bit 7 is " + "set to 0. Bit 0 is moved into the carry flag."; +const char specasm_doc_sub[] = "The operand is subracted from a."; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +const char specasm_doc_swapnib[] = "Swaps the nibbles in a"; +const char specasm_doc_test[] = + "ANDs the immediate operand with a, sets the flags and discards " + "the result."; +#endif + +const char specasm_doc_xor[] = + "The result of a bitwise XOR of a and the argument is stored " + "in a."; +const char specasm_doc_zx81[] = + "Linker directive that causes string and character literals to " + "be transliterated from ASCII to ZX81 encoding. It also sets " + "org address to 16514."; diff --git a/src/descrm2z.h b/src/descrm2z.h new file mode 100644 index 0000000..8b5683d --- /dev/null +++ b/src/descrm2z.h @@ -0,0 +1,96 @@ +/* + * 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 DESCRM2Z_H +#define DESCRM2Z_H + +extern const char specasm_doc_map[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_mirror[]; +extern const char specasm_doc_mul[]; +#endif + +extern const char specasm_doc_neg[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_nextreg[]; +#endif + +extern const char specasm_doc_nop[]; +extern const char specasm_doc_or[]; +extern const char specasm_doc_org[]; +extern const char specasm_doc_out1[]; +extern const char specasm_doc_out2[]; +extern const char specasm_doc_outi[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_outinb[]; +#endif + +extern const char specasm_doc_outir[]; +extern const char specasm_doc_outd[]; +extern const char specasm_doc_outdr[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_pixelad[]; +extern const char specasm_doc_pixeldn[]; +#endif + +extern const char specasm_doc_pop[]; +extern const char specasm_doc_push[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_push_imm[]; +#endif + +extern const char specasm_doc_res[]; +extern const char specasm_doc_ret[]; +extern const char specasm_doc_reti[]; +extern const char specasm_doc_retn[]; +extern const char specasm_doc_rl[]; +extern const char specasm_doc_rla[]; +extern const char specasm_doc_rlc[]; +extern const char specasm_doc_rlca[]; +extern const char specasm_doc_rld[]; +extern const char specasm_doc_rr[]; +extern const char specasm_doc_rra[]; +extern const char specasm_doc_rrc[]; +extern const char specasm_doc_rrca[]; +extern const char specasm_doc_rrd[]; +extern const char specasm_doc_rst[]; +extern const char specasm_doc_sbc[]; +extern const char specasm_doc_scf[]; +extern const char specasm_doc_set[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_setae[]; +#endif + +extern const char specasm_doc_sla[]; +extern const char specasm_doc_sra[]; +extern const char specasm_doc_srl[]; +extern const char specasm_doc_sub[]; + +#ifdef SPECASM_TARGET_NEXT_OPCODES +extern const char specasm_doc_swapnib[]; +extern const char specasm_doc_test[]; +#endif + +extern const char specasm_doc_xor[]; +extern const char specasm_doc_zx81[]; + +#endif \ No newline at end of file diff --git a/src/doc.c b/src/doc.c index e41821f..2ff8efb 100644 --- a/src/doc.c +++ b/src/doc.c @@ -12,12 +12,14 @@ * 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 "descra2l.h" +#include "descrm2z.h" #include "doc.h" #include "editor.h" #include "line_common.h" @@ -26,6 +28,10 @@ #include +#ifdef SPECASM_NEXT_BANKED +void specasm_descr_bank(const char *ins_name); +#endif + #define SPECASM_DOC_ENCODING_X 16 #define SPECASM_DOC_M_CYCLES_X 27 #define SPECASM_DOC_T_STATES_X 30 @@ -39,6 +45,8 @@ struct specasm_ins_form_t_ { typedef struct specasm_ins_form_t_ specasm_ins_form_t; +/* clang-format off */ + #define SPECASM_DOC_ADC_FORMS 0 #define SPECASM_DOC_ADC_NUM_FORMS 6 #define SPECASM_DOC_ADD1_FORMS \ @@ -53,8 +61,21 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_ADD4_FORMS \ (SPECASM_DOC_ADD3_FORMS + SPECASM_DOC_ADD3_NUM_FORMS) #define SPECASM_DOC_ADD4_NUM_FORMS 1 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_ADD5_FORMS \ + (SPECASM_DOC_ADD4_FORMS + SPECASM_DOC_ADD4_NUM_FORMS) +#define SPECASM_DOC_ADD5_NUM_FORMS 1 +#define SPECASM_DOC_ADD6_FORMS \ + (SPECASM_DOC_ADD5_FORMS + SPECASM_DOC_ADD5_NUM_FORMS) +#define SPECASM_DOC_ADD6_NUM_FORMS 1 +#define SPECASM_DOC_ALIGN_FORMS \ + (SPECASM_DOC_ADD6_FORMS + SPECASM_DOC_ADD6_NUM_FORMS) +#else #define SPECASM_DOC_ALIGN_FORMS \ (SPECASM_DOC_ADD4_FORMS + SPECASM_DOC_ADD4_NUM_FORMS) +#endif + #define SPECASM_DOC_ALIGN_NUM_FORMS 1 #define SPECASM_DOC_AND_FORMS \ (SPECASM_DOC_ALIGN_FORMS + SPECASM_DOC_ALIGN_NUM_FORMS) @@ -62,8 +83,30 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_BIT_FORMS \ (SPECASM_DOC_AND_FORMS + SPECASM_DOC_AND_NUM_FORMS) #define SPECASM_DOC_BIT_NUM_FORMS 4 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_BRLC_FORMS \ + (SPECASM_DOC_BIT_FORMS + SPECASM_DOC_BIT_NUM_FORMS) +#define SPECASM_DOC_BRLC_NUM_FORMS 1 +#define SPECASM_DOC_BSLA_FORMS \ + (SPECASM_DOC_BRLC_FORMS + SPECASM_DOC_BRLC_NUM_FORMS) +#define SPECASM_DOC_BSLA_NUM_FORMS 1 +#define SPECASM_DOC_BSRA_FORMS \ + (SPECASM_DOC_BSLA_FORMS + SPECASM_DOC_BSLA_NUM_FORMS) +#define SPECASM_DOC_BSRA_NUM_FORMS 1 +#define SPECASM_DOC_BSRF_FORMS \ + (SPECASM_DOC_BSRA_FORMS + SPECASM_DOC_BSRA_NUM_FORMS) +#define SPECASM_DOC_BSRF_NUM_FORMS 1 +#define SPECASM_DOC_BSRL_FORMS \ + (SPECASM_DOC_BSRF_FORMS + SPECASM_DOC_BSRF_NUM_FORMS) +#define SPECASM_DOC_BSRL_NUM_FORMS 1 +#define SPECASM_DOC_CALL_FORMS \ + (SPECASM_DOC_BSRL_FORMS + SPECASM_DOC_BSRL_NUM_FORMS) +#else #define SPECASM_DOC_CALL_FORMS \ (SPECASM_DOC_BIT_FORMS + SPECASM_DOC_BIT_NUM_FORMS) +#endif + #define SPECASM_DOC_CALL_NUM_FORMS 3 #define SPECASM_DOC_CCF_FORMS \ (SPECASM_DOC_CALL_FORMS + SPECASM_DOC_CALL_NUM_FORMS) @@ -152,8 +195,18 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_JP_FORMS \ (SPECASM_DOC_INIR_FORMS + SPECASM_DOC_INIR_NUM_FORMS) #define SPECASM_DOC_JP_NUM_FORMS 6 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_JP_C_FORMS \ + (SPECASM_DOC_JP_FORMS + SPECASM_DOC_JP_NUM_FORMS) +#define SPECASM_DOC_JP_C_NUM_FORMS 1 +#define SPECASM_DOC_JR_FORMS \ + (SPECASM_DOC_JP_C_FORMS + SPECASM_DOC_JP_C_NUM_FORMS) +#else #define SPECASM_DOC_JR_FORMS \ (SPECASM_DOC_JP_FORMS + SPECASM_DOC_JP_NUM_FORMS) +#endif + #define SPECASM_DOC_JR_NUM_FORMS 3 #define SPECASM_DOC_LD1_FORMS \ (SPECASM_DOC_JR_FORMS + SPECASM_DOC_JR_NUM_FORMS) @@ -185,12 +238,57 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_LDDR_FORMS \ (SPECASM_DOC_LDD_FORMS + SPECASM_DOC_LDD_NUM_FORMS) #define SPECASM_DOC_LDDR_NUM_FORMS 2 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_LDDRX_FORMS \ + (SPECASM_DOC_LDDR_FORMS + SPECASM_DOC_LDDR_NUM_FORMS) +#define SPECASM_DOC_LDDRX_NUM_FORMS 2 +#define SPECASM_DOC_LDDX_FORMS \ + (SPECASM_DOC_LDDRX_FORMS + SPECASM_DOC_LDDRX_NUM_FORMS) +#define SPECASM_DOC_LDDX_NUM_FORMS 1 +#define SPECASM_DOC_LDI_FORMS \ + (SPECASM_DOC_LDDX_FORMS + SPECASM_DOC_LDDX_NUM_FORMS) +#else #define SPECASM_DOC_LDI_FORMS \ (SPECASM_DOC_LDDR_FORMS + SPECASM_DOC_LDDR_NUM_FORMS) +#endif + #define SPECASM_DOC_LDI_NUM_FORMS 1 #define SPECASM_DOC_LDIR_FORMS \ (SPECASM_DOC_LDI_FORMS + SPECASM_DOC_LDI_NUM_FORMS) #define SPECASM_DOC_LDIR_NUM_FORMS 2 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_LDIRX_FORMS \ + (SPECASM_DOC_LDIR_FORMS + SPECASM_DOC_LDIR_NUM_FORMS) +#define SPECASM_DOC_LDIRX_NUM_FORMS 2 +#define SPECASM_DOC_LDIX_FORMS \ + (SPECASM_DOC_LDIRX_FORMS + SPECASM_DOC_LDIRX_NUM_FORMS) +#define SPECASM_DOC_LDIX_NUM_FORMS 1 +#define SPECASM_DOC_LDPIRX_FORMS \ + (SPECASM_DOC_LDIX_FORMS + SPECASM_DOC_LDIX_NUM_FORMS) +#define SPECASM_DOC_LDPIRX_NUM_FORMS 2 +#define SPECASM_DOC_LDWS_FORMS \ + (SPECASM_DOC_LDPIRX_FORMS + SPECASM_DOC_LDPIRX_NUM_FORMS) +#define SPECASM_DOC_LDWS_NUM_FORMS 1 +#define SPECASM_DOC_MAP_FORMS \ + (SPECASM_DOC_LDWS_FORMS + SPECASM_DOC_LDWS_NUM_FORMS) +#define SPECASM_DOC_MAP_NUM_FORMS 1 +#define SPECASM_DOC_MIRROR_FORMS \ + (SPECASM_DOC_MAP_FORMS + SPECASM_DOC_MAP_NUM_FORMS) +#define SPECASM_DOC_MIRROR_NUM_FORMS 1 +#define SPECASM_DOC_MUL_FORMS \ + (SPECASM_DOC_MIRROR_FORMS + SPECASM_DOC_MIRROR_NUM_FORMS) +#define SPECASM_DOC_MUL_NUM_FORMS 1 +#define SPECASM_DOC_NEG_FORMS \ + (SPECASM_DOC_MUL_FORMS + SPECASM_DOC_MUL_NUM_FORMS) +#define SPECASM_DOC_NEG_NUM_FORMS 1 +#define SPECASM_DOC_NEXTREG_FORMS \ + (SPECASM_DOC_NEG_FORMS + SPECASM_DOC_NEG_NUM_FORMS) +#define SPECASM_DOC_NEXTREG_NUM_FORMS 2 +#define SPECASM_DOC_NOP_FORMS \ + (SPECASM_DOC_NEXTREG_FORMS + SPECASM_DOC_NEXTREG_NUM_FORMS) +#else #define SPECASM_DOC_MAP_FORMS \ (SPECASM_DOC_LDIR_FORMS + SPECASM_DOC_LDIR_NUM_FORMS) #define SPECASM_DOC_MAP_NUM_FORMS 1 @@ -199,6 +297,8 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_NEG_NUM_FORMS 1 #define SPECASM_DOC_NOP_FORMS \ (SPECASM_DOC_NEG_FORMS + SPECASM_DOC_NEG_NUM_FORMS) +#endif + #define SPECASM_DOC_NOP_NUM_FORMS 1 #define SPECASM_DOC_OR_FORMS \ (SPECASM_DOC_NOP_FORMS + SPECASM_DOC_NOP_NUM_FORMS) @@ -221,17 +321,46 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_OUTI_FORMS \ (SPECASM_DOC_OUTDR_FORMS + SPECASM_DOC_OUTDR_NUM_FORMS) #define SPECASM_DOC_OUTI_NUM_FORMS 1 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_OUTINB_FORMS \ + (SPECASM_DOC_OUTI_FORMS + SPECASM_DOC_OUTI_NUM_FORMS) +#define SPECASM_DOC_OUTINB_NUM_FORMS 1 +#define SPECASM_DOC_OUTIR_FORMS \ + (SPECASM_DOC_OUTINB_FORMS + SPECASM_DOC_OUTINB_NUM_FORMS) +#define SPECASM_DOC_OUTIR_NUM_FORMS 2 + +#define SPECASM_DOC_PIXELAD_FORMS \ + (SPECASM_DOC_OUTIR_FORMS + SPECASM_DOC_OUTIR_NUM_FORMS) +#define SPECASM_DOC_PIXELAD_NUM_FORMS 1 +#define SPECASM_DOC_PIXELDN_FORMS \ + (SPECASM_DOC_PIXELAD_FORMS + SPECASM_DOC_PIXELAD_NUM_FORMS) +#define SPECASM_DOC_PIXELDN_NUM_FORMS 1 +#define SPECASM_DOC_POP_FORMS \ + (SPECASM_DOC_PIXELDN_FORMS + SPECASM_DOC_PIXELDN_NUM_FORMS) +#else #define SPECASM_DOC_OUTIR_FORMS \ (SPECASM_DOC_OUTI_FORMS + SPECASM_DOC_OUTI_NUM_FORMS) #define SPECASM_DOC_OUTIR_NUM_FORMS 2 #define SPECASM_DOC_POP_FORMS \ (SPECASM_DOC_OUTIR_FORMS + SPECASM_DOC_OUTIR_NUM_FORMS) +#endif + #define SPECASM_DOC_POP_NUM_FORMS 3 #define SPECASM_DOC_PUSH_FORMS \ (SPECASM_DOC_POP_FORMS + SPECASM_DOC_POP_NUM_FORMS) #define SPECASM_DOC_PUSH_NUM_FORMS 3 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_PUSH_IMM_FORMS \ + (SPECASM_DOC_PUSH_FORMS + SPECASM_DOC_PUSH_NUM_FORMS) +#define SPECASM_DOC_PUSH_IMM_NUM_FORMS 1 +#define SPECASM_DOC_RES_FORMS \ + (SPECASM_DOC_PUSH_IMM_FORMS + SPECASM_DOC_PUSH_IMM_NUM_FORMS) +#else #define SPECASM_DOC_RES_FORMS \ (SPECASM_DOC_PUSH_FORMS + SPECASM_DOC_PUSH_NUM_FORMS) +#endif #define SPECASM_DOC_RES_NUM_FORMS 4 #define SPECASM_DOC_RET_FORMS \ (SPECASM_DOC_RES_FORMS + SPECASM_DOC_RES_NUM_FORMS) @@ -284,8 +413,18 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_SET_FORMS \ (SPECASM_DOC_SCF_FORMS + SPECASM_DOC_SCF_NUM_FORMS) #define SPECASM_DOC_SET_NUM_FORMS 4 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_SETAE_FORMS \ + (SPECASM_DOC_SET_FORMS + SPECASM_DOC_SET_NUM_FORMS) +#define SPECASM_DOC_SETAE_NUM_FORMS 1 +#define SPECASM_DOC_SLA_FORMS \ + (SPECASM_DOC_SETAE_FORMS + SPECASM_DOC_SETAE_NUM_FORMS) +#else #define SPECASM_DOC_SLA_FORMS \ (SPECASM_DOC_SET_FORMS + SPECASM_DOC_SET_NUM_FORMS) +#endif + #define SPECASM_DOC_SLA_NUM_FORMS 4 #define SPECASM_DOC_SRA_FORMS \ (SPECASM_DOC_SLA_FORMS + SPECASM_DOC_SLA_NUM_FORMS) @@ -296,10 +435,22 @@ typedef struct specasm_ins_form_t_ specasm_ins_form_t; #define SPECASM_DOC_SUB_FORMS \ (SPECASM_DOC_SRL_FORMS + SPECASM_DOC_SRL_NUM_FORMS) #define SPECASM_DOC_SUB_NUM_FORMS 5 + +#ifdef SPECASM_TARGET_NEXT_OPCODES +#define SPECASM_DOC_SWAPNIB_FORMS \ + (SPECASM_DOC_SUB_FORMS + SPECASM_DOC_SUB_NUM_FORMS) +#define SPECASM_DOC_SWAPNIB_NUM_FORMS 1 +#define SPECASM_DOC_TEST_FORMS \ + (SPECASM_DOC_SWAPNIB_FORMS + SPECASM_DOC_SWAPNIB_NUM_FORMS) +#define SPECASM_DOC_TEST_NUM_FORMS 1 +#define SPECASM_DOC_XOR_FORMS \ + (SPECASM_DOC_TEST_FORMS + SPECASM_DOC_TEST_NUM_FORMS) +#else #define SPECASM_DOC_XOR_FORMS \ (SPECASM_DOC_SUB_FORMS + SPECASM_DOC_SUB_NUM_FORMS) -#define SPECASM_DOC_XOR_NUM_FORMS 5 +#endif +#define SPECASM_DOC_XOR_NUM_FORMS 5 static const specasm_ins_form_t specasm_forms[] = { /* SPECASM_DOC_ADC_FORMS */ @@ -326,6 +477,13 @@ static const specasm_ins_form_t specasm_forms[] = { /* SPECASM_DOC_ADD4_FORMS */ {"iy,rr", "FD 9+rr", 4, 15 }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_ADD5_FORMS */ + {"rr,a", "ED 30+rr", 2, 8 }, + + /* SPECASM_DOC_ADD6_FORMS */ + {"rr,nn", "ED 34+nn", 4, 16 }, +#endif /* SPECASM_DOC_ALIGN_FORMS */ {"n", "0", 1, 4 }, @@ -337,11 +495,27 @@ static const specasm_ins_form_t specasm_forms[] = { {"(iy+d)", "FD A6 d", 5, 19 }, /* SPECASM_DOC_BIT_FORMS */ - {"n,r", "CB 40+b+r", 2, 8}, - {"n,(hl)", "CB 46+b", 3, 12}, - {"n,(ix+d)", "DDCBd46+b", 5, 20}, - {"n,(iy+d)", "FDCBd46+b", 5, 20}, + {"n,r", "CB 40+n+r", 2, 8}, + {"n,(hl)", "CB 46+n", 3, 12}, + {"n,(ix+d)", "DDCBd46+n", 5, 20}, + {"n,(iy+d)", "FDCBd46+n", 5, 20}, + +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_BRLC_FORMS */ + {"de,b", "ED 2C", 2, 8}, + + /* SPECASM_DOC_BSLA_FORMS */ + {"de,b", "ED 28", 2, 8}, + /* SPECASM_DOC_BSRA_FORMS */ + {"de,b", "ED 29", 2, 8}, + + /* SPECASM_DOC_BSRF_FORMS */ + {"de,b", "ED 2B", 2, 8}, + + /* SPECASM_DOC_BSRL_FORMS */ + {"de,b", "ED 2A", 2, 8}, +#endif /* SPECASM_DOC_CALL_FORMS */ {"nn", "CD n n", 5, 17}, {"cc,nn", "C4+cc n n", 5, 17}, @@ -468,6 +642,11 @@ static const specasm_ins_form_t specasm_forms[] = { {"cc,nn", "C2+cc n n", 3, 12}, {"cc,nn", "C2+cc n n", 2, 7}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_JP_C_FORMS */ + {"(c)", "ED 98", 3, 13}, +#endif + /* SPECASM_DOC_JR_FORMS */ {"n", "18 n", 3, 12}, {"cc,n", "40+cc n", 3, 12}, @@ -530,6 +709,15 @@ static const specasm_ins_form_t specasm_forms[] = { {"", "ED B8", 4, 16}, {"", "ED B8", 5, 21}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_LDDRX_FORMS */ + {"", "ED BC", 4, 16}, + {"", "ED BC", 5, 21}, + + /* SPECASM_DOC_LDDX_FORMS */ + {"", "ED AC", 4, 16}, +#endif + /* SPECASM_DOC_LDI_FORMS */ {"", "ED A0", 4, 16}, @@ -537,12 +725,40 @@ static const specasm_ins_form_t specasm_forms[] = { {"", "ED B0", 4, 16}, {"", "ED B0", 5, 21}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_LDIRX_FORMS */ + {"", "ED B4", 4, 16}, + {"", "ED B4", 5, 21}, + + /* SPECASM_DOC_LDIX_FORMS */ + {"", "ED A4", 4, 16}, + + /* SPECASM_DOC_LDPIRX_FORMS */ + {"", "ED B7", 4, 16}, + {"", "ED B7", 5, 21}, + + /* SPECASM_DOC_LDWS_FORMS */ + {"", "ED A5", 4, 16}, +#endif /* SPECASM_DOC_MAP_FORMS */ {"", "", 0, 0}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_MIRROR_FORMS */ + {"a", "ED 24", 2, 8}, + + /* SPECASM_DOC_MUL_FORMS */ + {"d,e", "ED 30", 2, 8}, +#endif + /* SPECASM_DOC_NEG_FORMS */ {"", "ED 44", 2, 8}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_NEXTREG_FORMS */ + {"n,a", "ED 92 n", 4, 17}, + {"n,m", "ED 91 n m", 5, 20}, +#endif /* SPECASM_DOC_NOP_FORMS */ {"", "0", 1, 4}, @@ -572,10 +788,21 @@ static const specasm_ins_form_t specasm_forms[] = { /* SPECASM_DOC_OUTI_FORMS */ {"","ED A3", 4, 16}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_OUTINB_FORMS */ + {"","ED 90", 4, 16}, +#endif /* SPECASM_DOC_OUTIR_FORMS */ {"","ED B3", 4, 16}, {"","ED B3", 5, 21}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_PIXELAD_FORMS */ + {"","ED 94", 2, 8}, + + /* SPECASM_DOC_PIXELDN_FORMS */ + {"","ED 93", 2, 8}, +#endif /* SPECASM_DOC_POP_FORMS */ {"rr", "C1+rr", 3, 10}, {"ix", "DD E1", 4, 14}, @@ -586,11 +813,15 @@ static const specasm_ins_form_t specasm_forms[] = { {"ix", "DD E5", 4, 15}, {"iy", "FD E5", 4, 15}, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_PUSH_IMM_FORMS */ + {"nm", "ED 8A+nm", 6, 23}, +#endif /* SPECASM_DOC_RES_FORMS */ {"n,r", "CB 80+b+r", 2, 8}, - {"n,(hl)", "CB 86+b", 4, 15}, - {"n,(ix+d)", "DDCBd86+b", 6, 23}, - {"n,(iy+d)", "FDCBd86+b", 6, 23}, + {"n,(hl)", "CB 86+n", 4, 15}, + {"n,(ix+d)", "DDCBd86+n", 6, 23}, + {"n,(iy+d)", "FDCBd86+n", 6, 23}, /* SPECASM_DOC_RET_FORMS */ {"", "C9", 3, 10}, @@ -661,10 +892,14 @@ static const specasm_ins_form_t specasm_forms[] = { /* SPECASM_DOC_SET_FORMS */ {"n,r", "CB C0+b+r", 2, 8}, - {"n,(hl)", "CB C6+b", 4, 15}, - {"n,(ix+d)", "DDCBdC6+b", 6, 23}, - {"n,(iy+d)", "FDCBdC6+b", 6, 23}, - + {"n,(hl)", "CB C6+n", 4, 15}, + {"n,(ix+d)", "DDCBdC6+n", 6, 23}, + {"n,(iy+d)", "FDCBdC6+n", 6, 23}, + +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_SETAE_FORMS */ + {"", "ED 95", 2, 8}, +#endif /* SPECASM_DOC_SLA_FORMS */ {"r", "CB 20+r", 2, 8}, {"(hl)", "CB 26", 4, 15}, @@ -690,6 +925,15 @@ static const specasm_ins_form_t specasm_forms[] = { {"(ix+d)", "DD 96 d", 5, 19 }, {"(iy+d)", "FD 96 d", 5, 19 }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + /* SPECASM_DOC_SWAPNIB_FORMS */ + {"", "ED 23", 2, 8}, + + /* SPECASM_DOC_TEST_FORMS */ + + {"n", "ED 27 n", 3, 11}, +#endif + /* SPECASM_DOC_XOR_FORMS */ {"r", "A8+r", 1, 4}, {"n", "EE n", 2, 7}, @@ -738,21 +982,14 @@ const uint8_t reg_encodings[][SPECASM_DOC_MAX_REG_ENCODING] = { { 0, 0, 0, 0, 0, 0, 0, 1, 17, 0, 0, 49, 0, 33}, { 57, 1, 9, 17, 25, 33, 49}, { 0, 0, 0, 0, 0, 0, 0, 1, 17, 33, 49 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 3, 2, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 2, 1, 0 }, }; /* * Must be in the same order as the opcode_table in line_parse.c. */ -const static char specasm_doc_add_16[] = - "The second argument is added to the contents of the " - "destination register. The carry flag is set according " - "to the result of the addition. h represents carry from " - "bit 11."; - -const static char specasm_doc_inc[] = - "The specified operand is incremented by 1."; - const static specasm_ins_doc_t docs[] = { { "adc", @@ -761,8 +998,7 @@ const static specasm_ins_doc_t docs[] = { 1, 0, 0, - "The second argument and the carry flag are added to the " - "contents of the destination register.", + specasm_doc_adc, "XX X X0X", }, { @@ -772,8 +1008,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The second argument is added to the contents of the " - "destination register.", + specasm_doc_add, "XX X X0X", }, { @@ -806,6 +1041,28 @@ const static specasm_ins_doc_t docs[] = { specasm_doc_add_16, " X 0X", }, + #ifdef SPECASM_TARGET_NEXT_OPCODES + { + "add", + SPECASM_DOC_ADD5_FORMS, + SPECASM_DOC_ADD5_NUM_FORMS, + 8, + 0, + 0, + specasm_doc_add_rra, + " ?", + }, + { + "add", + SPECASM_DOC_ADD6_FORMS, + SPECASM_DOC_ADD6_NUM_FORMS, + 9, + 0, + 0, + specasm_doc_add_16imm, + NULL, + }, + #endif { "align", SPECASM_DOC_ALIGN_FORMS, @@ -813,11 +1070,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "The align directive takes one immediate argument that must be " - "a power of 2, >= 2 and <= 256. It inserts null bytes into " - "the binary until the requested alignment is achieved. The " - "number of t-states consumed by an align directive is the " - "number of bytes inserted * 4.", + specasm_doc_align, NULL, }, { @@ -827,8 +1080,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The result of a bitwise AND of a and the " - "argument is stored in a.", + specasm_doc_and, "XX 1 X00", }, { @@ -838,10 +1090,62 @@ const static specasm_ins_doc_t docs[] = { 2, 8, 0, - "Sets the zero flag to 1 if bit n of the 2nd operand " - "is 0, or to 0 if bit N is 1.", + specasm_doc_bit, "?X 1 ?0 ", }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "brlc", + SPECASM_DOC_BRLC_FORMS, + SPECASM_DOC_BRLC_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_brlc, + NULL, + }, + { + "bsla", + SPECASM_DOC_BSLA_FORMS, + SPECASM_DOC_BSLA_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_bsla, + NULL, + }, + { + "bsra", + SPECASM_DOC_BSRA_FORMS, + SPECASM_DOC_BSRA_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_bsra, + NULL, + }, + { + "bsrf", + SPECASM_DOC_BSRF_FORMS, + SPECASM_DOC_BSRF_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_bsrf, + NULL, + }, + { + "bsrl", + SPECASM_DOC_BSRL_FORMS, + SPECASM_DOC_BSRL_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_bsrl, + NULL, + }, + +#endif { "call", SPECASM_DOC_CALL_FORMS, @@ -849,10 +1153,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, - "Pushes the PC on the stack and jumps to nn. The conditional " - "version of the instruction takes fewer t-states to execute " - "if the call is not taken." - , + specasm_doc_call, NULL, }, { @@ -862,7 +1163,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Inverts the carry flag.", + specasm_doc_ccf, " ? 0X" }, { @@ -872,9 +1173,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is subtracted from a setting the " - "flags accordingly. The result of the subtraction is " - "discarded.", + specasm_doc_cp, "XX X X1X", }, { @@ -884,10 +1183,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "(hl) is subtracted from a and the flags are set " - "accordingly. The result is discarded. bc and hl are " - "decremented. The p flag is set if bc != 0 after the " - "instruction has finished and is otherwise reset.", + specasm_doc_cpd, "XX X X1 ", }, { @@ -897,12 +1193,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "(hl) is subtracted from a and the flags are set accordingly. " - "The result is discarded. bc and hl are decremented. If bc>0 " - "and the result of the subtraction is != 0 cpdr " - "repeats. The p flag is set if bc!=0 after cpdr has " - "finished and is otherwise reset. The slower timings apply " - "when cpdr repeats.", + specasm_doc_cpdr, "XX X X1 ", }, { @@ -912,10 +1203,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "(hl) is subtracted from a and the flags are set accordingly. " - "The result is discarded. bc is decremented while hl is " - "incremented. The p flag is set if bc != 0 after the cpi has " - "finished and is otherwise reset.", + specasm_doc_cpi, "XX X X1 ", }, { @@ -925,12 +1213,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "(hl) is subtracted from a and the flags are set accordingly. " - "The result is discarded. bc is decremented while hl is " - "incremented. If bc>0 and the result of the subtraction is != 0" - " cpir repeats. The p flag is set if bc!=0 after cpir " - "has finished and is otherwise reset. The slower " - "timings apply when the cpir repeats.", + specasm_doc_cpir, "XX X X1 ", }, { @@ -940,7 +1223,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Invert a.", + specasm_doc_cpl, " 1 1 ", }, { @@ -950,7 +1233,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Conditionally adjusts a for BCD addition and subtraction.", + specasm_doc_daa, "XX X X X", }, { @@ -960,9 +1243,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Stores up to 4 bytes in the program binary. All ns must be " - "formatted in the same way. Only one byte can be specified if " - "an expression is used.", + specasm_doc_db, NULL, }, { @@ -972,7 +1253,7 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, - "The specified operand is decremented by 1.", + specasm_doc_dec, "XX X X1 ", }, { @@ -982,7 +1263,7 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, - "The specified operand is decremented by 1.", + specasm_doc_dec, NULL, }, { @@ -992,7 +1273,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Disables maskable interrupts.", + specasm_doc_di, NULL, }, { @@ -1002,9 +1283,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "b is decremented by 1. If the result is>0 the cpu jumps to " - "PC+2+n, where n is a signed 8 byte. djnz executes " - "more quickly when the jump it not taken.", + specasm_doc_djnz, NULL, }, { @@ -1014,7 +1293,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Stores c copies of the byte n in the binary.", + specasm_doc_ds, NULL, }, { @@ -1024,9 +1303,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Stores up to 2 words in the program binary. All nns must be " - "formatted in the same way. Only one word can be specified if " - "an expression is used.", + specasm_doc_dw, NULL, }, { @@ -1036,7 +1313,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Enables maskable interrupts.", + specasm_doc_ei, NULL, }, { @@ -1046,9 +1323,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "The contents of the two operands are exchanged. " - "ex af, af' affects all the flags while the other forms of the " - "instruction have no effect on the flags.", + specasm_doc_ex, "XXXXXXXX", }, { @@ -1058,7 +1333,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Exchange bc, de and hl with bc', de', hl'.", + specasm_doc_exx, NULL, }, { @@ -1068,7 +1343,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "CPU execution is suspended until the next interrupt or reset.", + specasm_doc_halt, NULL, }, { @@ -1078,8 +1353,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Sets the interrupt mode. With im 2 the MSB of the vector " - "address is taken from the i register.", + specasm_doc_im, NULL, }, { @@ -1089,8 +1363,7 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, - "Reads a byte from the device identified by bc and stores it " - "in r.", + specasm_doc_in, "XX X X0 ", }, { @@ -1100,9 +1373,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Reads a byte from the device adddress whose MSB is " - "taken from a and whose LSB is n. The byte is " - "stored in a.", + specasm_doc_in2, NULL, }, { @@ -1132,8 +1403,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "A byte is read from the device identified by bc and stored " - "in (hl). b and hl are decremented.", + specasm_doc_ind, "?X ? ?1 ", }, { @@ -1143,10 +1413,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "A byte is read from the device identified by bc and stored " - "(hl). b and hl are decremented. If bc!=0 indr " - "repeats. indr consumes more t-states when it " - "repeats.", + specasm_doc_indr, "?1 ? ?1 " }, { @@ -1156,8 +1423,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "A byte is read from the device identified by bc " - "and stored in (hl). b is decremented and h is incremented.", + specasm_doc_ini, "?X ? ?1 ", }, { @@ -1167,10 +1433,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "A byte is read from the device identified by bc " - "and stored in (hl). b is decremented and h is incremented. " - "If bc!=0 inir repeats. inir consumes more t-states when it " - "repeats.", + specasm_doc_inir, "?1 ? ?1 " }, { @@ -1180,11 +1443,21 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, - "Jump to the last operand if the condition is met or no " - "condition is supplied. Instruction consumes fewer t-states " - "if condition is not met.", + specasm_doc_jp, NULL, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "jp", + SPECASM_DOC_JP_C_FORMS, + SPECASM_DOC_JP_C_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_jp_c, + "?? ? ???" + }, +#endif { "jr", SPECASM_DOC_JR_FORMS, @@ -1192,9 +1465,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, - "Jump to PC+2+n if the condition is met or no condition is " - "supplied. Range of jump is -126 + 129 bytes. Instruction " - "consumes fewer t-states if condition is not met.", + specasm_doc_jr, NULL, }, { @@ -1204,8 +1475,7 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, - "Loads register pair from an immediate value or from an " - "absolute address.", + specasm_doc_ld1, NULL, }, { @@ -1215,7 +1485,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Loads SP with the value of another 16 bit register pair.", + specasm_doc_ld2, NULL, }, { @@ -1225,7 +1495,7 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, - "Loads byte register an immediate value or another register.", + specasm_doc_ld3, NULL, }, { @@ -1235,8 +1505,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "Stores a byte register to the memory location provided " - "by the first operand.", + specasm_doc_ld4, NULL, }, { @@ -1246,8 +1515,7 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, - "Loads a 16 bit value from a register into an absolute " - "address.", + specasm_doc_ld5, NULL, }, { @@ -1257,8 +1525,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Stores an 8 bit immediate to the memory location provided " - "by the first operand.", + specasm_doc_ld6, NULL, }, { @@ -1268,8 +1535,7 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, - "Indirectly loads a byte into register using the pointer given " - "in the second operator.", + specasm_doc_ld7, NULL, }, { @@ -1279,8 +1545,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Instructions to load and store the interrupt vector and " - "memory refresh registers to and from a.", + specasm_doc_ld8, NULL, }, { @@ -1290,7 +1555,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Load (hl) into (de). hl, de and bc are decremented.", + specasm_doc_ldd, " 0 X0 ", }, { @@ -1300,11 +1565,32 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Load (hl) into (de). hl, de and bc are decremented. If " - "bc!=0 lddr repeats. lddr consumes more t-states when it " - "repeats.", + specasm_doc_lddr, " 0 00 ", }, + +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "lddrx", + SPECASM_DOC_LDDRX_FORMS, + SPECASM_DOC_LDDRX_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_lddrx, + NULL, + }, + { + "lddx", + SPECASM_DOC_LDDX_FORMS, + SPECASM_DOC_LDDX_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_lddx, + NULL, + }, +#endif { "ldi", SPECASM_DOC_LDI_FORMS, @@ -1312,8 +1598,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Load (hl) into (de). Both hl and de are incremented while bc " - "is decremented.", + specasm_doc_ldi, " 0 X0 ", }, { @@ -1323,11 +1608,51 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Load (hl) into (de). Both hl and de are incremented while bc " - "is decremented. If bc!=0 ldir repeats. ldir " - "consumes more t-states when it repeats.", + specasm_doc_ldir, " 0 00 ", }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "ldirx", + SPECASM_DOC_LDIRX_FORMS, + SPECASM_DOC_LDIRX_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_ldirx, + NULL, + }, + { + "ldix", + SPECASM_DOC_LDIX_FORMS, + SPECASM_DOC_LDIX_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_ldix, + NULL, + }, + { + "ldpirx", + SPECASM_DOC_LDPIRX_FORMS, + SPECASM_DOC_LDPIRX_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_ldpirx, + NULL, + }, + { + "ldws", + SPECASM_DOC_LDWS_FORMS, + SPECASM_DOC_LDWS_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_ldws, + "XX X X0 ", + }, +#endif { "map", SPECASM_DOC_MAP_FORMS, @@ -1335,9 +1660,31 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Instructs the linker to generate a map file.", + specasm_doc_map, NULL, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "mirror", + SPECASM_DOC_MIRROR_FORMS, + SPECASM_DOC_MIRROR_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_mirror, + NULL, + }, + { + "mul", + SPECASM_DOC_MUL_FORMS, + SPECASM_DOC_MUL_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_mul, + NULL, + }, +#endif { "neg", SPECASM_DOC_NEG_FORMS, @@ -1345,9 +1692,21 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Let a = 0 - a.", + specasm_doc_neg, "XX X X1X", }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "nextreg", + SPECASM_DOC_NEXTREG_FORMS, + SPECASM_DOC_NEXTREG_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_nextreg, + NULL, + }, +#endif { "nop", SPECASM_DOC_NOP_FORMS, @@ -1355,7 +1714,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "CPU does nothing for 1 m-cycle.", + specasm_doc_nop, NULL, }, { @@ -1365,8 +1724,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The result of a bitwise OR of a and the argument is stored " - "in a.", + specasm_doc_or, "XX 0 X00", }, { @@ -1376,9 +1734,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Assembler directive that sets the org address of the program, " - "i.e., the address the first byte in the .x or .t file that " - "contains the Main label is assembled at.", + specasm_doc_org, NULL, }, { @@ -1388,7 +1744,7 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, - "Writes the register r to the device identified by bc.", + specasm_doc_out1, NULL, }, { @@ -1398,8 +1754,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Writes a to the device at the adddress whose MSB " - "is taken from the a and whose LSB is n.", + specasm_doc_out2, NULL, }, { @@ -1409,9 +1764,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "b is decremented. (hl) is written to the device identified " - "by bc, where b has already been decremented. hl is " - "decremented.", + specasm_doc_outd, "?X ? ?1 ", }, { @@ -1421,10 +1774,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "b is decremented. (hl) is written to the device identified " - "by bc, where b has already been decremented. hl is " - "decremented. outdr repeats if b!=0. It consumes more t-states " - "when it repeats.", + specasm_doc_outdr, "?1 ? ?1 ", }, { @@ -1434,10 +1784,21 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "b is decremented. (hl) is written to the device identified by " - "bc, where b has already been decremented. hl is incremented.", + specasm_doc_outi, "?X ? ?1 ", }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "outinb", + SPECASM_DOC_OUTINB_FORMS, + SPECASM_DOC_OUTINB_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_outinb, + "?? ? ???", + }, +#endif { "outir", SPECASM_DOC_OUTIR_FORMS, @@ -1445,12 +1806,31 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "b is decremented. (hl) is written to the device identified by " - "bc, where b has already been decremented. hl is incremented. " - "outir repeats if b!=0. It consumes more t-states " - "when it repeats.", + specasm_doc_outir, "?1 ? ?1 ", }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "pixelad", + SPECASM_DOC_PIXELAD_FORMS, + SPECASM_DOC_PIXELAD_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_pixelad, + NULL, + }, + { + "pixeldn", + SPECASM_DOC_PIXELDN_FORMS, + SPECASM_DOC_PIXELDN_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_pixeldn, + NULL, + }, +#endif { "pop", SPECASM_DOC_POP_FORMS, @@ -1458,7 +1838,7 @@ const static specasm_ins_doc_t docs[] = { 7, 0, 0, - "Pops 2 bytes off the stack into the operand.", + specasm_doc_pop, NULL, }, { @@ -1468,9 +1848,21 @@ const static specasm_ins_doc_t docs[] = { 7, 0, 0, - "Pushes the operand onto the stack.", + specasm_doc_push, NULL, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "push", + SPECASM_DOC_PUSH_IMM_FORMS, + SPECASM_DOC_PUSH_IMM_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_push_imm, + NULL, + }, +#endif { "res", SPECASM_DOC_RES_FORMS, @@ -1478,7 +1870,7 @@ const static specasm_ins_doc_t docs[] = { 2, 8, 0, - "Resets bit n in the second operand.", + specasm_doc_res, NULL, }, { @@ -1488,10 +1880,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, - "If there is no condition or the condition is met, a word " - "is popped off the stack into the PC, from which program " - "execution continues. ret takes fewer cycles if the condition " - "is not met.", + specasm_doc_ret, NULL, }, { @@ -1501,9 +1890,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Return from interrupt. A word is popped off the stack into PC." - " An ei must be executed prior to the reti to renable " - "maskable interrupts.", + specasm_doc_reti, NULL, }, { @@ -1513,9 +1900,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Return from NMI. A word is popped off the stack into PC and " - "the maskable interrupts are re-enabled if they were enabled " - "before the NMI.", + specasm_doc_retn, NULL, }, { @@ -1525,9 +1910,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is shifted left by 1 bit. The carry flag " - "is moved into bit 0 of the operand and the old bit 7 of " - "the operand is moved into the carry flag.", + specasm_doc_rl, "XX 0 X0X", }, { @@ -1537,8 +1920,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "a is rotated left 1 bit. The carry flag is moved to bit 0 of " - "a and its old bit 7 is moved to the carry flag.", + specasm_doc_rla, " 0 0X", }, { @@ -1548,8 +1930,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is rotated left 1 bit. The old bit 7 is moved to " - "both the carry flag and bit 0 of the operand.", + specasm_doc_rlc, "XX 0 X0X", }, { @@ -1559,8 +1940,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "a is rotated left 1 bit. The old bit 7 is moved " - "to both the carry flag and bit 0 of a.", + specasm_doc_rlca, " 0 0X", }, { @@ -1570,9 +1950,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Let tmp = (hl) >> 4 " - "Let (hl) = ((hl) << 4)|(a & $f) " - "Let a = (a & $f0)|tmp", + specasm_doc_rld, "XX 0 X0 ", }, { @@ -1582,9 +1960,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is shifted right by 1 bit. The carry flag " - "is moved into bit 7 of the operand and the old bit 0 of " - "the operand is moved into the carry flag.", + specasm_doc_rr, "XX 0 X0X", }, { @@ -1594,9 +1970,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "a is shifted right 1 bit. The carry flag is " - "moved into bit 7 of a and the old contents of a's " - "bit 0 are moved into the carry flag.", + specasm_doc_rra, " 0 0X", }, { @@ -1606,8 +1980,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is rotated right by 1 bit. Its old bit 0 is " - "moved into both the carry flag and bit 7 of the operand.", + specasm_doc_rrc, "XX 0 X0X", }, { @@ -1617,8 +1990,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "a is rotated right by 1 bit. a's old bit 0 is " - "moved into both the carry flag and bit 7 of a.", + specasm_doc_rrca, " 0 0X", }, { @@ -1628,9 +2000,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Let tmp = a << 4 " - "Let a = (a & $f0)|((hl) & $f) " - "Let (hl) = tmp | ((hl) >> 4)", + specasm_doc_rrd, "XX 0 X0 ", }, { @@ -1640,9 +2010,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "The PC is pushed to the stack and the CPU jumps to the " - "address n. Valid values of n are 0, 8, 16, 24, 32, 40, 48," - " and 56.", + specasm_doc_rst, NULL, }, { @@ -1652,8 +2020,7 @@ const static specasm_ins_doc_t docs[] = { 1, 0, 0, - "The second argument and the carry flag are subtracted from " - "the contents of the destination register, either a or hl.", + specasm_doc_sbc, "XX X X1X", }, { @@ -1663,7 +2030,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Sets the carry flag.", + specasm_doc_scf, " 0 01" }, { @@ -1673,9 +2040,21 @@ const static specasm_ins_doc_t docs[] = { 2, 8, 0, - "Sets bit n in the second operand.", + specasm_doc_set, NULL, }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "setae", + SPECASM_DOC_SETAE_FORMS, + SPECASM_DOC_SETAE_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_setae, + NULL, + }, +#endif { "sla", SPECASM_DOC_SLA_FORMS, @@ -1683,8 +2062,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is shifted left by 1 bit. Bit 0 " - "is set to 0. Bit 7 is moved into the carry flag.", + specasm_doc_sla, "XX 0 X0X", }, { @@ -1694,8 +2072,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is arithmetically shifted right by 1 bit. Bit 7 " - "remains unchanged. Bit 0 is moved into the carry flag.", + specasm_doc_sra, "XX 0 X0X", }, { @@ -1705,8 +2082,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is logically shifted right by 1 bit. Bit 7 is " - "set to 0. Bit 0 is moved into the carry flag.", + specasm_doc_srl, "XX 0 X0X", }, { @@ -1716,9 +2092,31 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The operand is subracted from a.", + specasm_doc_sub, "XX X X1X", }, +#ifdef SPECASM_TARGET_NEXT_OPCODES + { + "swapnib", + SPECASM_DOC_SWAPNIB_FORMS, + SPECASM_DOC_SWAPNIB_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_swapnib, + NULL, + }, + { + "test", + SPECASM_DOC_TEST_FORMS, + SPECASM_DOC_TEST_NUM_FORMS, + 0, + 0, + 0, + specasm_doc_test, + "XX X X?X", + }, +#endif { "xor", SPECASM_DOC_XOR_FORMS, @@ -1726,8 +2124,7 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, - "The result of a bitwise XOR of a and the argument is stored " - "in a.", + specasm_doc_xor, "XX 0 X00", }, { @@ -1737,17 +2134,17 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, - "Linker directive that causes string and character literals to " - "be transliterated from ASCII to ZX81 encoding. It also sets " - "org address to 16514.", + specasm_doc_zx81, NULL, }, }; +/* clang-format on */ + const uint8_t max_docs = sizeof(docs) / sizeof(specasm_ins_doc_t); -static uint8_t prv_pretty_print(uint8_t y, const char * text) +static uint8_t prv_pretty_print(uint8_t y, const char *text) { uint8_t i; @@ -1789,16 +2186,16 @@ static uint8_t prv_print_register_encoding(const specasm_ins_doc_t *doc, { uint8_t i; uint8_t reg_encoding; - const char* reg_name; + const char *reg_name; char *s2; char *rr_start = NULL; - const uint8_t* enc; + const uint8_t *enc; char *s = scratch; if (!doc->reg_encoding) return y; - enc = (const uint8_t*) ®_encodings[doc->reg_encoding-1]; + enc = (const uint8_t *)®_encodings[doc->reg_encoding - 1]; for (i = 0; i < SPECASM_DOC_MAX_REG_ENCODING; i++) { reg_encoding = enc[i]; @@ -1836,7 +2233,7 @@ static uint8_t prv_print_register_encoding(const specasm_ins_doc_t *doc, if (!reg_name[1]) s2++; memcpy(s2, reg_name, strlen(reg_name)); - s +=3; + s += 3; } } specasm_util_print(scratch, 0, y, PAPER_BLUE | INK_WHITE); @@ -1852,7 +2249,7 @@ static uint8_t prv_print_register_encoding(const specasm_ins_doc_t *doc, s2++; itoa(reg_encoding, s2, 16); s2[strlen(s2)] = ' '; - s +=3; + s += 3; } } @@ -1872,8 +2269,7 @@ static void prv_print_flags(uint8_t y, const char *flags) specasm_util_print(flags, 12, y, PAPER_BLACK | INK_WHITE | 64); } -static uint8_t prv_print_bits_encoding(const specasm_ins_doc_t *doc, - uint8_t y) +static uint8_t prv_print_bits_encoding(const specasm_ins_doc_t *doc, uint8_t y) { uint8_t i; uint8_t x; @@ -1884,7 +2280,7 @@ static uint8_t prv_print_bits_encoding(const specasm_ins_doc_t *doc, return y; memset(scratch, ' ', SPECASM_LINE_MAX_LEN); - scratch[15] = 'b'; + scratch[15] = 'n'; specasm_util_print(scratch, 0, y, PAPER_WHITE | INK_BLACK); y++; @@ -1900,7 +2296,7 @@ static uint8_t prv_print_bits_encoding(const specasm_ins_doc_t *doc, for (i = 0; i < 8; i++) { num = doc->bits * i; itoa(num, scratch, 16); - x = (i*4)+1; + x = (i * 4) + 1; if (num < 16) x++; specasm_util_print(scratch, x, y, PAPER_BLACK | INK_WHITE); @@ -1909,8 +2305,7 @@ static uint8_t prv_print_bits_encoding(const specasm_ins_doc_t *doc, return y + 2; } -static uint8_t prv_print_cc_encoding(const specasm_ins_doc_t *doc, - uint8_t y) +static uint8_t prv_print_cc_encoding(const specasm_ins_doc_t *doc, uint8_t y) { uint8_t i; uint8_t x; @@ -1929,19 +2324,19 @@ static uint8_t prv_print_cc_encoding(const specasm_ins_doc_t *doc, if (strcmp(doc->name, "jr")) { limit = 8; - specasm_util_print(" nz z nc c po pe p m ", 0, - y, PAPER_BLUE | INK_WHITE); + specasm_util_print(" nz z nc c po pe p m ", 0, y, + PAPER_BLUE | INK_WHITE); } else { limit = 4; - specasm_util_print(" nz z nc c ", 0, - y, PAPER_BLUE | INK_WHITE); + specasm_util_print(" nz z nc c ", 0, y, + PAPER_BLUE | INK_WHITE); } y++; for (i = 0; i < limit; i++) { num = doc->all_cc * i; itoa(num, scratch, 16); - x = (i*4)+1; + x = (i * 4) + 1; if (num < 16) x++; specasm_util_print(scratch, x, y, PAPER_BLACK | INK_WHITE); @@ -1956,9 +2351,9 @@ static void prv_draw_help(uint8_t ins_id) uint8_t i; uint8_t y; uint8_t col; - const specasm_ins_doc_t* doc = &docs[ins_id]; + const specasm_ins_doc_t *doc = &docs[ins_id]; const specasm_ins_form_t *form; - const char* ins_name = doc->name; + const char *ins_name = doc->name; uint8_t ins_name_len = strlen(ins_name); specasm_cls(PAPER_BLACK | INK_WHITE); @@ -1996,6 +2391,9 @@ static void prv_draw_help(uint8_t ins_id) y = prv_print_register_encoding(doc, y + 1); y = prv_print_bits_encoding(doc, y); y = prv_print_cc_encoding(doc, y); +#ifdef SPECASM_NEXT_BANKED + specasm_descr_bank(ins_name); +#endif y = prv_pretty_print(y, doc->description); prv_print_flags(y + 1, doc->flags); } @@ -2011,7 +2409,7 @@ static uint8_t prv_find_mnemomic(const char *ins_name) return 0; l = 0; - r =max_docs - 1; + r = max_docs - 1; while (l <= r) { m = (l + r) >> 1; @@ -2023,7 +2421,9 @@ static uint8_t prv_find_mnemomic(const char *ins_name) break; r = m - 1; } else { - for (; m > 0 && !strcmp(docs[m-1].name, ins_name); m--); + for (; m > 0 && !strcmp(docs[m - 1].name, ins_name); + m--) + ; return m; } } @@ -2050,7 +2450,7 @@ void specasm_help_banked(const char *ins_name) prv_draw_help(id); do { specasm_sleep_ms(25); - } while(!(k = in_inkey())); + } while (!(k = in_inkey())); redraw = 0; if (k == SPECASM_KEY_LEFT) { @@ -2072,7 +2472,7 @@ void specasm_help_banked(const char *ins_name) } else { break; } - } while(1); + } while (1); specasm_cls(PAPER_BLACK | INK_WHITE); } diff --git a/src/specasm_trampolines_next.c b/src/specasm_trampolines_next.c index 2775f82..44133f4 100644 --- a/src/specasm_trampolines_next.c +++ b/src/specasm_trampolines_next.c @@ -12,7 +12,7 @@ * 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 @@ -23,6 +23,9 @@ #define SPECASM_NEXT_DUMP_BANK (44 << 1) #define SPECASM_NEXT_EDITOR_BANK (45 << 1) #define SPECASM_NEXT_CLIP_BANK (46 << 1) +#define SPECASM_NEXT_HELP_BANK (47 << 1) +#define SPECASM_NEXT_HELP_AL_BANK (47 << 1) + 1 +#define SPECASM_NEXT_HELP_MZ_BANK (48 << 1) + 1 #ifdef UNITTESTS #define SPECASM_NEXT_UNIT_BANK (45 << 1) @@ -51,6 +54,7 @@ void specasm_draw_status_banked(void); void specasm_handle_key_press_banked(uint8_t k); void specasm_editor_reset_banked(void); void specasm_editor_preload_banked(const char *fname); +void specasm_help_banked(const char *ins_name); #ifdef UNITTESTS void specasm_peer_write_state_banked_e(const char *fname, uint16_t checksum); @@ -64,7 +68,7 @@ char *specasm_get_long_imm_e(const char *str, long *val, uint8_t *flags) char *e; ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_PARSE_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK + 1]); e = specasm_get_long_imm_banked_e(str, val, flags); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -72,14 +76,13 @@ char *specasm_get_long_imm_e(const char *str, long *val, uint8_t *flags) return e; } - void specasm_append_empty_line_e(void) { unsigned char bank_l = ZXN_READ_MMU6(); unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_PARSE_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK + 1]); specasm_append_empty_line_banked_e(); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -91,7 +94,7 @@ void specasm_delete_lines(unsigned int start, unsigned int end) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_PARSE_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK + 1]); specasm_delete_lines_banked(start, end); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -103,7 +106,7 @@ void specasm_insert_lines_e(unsigned int l, unsigned int count) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_PARSE_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK + 1]); specasm_insert_lines_banked_e(l, count); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -115,7 +118,7 @@ void specasm_parse_line_e(unsigned int l, const char *str) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_PARSE_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK + 1]); specasm_parse_line_banked_e(l, str); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -130,7 +133,7 @@ uint8_t specasm_parse_mnemomic_e(const char *str, uint8_t i, unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_PARSE_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_PARSE_BANK + 1]); e = specasm_parse_mnemomic_banked_e(str, i, line); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -145,7 +148,7 @@ void specasm_init_dump_table(void) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_DUMP_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK + 1]); specasm_init_dump_table_banked(); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -159,7 +162,7 @@ uint8_t specasm_dump_opcode_e(const specasm_line_t *line, char *buf) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_DUMP_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK + 1]); e = specasm_dump_opcode_banked_e(line, buf); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -174,7 +177,7 @@ void specasm_format_line_e(char *buf, unsigned int l) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_DUMP_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_DUMP_BANK + 1]); specasm_format_line_banked_e(buf, l); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -186,7 +189,7 @@ void specasm_clip_reset(void) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_CLIP_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK + 1]); specasm_clip_reset_banked(); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -198,7 +201,7 @@ void specasm_clip_add_line_e(const char *line) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_CLIP_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK + 1]); specasm_clip_add_line_banked_e(line); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -211,7 +214,7 @@ uint16_t specasm_clip_get_line(uint16_t ptr, char *buffer) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_CLIP_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK + 1]); e = specasm_clip_get_line_banked(ptr, buffer); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -226,7 +229,7 @@ uint16_t specasm_clip_get_line_count(void) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_CLIP_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_CLIP_BANK + 1]); e = specasm_clip_get_line_count_banked(); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -241,7 +244,7 @@ void specasm_draw_status(void) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_EDITOR_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK + 1]); specasm_draw_status_banked(); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -253,7 +256,7 @@ void specasm_handle_key_press(uint8_t k) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_EDITOR_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK + 1]); specasm_handle_key_press_banked(k); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -265,7 +268,7 @@ void specasm_editor_reset(void) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_EDITOR_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK + 1]); specasm_editor_reset_banked(); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -277,11 +280,32 @@ void specasm_editor_preload(const char *fname) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_EDITOR_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_EDITOR_BANK + 1]); specasm_editor_preload_banked(fname); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); } + +void specasm_descr_bank(const char *ins_name) +{ + unsigned char bank = (ins_name[0] == 0 || ins_name[0] < 'm') + ? SPECASM_NEXT_HELP_AL_BANK + : SPECASM_NEXT_HELP_MZ_BANK; + ZXN_WRITE_MMU7(_z_page_table[bank]); +} + +void specasm_help(const char *ins_name) +{ + unsigned char bank_l = ZXN_READ_MMU6(); + unsigned char bank_h = ZXN_READ_MMU7(); + + ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_HELP_BANK]); + /* We'll set MMU7 when we're displaying the description. */ + specasm_help_banked(ins_name); + ZXN_WRITE_MMU7(bank_h); + ZXN_WRITE_MMU6(bank_l); +} + #else void specasm_peer_write_state_e(const char *fname, uint16_t checksum) { @@ -289,7 +313,7 @@ void specasm_peer_write_state_e(const char *fname, uint16_t checksum) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_UNIT_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_UNIT_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_UNIT_BANK + 1]); specasm_peer_write_state_banked_e(fname, checksum); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); @@ -302,7 +326,7 @@ uint16_t specasm_peer_read_state_e(const char *fname) unsigned char bank_h = ZXN_READ_MMU7(); ZXN_WRITE_MMU6(_z_page_table[SPECASM_NEXT_UNIT_BANK]); - ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_UNIT_BANK+1]); + ZXN_WRITE_MMU7(_z_page_table[SPECASM_NEXT_UNIT_BANK + 1]); e = specasm_peer_read_state_banked_e(fname); ZXN_WRITE_MMU7(bank_h); ZXN_WRITE_MMU6(bank_l); From 3ece48eef571b9dc6416808573aba94a02c963a7 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 8 Feb 2025 23:34:11 +0100 Subject: [PATCH 11/24] unittests: don't include docs in unittests There are no tests for the docs (and they aren't really needed and would be tricky to write) and docs.c currently contains spectrum specific code. Signed-off-by: Mark Ryan --- src/editor.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/editor.c b/src/editor.c index 26b94c9..1e4bd20 100644 --- a/src/editor.c +++ b/src/editor.c @@ -12,7 +12,7 @@ * 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 "editor.h" #include "editor_buffers.h" @@ -26,7 +26,6 @@ #include "scratch.h" #include "state.h" - #include #include @@ -106,8 +105,9 @@ static void specasm_dump_line_e(unsigned int l, uint8_t r, uint8_t inv) (void)specasm_text_print(scratch, 0, r, col); return; } - for (i = SPECASM_LINE_MAX_OPCODE + 1; (i < SPECASM_LINE_MAX_LEN) && - (scratch[i] != ';'); i++); + for (i = SPECASM_LINE_MAX_OPCODE + 1; + (i < SPECASM_LINE_MAX_LEN) && (scratch[i] != ';'); i++) + ; scratch[i] = 0; (void)specasm_text_print(scratch, 0, r, col); if (i < SPECASM_LINE_MAX_LEN - 2) { @@ -562,6 +562,7 @@ static uint8_t prv_single_char_command_e(uint8_t ch) case 'v': specasm_selecting_clip_paste_e(); break; +#ifndef UNITTESTS case 'h': specasm_text_set_flash(col, row, 0); specasm_help("a"); @@ -569,6 +570,7 @@ static uint8_t prv_single_char_command_e(uint8_t ch) specasm_draw_screen(line - row); specasm_draw_status(); break; +#endif #endif default: err_type = SPECASM_ERROR_BAD_COMMAND; @@ -695,12 +697,14 @@ static uint8_t prv_long_command_e(char *com, uint8_t len) return 0; } else if ((com[0] == 'f') && (com[1] == 'l') && !com[2]) { specasm_selecting_flags(); +#ifndef UNITTESTS } else if (com[0] == 'h' && com[1] == ' ') { specasm_text_set_flash(col, row, 0); specasm_help(&com[2]); specasm_text_set_flash(col, row, SPECASM_FLASH); specasm_draw_screen(line - row); specasm_draw_status(); +#endif #endif } else { err_type = SPECASM_ERROR_BAD_COMMAND; @@ -1207,7 +1211,6 @@ static uint8_t prv_select_block_down(uint16_t adj) return 1; } - static uint8_t prv_select_block_up(uint8_t adj) { unsigned int new_line; From c5713a450518cc193243bbabfc3d328cb42f50ad Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 26 Jan 2025 16:11:09 +0100 Subject: [PATCH 12/24] specasm: fix a bug in the help command The help command with no argument was supposed to take the user to the first instruction, adc. This didn't work however as the string passed to the specasm_help function was stored in the editor bank and swapped out when the help function was called. Now we store it on the stack. A bit of code reformatting is done as well. Signed-off-by: Mark Ryan --- src/editor.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/editor.c b/src/editor.c index 1e4bd20..9d49287 100644 --- a/src/editor.c +++ b/src/editor.c @@ -513,6 +513,11 @@ static void prv_exit_command_mode(uint8_t m) static uint8_t prv_single_char_command_e(uint8_t ch) { uint8_t reset = 0; +#if defined(SPECASM_TARGET_NEXT_OPCODES) || defined(SPECASM_TARGET_128) +#ifndef UNITTESTS + char start_help[2]; +#endif +#endif switch (ch) { case 'n': @@ -564,8 +569,10 @@ static uint8_t prv_single_char_command_e(uint8_t ch) break; #ifndef UNITTESTS case 'h': + start_help[0] = 'a'; + start_help[1] = 0; specasm_text_set_flash(col, row, 0); - specasm_help("a"); + specasm_help(start_help); specasm_text_set_flash(col, row, SPECASM_FLASH); specasm_draw_screen(line - row); specasm_draw_status(); From 9890d32177a2650644a1de1cc136cdf57b2f4fe9 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 26 Jan 2025 17:20:24 +0100 Subject: [PATCH 13/24] specasm: Mark zxn specific instructions in the help Print a little zxn identifier in red at the top left of the documentation for all Spectrum Next specific instructions. Signed-off-by: Mark Ryan --- src/doc.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 295 insertions(+), 2 deletions(-) diff --git a/src/doc.c b/src/doc.c index 2ff8efb..69ffd2b 100644 --- a/src/doc.c +++ b/src/doc.c @@ -951,6 +951,9 @@ struct specasm_ins_doc_t_ { uint8_t reg_encoding; uint8_t bits; uint8_t all_cc; +#ifdef SPECASM_TARGET_NEXT_OPCODES + uint8_t zxn; +#endif const char *description; const char *flags; }; @@ -998,6 +1001,9 @@ const static specasm_ins_doc_t docs[] = { 1, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_adc, "XX X X0X", }, @@ -1008,6 +1014,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_add, "XX X X0X", }, @@ -1018,6 +1027,9 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_add_16, " X 0X", }, @@ -1028,6 +1040,9 @@ const static specasm_ins_doc_t docs[] = { 4, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_add_16, " X 0X", }, @@ -1038,6 +1053,9 @@ const static specasm_ins_doc_t docs[] = { 5, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_add_16, " X 0X", }, @@ -1049,6 +1067,7 @@ const static specasm_ins_doc_t docs[] = { 8, 0, 0, + 1, specasm_doc_add_rra, " ?", }, @@ -1059,6 +1078,7 @@ const static specasm_ins_doc_t docs[] = { 9, 0, 0, + 1, specasm_doc_add_16imm, NULL, }, @@ -1070,6 +1090,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_align, NULL, }, @@ -1080,6 +1103,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_and, "XX 1 X00", }, @@ -1090,6 +1116,9 @@ const static specasm_ins_doc_t docs[] = { 2, 8, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_bit, "?X 1 ?0 ", }, @@ -1101,6 +1130,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_brlc, NULL, }, @@ -1111,6 +1141,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_bsla, NULL, }, @@ -1121,6 +1152,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_bsra, NULL, }, @@ -1131,6 +1163,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_bsrf, NULL, }, @@ -1141,6 +1174,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_bsrl, NULL, }, @@ -1153,6 +1187,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_call, NULL, }, @@ -1163,6 +1200,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ccf, " ? 0X" }, @@ -1173,6 +1213,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_cp, "XX X X1X", }, @@ -1183,6 +1226,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_cpd, "XX X X1 ", }, @@ -1193,6 +1239,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_cpdr, "XX X X1 ", }, @@ -1203,6 +1252,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_cpi, "XX X X1 ", }, @@ -1213,6 +1265,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_cpir, "XX X X1 ", }, @@ -1223,6 +1278,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_cpl, " 1 1 ", }, @@ -1233,6 +1291,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_daa, "XX X X X", }, @@ -1243,6 +1304,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_db, NULL, }, @@ -1253,6 +1317,9 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_dec, "XX X X1 ", }, @@ -1263,6 +1330,9 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_dec, NULL, }, @@ -1273,6 +1343,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_di, NULL, }, @@ -1283,6 +1356,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_djnz, NULL, }, @@ -1293,6 +1369,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ds, NULL, }, @@ -1303,6 +1382,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_dw, NULL, }, @@ -1313,6 +1395,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ei, NULL, }, @@ -1323,6 +1408,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ex, "XXXXXXXX", }, @@ -1333,6 +1421,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_exx, NULL, }, @@ -1343,6 +1434,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_halt, NULL, }, @@ -1353,6 +1447,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_im, NULL, }, @@ -1363,6 +1460,9 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_in, "XX X X0 ", }, @@ -1373,6 +1473,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_in2, NULL, }, @@ -1383,6 +1486,9 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_inc, "XX X X0 ", }, @@ -1393,6 +1499,9 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_inc, NULL, }, @@ -1403,6 +1512,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ind, "?X ? ?1 ", }, @@ -1413,6 +1525,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_indr, "?1 ? ?1 " }, @@ -1423,6 +1538,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ini, "?X ? ?1 ", }, @@ -1433,6 +1551,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_inir, "?1 ? ?1 " }, @@ -1443,6 +1564,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_jp, NULL, }, @@ -1454,6 +1578,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_jp_c, "?? ? ???" }, @@ -1465,6 +1590,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_jr, NULL, }, @@ -1475,6 +1603,9 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld1, NULL, }, @@ -1485,6 +1616,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld2, NULL, }, @@ -1495,6 +1629,9 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld3, NULL, }, @@ -1505,6 +1642,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld4, NULL, }, @@ -1515,6 +1655,9 @@ const static specasm_ins_doc_t docs[] = { 3, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld5, NULL, }, @@ -1525,6 +1668,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld6, NULL, }, @@ -1535,6 +1681,9 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld7, NULL, }, @@ -1545,6 +1694,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ld8, NULL, }, @@ -1555,6 +1707,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ldd, " 0 X0 ", }, @@ -1565,6 +1720,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_lddr, " 0 00 ", }, @@ -1577,6 +1735,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_lddrx, NULL, }, @@ -1587,6 +1746,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_lddx, NULL, }, @@ -1598,6 +1758,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ldi, " 0 X0 ", }, @@ -1608,6 +1771,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ldir, " 0 00 ", }, @@ -1619,6 +1785,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_ldirx, NULL, }, @@ -1629,6 +1796,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_ldix, NULL, }, @@ -1639,6 +1807,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_ldpirx, NULL, }, @@ -1649,6 +1818,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_ldws, "XX X X0 ", }, @@ -1660,6 +1830,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_map, NULL, }, @@ -1671,6 +1844,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_mirror, NULL, }, @@ -1681,6 +1855,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_mul, NULL, }, @@ -1692,6 +1867,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_neg, "XX X X1X", }, @@ -1703,6 +1881,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_nextreg, NULL, }, @@ -1714,6 +1893,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_nop, NULL, }, @@ -1724,6 +1906,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_or, "XX 0 X00", }, @@ -1734,6 +1919,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_org, NULL, }, @@ -1744,6 +1932,9 @@ const static specasm_ins_doc_t docs[] = { 6, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_out1, NULL, }, @@ -1754,6 +1945,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_out2, NULL, }, @@ -1764,6 +1958,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_outd, "?X ? ?1 ", }, @@ -1774,6 +1971,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_outdr, "?1 ? ?1 ", }, @@ -1784,6 +1984,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_outi, "?X ? ?1 ", }, @@ -1795,6 +1998,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_outinb, "?? ? ???", }, @@ -1806,6 +2010,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_outir, "?1 ? ?1 ", }, @@ -1817,6 +2024,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_pixelad, NULL, }, @@ -1827,6 +2035,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_pixeldn, NULL, }, @@ -1838,6 +2047,9 @@ const static specasm_ins_doc_t docs[] = { 7, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_pop, NULL, }, @@ -1848,6 +2060,9 @@ const static specasm_ins_doc_t docs[] = { 7, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_push, NULL, }, @@ -1859,6 +2074,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_push_imm, NULL, }, @@ -1870,6 +2086,9 @@ const static specasm_ins_doc_t docs[] = { 2, 8, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_res, NULL, }, @@ -1880,6 +2099,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 8, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_ret, NULL, }, @@ -1890,6 +2112,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_reti, NULL, }, @@ -1900,6 +2125,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_retn, NULL, }, @@ -1910,6 +2138,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rl, "XX 0 X0X", }, @@ -1920,6 +2151,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rla, " 0 0X", }, @@ -1930,6 +2164,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rlc, "XX 0 X0X", }, @@ -1940,6 +2177,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rlca, " 0 0X", }, @@ -1950,6 +2190,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rld, "XX 0 X0 ", }, @@ -1960,6 +2203,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rr, "XX 0 X0X", }, @@ -1970,6 +2216,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rra, " 0 0X", }, @@ -1980,6 +2229,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rrc, "XX 0 X0X", }, @@ -1990,6 +2242,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rrca, " 0 0X", }, @@ -2000,6 +2255,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rrd, "XX 0 X0 ", }, @@ -2010,6 +2268,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_rst, NULL, }, @@ -2020,6 +2281,9 @@ const static specasm_ins_doc_t docs[] = { 1, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_sbc, "XX X X1X", }, @@ -2030,6 +2294,9 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_scf, " 0 01" }, @@ -2040,6 +2307,9 @@ const static specasm_ins_doc_t docs[] = { 2, 8, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_set, NULL, }, @@ -2051,6 +2321,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_setae, NULL, }, @@ -2062,6 +2333,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_sla, "XX 0 X0X", }, @@ -2072,6 +2346,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_sra, "XX 0 X0X", }, @@ -2082,6 +2359,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_srl, "XX 0 X0X", }, @@ -2092,6 +2372,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_sub, "XX X X1X", }, @@ -2103,6 +2386,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_swapnib, NULL, }, @@ -2113,6 +2397,7 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, + 1, specasm_doc_test, "XX X X?X", }, @@ -2124,6 +2409,9 @@ const static specasm_ins_doc_t docs[] = { 2, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_xor, "XX 0 X00", }, @@ -2134,10 +2422,12 @@ const static specasm_ins_doc_t docs[] = { 0, 0, 0, +#ifdef SPECASM_TARGET_NEXT_OPCODES + 0, +#endif specasm_doc_zx81, NULL, }, - }; /* clang-format on */ @@ -2364,7 +2654,10 @@ static void prv_draw_help(uint8_t ins_id) memcpy(&scratch[x], ins_name, ins_name_len); specasm_util_print(scratch, 0, 0, PAPER_BLUE | INK_WHITE | 64); - +#ifdef SPECASM_TARGET_NEXT_OPCODES + if (doc->zxn) + specasm_util_print("zxn", 0, 0, PAPER_BLUE | INK_RED | 64); +#endif y = 2; memcpy(scratch, "Opcode Encoding M T", SPECASM_LINE_MAX_LEN); From 0bbc1a016474b868120f16218bb71be1423497b0 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 26 Jan 2025 17:29:38 +0100 Subject: [PATCH 14/24] specasm: fix a small off by one bug in help It happens when typing something like help m. There's no instruction called 'm', but there is one called map. Up until now the search algorithm would take you to the last instruction beginning with 'l', rather than the first beginning with m, which is what happens when you press the m key from inside the help viewer. This issue is fixed in this commit. Signed-off-by: Mark Ryan --- src/doc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/doc.c b/src/doc.c index 69ffd2b..a4e98d7 100644 --- a/src/doc.c +++ b/src/doc.c @@ -2725,6 +2725,10 @@ static uint8_t prv_find_mnemomic(const char *ins_name) * Opcode not found. Let's return something close. */ + if ((r < max_docs - 1) && (ins_name[0] != docs[r].name[0]) && + (ins_name[0] == docs[r + 1].name[0])) + r++; + return r; } @@ -2759,8 +2763,6 @@ void specasm_help_banked(const char *ins_name) } else if (k >= 'a' && k <= 'z') { jmp[0] = k; id = prv_find_mnemomic(jmp); - if (id < max_docs - 1) - id++; redraw = 1; } else { break; From e1c944f171596d183508983e5af35d70ed5e462b Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 26 Jan 2025 21:55:51 +0100 Subject: [PATCH 15/24] specasm: fix a few errors in the documentation Mostly in the new Next instructions. Signed-off-by: Mark Ryan --- src/descra2l.c | 24 ++++++++++++------------ src/descrm2z.c | 4 ++-- src/doc.c | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/descra2l.c b/src/descra2l.c index a6144e6..1cab786 100644 --- a/src/descra2l.c +++ b/src/descra2l.c @@ -147,7 +147,7 @@ const char specasm_doc_halt[] = "CPU execution is suspended until the next interrupt or reset."; const char specasm_doc_im[] = - "Sets the interrupt mode. With im 2 the MSB of the vector " + "Sets the interrupt mode. With im 2 the MSB of the vector " "address is taken from the i register."; const char specasm_doc_in[] = @@ -189,7 +189,7 @@ const char specasm_doc_jp[] = const char specasm_doc_jp_c[] = "Jumps to address formed from the address of the next instruction " "and data read from an io port. Bit 14 and 15 are taken " - "from the PC of the next instruction. Bits 0-5 are 0. Bits " + "from the PC of the next instruction. Bits 0-5 are 0. Bits " "6-13 come from an implicit in (c)."; #endif @@ -203,7 +203,7 @@ const char specasm_doc_ld1[] = "absolute address."; const char specasm_doc_ld2[] = - "Loads SP with the value of another 16 bit register pair."; + "Loads sp with the value of another 16 bit register pair."; const char specasm_doc_ld3[] = "Loads byte register an immediate value or another register."; @@ -233,11 +233,11 @@ const char specasm_doc_ldd[] = #ifdef SPECASM_TARGET_NEXT_OPCODES const char specasm_doc_lddrx[] = - "Load (hl) into (de) if (hl) != a. hl and bc are decremented while" - "de in incremented. If bc!=0 lddrx repeats. lddrx consumes more " + "Load (hl) into (de) if (hl)!=a. hl and bc are decremented while " + "de is incremented. If bc!=0 lddrx repeats. lddrx consumes more " "t-states when it repeats."; const char specasm_doc_lddx[] = - "Load (hl) into (de) if (hl) != a. de is incremented. bc and " + "Load (hl) into (de) if (hl)!=a. de is incremented. bc and " "hl are decremented."; #endif @@ -257,22 +257,22 @@ const char specasm_doc_ldir[] = #ifdef SPECASM_TARGET_NEXT_OPCODES const char specasm_doc_ldirx[] = - "Load (hl) into (de) if (hl) != a. hl and de are incremented, while " - "bc is decremented. If bc != 0 ldirx repeats. ldirx consumes more " + "Load (hl) into (de) if (hl)!=a. hl and de are incremented while " + "bc is decremented. If bc!=0 ldirx repeats. ldirx consumes more " "t-states when it repeats."; const char specasm_doc_ldix[] = - "Load (hl) into (de) if (hl) != a. Both hl and de " + "Load (hl) into (de) if (hl)!=a. Both hl and de " "are incremented while bc is decremented."; const char specasm_doc_ldpirx[] = "Load the byte whose address is formed from the top 13 bits of hl " - "and bottom 3 bits of de into (de) if the byte != a. de is incremented, " - "while bc is decremented. If bc != 0 ldpirx repeats. ldpirx consumes more " + "and bottom 3 bits of de into (de) if the byte != a. de is incremented " + "while bc is decremented. If bc!=0 ldpirx repeats. ldpirx consumes more " "t-states when it repeats."; const char specasm_doc_ldws[] = "Load (hl) into (de) and increment l and d. The v flag is set " - "if d was $7f before increment."; + "if d was $7f before incremented."; #endif \ No newline at end of file diff --git a/src/descrm2z.c b/src/descrm2z.c index a846836..baca1a6 100644 --- a/src/descrm2z.c +++ b/src/descrm2z.c @@ -74,7 +74,7 @@ const char specasm_doc_pixelad[] = "that contains the pixel addressed by the x and y coordinates in " "the e and d registers, respectively."; const char specasm_doc_pixeldn[] = - "On entry hl should point to the start of a 8 bit pixel block in the " + "On entry hl should point to the start of an 8 bit pixel block in the " "Spectrum's display file. On exit hl points to the same pixel block " "one line down."; #endif @@ -164,7 +164,7 @@ const char specasm_doc_srl[] = const char specasm_doc_sub[] = "The operand is subracted from a."; #ifdef SPECASM_TARGET_NEXT_OPCODES -const char specasm_doc_swapnib[] = "Swaps the nibbles in a"; +const char specasm_doc_swapnib[] = "Swaps the nibbles in a."; const char specasm_doc_test[] = "ANDs the immediate operand with a, sets the flags and discards " "the result."; diff --git a/src/doc.c b/src/doc.c index a4e98d7..059605a 100644 --- a/src/doc.c +++ b/src/doc.c @@ -815,7 +815,7 @@ static const specasm_ins_form_t specasm_forms[] = { #ifdef SPECASM_TARGET_NEXT_OPCODES /* SPECASM_DOC_PUSH_IMM_FORMS */ - {"nm", "ED 8A+nm", 6, 23}, + {"nm", "ED 8A n m", 6, 23}, #endif /* SPECASM_DOC_RES_FORMS */ {"n,r", "CB 80+b+r", 2, 8}, From 93896be32bfb989676535852733cfafbcaf66581 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 8 Feb 2025 16:59:54 +0100 Subject: [PATCH 16/24] samac: don't link to error.c It pulls in all the error strings which are not used in the 48/128 version of SAMAC. We use the BASIC error strings instead. We do need the err_type though, so this is moved to state_base.c. This saves about 500 bytes from the binary. Signed-off-by: Mark Ryan --- build/48/specasm/Makefile | 1 - src/error.c | 2 -- src/state_base.c | 1 + 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build/48/specasm/Makefile b/build/48/specasm/Makefile index f0798a3..614c0f3 100644 --- a/build/48/specasm/Makefile +++ b/build/48/specasm/Makefile @@ -87,7 +87,6 @@ SAMAC = \ line_dump_common.o \ line_parse.o \ line_parse_common.o \ - error.o \ scratch.o \ state_base.o \ state_dump.o \ diff --git a/src/error.c b/src/error.c index d9ab9c2..4073660 100644 --- a/src/error.c +++ b/src/error.c @@ -16,8 +16,6 @@ #include "error.h" -specasm_error_t err_type; - /* clang-format off */ const char* error_msgs[SPECASM_MAX_ERRORS] = { diff --git a/src/state_base.c b/src/state_base.c index 6792e44..180c384 100644 --- a/src/state_base.c +++ b/src/state_base.c @@ -21,6 +21,7 @@ #include "state_base.h" specasm_state_t state; +specasm_error_t err_type; void specasm_state_reset(void) { From 1816cf59edd90e3e6865ceda32b9231aa63c0ef7 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 9 Feb 2025 12:51:07 +0100 Subject: [PATCH 17/24] samac: reduce org address of SAMAC We could use an ORG address of 32768 for the SAMAC binary, but this would be cutting it a bit fine, leaving only 500 or so bytes free. If the code grew in a subsequent release we'd end up having to lower the ORG address and this would break users' existing macros. So let's leave ourselves some room. Signed-off-by: Mark Ryan --- build/48/specasm/Makefile | 3 +-- src/samake.c | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/build/48/specasm/Makefile b/build/48/specasm/Makefile index 614c0f3..c6eb2c9 100644 --- a/build/48/specasm/Makefile +++ b/build/48/specasm/Makefile @@ -112,8 +112,7 @@ SAMAKE: $(SAMAKE) $(CC) $(CFLAGS) -startup=30 -o $@ $(SAMAKE) -subtype=dotx -Cz"--clean" -create-app SAMAC: $(SAMAC) - $(CC) $(CFLAGS) -m -zorg=32768 -pragma-output="CLIB_MALLOC_HEAP_SIZE=0" -pragma-output="REGISTER_SP=-1" -startup=31 -o $@ $^ -# $(CC) $(CFLAGS) -m -zorg=26000 -startup=31 -o $@ $^ -Cz"--bin" -create-app -pragma-output="REGISTER_SP=-1" + $(CC) $(CFLAGS) -m -zorg=30720 -pragma-output="CLIB_MALLOC_HEAP_SIZE=0" -pragma-output="REGISTER_SP=-1" -startup=31 -o $@ $^ clean: - rm -rf specasm *.zip -rf unitzx diff --git a/src/samake.c b/src/samake.c index 00ed48c..dc1145c 100644 --- a/src/samake.c +++ b/src/samake.c @@ -35,6 +35,16 @@ #define SAMAKE_CODE_BUF_SIZE 1024 +/* + * We could use an ORG address of 32768 for the SAMAC binary, but + * this would be cutting it a bit fine, leaving only 500 or so bytes + * free. If the code grew in a subsequent release we'd end up having + * to lower the ORG address and this would break users' macros. So + * let's leave ourselves some room. + */ + +#define SAMAC_ORG_ADDRESS 30720 + static char bin_name[MAX_FNAME + 1]; static char app_name[MAX_FNAME + 1]; static uint8_t bin_name_len; @@ -71,6 +81,14 @@ static union { static specasm_dirent_t dirent; +static void prv_set_org_address(uint16_t sa) +{ + org_address = sa; + (void)utoa(sa, start_address, 10); + sa--; + (void)utoa(sa, clear_address, 10); +} + static uint8_t prv_parse_obj_e(const char *fname) { uint16_t i; @@ -106,11 +124,8 @@ static uint8_t prv_parse_obj_e(const char *fname) return 1; } else if (!got_org && (line->type == SPECASM_LINE_TYPE_ORG)) { sa = *((uint16_t *)&line->data.op_code[0]); - org_address = sa; + prv_set_org_address(sa); got_org = 1; - (void)utoa(sa, start_address, 10); - sa--; - (void)utoa(sa, clear_address, 10); if (bin_name[0] && got_zx81) return 1; } else if (!got_zx81 && @@ -1175,6 +1190,7 @@ static void prv_make_e(const char *dir, uint8_t target_type) if (target_type == SAMAKE_TARGET_TYPE_MAC) { strcpy(bin_name, samac_name); bin_name_len = strlen(samac_name); + prv_set_org_address(SAMAC_ORG_ADDRESS); } else { prv_find_bin_name_e(dir, target_type); if (err_type != SPECASM_ERROR_OK) From 5a0d517b6464aa36eeebaabc1504791f90328241 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Thu, 13 Feb 2025 21:44:58 +0100 Subject: [PATCH 18/24] samake: add Next support for macros These work in a different way from ZX Spectrum macros. samake mac generates a Next BASIC file that defines 4 procedures that can be used to generate .x files programmatically. PROCasm PROCnewx PROCloadx PROCsavex Signed-off-by: Mark Ryan --- bas/next/MAC.BAS | Bin 0 -> 1017 bytes build/next/specasm/Make.include | 1 + build/next/specasm/Makefile | 30 +++++- src/samac_cmds_next.asm | 184 ++++++++++++++++++++++++++++++++ src/samake.c | 101 ++++++++++++++---- 5 files changed, 295 insertions(+), 21 deletions(-) create mode 100755 bas/next/MAC.BAS create mode 100644 src/samac_cmds_next.asm diff --git a/bas/next/MAC.BAS b/bas/next/MAC.BAS new file mode 100755 index 0000000000000000000000000000000000000000..b8c73568b57e74d99c5906a1b0a62ba4e6527182 GIT binary patch literal 1017 zcmcgqJ!n%=6#kmVl15J)?yZ(8r93*k&_Hc0Rn#Y~DNYs(#l`LV-i;BGCM2mw5D`HT z5h1O#i-VJcOBJ0QN)bV!;NaxoVnGKf_(Ll+AbRf2k3n?xF7KXmzVCeBJ?~7PIx}-{ z^7Kq!2evx_T<*lRDRcQhXB8cL@FRJ6?9fOI_?gF%g_17M72G%>+KW$~H=R|!9+*;o zF+Y&eMeXq+QMNU6|MQTz+J%jAJsXg?DNyxQHk--?uAd7Ms(5}dPGWupchAkGy_t!r ziPVX?bXpe^7s|Tm84n#mBBv_eW7nb+MO{kx84~yQp~m^+qX!t|0&51;3`jf(VTr_} zC>Bkpms}+AOkk6B63e!gYYK6ee?j2vP*aV>>ljR}E%P>tr8`E54~ccE_h}_Iy1}-* zRelqR@4HbmQKPyxPZGwAj7H#RycMpIlrhveXku=V?2lmUx$9@*M@hP5`u*0I$##8t zEQ~G3A$c-{b&}H|u*BJ&sH#$~J|vPs9~PREzGde+smiJ`a5-x!6+$(eS3#vox<1%O za$BsSBtJX^#GCZnmcAH4byi=h@M_5W&fl4%%nTc*<1p%> release/specasm/specasm.txt cp SPECASM SALINK SAIMPORT SAEXPORT SAMAKE release/specasm + cp SAMAC_CODE.bin release/specasm/SAMAC cp ../../../bas/next/INSTALL.BAS release/specasm cp ../../../bas/next/REMOVE.BAS release/specasm + cp ../../../bas/next/MAC.BAS release/specasm ../../../buildlib.sh `realpath release` next cd release && zip -r specasmnext.zip specasm diff --git a/src/samac_cmds_next.asm b/src/samac_cmds_next.asm new file mode 100644 index 0000000..2c91d10 --- /dev/null +++ b/src/samac_cmds_next.asm @@ -0,0 +1,184 @@ +; 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. + +SECTION code_compiler + +PUBLIC _specasm_samac_init +EXTERN _specasm_samac_new +EXTERN _specasm_samac_asm +EXTERN _specasm_samac_load +EXTERN _specasm_samac_save +EXTERN _error_msgs + +defc SPECASM_SAMAC_ORG_ADDRESS = 28736 +defc SPECASM_SAMAC_TABLE = SPECASM_SAMAC_ORG_ADDRESS - 64 +defc SPECASM_SAMAC_BUF_ADDRESS = SPECASM_SAMAC_TABLE + 24 + +_specasm_samac_init: + ld bc, asmH + ld (SPECASM_SAMAC_TABLE), bc + + ld bc, loadH + ld (SPECASM_SAMAC_TABLE + 2), bc + + ld bc, newH + ld (SPECASM_SAMAC_TABLE + 4), bc + + ld bc, saveH + ld (SPECASM_SAMAC_TABLE + 6), bc + + ; 56 bytes left for the buffer. Let's set them to 0. + ld hl, SPECASM_SAMAC_TABLE + 8 + ld de, SPECASM_SAMAC_TABLE + 9 + xor a + ld (hl), a + ld bc, 55 + ldir + + ret + +; in: +; a contains error number +; out: +; bc contain pointer to error string + +find_error: + or a + jr nz, got_an_error + ld bc, 0 + ret + +got_an_error: + sla a + ld h, 0 + ld l, a + ld bc, _error_msgs + add hl, bc + ld c, (hl) + inc hl + ld b, (hl) + ret + +find_len: + ld hl, SPECASM_SAMAC_BUF_ADDRESS + xor a + ld b, 40 +find_len_loop: + cp (hl) + jr z, find_len_end + inc hl + djnz find_len_loop +find_len_end: + ld a, 40 + sub b + ld c, a + ld b, 0 + ret + +asmH: + ; note as we're calling into C code here we + ; need to duplicate what the z88dk crt does and + ; preserve certain registers and disable interrupts. + ; Otherwise it all gets a bit crashy. + + di + push iy + exx + push hl + exx + + call find_len + push bc + push SPECASM_SAMAC_BUF_ADDRESS + call _specasm_samac_asm + ld a, l + pop de + pop bc + + exx + pop hl + exx + pop iy + ei + + call find_error + ret + +newH: + + di + push iy + exx + push hl + exx + + call _specasm_samac_new + + exx + pop hl + exx + pop iy + ei + + ld bc, 0 + + ret + +loadH: + di + push iy + exx + push hl + exx + + call find_len + push bc + push SPECASM_SAMAC_BUF_ADDRESS + call _specasm_samac_load + ld a, l + pop de + pop bc + + exx + pop hl + exx + pop iy + ei + + call find_error + ret + +saveH: + di + push iy + exx + push hl + exx + + call find_len + push bc + push SPECASM_SAMAC_BUF_ADDRESS + call _specasm_samac_save + ld a, l + pop de + pop bc + + exx + pop hl + exx + pop iy + ei + + call find_error + ret diff --git a/src/samake.c b/src/samake.c index dc1145c..541f24f 100644 --- a/src/samake.c +++ b/src/samake.c @@ -81,6 +81,9 @@ static union { static specasm_dirent_t dirent; +static uint8_t prv_write_code_e(specasm_handle_t in_f, specasm_handle_t out_f, + uint8_t checksum); + static void prv_set_org_address(uint16_t sa) { org_address = sa; @@ -369,6 +372,7 @@ static void prv_make_bas_e(void) prv_create_bas_file_e(); } +#ifndef SPECASM_TARGET_NEXT static void prv_make_mac_e(const char *apn) { specasm_handle_t f; @@ -395,6 +399,50 @@ static void prv_make_mac_e(const char *apn) prv_create_bas_file_e(); } +/* + * For the next macros we're just going to copy a template file from + * the /specasm directory. We're not going to generate anything. The + * file is a little too large to encode in the samake binary. + */ + +#else +static void prv_copy_next_mac_e(const char *dir) +{ + specasm_handle_t in_f; + specasm_handle_t out_f; + + if (strcmp(dir, ".")) { + if (!strchr(dir, '.')) { + prv_make_app_name_gen("bas", dir, strlen(dir)); + } else { + if (strlen(dir) > MAX_FNAME) { + err_type = SPECASM_ERROR_BAD_FNAME; + return; + } + strcpy(app_name, dir); + } + } else { + strcpy(app_name, "mac.bas"); + } + + in_f = specasm_file_ropen_e("/specasm/MAC.BAS"); + if (err_type != SPECASM_ERROR_OK) + return; + + out_f = specasm_file_wopen_e(app_name); + if (err_type != SPECASM_ERROR_OK) { + specasm_file_close_e(in_f); + return; + } + + (void)prv_write_code_e(in_f, out_f, 0); + +on_error: + specasm_file_close_e(out_f); + specasm_file_close_e(in_f); +} +#endif + static void prv_make_basic_header(void) { uint8_t i; @@ -408,7 +456,8 @@ static void prv_make_basic_header(void) 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 */ + /* 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); @@ -445,7 +494,8 @@ static void prv_make_code_header(uint16_t bin_size) 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 */ + /* 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); @@ -712,8 +762,8 @@ static void prv_make_tap_e(void) return; /* - * That's the header. Now we can write our BASIC program followed - * by the checksum. + * That's the header. Now we can write our BASIC program + * followed by the checksum. */ out_f = specasm_file_wopen_e(app_name); @@ -1188,9 +1238,14 @@ static void prv_make_e(const char *dir, uint8_t target_type) const char *apn; if (target_type == SAMAKE_TARGET_TYPE_MAC) { +#ifdef SPECASM_TARGET_NEXT + prv_copy_next_mac_e(dir); + return; +#else strcpy(bin_name, samac_name); bin_name_len = strlen(samac_name); prv_set_org_address(SAMAC_ORG_ADDRESS); +#endif } else { prv_find_bin_name_e(dir, target_type); if (err_type != SPECASM_ERROR_OK) @@ -1205,12 +1260,13 @@ static void prv_make_e(const char *dir, uint8_t target_type) } /* - * We could perform the opposite check here, that if you select p - * and your program doesn't include the zx81 directive then we report - * an error. The thing is though that this should still work, providing - * you set the org correctly and do the character conversion yourself. - * This might actually be something you want to do if you have some - * existing code that does the conversion at runtime. + * We could perform the opposite check here, that if you select + * p and your program doesn't include the zx81 directive then we + * report an error. The thing is though that this should still + * work, providing you set the org correctly and do the + * character conversion yourself. This might actually be + * something you want to do if you have some existing code that + * does the conversion at runtime. */ if (target_type == SAMAKE_TARGET_TYPE_NONE) @@ -1218,12 +1274,13 @@ static void prv_make_e(const char *dir, uint8_t target_type) got_zx81 ? SAMAKE_TARGET_TYPE_P : SAMAKE_TARGET_TYPE_BAS; /* - * 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. + * 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) { @@ -1245,9 +1302,11 @@ static void prv_make_e(const char *dir, uint8_t target_type) prv_make_autoace_e(); } else if (target_type == SAMAKE_TARGET_TYPE_BAS) { prv_make_bas_e(); +#ifndef SPECASM_TARGET_NEXT } else if (target_type == SAMAKE_TARGET_TYPE_MAC) { apn = strcmp(dir, ".") ? dir : "mac"; prv_make_mac_e(apn); +#endif } else if (target_type == SAMAKE_TARGET_TYPE_TAP) { prv_make_tap_e(); } else if (target_type == SAMAKE_TARGET_TYPE_P) { @@ -1305,10 +1364,14 @@ int main(int argc, char *argv[]) on_error: if (err_type != SPECASM_ERROR_OK) { - if (err_type < SPECASM_MAX_ERRORS) + 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"); + } else if (err_type == SAMAKE_ERROR_USAGE) { + printf("Usage:\n"); + printf(" samake (bas|tap|p|tst) [dir]\n"); + printf(" samake (ace|autoace) [dir]\n"); + printf(" samake mac [macro filename]\n"); + } ret = 1; } else { printf("Created %s\n", app_name); From 8ec5024f488f9360a4e5ec569447625258d9df59 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Thu, 13 Feb 2025 22:22:29 +0100 Subject: [PATCH 19/24] specasm: increase key timeout in help To prevent skipping two pages of help at a time when pressing the cursor keys. Signed-off-by: Mark Ryan --- src/doc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc.c b/src/doc.c index 059605a..a1652a3 100644 --- a/src/doc.c +++ b/src/doc.c @@ -2746,7 +2746,7 @@ void specasm_help_banked(const char *ins_name) if (redraw) prv_draw_help(id); do { - specasm_sleep_ms(25); + specasm_sleep_ms(50); } while (!(k = in_inkey())); redraw = 0; From ac5bff6ed7233e23e8d650288c7869a011d967b2 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Fri, 14 Feb 2025 10:58:38 +0100 Subject: [PATCH 20/24] specasm: fix a paste crash Pasting a block of code containing long strings into a .x file that has no long strings available crashes the spectrum on the latest version of Specasm. This is because the code mistakenly assumed that an error could not occur and didn't handle the failure from specasm_parse_line_e. We do now trap, handle and report the error. We stop inserting lines after the first error occurs. Signed-off-by: Mark Ryan --- src/editor_extra.c | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/editor_extra.c b/src/editor_extra.c index df9d8ed..b8e10a8 100644 --- a/src/editor_extra.c +++ b/src/editor_extra.c @@ -12,7 +12,7 @@ * 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. -*/ + */ #if defined(SPECASM_TARGET_NEXT_OPCODES) || defined(SPECASM_TARGET_128) #include @@ -27,6 +27,15 @@ #include "scratch.h" #include "state.h" +static void prv_restore_line_buf(const char *command) +{ + memset(line_buf, ' ', SPECASM_LINE_MAX_LEN); + line_buf[0] = '>'; + line_buf[2] = command[0]; + if (command[1]) + line_buf[3] = command[1]; +} + static void prv_clip_copy_e(const char *command) { unsigned int i; @@ -49,11 +58,7 @@ static void prv_clip_copy_e(const char *command) * We need to put line_buf back the way it was. */ - memset(line_buf, ' ', SPECASM_LINE_MAX_LEN); - line_buf[0] = '>'; - line_buf[2] = command[0]; - if (command[1]) - line_buf[3] = command[1]; + prv_restore_line_buf(command); } void specasm_selecting_clip_copy_e(void) @@ -102,6 +107,7 @@ uint8_t specasm_selecting_clip_cut_e(void) void specasm_selecting_clip_paste_e(void) { uint16_t i; + specasm_error_t paste_err = SPECASM_ERROR_OK; uint16_t ptr = 0; uint16_t line_count = specasm_clip_get_line_count(); @@ -118,15 +124,23 @@ void specasm_selecting_clip_paste_e(void) specasm_parse_line_e(i, line_buf); /* - * This can't really error unless there's some memory - * corruption. If we return an error here we'll need - * to reset line_buf. As this cannot happen it's not - * worth the code to do it. We'll clear the error here - * to prevent the corrupted line_buf from being used - * when the command prompt is redisplayed. + * Although the line is guaranteed to be syntactically correct + * we could still get an error, if for example, there are no + * strings left. When this happens in the editor, you can't add + * the line, but here the line is already added, and then + * corrupted by the failed call to specasm_parse_line_e, so + * we'll need to set it back to empty before reporting the + * error. */ + if (err_type != SPECASM_ERROR_OK) { + state.lines.lines[i].type = SPECASM_LINE_TYPE_EMPTY; + prv_restore_line_buf("v"); + paste_err = err_type; + err_type = SPECASM_ERROR_OK; + specasm_delete_lines(i, line + line_count); + break; + } - err_type = SPECASM_ERROR_OK; ptr = specasm_clip_get_line(ptr, line_buf); i++; } @@ -135,6 +149,7 @@ void specasm_selecting_clip_paste_e(void) select_end = select_start = 0; specasm_draw_status(); } + err_type = paste_err; } void specasm_selecting_cycles(void) From 2d1fd29b8c3b0514f87a5b957d6fac5ac7d04e9f Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Fri, 14 Feb 2025 17:53:31 +0100 Subject: [PATCH 21/24] docs: Update specasm.md for release v11 Document macros, help and jupiter ace support. Signed-off-by: Mark Ryan --- docs/specasm.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/docs/specasm.md b/docs/specasm.md index d95e490..d43217a 100644 --- a/docs/specasm.md +++ b/docs/specasm.md @@ -78,6 +78,12 @@ The Next and the Spectrum 128 versions of Specasm provide two additional command | t | Displays the number of M cycles and T states the selected code will take to execute. Specasm prints two numbers for each value, a minimum and a maximum. The value is likely to be inaccurate for instructions whose running times depend on runtime state, e.g., LDIR. | | fl | Displays the flags modified by a selected block of code. | +The Next and the Spectrum 128 versions of Specasm provide a help command that describes each of the mnemonics supported by the assembler. A brief description of each mnemonic is provided, along with its various encodings, timings and the flags it affects. The Next version of Specasm includes documentation for the ZXN opcodes, which are identified by the string "zxn" highlighted in red in the top left hand corner of the screen. + +| Command | Description | +|---------|-------------| +| h [mnemonic] | Enters the help system. Takes one optional argument, a mnemonic, which, when given, displays information on that mnemonic. Help for "adc" is shown when the command is used without a argument. Use the arrow keys to navigate inside the help system. Pressing a letter takes you to the first mnemonic that begins with that letter. Pressing any key other than a letter or left and right exits help. | + Finally, the Next and the Spectrum 128 versions of Specasm have a garbage collection command to reclaim unused strings. See the *Limitations* section below for more details. | Command | Description | @@ -764,6 +770,24 @@ The 'p' argument can be omitted if one of the .x files in the current directory On the Spectrum Next, the generate .p file can be run from the browser which will execute it in the built-in ZX81 emulator. +## Jupiter Ace Support + +Specasm itself does run on the Jupiter Ace, but it can be used to cross assemble Ace programs from the Spectrum. The samake shipped with Version v11 and above of Specasm can be used to generate Jupiter Ace tap files. There are two samake sub commands for the Ace. + +- **ace** generates a tap file containing the linked binary of the current project. The tap file can be loaded with `0 0 bload ` and then run with ` call`. +- **autoace** generates a auto-running tap file. The tap file can be loaded and run with a single command; `0 0 bload autorun`. For this to work, the project needs to have an org address of 15451. + +So to create an auto-running tap file for the Ace simply add an `org 15451` statement to your program, link it and type + +``` +CLEAR 32767 +.samake autoace +``` + +> [!TIP] +> Note the CLEAR statement is not needed on the ZX Spectrum Next as .samake is implemented as a dotn file. + + ## Unit Tests Specasm v9 adds basic support for unit testing. Test content is placed in a .t file. .t files are binary files like .x files that can be edited by Specasm but are intended to contain test code only. .t files can be saexported and saimported to and from .ts source files. .t files can be included in the same directory as the .x files that constitute the main program. When salink is run it builds a main binary out of all the .x files in the project. In Specasm v9, if the project contains any .t files, it will also generate a second binary that contains the contents of all the .t and all the .x files in the project. The second binary has the same name as the main binary with a '.tst' extension appended. @@ -980,3 +1004,68 @@ zx81 ... ``` +## Scripting support + +Specasm does not directly support macros. It does support numeric expressions but these are mostly used for constants and for address calculations. It has no facility to programmatically generate assembly language code or data directives directly in its source files. Although Specasm itself doesn't provide a macro language your ZX Spectrum does (Sinclair BASIC). Rather than trying to squeeze a custom macro language into Specasm, versions v11 and above of Specasm allow you to use your Spectrum's native language to programmatically create .x files. So while Specasm doesn't support macros it is scriptable. The way this works differs on the ZX Spectrum and the Spectrum Next. + +### Scripting on the ZX Spectrum + +To create a new Specasm script on the ZX Spectrum, simply use the samake **mac** sub command. This sub command creates a new Specasm BASIC script. If used without arguments the name of that script is mac.bas. If an argument is provided the script file name is derived from the argument. The script contains a single line of code that loads and executes a BASIC extension. Once executed the extension initialises a new .x file in memory and adds four new commands to Sinclair BASIC, each of which begins with a '*'. These are + +- \*asm which accepts one string argument which is expected to be a valid line of Specasm code. The line is assembled and appended to the in memory .x file. +- \*load which accepts one string argument, the path of an .x file, and loads that file into memory, replacing the contents of the previous in memory .x file. +- \*new which accepts no arguments and resets the in memory .x file. +- \*save which accepts one string argument, the path of an .x file, and saves the in memory .x file to the given path. + +For example, to create and load a new Specasm script type + +``` +CLEAR 32767 +.samake mac +LOAD * "mac.bas" +``` + +Then add the following lines and run the program. + +``` +20 *asm ".sinwaveY" +30 FOR n=0 to 255 +40 *asm "db " + STR$(88+INT(80*SIN(n/128*PI))) +50 NEXT n +60 *save "sin.x" +RUN +``` + +You should end up with a .x file that contains the y coordinates of a sine wave, e.g., + +``` +.sinwaveY +db 88 +db 89 +db 91 +db 93 +db 95 +... +``` + +One consequence of the way the extensions work is that line 10 of each Specasm script needs to be executed before you can start entering the new Specasm BASIC commands. This is not an issue the first time you create a script, as the first time you load the script it will only contain one line, line 10, which will be automatically executed. It may however be an issue if you want to re-edit the script at a later stage. If you load or merge the file without executing it, you will not be able to add new Specasm commands. To edit the script you'll need to execute line 10. I usually do this by executing NEW and then merging the file (so it doesn't execute). I then add `15 STOP` to the program to prevent all but the first line from executing, run it and then delete line 15. The Specasm BASIC extension commands can then be freely used. + +### Scripting on the ZX Spectrum Next + +The mechanism used to extend Sinclair BASIC on the ZX Spectrum does not work on the ZX Spectrum Next. It may be possible to use a variation of the same trick to get it to work, but it's not worth the effort. Next BASIC has procedures so we can just use them instead. A macro file can be created in the same way on the Next as on the ZX Spectrum. Simply type + +``` +.samake mac +``` + +This will generate a BASIC program that loads and executes some machine code. The machine code initialises a new .x file in memory and then sets up some entry points that can be used to manipulate the file. These entry points can be acccessed from the BASIC program via 4 pre-supplied procedures; PROCasm, PROCloadx, PROCnewx and PROCsavex. These procedures have the same use as the Sinclair BASIC extensions with similar names described above. For example, to create the sin.x file in Next BASIC, we would add the following code to the generated macro. + +``` +20 PROC asm(".sinwaveY") +30 FOR n=0 to 255 +40 PROC asm("db " + STR$(88+INT(80*SIN(n/128*PI)))) +50 NEXT n +60 PROCsavex("sin.x") +``` + +Unlike the macros on the ZX Spectrum, there's no need to execute a Specasm macro on the Next before editing it. \ No newline at end of file From 3a03dce1fd0188aeeb57f841b56382d492cfd051 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 15 Feb 2025 16:42:22 +0100 Subject: [PATCH 22/24] release: combine 128 and 48 zip files We'll now only distribute the 128kb zip file, which contains the install script and the Specasm binary for the 48kb machine as well as for the 128kb Sectrum. The user will need to run the appropriate install script to install the version that best suits their machine, INST48.BAS for the 48kb machine and INST128.BAS for the Spectrum 128. We remove the release creation code for the 48kb Spectrum. Signed-off-by: Mark Ryan --- README.md | 11 +++++++---- bas/128/INST128.BAS | Bin 0 -> 1123 bytes bas/48/{INSTALL.BAS => INST48.BAS} | Bin build/128/specasm/Makefile | 10 ++++++---- build/48/specasm/Makefile | 13 ------------- 5 files changed, 13 insertions(+), 21 deletions(-) create mode 100755 bas/128/INST128.BAS rename bas/48/{INSTALL.BAS => INST48.BAS} (100%) diff --git a/README.md b/README.md index 0bba2b8..60b7b69 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,22 @@ # Specasm -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. +Specasm is a Z80 assembler designed to run on the 48k and 128k ZX Spectrums 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](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 three different Zip files available, specasm48.zip for the 48kb, specasm128.zip for the 128kb Spectrum, and specasmnext.zip for the ZX Spectrum Next. You must download the appropriate version for your machine. +> There are two different Zip files available, specasm.zip for the 48kb and 128kb Spectrums, and specasmnext.zip for the ZX Spectrum Next. The specasm.zip file contains separate binaries and install scripts for both the 48kb and 128kb versions. 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 +You should now have a folder in your root directory called SPECASM. It should look something like this if you downloaded specasm.zip ![Installing](/docs/install.png) -Now navigate to the INSTALL.BAS 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. +To install Specasm you need to run a short BASIC program. Run INST48.BAS when installing on a 48kb Spectrum, INST128.BAS when installing on a 128kb Spectrum and INSTALL.BAS when running on the Next. Navigate to the appropriate install file 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. + +> [!TIP] +> Note on previous releases of Specasm, there were separate zip files just for the 48kb and the 128kb Spectrums and users ran a BASIC script called INSTALL.BAS to install Specasm on these machines. From release v11 onwards, the zip files for the 48kb and 128kb Spectrums have been combined and you must run INST48.BAS or INST128.BAS to install the correct version. If you run the wrong install program by mistake just run REMOVE.BAS to uninstall Specasm and then execute the correct installer. ## Reinstalling Specasm diff --git a/bas/128/INST128.BAS b/bas/128/INST128.BAS new file mode 100755 index 0000000000000000000000000000000000000000..6f57f9cf129bd73244dc4ce1f0cb9d6eb2a7066e GIT binary patch literal 1123 zcmcgr&ubGw6#nv~6@=-jig=JIDAXqIZf3We6s)qfD}*-bl8vy}kPUQc8%krLw-!$x zJbUpXh!_6}y^4Z=fd{D`#8atW1kv}h**b0N)xFI3-uvG7zL}i~Rv(1+@>+N`hi(zT zVIhat1p@!)dyL!^ZgDq)>fq%~n8)oQLRLy96fxz@QyR(_}H+1r9y}6RI zuiYT+?zUd^qhPJoP920^8VSsr{~#KXW|oYk4MrrO#uR^a~3 zn4N~V;%BZI3k0QeNoQ4=%zk4V^0RX0=}-Is%1SK|`Z7cnLLX1%Tt%rnd=dJ$sLkPU zjuv#h2d6ZZ`a6eReB!FbSMdW6dc=b&E(^Y(io*gH z0j5sS%FQ`iN23Lv+@*%hh;pcWMWdf6mn-uu@l2B#NeMhvy2N?@)QMAvyN=lD^9AZs z7RPT%)!{9e1YACyB?496M4bU|qN#BNUR9~t<+JxGUt>bVfc>`_@KLu2xIEb-kZt%Y WVSc6bK8GifM?PkVm;C&Whq*ucAi`<@ literal 0 HcmV?d00001 diff --git a/bas/48/INSTALL.BAS b/bas/48/INST48.BAS similarity index 100% rename from bas/48/INSTALL.BAS rename to bas/48/INST48.BAS diff --git a/build/128/specasm/Makefile b/build/128/specasm/Makefile index 5535137..4fbcc9e 100644 --- a/build/128/specasm/Makefile +++ b/build/128/specasm/Makefile @@ -94,7 +94,7 @@ specasm.tap: specasm_bare.tap sald128/sald128 z88dk-appmake +zx -b specasm_bare_BANK_6.bin -o bank6.tap --org 49152 --noloader z88dk-appmake +zx -b specasm_bare_BANK_3.bin -o bank3.tap --org 49152 --noloader z88dk-appmake +zx -b sald128/sald128 -o sald128.tap --org 32768 --noloader - cat ../../../bas/128/specld.tap sald128.tap bank0.tap bank4.tap bank6.tap bank3.tap specasm_bare.tap > specasm.tap + cat ../../../bas/128/specld.tap sald128.tap bank0.tap bank4.tap bank6.tap bank3.tap specasm_bare.tap > sa128.tap clean: - rm -rf specasm *.zip -rf unitzx sald128 @@ -104,13 +104,15 @@ clean: release: - rm -rf release mkdir -p release/specasm - cp specasm.tap release/specasm + cp sa128.tap release/specasm cp ../../48/specasm/salink.tap release/specasm + cp ../../48/specasm/specasm.tap release/specasm cp ../../48/specasm/SAMAC_CODE.bin release/specasm/SAMAC cp ../../../COPYING release/specasm sed 's/```//g' ../../../docs/specasm.md > release/specasm/specasm.txt cp ../../48/specasm/SAMAKE ../../48/specasm/SAIMPORT ../../48/specasm/SAEXPORT ../../48/specasm/*.X release/specasm - cp ../../../bas/48/INSTALL.BAS release/specasm + cp ../../../bas/128/INST128.BAS release/specasm + cp ../../../bas/48/INST48.BAS release/specasm cp ../../../bas/48/REMOVE.BAS release/specasm ../../../buildlib.sh `realpath release` 128 specasm - cd release && zip -r specasm128.zip specasm + cd release && zip -r specasm.zip specasm diff --git a/build/48/specasm/Makefile b/build/48/specasm/Makefile index c6eb2c9..9932aa7 100644 --- a/build/48/specasm/Makefile +++ b/build/48/specasm/Makefile @@ -118,16 +118,3 @@ clean: - rm -rf specasm *.zip -rf unitzx - rm *.X *.o *.bin *.tap SAIMPORT SAEXPORT SAMAKE SAMAC -.PHONY: release -release: - - rm -rf release - mkdir -p release/specasm - cp specasm.tap salink.tap release/specasm - cp ../../../COPYING release/specasm - sed 's/```//g' ../../../docs/specasm.md > release/specasm/specasm.txt - cp SAMAKE SAIMPORT SAEXPORT *.X release/specasm - cp SAMAC_CODE.bin release/specasm/SAMAC - cp ../../../bas/48/INSTALL.BAS release/specasm - cp ../../../bas/48/REMOVE.BAS release/specasm - ../../../buildlib.sh `realpath release` 48 specasm - cd release && zip -r specasm48.zip specasm From fea0d3df6959b8394805b404bb3fbd6e173e6c2e Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sun, 16 Feb 2025 20:19:16 +0100 Subject: [PATCH 23/24] update version number to v11 For the long awaited v11 release. Signed-off-by: Mark Ryan --- src/state_base.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/state_base.h b/src/state_base.h index 73e721a..c6a9459 100644 --- a/src/state_base.h +++ b/src/state_base.h @@ -12,17 +12,17 @@ * 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_STATE_READ_H #define SPECASM_STATE_READ_H #ifdef SPECASM_TARGET_NEXT_OPCODES -#define SPECASM_VERSION 0x800a -#define SPECASM_VERSION_STR "v10n" +#define SPECASM_VERSION 0x800b +#define SPECASM_VERSION_STR "v11n" #else -#define SPECASM_VERSION 10 -#define SPECASM_VERSION_STR "v10" +#define SPECASM_VERSION 11 +#define SPECASM_VERSION_STR "v11" #endif #include From f7dda4216bd9aa8e2b2d835af20aecd41a183727 Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Sat, 22 Feb 2025 16:31:33 +0100 Subject: [PATCH 24/24] docs: update the install image for v11 This has changed a bit since the last release. Signed-off-by: Mark Ryan --- docs/install.png | Bin 51537 -> 50133 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/install.png b/docs/install.png index 34973fbd0055f06f0f55cdf68a1cbbe49206e2f7..2e244cf6e485b6f2f01f754b76da44d15c3c9b0f 100644 GIT binary patch literal 50133 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 Kn7?WNcmE3wRT$|2 literal 51537 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 Y(38Q`d2D;22xn~GpegIFD>q|H2?qr