News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_roudoudou

Rasm Z80 assembler

Started by roudoudou, 08:58, 22 February 17

Previous topic - Next topic

0 Members and 3 Guests are viewing this topic.

andycadley

But then why do you need the assembler to do anything, just use macros instead like:

macro PACKADDR bnk,addr
defw {bnk}, {addr} / 256
mend

; init cartridge output mode
buildcpr

bank 0

label1
jp label1
PACKADDR {bank}table,table

bank 1

ALIGN 256
table defb 0

because you know at this point that those addresses are always going to be in a FAR location, you have to because you have to write specific code to handle all the banking necessary to access them.

To be honest you can probably use rasms macros to do the substitution of a CALL instruction conditionally too in a similar fashion, although I'm not au-fait enough with the syntax to do it. Then the only issue is code overlapping from one bank to another which you can easily do by keeping track of the instructions you generate.

roudoudou

As mention in the very first chapter of the documentation, please DO NOT USE /256 to get high weight of an address or a value with Rasm

Rasm does correct rounding!

You must use a proper method for this

LD H,hi(myaddress)
or
LD H,myaddress>>8

My pronouns are RASM and ACE

zhulien

Quote from: andycadley on 15:21, 25 June 23But then why do you need the assembler to do anything, just use macros instead like:

macro PACKADDR bnk,addr
defw {bnk}, {addr} / 256
mend

; init cartridge output mode
buildcpr

bank 0

label1
jp label1
PACKADDR {bank}table,table

bank 1

ALIGN 256
table defb 0

because you know at this point that those addresses are always going to be in a FAR location, you have to because you have to write specific code to handle all the banking necessary to access them.

To be honest you can probably use rasms macros to do the substitution of a CALL instruction conditionally too in a similar fashion, although I'm not au-fait enough with the syntax to do it. Then the only issue is code overlapping from one bank to another which you can easily do by keeping track of the instructions you generate.
Of course, it is easy if I am coding in assembly language.  It is not at all straight forward if the assembly language was generated by a high level language.

andycadley

Quote from: zhulien on 16:21, 25 June 23
Quote from: andycadley on 15:21, 25 June 23But then why do you need the assembler to do anything, just use macros instead like:

macro PACKADDR bnk,addr
defw {bnk}, {addr} / 256
mend

; init cartridge output mode
buildcpr

bank 0

label1
jp label1
PACKADDR {bank}table,table

bank 1

ALIGN 256
table defb 0

because you know at this point that those addresses are always going to be in a FAR location, you have to because you have to write specific code to handle all the banking necessary to access them.

To be honest you can probably use rasms macros to do the substitution of a CALL instruction conditionally too in a similar fashion, although I'm not au-fait enough with the syntax to do it. Then the only issue is code overlapping from one bank to another which you can easily do by keeping track of the instructions you generate.
Of course, it is easy if I am coding in assembly language.  It is not at all straight forward if the assembly language was generated by a high level language.
It's exactly the same. Generate the macros as part of the preamble to your output (since you can know up front what all of them will have to be) and then use the macros in place of generating the corresponding instructions.

zhulien

#354
Its not the same.  To do it in a high level language means needing to calculate the size of generated code to know where banks overflow before we actually generate the assembly output.  If you are handcoding assembler then you likely planned the placement of everything by hand to some degree already and only need to do it once.  High level language do you expect someone to hand edit the assembler every time they press compile?

From our dialog, I suspect you don't understand the use and benefit.

Prodatron

#355
Quote from: zhulien on 16:21, 25 June 2316bit packed far addresses

Can anyone tell me, what a
"16bit packed far addresses" is?
From my x86 asm time I know that far addresses are addresses >16bit (you had to specify the segment as well, which in total was already 32bit).
In SymbOS an address is specified by the 64k bank (currently 4bit, up to 16 banks) + 16bit address inside the bank, which is in total 20bit (1MB).

But what is a "16bit packed far address"??

GRAPHICAL Z80 MULTITASKING OPERATING SYSTEM

andycadley

Quote from: Prodatron on 22:27, 25 June 23
Quote from: zhulien on 16:21, 25 June 2316bit packed far addresses

Can anyone tell me, what a
"16bit packed far addresses" is?
From my x86 asm time I know that far addresses are addresses >16bit (you had to specify the segment as well, which in total was already 32bit).
In SymbOS an address is specified by the 64k bank (currently 4bit, up to 16 banks) + 16bit address inside the bank, which is in total 20bit (1MB).

But what is a "16bit packed far address"??
If you know the pointer is 256 byte aligned, the bottom byte is always zero. So you can instead use that byte to store the bank number.

The firmware does similar things with ROM calls because it knows the address is never greater than #3fff 

andycadley

Quote from: zhulien on 19:22, 25 June 23Its not the same.  To do it in a high level language means needing to calculate the size of generated code to know where banks overflow before we actually generate the assembly output.  If you are handcoding assembler then you likely planned the placement of everything by hand to some degree already and only need to do it once.  High level language do you expect someone to hand edit the assembler every time they press compile?

From our dialog, I suspect you don't understand the use and benefit.
You only need a very rough approximation because code blocks are going to be at least 256 byte aligned anyway and as long as you tend to overestimate it'll work out fine (with MBs of RAM you can afford to be generous). You can keep a worse case scenario count based on the instructions you generate, it doesn't need to be accurate enough to know actual addresses, just to get a feel for when you're close to the end of a bank. Heck you could just go with 3*instruction count and it'd probably do 90% of the time, especially if you leave some buffer space at the end of each bank.

zhulien

#358
@Prodatron in combination to the packed address schemes below, an unexpanded 6128 has 5 x 16k banks between 4000 and 7fff and we can page out the last used bank to have a fairly fast virtual memory scheme.  Since only the last accessed ones are paged out, you effectively have a minimum of 64k always paged in based on how you control your program.  You can quite regularly make fast interbank calls without disc paging as long as it isn't too random.  I.e  let's say of you have lots of game assets in 16mb ram.  But you only need 64k of them per level.  Of course you could load the data from disc in a traditional sense, but its a lot easier to code (debatably) if you can just call a function to grab the asset.  More importantly is you can call code all over the place with reasonable performance.

Thanks God for symbiface and x-mass and m4.

OPTION 1: 16mb

address: xxxxxxxxyyyyyyyy
capacity: x = 256 x 64kb = 16mb
alignment: y = 64 x 256byte blocks per 16kb

stats:

64kb has 256 blocks
256kb has 1024 blocks
512kb has 2048 blocks
16mb has 65536 blocks

pros:

address fits within 16 bits
likely fast to translate due to 256 byte alignment
perfect fit for use with IX and IY registers

cons:

perhaps not enough blocks for certain types of applications



OPTION 2: 4mb

address: xxxxxxyyyyyyyyyy
capacity: x = 64 x 64kb = 4mb
alignment: y = 256 x 64byte blocks per 16kb

stats:

64kb has 1024 blocks
256kb has 4096 blocks
512kb has 8192 blocks
4mb has 65536 blocks

pros:

address fits within 16 bits
still works with IX and IY registers
lots of blocks for certain types of applications

cons:

slower to translate due to 64 byte alignment



Far address is the firmware guide terminology for an address that isn't paged in.  I think there are rst instructions already catering for far address calls in ROM, so a similar technique can be used for RAM... just intelligent use of bits makes it more convenient in 16bits to cover the full memory map than eg... 24 bit addressing.

zhulien

Just some other notes on Virtual Memory.

Banks with code or data that hasn't been modified do not need to be 'saved' when paged - only read.  This involves keeping a dirty flag for banks that you set when modifying something in a bank.

As mentioned, an unexpanded 6128 has 5 x 16k banks in which 4 are cached at any one time out of a potential 65536 banks (the 65536 banks are packed into 8 bits because code blocks or data blocks are byte aligned)

If someone has 5128kb physical RAM added, 576kb total - then they have 33 most recently used banks cached at any one time.
If someone has 2048kb physical RAM added, 2112kb total - then they have 65 most recently used banks cached at any one time.
If someone has 4096kb physical RAM added, 4160kb total - then they have 129 most recently used banks cached at any one time.

Considering that some OSs like UZI perform complete 32kb swaps (at least it used to have no intelligence in the way it did it) and it still performed ok, this is actually a pretty good memory solution for CPC.

I am not expecting an influx of 16mb software for CPC - in fact likely none will ever appear.  But... if someone does make it program that is several mb - it will seamlessly work on an unexpanded 6128 or a 4mb CPC.

GUNHED

Fun thing! Liked to read about the 16 bit far-address conventions.  :)

Under FutureOS a 16 bit far address is actually consisting of 24 bit. 8 bits for the 16 KB RAM block to be used (256 * 16K = 4 MB). And then the 16 bit address of the RAM the Z80 has in direct access. Now the block only has 16 KB, not 64 KB. Therefore 48 KB remain untouched form banking. 
http://futureos.de --> Get the revolutionary FutureOS (Update: 2023.11.30)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> Get the RSX-ROM for LambdaSpeak :-) (Updated: 2021.12.26)

zhulien

This scheme I am building into PrimeOS which will be sure to work inside FutureOS which I have already started (but not tested).  There should be no issues with FutureOS also "optionally" supporting the 4mb or 16mb virtual memory scheme too - If you'd like the VM banking functions I will send you them later, or if you prefer just the algorithm you can have that.  Becase the most recent physical memory banks are in physical memory always except for or 1 bank that is always the last accessed (and next to be paged out), you can probably calculate performance i.e. to fill 16mb with NOPS - on a 128k system vs a 4mb expanded system.  Or worst case, an unexpanded 464 which.  In reality the difference just for this artificial NOP test isn't realistic except for example... loading a 16mb program where you will need to read and write every single 16kb block.  But, for logic where you do repeated calls to different banks - the physical RAM hit is pretty high if the number of banks you are randomly calling is equal or less than the physical number of banks you have - there are programming restrictions / guidelines... e.g. don't trust an actual bank number - because  we are using handles to banking, not real bank numbers.  The ultimate purpose of this wasn't because we want 16mb RAM - although why not support that now for a future 16mb RAM expansion - but rather if we do make a 2mb or 4mb program, "it will still work (not necessarily fast) on an unexpanded 464 without modification" and "with reasonable speed on an unexpanded 6128".

roudoudou

#362
download link https://github.com/EdouardBERGE/rasm/releases/tag/v2.0

New release Rasm v2.0 - 2023 Blue Hedgehog

What? Less than a month since two releases?
Yeah, sorry, but there was a huge rework and enhancement on dynamic LZ sections
You can now overflow the 64K memory space allocated IF YOU SQUEEZE enough your data to fit the 64K at the end ^_^
You may jump on another crunched section from anywhere if the destination is not 'in place', i mean, with a relocated ORG like "ORG #A000,$"
As usual, new features use the unlimited design, you may overflow as much as you want in the memory
As usual, the feature is already documented

Enjoy!

Note : There is also new AY conversion value, expect some audio changes

MacOS binaries & Windows binaries on github
My pronouns are RASM and ACE

norecess464

Yes, that LZX0/LZCLOSE feature is insane, first time I'm seeing this in an assembler, thanks a lot Roudoudou!!!!  :-*
My personal website: https://norecess.cpcscene.net
My current project is Sonic GX, a remake of Sonic the Hedgehog for the awesome Amstrad GX-4000 game console!

isidoro

Hi,
I've noticed in the binary generated with last release (2.1.5) some zeros are added before metastructs



buildsna
bankset 0
org #4000
run $

struct COORD
    x   db #77
    y   db #78
endstruct

struct COORD obj_coord, 5


struct st1
  ch1 defw #0201
  ch2 defb #03
endstruct

struct metast1
  struct st1 pr1
  struct st1 pr2
endstruct

struct metast1 bigst

ld hl,bigst.pr2.ch1
ld a,(hl)

ld a,(ix+metast1.pr2.ch1)

;; Output before 2.1.5:
;; 77 78 77 78 77 78 77 78 77 78 01 02 03 01 02 03 21 0D 40 7E DD 7E 03

;; Output with 2.1.5:
;; 77 78 77 78 77 78 77 78 77 78 00 00 01 02 03 00 00 01 02 03 21 0D 40 7E DD 7E 03

roudoudou

Quote from: isidoro on 18:41, 06 January 24Hi,
I've noticed in the binary generated with last release (2.1.5) some zeros are added before metastructs
thanks, i commited a fix + autotest
will publish another subversion soon
My pronouns are RASM and ACE

isidoro

Quote from: roudoudou on 20:15, 06 January 24
Quote from: isidoro on 18:41, 06 January 24Hi,
I've noticed in the binary generated with last release (2.1.5) some zeros are added before metastructs
thanks, i commited a fix + autotest
will publish another subversion soon
Thanks for your work, your assembler is amazing!!

isidoro

Hi again,

One question, is there any way to insert data into struct when creating instances?

E.g., this code creates 4 instances of DIRTY_RECT, each one points to different GFX, how could I set GFX pointer of each instance?

struct COORD
    x    db    #00
    y    db    #00
endstruct

struct DIRTY_RECT
    struct COORD screen
    gfx_pointer    dw
endstruct



struct DIRTY_RECT DIRTY_RECT_NINJA
    ;;db #00, #00
    ;;dw GFX_DIRTY_RECT_NINJA

struct DIRTY_RECT DIRTY_RECT_NINJA_WEAPON
    ;;db #00, #00
    ;;dw GFX_DIRTY_RECT_NINJA_WEAPON

struct DIRTY_RECT DIRTY_RECT_ENEMY
    ;;db #00, #00
    ;;dw GFX_DIRTY_RECT_ENEMY

struct DIRTY_RECT DIRTY_RECT_ENEMY_WEAPON
    ;;db #00, #00
    ;;dw GFX_DIRTY_RECT_ENEMY_WEAPON
Is there any way to fill data in struct instances?

roudoudou

Quote from: isidoro on 20:54, 11 January 24Hi again,

One question, is there any way to insert data into struct when creating instances?
 Is there any way to fill data in struct instances?

it's a project I've been planning for a long time, but I still haven't decided how I'm going to do it. So... It's planned.
My pronouns are RASM and ACE

isidoro

Ok, meanwhile can write data in emulator's assembler.

Thanks


roudoudou

New release v2.2 Valentine
https://github.com/EdouardBERGE/rasm/releases/tag/v2.2

Important version because RASM and ACE were sharing the same wrong RLE encoding/decoding
 No Impact for people using ACE & RASM only
 But when sharing from other Emulator to ACE or importing from other Emulators to Rasm, you could have trouble with V3 version
bugfix Snapshot RLE encodingadd new math function pow2 to get fractionnal power of 2documentation updated

Developpers using ACE-DL must get new Valentine release http://www.roudoudou.com/ACE-DL/
My pronouns are RASM and ACE

isidoro

Hi, I've just tested v2.2, and I get an output of 0kb .sna file. I build my project with:

buildsna
bankset 0
org CODE_INI
run entry_point

And this VSCode tasks.json:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "bin\\.\\rasm \"${file}\" -ob \"output\\${fileBasenameNoExtension}.bin\" -oi \"output\\${fileBasenameNoExtension}.sna\"
        },
        {
            "label": "test",
            "type": "shell",
            "command": "bin\\.\\rasm \"${file}\" -ob \"output\\${fileBasenameNoExtension}.bin\" && AceDL\\.\\AceDL.exe"
        }
    ]
}

roudoudou

Quote from: isidoro on 18:46, 13 February 24Hi, I've just tested v2.2, and I get an output of 0kb .sna file. I build my project with:

buildsna
bankset 0
org CODE_INI
run entry_point
rasm wont ouput anything if there is no code

about VS i dont know how its working, maybe you should try command line first

My pronouns are RASM and ACE

isidoro

Quote from: roudoudou on 20:37, 13 February 24
Quote from: isidoro on 18:46, 13 February 24Hi, I've just tested v2.2, and I get an output of 0kb .sna file. I build my project with:

buildsna
bankset 0
org CODE_INI
run entry_point
rasm wont ouput anything if there is no code

about VS i dont know how its working, maybe you should try command line first



Excuse me, my English is not very good, maybe I have not expained it enough.
There is code in my project, I've only posted that related to rasm. With previous version my code generates the .sna correctly, I've only replaced rasm executable with the new one. Compiler outputs messages of inserting data, truncating values, and creates the sna file, but the file is empty. I've tried with:
.\rasm.exe "project.asm"With the same result.
Maybe I'm missing something ...

Powered by SMFPacks Menu Editor Mod