Due to unusually high levels of website traffic you will be presenting with regular Cloudflare checks for the time being.

Making reusable code in assembler

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Making reusable code in assembler

Post by presh »

I'm working on some display routines which handle scrolling and whatnot.

I want to make the code as reusable as possible, so I've used constants to allow values such as the screen dimensions to be specified, e.g.

Code: Select all

buffer_width_in_cells EQU 24
buffer_width_in_cells EQU 24
This is fine, but means I am having to use "generic" functions to do multiplications, which could be coded more efficiently

e.g. instead of using something such as this function (renamed HL_equals_HxE below), which clocks in around 249 Ts minimum...

Code: Select all

  ; HL = bg_attr_addr + (top_offset * buffer_width_in_cells) 
  ; Use HL_equals_HxE
  LD A, (top_offset)
  LD H, A
  LD E, buffer_width_in_cells
  CALL HL_equals_HxE     ; 187 Ts - 238 Ts ... see link above for source!
  LD DE, bg_attr_addr
  ADD HL, DE
...it would be nice to be able to use the following, which is much more efficient at just 87 Ts

Code: Select all

  LD A, (top_offset)      ; A = top_offset (0-23)
  ADD A, A	; x2, max 46
  ADD A, A	; x4, max 92
  ADD A, A	; x8, max 184… over 127, so switch to 16 bit shift
  LD H, 0
  LD L, A	; HL = x8
  LD D, H
  LD E, L       ; DE = x8
  ADD HL, HL    ; x16
  ADD HL, DE    ; x24
  LD DE, bg_attr_addr
  ADD HL, DE
My first thought was to create a config file, and specify which file we want to use there (if any ) to override the multiplication, e.g.

Code: Select all

optimised_include EQU 'better_multiply.asm'
In this case, 'better_multiply.asm' would contain the optimised snippet from above.

Then adjust the code:

Code: Select all

; HL = bg_attr_addr + (top_offset * buffer_width_in_cells) 
if optimised_include 
  INCLUDE optimised_include   ; 'better_multiply.asm' in this case!
else
  ; Use HL_equals_HxE
  LD A, (top_offset)
  LD H, A
  LD E, buffer_width_in_cells
  CALL HL_equals_HxE
  LD DE, bg_attr_addr
  ADD HL, DE
endif
Sadly, the pasmo assembler doesn't like this. The manual states that "the use of IF directives to conditionally include different files is not allowed" :(

So that leads me to the questions:

1. Are there any assemblers which do support conditional INCLUDEing of files in a manner similar to that described above?

I'm not too fussed if the filename can not be specified by the author, i.e. "you must place your optimised code in OverrideFile.asm and activate in config.asm by specifying ActivateOverrideFile EQU 1" - though it'd be "nice to have" such flexibility if available. The code would be bundled with examples which can be tweaked to suit... and the "generic function method" is always available as a default/failsafe option if the author isn't confident making the optimisations.
In short, I want it to be tweakable without affecting the original code.

2. Portability implications

I was looking at other assemblers and took a brief look at sjasmplus, and realised that there is syntax which differ between assemblers...
sjasmplus did not like the if/else/endif statements, nor REPT/ENDM directives, and treats them as labels.
It seems you have to write code which works with your assembler of choice... or are there common tricks I am missing?
It would be nice if others could have access to the finished code without having to modify it, but I'm guessing this is not gonna happen! :|
User avatar
ketmar
Manic Miner
Posts: 740
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Making reusable code in assembler

Post by ketmar »

1. yep. my UrAsm allows that, and i'm using such conditional includes a lot. you can get UrAsm along with my XT engine (here).

2. yes, each asm is using its own set of directives. sometimes they are compatible, and sometimes they aren't. that's why we have to tell people to "compile this code with XXXasm to make it work". alas.
Nienn Heskil
Microbot
Posts: 135
Joined: Tue Jun 09, 2020 6:14 am
Contact:

Re: Making reusable code in assembler

Post by Nienn Heskil »

Sjasmplus is more or less the default choice of a Spectrum cross-assembler these days for anything bigger than hello world projects. It has LUA support in it, and just plenty of nice and useful features for keeping one's sanity (...ok, sometimes that works the other way too I guess xd).
presh wrote: Wed Jan 18, 2023 1:45 am

Code: Select all

optimised_include EQU 'better_multiply.asm'
Close enough. In sjasmplus, you use DEFINE and IFDEF for this:

Code: Select all

 ;config.asm
 DEFINE optimised_include "better_multiply.asm"
 
 ;elsewhere.asm
 IFDEF optimised_include 
   INCLUDE optimised_include
 ELSE
  ; other code
 ENDIF
Note that, by default, sjasmplus doesn't recognize directives starting from the beginning of a line, and treats such expressions as labels (though there is an option to override this IIRC). Put at least a single space before using a directive in your code.
User avatar
Bedazzle
Manic Miner
Posts: 309
Joined: Sun Mar 24, 2019 9:03 am

Re: Making reusable code in assembler

Post by Bedazzle »

ketmar wrote: Wed Jan 18, 2023 4:53 am that's why we have to tell people to "compile this code with XXXasm to make it work". alas.
...or code must be written with aim to be compiled with different assemblers, and use IFDEFs where needed.
AndyC
Dynamite Dan
Posts: 1448
Joined: Mon Nov 13, 2017 5:12 am

Re: Making reusable code in assembler

Post by AndyC »

Bedazzle wrote: Wed Jan 18, 2023 9:29 am ...or code must be written with aim to be compiled with different assemblers, and use IFDEFs where needed.
Not all assemblers support IFDEF or use the same syntax for it if they do. And many vary in even the most basic support of things like labels.

Trying to make Z80 code portable between assemblers is basically a lost cause. And it's one of the reasons that later processor designs got much more strict about things like assembler directive, label and even macro syntaxes.
User avatar
ketmar
Manic Miner
Posts: 740
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Making reusable code in assembler

Post by ketmar »

Bedazzle wrote: Wed Jan 18, 2023 9:29 am ...or code must be written with aim to be compiled with different assemblers, and use IFDEFs where needed.
…like using IF to comment out the part with INCLUDE file to make it work with Pas… oh, wait…
User avatar
Seven.FFF
Manic Miner
Posts: 753
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Making reusable code in assembler

Post by Seven.FFF »

Heh yes. That kind of cross-assembler portability only works if all assemblers support the same subset of conditional directives. They don’t and even for some that do, the portable code would be a huge unreadable mess.

Definitely a lost cause. Just pick one that's popular, relatively bug free, and actively developed. Sjasmplus ticks all those boxes.

If you want to make reusable and sharable code you can also buy into the z88dk ecosystem and contribute/maintain libs. Although most people know this as a C compiler it also has a linking assembler, and can be used for 100% asm projects. Linking means that only those parts of the libs you actively reference in your project get included, which makes code re-use much easier.

The learning curve is high compared with picking a full-featured standalone assembler like sjasmplus though, and you will have trouble convincing other users to use your libs, as most people just want to keep it simple and only use a setup that makes sense for them.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

Nienn Heskil wrote: Wed Jan 18, 2023 5:58 am Note that, by default, sjasmplus doesn't recognize directives starting from the beginning of a line, and treats such expressions as labels (though there is an option to override this IIRC). Put at least a single space before using a directive in your code.
Ooh, good shout! I didn't know this so you've definitely saved me a headache there! :dance
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

AndyC wrote: Wed Jan 18, 2023 9:36 am Trying to make Z80 code portable between assemblers is basically a lost cause.
That's certainly the impression I'm getting from you lot :lol:

I do have one glimmer of hope...

I'm already dynamically generating one of the .asm files (to do display & attribute transfer based on the width of the buffers) using a PHP script, the idea eventually being that you enter your buffer dimensions etc into a webpage and it generates a .zip with all customisation taken care of. I'd already considered building a script to optimise these multiplications too (which would prevent the need for custom includes in the first place... take that, Pasmo incompatibility!!)... so perhaps differences in assembler syntax could be incorporated too?! But also I should probably document what's already there before moving onto these side-quests :)
User avatar
evilpaul
Manic Miner
Posts: 244
Joined: Tue Jul 28, 2020 8:04 am
Location: Espoo, Finland
Contact:

Re: Making reusable code in assembler

Post by evilpaul »

presh wrote: Wed Jan 18, 2023 7:03 pm I'm already dynamically generating one of the .asm files (to do display & attribute transfer based on the width of the buffers) using a PHP script, the idea eventually being that you enter your buffer dimensions etc into a webpage and it generates a .zip with all customisation taken care of. I'd already considered building a script to optimise these multiplications too (which would prevent the need for custom includes in the first place... take that, Pasmo incompatibility!!)... so perhaps differences in assembler syntax could be incorporated too?! But also I should probably document what's already there before moving onto these side-quests :)
The five stages of grief model (or the Kübler-Ross model) is popularly known as a model that describes a series of emotions experienced by people who are grieving over a lack of features in their chosen assembler. The five stages are: denial, anger, writing your own pre-processor, depression and acceptance of sjasmplus.
User avatar
ketmar
Manic Miner
Posts: 740
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Making reusable code in assembler

Post by ketmar »

evilpaul wrote: Wed Jan 18, 2023 9:13 pm and acceptance of sjasmplus.
then i'm the lost cause, i guess: i wrote my own asm. of course, it is totally incompatible with all other asms out there…
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

Seven.FFF wrote: Wed Jan 18, 2023 4:16 pm most people just want to keep it simple and only use a setup that makes sense for them.
Indeed... ultimately it's unlikely many people will actually use my code, but the simpler it is to implement in their chosen setup, the more likely they are to at least have a play around with it. That's why I figured a web interface to take care of the things behind the scenes would be a good idea, if only for my own sanity when a few months down the line I decide it'd be a good fit for something I'm fiddling with, and can in incorpoate it quickly instead of via a ton of trial-and-error.
Also if partway into development you decide to, say, reduce the size of the visible play area to make things run a bit faster, it's much easier to download a freah copy with the revised settings than to prod around the source code trying to "fix it"... one less thing to worry about in an already fraught process.

Applying that to different assemblers, it's not that difficult to e.g. replace REPT with DUP, so it makes sense to have a script do that so the developer doesn't have to... eventually ;)
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

evilpaul wrote: Wed Jan 18, 2023 9:13 pm The five stages of grief model (or the Kübler-Ross model) is popularly known as a model that describes a series of emotions experienced by people who are grieving over a lack of features in their chosen assembler. The five stages are: denial, anger, writing your own pre-processor, depression and acceptance of sjasmplus.
:lol:

I'm still figuring out how to output files in an appropriate format for my needs via sjasmplus, so I think the kind of scripting required to customise .asm on-the-fly to the level I require is well beyond me at this stage!
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

Here's my first stab at a web script to optimise multiplication of a constant factor by a variable factor (A = 0 to 255)

It uses the maximum possible value of A to switch between 8 & 16 bit processing.

Assumes BC, DE & HL are all available, and that the output is 16-bit (i.e. constant × maximum variable value >= 256) as the process for optimising 8-bit seems substantially different and I haven't had time to consider it properly yet.

Output is: HL = A * constant

https://zx.preshaudio.co.uk/multiply.php

Let me know if you spot any issues or scope for improvement! :)
AndyC
Dynamite Dan
Posts: 1448
Joined: Mon Nov 13, 2017 5:12 am

Re: Making reusable code in assembler

Post by AndyC »

presh wrote: Fri Jan 20, 2023 1:25 pm Applying that to different assemblers, it's not that difficult to e.g. replace REPT with DUP, so it makes sense to have a script do that so the developer doesn't have to... eventually ;)
It's probably easier to just write a script to port assembly source from one assembler to another. Which is easier to apply to source from other sources etc as well. It's not as if any of the assemblers out their are likely to do anything that drastically different.
User avatar
Seven.FFF
Manic Miner
Posts: 753
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Making reusable code in assembler

Post by Seven.FFF »

AndyC wrote: Fri Jan 20, 2023 3:20 pm It's probably easier to just write a script to port assembly source from one assembler to another.
This. Experienced devs treat porting between two tool flavours as trivial, on a similar level to spell checking or grammar checking. They would see a tool that emits magic converted code for them as something like Clippy in MS Word - something opaque that gets in the way of what you aready know how to do quickly and easily.

Beginner devs struggle with just about anything, including the dialect of their own assembler. They even get stuck following a step by step tutorial article because those are usually written for one specific scenario that likely differs from the dev's own scenario. Giving them another tool that's supposed to make it easier by hiding details that they should be learning just gives them more things to get stuck on. I see this with other helper tools a lot on a daily basis.

Not trying to discourage the OP at all, though. Doing something like this is rewarding in its own right, even if nobody but yourself uses it. Just don't get disillusioned if your expectations are not met.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

AndyC wrote: Fri Jan 20, 2023 3:20 pm It's probably easier to just write a script to port assembly source from one assembler to another. Which is easier to apply to source from other sources etc as well. It's not as if any of the assemblers out their are likely to do anything that drastically different.
Yeah, that's the plan. e.g. write for pasmo, then have a script to swap the syntax (like the previously mentioned pasmo REPT -> sjasmplus DUP) based on the selected assembler.

There's not much in the code that's assembler-specific, and barely a handful of incompatible directives, so the actual code modification doesn't seem like a big job.
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

Seven.FFF wrote: Fri Jan 20, 2023 3:30 pm This. Experienced devs treat porting between two tool flavours as trivial, on a similar level to spell checking or grammar checking. They would see a tool that emits magic converted code for them as something like Clippy in MS Word - something opaque that gets in the way of what you aready know how to do quickly and easily.

Beginner devs struggle with just about anything, including the dialect of their own assembler. They even get stuck following a step by step tutorial article because those are usually written for one specific scenario that likely differs from the dev's own scenario. Giving them another tool that's supposed to make it easier by hiding details that they should be learning just gives them more things to get stuck on. I see this with other helper tools a lot on a daily basis.
I understand what you mean. The tool I posted is just a debugging playground to check the code which would be automatically inserted, so the developer would never see the actual tool in this case. They just receive some code which works out-of-the-box for the parameters they chose. But I'm keen to ensure that the code they receive is as efficient as possible without requiring modification.
Not trying to discourage the OP at all, though. Doing something like this is rewarding in its own right, even if nobody but yourself uses it. Just don't get disillusioned if your expectations are not met.
Yes, it's fun just messing around with stuff tbh, and it's interesting to discuss :)
User avatar
Seven.FFF
Manic Miner
Posts: 753
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Making reusable code in assembler

Post by Seven.FFF »

presh wrote: Fri Jan 20, 2023 8:39 pm Yeah, that's the plan. e.g. write for pasmo, then have a script to swap the syntax (like the previously mentioned pasmo REPT -> sjasmplus DUP) based on the selected assembler.

There's not much in the code that's assembler-specific, and barely a handful of incompatible directives, so the actual code modification doesn't seem like a big job.
It's still tricky on occasion. Pasmo REPT has an optional loop var symbol, sjasmplus only added that feature in a very recent change that hasn't been been released yet. Pasmo has extremely eccentric operator precedence, so you would have to add brackets in the converted expression code to force other assemblers to use that precedence. Pasmo has very little range checking, so other assemblers will output different results or emit errors for the equivalent pasmo code. Pasmo source code doesn't even do what the documentation says it does, in a few cases.

I could go on! That's just scratching the surface.

I don't think you can really do it without building a massive set of unit tests for all the assemblers you will target.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Bedazzle
Manic Miner
Posts: 309
Joined: Sun Mar 24, 2019 9:03 am

Re: Making reusable code in assembler

Post by Bedazzle »

presh wrote: Fri Jan 20, 2023 8:39 pm There's not much in the code that's assembler-specific, and barely a handful of incompatible directives, so the actual code modification doesn't seem like a big job.
It really depends on code complexity. There are sources with different macroses, or even partially covered by LUA, so porting back from sjasmplus to different assembler can be kind of tricky.
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

Re: Making reusable code in assembler

Post by presh »

Seven.FFF wrote: Fri Jan 20, 2023 9:21 pm It's still tricky on occasion. Pasmo REPT has an optional loop var symbol, sjasmplus only added that feature in a very recent change that hasn't been been released yet. Pasmo has extremely eccentric operator precedence, so you would have to add brackets in the converted expression code to force other assemblers to use that precedence. Pasmo has very little range checking, so other assemblers will output different results or emit errors for the equivalent pasmo code. Pasmo source code doesn't even do what the documentation says it does, in a few cases.

I could go on! That's just scratching the surface.

I don't think you can really do it without building a massive set of unit tests for all the assemblers you will target.
Bedazzle wrote: Fri Jan 20, 2023 11:51 pm It really depends on code complexity. There are sources with different macroses, or even partially covered by LUA, so porting back from sjasmplus to different assembler can be kind of tricky.
Oh, I realise the scale of doing something like that properly. That's way beyond the scope of what I need, I'd only be planning to address those incompatibilities which appear in my source code... which is literally just REPT n / ENDM (which I could even expand to avoid needing them, at the expense of code compactness/readability), and a couple of 256-byte boundary aligns.
Post Reply