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

JR and DJNZ fake instructions to manage out of range offsets #117

Closed
stevexyz opened this issue Jul 28, 2020 · 10 comments
Closed

JR and DJNZ fake instructions to manage out of range offsets #117

stevexyz opened this issue Jul 28, 2020 · 10 comments

Comments

@stevexyz
Copy link

Is your feature request related to a problem?

Improve useful fake instructions optimizing automatic code generation.

Describe the suggested solution:

Among fake instructions I didn't see the very useful ones to convert "JR" and "DJNZ" to respectively "JP" and "DEC B, JP NZ" instructions if the relative branch offset is out of range.
To explicitly invoke this feature without implicitly do it a suffix or a prefix letter can be used on the instructions (just as an example: "JRM" and "DJNZM", or "MJR" and "MDJNZ").
I personally use almost exclusively these modified instructions (with a custom definition for fasmg) to automatically minimize and speed up code.

@ped7g
Copy link
Collaborator

ped7g commented Jul 28, 2020

I'm afraid the 3-pass assembling may be too slow with forward jr jumps, but djnz is lot more often using backward, so I guess for djnz such feature would mostly work... Then again dec b + jp nz is not 100% "long djnz" because it affects flags, so it may be quite confusing in the end unless somebody well informed is using it and understands fully the implications.

But this would be much better with multi-pass (#63 ), I think with 3-pass this may underperform to the point it may feel useless.

Thinking about it, I guess you can use macros for these to get a feel how native inclusion would [not] work.
Like this:

djnzm   MACRO   label?
            IF (label? - $) < -126 || +129 < (label? - $)   ; ok fwd reference
                dec b : jp nz,label?
            ELSE
                djnz label?
            ENDIF
        ENDM

    ORG     $8000

loop1:
        .126 nop
        djnzm   loop1   ; just within the -128 relative jump (126x nop + 2B djnz)
        djnzm   loop1   ; outside, using JP
    ; forward variant test
        djnzm   loop2   ; outside, using JP
        djnzm   loop2   ; *can* be relative DJNZ, but the forward reference math fails
                        ; and it stays stuck on the long JP variant in all three passes
                        ; it will resolve to `djnz` with `.125 nop` block
        .127 nop
loop2:

Also shows how it's suffering from forward references.

Another problem with faking it with macros is that you need two macros... jrm (jr) and jrmc (jr cc) to have one and two arguments macros. Unless you want to write jrm <c,myLabel> to turn the whole "c,myLabel" string into single macro argument, but I guess jrmc nz,label is easier to write/read than using the <...> all the time. (at least until you can't create overload macros or variable argument macros, neither of which is planned in near future by me)

(TLDR conclusion: may happen, but most likely in the mythical "v2.x", ie. no idea when, maybe just year, maybe decades or maybe never)

@stevexyz
Copy link
Author

In my opinion jr is more important, and in fact I all djnz I ever used were always pretty short (and backward) ones, and no flags involved.

Apart the flags (that I didn't notice weren't affected), another thing I noticed is that despite one byte fetch more, probably since ALU involvment, jp instruction is also very slightly faster than jr when condition is met (and slower when not met).

In my use case ("extreme" 1K program), automatic byte saving is a key factor, since I have multiple conditional includes that can change lenghts depending on the compilations flags (and the importance of each single byte saved). And for the syntax "jrm label / jrm c,label" seems to me more fitting.

How many passes do you think would it take to resolve forward references in sjasmplus?

@ped7g
Copy link
Collaborator

ped7g commented Jul 29, 2020

the logic is, that you need last two passes to emit the same amount of machine code = same label addresses = same relative jumps/etc...

If you have complex code where multiple "jrm"s can reach relative-status based on some other jrm switching from jp to jr, like optimizing one more in every following pass, it may take even 10-20 passes before the stable state is reached and the same jp/jr set is generated for every following pass. At that point it makes sense to generate machine code bytes. The 3-pass is basically not enough for any fwd-ref, 4-pass would be enough for trivial case when all your jp/jr can be decided at second pass (not triggering further optimization of some other jp in third pass).

But the ugly truth is, that the assembler is either multi-pass, or not. If not, you have to account for that when writing your assembly source and try to write the source in sequential way, mostly defining everything before it is first time used. The benefit is that the assembling will be faster (not waiting X passes before the code "settles down"), and quite often easier to read and understand after years, the negative is that sometimes it requires more effort to lay down everything ahead, or adjust jp/jr set manually in the source.

I would like to change sjasmplus to support N-pass way, because in the end with complex source the 3-pass sometimes really forces you to reorganize in way which makes source more difficult to maintain. But I have no idea when I will successfully modify it to N-pass, I'm slowly cleaning up source also in this direction whenever I'm fixing other bugs, but I don't think it's completely ready for the shift (although it's probably much more ready than it was back in v1.10.4). (and once I support 4/5/6 passes, I may as easily make that just config option on command line, like x86 TASM from Borland had, so for very complex sources like set of jrm changing a lot depending on other jumps you would simply raise the limit as much as needed, if the default max would fail).

@lvd2
Copy link
Contributor

lvd2 commented Aug 21, 2020

Observation: we can have new mnemonics like J, JZ, JNZ, JC, JNC to implement auto-choice of JR or JP.
Also, that won't break older code in any way, only new code using those mnemonics would be optimized.

@stevexyz
Copy link
Author

Seems to me a nice option.
But as Peter said it will depend by the support of "multi-pass"...
There is at least some forecast about it?

@ped7g
Copy link
Collaborator

ped7g commented Aug 21, 2020

some forecast: would be nice, but I'm not aware of anyone actively working on it.

If nobody else shows up, I may eventually do it (with the whole "v2.x" stuff), but I have no idea about time frame... may be next year, may be never. Depends on the real life situation and mood. Also the sjasmplus as is in v1.17.0 works for my current ZX Spectrum Next projects extremely well, I resolved most of my "itches" in the past 1.5 year. All the remaining issues are not directly affecting my personal projects, so I have little incentive to work on these, except the personal pride and strive for making the sjasmplus even better tool, but I already spent kinda "unhealthy" amount of my time fixing the sjasmplus, can't afford another year of this without serious consequences in other areas of my life. ;) (yep, need to do some paid work too, at least sometimes).

@specke
Copy link

specke commented Aug 29, 2020

Just a quick suggestion: once sjasmplus becomes a proper N-pass assembler, there will be no need to have such fake instructions. The macro facility in sjasmplus is already powerful enough to define the necessary logic. And, of course, fake instructions should not be called JR or JP or DJNZ if they imply a possible command substitution.

@ped7g
Copy link
Collaborator

ped7g commented Aug 29, 2024

issues cleanup notes: at least #63 (n-pass), #103 / #150 (overload or variadic) is needed to implement jump macros which would allow both conditional/unconditional arguments. Having many macros for each condition like jz/jnz is also possibility, but with such "pollution" of macro space you are raising risk of hitting it by accident and some names will clash with original instructions (jump parity vs jp).

Even then I personally feel "wrong" about such feature, not a fan of it.

If ever such enhancements to sjasmplus happen, it will be possible to implement this with macros. I don't think I ever would like to make this part of sjasmplus built-in functionality, feels to "fragile" to me (how I imagine writing Z80 assembly and potential use cases). I would not hesitate to include it as macro example in tests / examples if it would happen, but not as part of the sjasmplus itself. So closing this for a moment, as I don't think this adds anything on top of those issues already mentioned (BTW those are not in any near future plan either, at least not my plan).

@ped7g ped7g closed this as not planned Won't fix, can't repro, duplicate, stale Aug 29, 2024
@lvd2
Copy link
Contributor

lvd2 commented Aug 29, 2024

Having many macros for each condition like jz/jnz is also possibility, but with such "pollution" of macro space you are raising risk of hitting it by accident and some names will clash with original instructions (jump parity vs jp).

There's only so many relative jumps in Z80, those are ONLY the following ones: JR Z, JR NZ, JR C, JR NC, JR and DJNZ. Having J/JZ/JNZ/JC/JNC (and not DJNZ as I see no way to flawlessy replace that to absolute jump) that would automatically select short or long form won't create any clashing with 'jump parity' or anything else since there's no such insns in Z80. BTW 'jump parity' in Z80 is only absolute and spelled as JP PO or JP PE

@ped7g
Copy link
Collaborator

ped7g commented Aug 29, 2024

you are right, my bad, I didn't think about that, thanks for reminding me (but doesn't change much about closing this and reasoning behind it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants
@ped7g @stevexyz @lvd2 @specke and others