Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced assembler #6

Closed
stevexyz opened this issue Jul 30, 2024 · 17 comments
Closed

Advanced assembler #6

stevexyz opened this issue Jul 30, 2024 · 17 comments
Labels
enhancement New feature or request

Comments

@stevexyz
Copy link

This is a wonderful project. And surely very nice for a beginner to jump start ZX81 assembler development!

The main limitation I see is related to the assembler, unfortunately very underpowered for more advanced needs (e.g. I’m trying to create a new version of the 1K Real Chess for ZX81),.

Without reimplementing things (standard undocumented instructions, powerful directives, etc), would it be possible to integrate a more powerful ready one as for example sjasmplus (https://github.com/z00m128/sjasmplus) or fasm/fasmg (https://github.com/jacobly0/fasmg-z80)?

@stevexyz stevexyz changed the title Integration of a powerful assembler Advanced assembler Jul 30, 2024
@andrivet
Copy link
Owner

What is missing exactly?

I have not integrated sjasmplus or fasm because there are external binaries. To make the VS Code extension work on any platform, I would have to compile and package those binaries for macOS, Linux and Windows. The embedded assembler is entirely written in Typescript so it does not have such a problem.

@stevexyz
Copy link
Author

stevexyz commented Jul 30, 2024

For starting - and this is really the bare minimum - the complete usable instruction set (meaning also the "documented undocumented").

But there are also a lot of useful directives that are of real help in complex projects: conditional compilation, complex expressions (with a lot of operators and predefined symbols), advanced relocation facilities (eg DISP of sjasmplus), includes/macros, syntactic sugar (eg pseudo ops), structures, etc. (you can look here http://z00m128.github.io/sjasmplus/documentation.html and https://flatassembler.net/docs.php?article=fasmg_manual).

Not everyone use everything, but believe me that even just myself in my last endeavor (the original 1k real chess) in the 10k lines source code I used a lot of those things during development. That said, with a proper subset it can be done with little additional effort or complexity, but some of them should be implemented to use this IDE for advanced projects.

The portability and usability is surely a very good thing, maybe the cosmopolitan C compiler might help in case? (https://github.com/jart/cosmopolitan).

@andrivet
Copy link
Owner

Do you have a link to the original 1k chess so I can figure out what is needed?

@stevexyz
Copy link
Author

stevexyz commented Jul 30, 2024

The source is a personal project and I have in another house. Anyway if you want you can contact me in private and I can send you the current files in few days.

As said anyway my one is just an example, there are surely many people with different cases (e.g. looking also on the various cases they found for some of the issues open and closed in sjasmplus repo!).

PS: not sure how complex it could be to extend at a proper level the assembler, but if you want to follow this endeavour I can at least be a tester :)

@andrivet
Copy link
Owner

I see two possibilities (one does not exclude the other):

  1. Allow using an external assembler. This would be sjasmplus since I already use the same SLD format. This is already partially implemented but it may need some adjustments.

  2. Extend the assembler.

  • adding the undocumented instructions should not be too complex.
  • adding the pseudo instructions should also be easy.
  • adding conditional compilation should also be relatively easy.
  • complex expressions should already work, but maybe there are some bugs
  • DISP looks also relatively simple
  • includes are already implemented.

So the only more complex features are struct and macros. They look quite feasible. I will think a little more about all this.

@andrivet andrivet added the enhancement New feature or request label Nov 19, 2024
@stevexyz
Copy link
Author

stevexyz commented Jan 9, 2025

I saw that in the meantime you have improved this already nice extension fork!

Among the other things I saw that there is the support for sjasmplus, and I'm not sure if you also improved the internal assembler. I was trying to find some related documentation but I was not able to find it (i.e. how to use it with sjasmplus or the instructions/directives available for the internal one). There is already somewhere and I didn't find?

@stevexyz
Copy link
Author

stevexyz commented Jan 13, 2025

Seems to work if I put a .sld and a .P with the same name already compiled with sjasmplus. Is this the way?

Regarding the assembler I tried the undocumented instructions (e.g. ld iyh,1) but wasn't working. Anyway it compiled a pretty complex program (sample2.zx81.txt, found on forum and used just to test), even if at the end was not able to execute it, I think since it uses a starting approach that is not the standard (and simple) one proposed by the hello world. For this purpose maybe can be managed having another device directive to distinguish between the two options.

@andrivet
Copy link
Owner

Seems to work if I put a .sld and a .P with the same name already compiled with sjasmplus. Is this the way?

yes

Regarding the assembler I tried the undocumented instructions (e.g. ld iyh,1) but wasn't working.

Undocumented instructions are not yet supported.

@stevexyz
Copy link
Author

Undocumented instructions are not yet supported.

If you want you can give me some indication and I can prepare a pull request with the draft update of the grammar and machinecode for the missing ones.

@andrivet
Copy link
Owner

If you want you can give me some indication

It will take me more time than doing it myself.

I can prepare a pull request

As you can now use other assemblers (such as sjasmplus), I do not see the point of creating a PR now. I do not have the time to make such a review. I will enhance my assembler one day, but not in a short term.

@stevexyz
Copy link
Author

What abouth the "non standard" program startup (e.g. the sample2.zx81.txt mentioned above) ?

@stevexyz
Copy link
Author

stevexyz commented Mar 21, 2025

More as a curiosity than real high expectations I tried to propose "Manus" the task with a simple prompt: "This link https://github.com/stevexyz/z80-assembler-andrivet points to a git hub repository of a Z80 assembler. I want you to propose the modifications needed to manage also the "undocumented Z80 documented" instructions."

To you to judge the result!...(I'm pretty impressed!)

Proposed Modifications for Supporting Undocumented Z80 Instructions

Based on my analysis of the Z80 assembler repository and research on undocumented Z80 instructions, I've identified the necessary modifications to support these instructions in the assembler.

Overview of Undocumented Z80 Instructions

The Z80 CPU contains several undocumented instructions that aren't officially documented by Zilog but are widely used in certain applications. These instructions include:

  1. IX/IY Register Half-Word Access: Instructions to access the high and low bytes of IX and IY registers (IXH, IXL, IYH, IYL)
  2. SLL Instruction: Shift Left Logical - an undocumented shift instruction
  3. IX/IY Bit Operations: Various bit operations with IX+d and IY+d addressing
  4. IN/OUT Instructions: Undocumented IN F,(C) and OUT (C),F instructions

Required Modifications

1. Modify LowLevel.ts

The LowLevel.ts file needs to be updated to support the undocumented registers:

// Add support for IXH, IXL, IYH, IYL in r_bits function
export function r_bits(r: string, offset = 0): byte {
    switch(r.toLowerCase()) {
        case 'b': return 0b000 << offset;
        case 'c': return 0b001 << offset;
        case 'd': return 0b010 << offset;
        case 'e': return 0b011 << offset;
        case 'h': return 0b100 << offset;
        case 'l': return 0b101 << offset;
        // Add undocumented registers
        case 'ixh': return 0b100 << offset;
        case 'ixl': return 0b101 << offset;
        case 'iyh': return 0b100 << offset;
        case 'iyl': return 0b101 << offset;
        case 'a': return 0b111 << offset;
        default: console.log(`Invalid register name: ${r}`); return 0;
    }
}

2. Modify z80.peg Grammar File

The z80.peg file needs to be updated to include grammar rules for the undocumented instructions:

2.1 Add Register Definitions

// Add undocumented register definitions
IXH = 'ixh' / 'IXH'
IXL = 'ixl' / 'IXL'
IYH = 'iyh' / 'IYH'
IYL = 'iyl' / 'IYL'

2.2 Add Undocumented Load Instructions

// Add undocumented load instructions for IX/IY register halves
Load8Instruction =
    // Existing instructions...
    
    // Undocumented IX/IY register half loads
    / 'ld' __ r:Reg8 ',' __ h:IXH     { .elements = AstElements { return [0xDD, 0x40 | r_bits(r, 3) | 0x04]; } }
    / 'ld' __ r:Reg8 ',' __ l:IXL     { .elements = AstElements { return [0xDD, 0x40 | r_bits(r, 3) | 0x05]; } }
    / 'ld' __ r:Reg8 ',' __ h:IYH     { .elements = AstElements { return [0xFD, 0x40 | r_bits(r, 3) | 0x04]; } }
    / 'ld' __ r:Reg8 ',' __ l:IYL     { .elements = AstElements { return [0xFD, 0x40 | r_bits(r, 3) | 0x05]; } }
    
    / 'ld' __ h:IXH ',' __ r:Reg8     { .elements = AstElements { return [0xDD, 0x60 | r_bits(r)]; } }
    / 'ld' __ l:IXL ',' __ r:Reg8     { .elements = AstElements { return [0xDD, 0x68 | r_bits(r)]; } }
    / 'ld' __ h:IYH ',' __ r:Reg8     { .elements = AstElements { return [0xFD, 0x60 | r_bits(r)]; } }
    / 'ld' __ l:IYL ',' __ r:Reg8     { .elements = AstElements { return [0xFD, 0x68 | r_bits(r)]; } }
    
    / 'ld' __ h:IXH ',' __ n:n        { .elements = AstElements { return [0xDD, 0x26, value8(n)]; } }
    / 'ld' __ l:IXL ',' __ n:n        { .elements = AstElements { return [0xDD, 0x2E, value8(n)]; } }
    / 'ld' __ h:IYH ',' __ n:n        { .elements = AstElements { return [0xFD, 0x26, value8(n)]; } }
    / 'ld' __ l:IYL ',' __ n:n        { .elements = AstElements { return [0xFD, 0x2E, value8(n)]; } }

2.3 Add Undocumented Arithmetic Instructions

// Add undocumented arithmetic instructions for IX/IY register halves
ArithmeticLogic8Instruction =
    // Existing instructions...
    
    // ADD A, IXH/IXL/IYH/IYL
    / 'add' __ 'a' ',' __ h:IXH       { .elements = AstElements { return [0xDD, 0x84]; } }
    / 'add' __ 'a' ',' __ l:IXL       { .elements = AstElements { return [0xDD, 0x85]; } }
    / 'add' __ 'a' ',' __ h:IYH       { .elements = AstElements { return [0xFD, 0x84]; } }
    / 'add' __ 'a' ',' __ l:IYL       { .elements = AstElements { return [0xFD, 0x85]; } }
    
    // ADC A, IXH/IXL/IYH/IYL
    / 'adc' __ 'a' ',' __ h:IXH       { .elements = AstElements { return [0xDD, 0x8C]; } }
    / 'adc' __ 'a' ',' __ l:IXL       { .elements = AstElements { return [0xDD, 0x8D]; } }
    / 'adc' __ 'a' ',' __ h:IYH       { .elements = AstElements { return [0xFD, 0x8C]; } }
    / 'adc' __ 'a' ',' __ l:IYL       { .elements = AstElements { return [0xFD, 0x8D]; } }
    
    // SUB IXH/IXL/IYH/IYL
    / 'sub' __ h:IXH                  { .elements = AstElements { return [0xDD, 0x94]; } }
    / 'sub' __ l:IXL                  { .elements = AstElements { return [0xDD, 0x95]; } }
    / 'sub' __ h:IYH                  { .elements = AstElements { return [0xFD, 0x94]; } }
    / 'sub' __ l:IYL                  { .elements = AstElements { return [0xFD, 0x95]; } }
    
    // SBC A, IXH/IXL/IYH/IYL
    / 'sbc' __ 'a' ',' __ h:IXH       { .elements = AstElements { return [0xDD, 0x9C]; } }
    / 'sbc' __ 'a' ',' __ l:IXL       { .elements = AstElements { return [0xDD, 0x9D]; } }
    / 'sbc' __ 'a' ',' __ h:IYH       { .elements = AstElements { return [0xFD, 0x9C]; } }
    / 'sbc' __ 'a' ',' __ l:IYL       { .elements = AstElements { return [0xFD, 0x9D]; } }
    
    // AND IXH/IXL/IYH/IYL
    / 'and' __ h:IXH                  { .elements = AstElements { return [0xDD, 0xA4]; } }
    / 'and' __ l:IXL                  { .elements = AstElements { return [0xDD, 0xA5]; } }
    / 'and' __ h:IYH                  { .elements = AstElements { return [0xFD, 0xA4]; } }
    / 'and' __ l:IYL                  { .elements = AstElements { return [0xFD, 0xA5]; } }
    
    // XOR IXH/IXL/IYH/IYL
    / 'xor' __ h:IXH                  { .elements = AstElements { return [0xDD, 0xAC]; } }
    / 'xor' __ l:IXL                  { .elements = AstElements { return [0xDD, 0xAD]; } }
    / 'xor' __ h:IYH                  { .elements = AstElements { return [0xFD, 0xAC]; } }
    / 'xor' __ l:IYL                  { .elements = AstElements { return [0xFD, 0xAD]; } }
    
    // OR IXH/IXL/IYH/IYL
    / 'or' __ h:IXH                   { .elements = AstElements { return [0xDD, 0xB4]; } }
    / 'or' __ l:IXL                   { .elements = AstElements { return [0xDD, 0xB5]; } }
    / 'or' __ h:IYH                   { .elements = AstElements { return [0xFD, 0xB4]; } }
    / 'or' __ l:IYL                   { .elements = AstElements { return [0xFD, 0xB5]; } }
    
    // CP IXH/IXL/IYH/IYL
    / 'cp' __ h:IXH                   { .elements = AstElements { return [0xDD, 0xBC]; } }
    / 'cp' __ l:IXL                   { .elements = AstElements { return [0xDD, 0xBD]; } }
    / 'cp' __ h:IYH                   { .elements = AstElements { return [0xFD, 0xBC]; } }
    / 'cp' __ l:IYL                   { .elements = AstElements { return [0xFD, 0xBD]; } }

2.4 Add SLL Instruction

// Add undocumented SLL instruction
BitManipulationInstruction =
    // Existing instructions...
    
    // SLL r
    / 'sll' __ r:'b'                  { .elements = AstElements { return [0xCB, 0x30]; } }
    / 'sll' __ r:'c'                  { .elements = AstElements { return [0xCB, 0x31]; } }
    / 'sll' __ r:'d'                  { .elements = AstElements { return [0xCB, 0x32]; } }
    / 'sll' __ r:'e'                  { .elements = AstElements { return [0xCB, 0x33]; } }
    / 'sll' __ r:'h'                  { .elements = AstElements { return [0xCB, 0x34]; } }
    / 'sll' __ r:'l'                  { .elements = AstElements { return [0xCB, 0x35]; } }
    / 'sll' __ r:'(hl)'               { .elements = AstElements { return [0xCB, 0x36]; } }
    / 'sll' __ r:'a'                  { .elements = AstElements { return [0xCB, 0x37]; } }
    
    // SLL (IX+d)
    / 'sll' __ pos:IX_$0              { .elements = AstElements { return [0xDD, 0xCB, index(pos), 0x36]; } }
    
    // SLL (IY+d)
    / 'sll' __ pos:IY_$0              { .elements = AstElements { return [0xFD, 0xCB, index(pos), 0x36]; } }

2.5 Add Undocumented IN/OUT Instructions

// Add undocumented IN/OUT instructions
InputInstruction =
    // Existing instructions...
    
    // IN F,(C)
    / 'in' __ 'f' ',' __ '(c)'        { .elements = AstElements { return [0xED, 0x70]; } }

OutputInstruction =
    // Existing instructions...
    
    // OUT (C),F
    / 'out' __ '(c)' ',' __ 'f'       { .elements = AstElements { return [0xED, 0x71]; } }

3. Update z80.ts

The z80.ts file will need to be updated to include the new AST kinds for the undocumented instructions:

export enum ASTKinds {
    // Existing kinds...
    
    // Add new kinds for undocumented registers
    IXH = "IXH",
    IXL = "IXL",
    IYH = "IYH",
    IYL = "IYL",
}

Implementation Strategy

  1. First Phase: Implement the IX/IY register half-word access instructions (IXH, IXL, IYH, IYL)
  2. Second Phase: Implement the SLL instruction
  3. Third Phase: Implement the undocumented IX/IY bit operations
  4. Fourth Phase: Implement the undocumented IN/OUT instructions

Testing Strategy

For each phase of implementation:

  1. Create test assembly files that use the undocumented instructions
  2. Verify that the assembler correctly translates these instructions to the expected machine code
  3. Compare the output with known-good binary files or with the output of other Z80 assemblers that support undocumented instructions

Conclusion

The proposed modifications will enable the Z80 assembler to support the undocumented Z80 instructions. These changes primarily involve updating the grammar definitions in the z80.peg file and adding support for the undocumented registers in the LowLevel.ts file. The implementation can be done in phases, starting with the most commonly used undocumented instructions.

@andrivet
Copy link
Owner

@stevexyz Since those modifications are for the Z80 assembler, this is not the right repository for this.

As I also mentioned, I do not have the time right now to look at this topic.

I also do not understand why you have made a fork (without any modification) with my name in the name of the fork. This is confusing and a little disturbing.

@stevexyz
Copy link
Author

Sorry, I made a fork since I was thinking to try to change it myself. I've added your name to make clear that was your own one (Z80 assembler was pretty generic as a name). Since your feedback I deleted it.

The above post was me surprised about the quality of current AI!.. Never mind. Sorry for the post.

Happy hacking.

@andrivet
Copy link
Owner

andrivet commented Mar 23, 2025

Thanks for the clarification. I am not sure to fully understand your last message. You mean you generated the code you mentioned by AI ?
I was not aware that “Manus” was referring to an AI.

@stevexyz
Copy link
Author

stevexyz commented Mar 24, 2025

Yes, I just simply said to Manus:

This link https://github.com/stevexyz/z80-assembler-andrivet points to a git hub repository of a Z80 assembler. I want you to propose the modifications needed to manage also the "undocumented Z80 documented" instructions.

Above I pasted the "raw" output I've received after around 150 seconds of reasoning (that was already formatted in markdown, including "Proposed Modifications for Supporting Undocumented Z80 Instructions" title): for me is pretty impressive, I was just sharing the awareness of this capability and also curious to know your opinion about the result (since you know better your repository)!...

@andrivet
Copy link
Owner

I look a little at the code generated by AI and it is very wrong. The syntax does not make sense. This is the problem of LLM when using them for very specific (and niche) things such as custom languages. It looks promissing on the surface, but in fact, it makes more work to find all the mistakes than doing things from scratch.

IMPORTANT: Please do not continue the conversation here. This is not the right repository. The Z80 Assembler is here: https://github.com/andrivet/z80-assembler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants