BASM: Best ASseMbler in my desk room.

Started by krusty, 15:21, 06 October 21

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

krusty

Hello,
these last months, I have worked on the Benediction cross ASseMbler that will be used in our next production.I have not yet tested it in real-world conditions, so ATM I have truly no idea of its efficiency/usability.
I write this message to let anyone give it some try and provide me feedback to fix potential bugs and eventually bring more features for its real official release.I guess the official release will be accompanied by a graphic version of basm for those that are not yet ready to use command line tools.


Its aim is not to replace rasm that is a really fast and good assembler. But it can be used in contexts where rasm cannot play. BTW it is not 100%compatible with rasm code.

Of course, there is no documentation ready yet, but you can get most of its possibilities in the files named good_xxx.asm of this directory https://github.com/cpcsdk/rust.cpclib/tree/master/basm/tests/asm.
Among what is not (yet) available for rasm you can check the section, basic, or iterate examples.
Note that basm uses two assembling passes by default, but can stop at the first pass if there is no need to do another one or add additional ones if necessary (which makes the ifused example compatible with basm but not rasm)

Here is its usage

USAGE:
    basm.exe [FLAGS] [OPTIONS] <--inline <INLINE>|INPUT> [--]

FLAGS:
        --basic               Request a Basic header (the very first instruction has to be the LOCOMOTIVE directive.
        --binary              Request a binary header
    -i, --case-insensitive    Configure the assembler to be case insensitive.
        --db                  Write a db list on screen (usefull to get the value of an opcode)
        --snapshot            Generate a snapshot
        --Werror              Warning are considered to be errors
    -h, --help                Prints help information
    -V, --version             Prints version information

OPTIONS:
    -I, --include <INCLUDE_DIRECTORIES>...    Provide additional directories used to search files.
        --inline <INLINE>                     Z80 code is provided inline
        --lst <LISTING_OUTPUT>                Filename of the listing output.
    -l <LOAD_SYMBOLS>...                      Load symbols from the given file
    -o, --output <OUTPUT>                     Filename of the output.
        --sym <SYMBOLS_OUTPUT>                Filename of the output symbols file.

ARGS:
    <INPUT>    Input file to read.


I have included the current windows release here https://ufile.io/rbg1k0es : for those that want to build it themselves, the sources are available here and the command is here https://www.cpcwiki.eu/forum/programming/basm-best-assembler-in-my-desk-room/msg207962/#msg207962

Feel free to create issues here https://github.com/cpcsdk/rust.cpclib/issues or post a message in the forum if it does not assemble your code as you expect (and provide a minimum asm code that reproduce it).

roudoudou

#1
People should hurry up for 《char》asm name  (basm, zasm, rasm, sjasm, vasm, tasm, dasm, ...)


Congratz for this first public release
use RASM, the best assembler ever made :p

I will survive

zhulien

liked... what happened to the like button anyway?

Targhan

Cool. But I don't think I would switch to yet another assembler... unless you can show me things you can do better than Rasm!
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

Sid_

Hi Krusty,
where can I find the sources to compile it on macOS and linux ?


krusty_benediction

Quote from: Targhan on 23:57, 06 October 21
Cool. But I don't think I would switch to yet another assembler... unless you can show me things you can do better than Rasm!
I do not know what to answer ;)If you are happy of your workflow with rasm, there is no reason to change.
If you want to use additional features, such as those I presented in the first post, there is an interest to use basm.

krusty_benediction

#6
Quote from: Sid_ on 11:43, 07 October 21
Hi Krusty,
where can I find the sources to compile it on macOS and linux ?

At the moment, basm is buried in my CPC toolbox. I may change that later and ease the installation procedure.
So you just need to clone the repository https://github.com/cpcsdk/rust.cpclib and use the command line of first post


See https://www.cpcwiki.eu/forum/programming/basm-best-assembler-in-my-desk-room/msg207962/#msg207962

Targhan

The Section looks interesting, though I don't think I would ever use it (doesn't it clutter the code?).
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

krusty_benediction

Quote from: Targhan on 00:38, 08 October 21
The Section looks interesting, though I don't think I would ever use it (doesn't it clutter the code?).
I think it is clean and nice for moderated size projects as you can provide some kind of semantic to your memory area in one place instead of relying on assertions to manually check if you write code in forbidden zones (and I am pretty sure it would be more unreadable for assertions in some code I have in mind).
I think it is wonderful for generated code where data and code are interleaved (think about a demo effect that use 100÷ of line to line splitting memory area and the rest for the demo effect). You can simplify the generator by letting basm doing the checks (instead of the generator that would have to know the size of generated code) and by generating separately data source and code source wheras thé would be mixed once assembled.
Finally it can ease writing a some ugly stuff that would have been useful for me when writing crtc (for example to have some code in an effect source code injected in the demo system area, instead of putting it in the demo system source)
I have not yet intensively tested this directive, but I will surely use it intensively in my next project


krusty

I guess it is becoming usable. The following piece of code is assembled by basm version on github that generates the accompanying snapshot. I'll search how to delegate to github the construction of windows/linux/mac binaries after each commit (if someone already masters that, help is welcomed)


    macro KILL_SYSTEM
        di
            ld hl, $c9fb
            ld ($38), hl
            ld sp, $4000
        ei
    endm

    macro WAIT_VSYNC
        ld b, $f5
@loop
        in a, (c)
        rra
        jr nc, @loop
    endm

    macro WAIT_VSYNC_STRICT
        ld b, $f5
@loop1
        in a, (c)
        rra
        jr c, @loop1
        WAIT_VSYNC (void)
    endm

    macro SET_CRTC reg, value
        ld bc, $bc00 + {reg} : out (c), c
        ld bc, $bd00 + {value} : out (c), c
    endm

BACKGROUND equ $54

    struct RASTER_BAR

ink1        db BACKGROUND ; space for ink1 of raster
ink2        db BACKGROUND
ink3        db BACKGROUND
ink4        db BACKGROUND
ink5        db BACKGROUND ; space for ink5 of raster

    endstruct
   
RASTER_HEIGHT equ RASTER_BAR

    org 0x4000

    KILL_SYSTEM (void)
    SET_CRTC 6, 0

    di
    WAIT_VSYNC_STRICT (void)
   
    call frame_loop
    jr $

frame_loop
        WAIT_VSYNC (void)


        ld b, 30
    .loop
            nop 64 - (duration(djnz .loop) + 1) ; duration of djnz is the one with no jump
        djnz .loop

        call show_raster
   
    jp frame_loop


show_raster

    ; manage the sine wave for the vertical position
    nop 15 
    ld hl, sine_curve
.curve_position equ $-2
    ld b, (hl)
    inc l
    ld (.curve_position), hl
.loop
    nop 64 - (duration(djnz .loop) + 1) ; duration of djnz is the one with no jump
    djnz .loop

    ; really display the raster bar
    ld bc, $7f10
    ld hl, raster_table.bar1
    out (c), c
   
    repeat RASTER_HEIGHT, loop

        ticker start @raster_line_duration
            ld a, (hl)
            out (c), a

            if {loop} != RASTER_HEIGHT
                inc hl
            endif
        ticker stop

        nop 64-@raster_line_duration

    endr
    ld a, BACKGROUND
    out (c), a

    ret

raster_table
.bar1
    RASTER_BAR $44, $55, $5C, $55, $44


    align 256
sine_curve
.height equ min(255, 312 - 30 - 8)

    repeat 256, i
        val set .height/2*cos({i}*360/256) + .height/2 + 1
        print {hex}$, "=", val
        db val
    endr





   


Targhan

Quote from: krusty on 13:17, 15 October 21I'll search how to delegate to github the construction of windows/linux/mac binaries after each commit
After each commit? Isn't that's a bit too much?
GitHub might have a CI available though, but it might not be free. For Arkos Tracker, I use a local CI (Jenkins).
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

roudoudou

Quote from: Targhan on 16:23, 15 October 21
After each commit? Isn't that's a bit too much?
isn't it the very common nightly build?
use RASM, the best assembler ever made :p

I will survive

krusty

Quote from: Targhan on 16:23, 15 October 21After each commit? Isn't that's a bit too much?
GitHub might have a CI available though, but it might not be free. For Arkos Tracker, I use a local CI (Jenkins).

I wanted to say push; but not it is not too much often for a hobbyist project I guess ;)
I am able to build with github (at least for Linux) but I have net succeded in publishing automatically.

krusty_benediction

New directive supported (not deeply tested however): FUNCTION
      ;;
    ; The function `name` takes 3 arguments arg1, arg2, and arg3,
    ; uses a local variable
    ; and returns a value (the sum of the two arguments).
    ; No z80 code is allowed there, but it is possible to use some directives
    FUNCTION name, arg1, arg2, arg3

        IF {arg3} > 0
                  local1 = {arg1} + {arg2}
        ELSE
                  local1 = {arg1} - {arg2}
        ENDIF

        IF {arg1} > 2
            return local1
        ENDIF

        repeat 3
            local1 = local1+1
        rend

        return local1
       
    ENDFUNCTION
   
    ; Use the function name
    ld a, name(0, 1, 2)
    assert name(0, 1, 2) == 4
    assert name(3, 3, -2) == 0

krusty_benediction

Recursivity is even possible
    function fibo nb
        if {nb} == 0
            return 0
        else if {nb} == 1
            return 1
        else
            return fibo({nb}-1) + fibo({nb}-2)
        endif

    endfunction

    assert fibo(0) == 0
    assert fibo(1) == 1
    assert fibo(5) == 5
    assert fibo(10) == 55

krusty_benediction

Basm is now able to handle strings and list of expressions in addition to standard numeric values.It is now possible to create functions that generate a string of z80 code and assemble it.This add abilities to generate code in a more powerfull way than macros.
For example here is an extract of what is possible:

...
code = get_specialized_code(main_mask, screen_lines_order, sprite_lines_order, 0)binary = assemble(.code)
        db binary
        assert memory($-1) == 0xc9, string_concat("The routine does not end by RET!", code, binary)

       
        print code
with get_specialized_code a function written in the asm file that returns z80 code in a string and takes as input 3 lists and a numeric value.The generateds code is also printed in the terminal to quickly check what is generated

GUNHED

Quote from: roudoudou on 15:37, 06 October 21
People should hurry up for 《char》asm name  (basm, zasm, rasm, sjasm, vasm, tasm, dasm, ...)


Can I call mine TASManian DEVil ??
http://futureos.de --> Get the revolutionary FutureOS (Update: 2022.03.09)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> Get the RSX-ROM for LambdaSpeak :-) (Updated: 2021.12.26)


krusty_benediction

I have finally been able to play with `github` actions to do continuous delivery.
You can find at this page https://github.com/cpcsdk/rust.cpclib/releases/tag/latest the executable for my various tools (including basm) that are updated after each push to the master branch.

The complete list of tools is (lots are probably barely usable at the moment as I implemented only what I needed at a specific moment, but I can update/fix on demand)

       
  • basm: z80 assembler. Fully usable for simple projects. Complex directives may not be deeply tested. Able to communicate with M4
  • bdasm: z80 disassembler. Requires still minor modifications to improve display of relative instructions
  • catalog: probably unfinished AMSDOS catalog manipulation
  • hideur: play with AMSDOS header files
  • imgconverter: PC to CPC image conversion. Used for CRTC demo. Some features (tiles ones for example) may not be finished. Able to communicate with M4
  • locomotive/ probably unfinished LOCOMOTIVE BASIC manipulation tool.
  • snapshot: SNA manipulation tool. Able to communicate with M4
  • visual-basm: Attempt to provide a GUI to basm. Could be useful to help people working with winape unable to switch to console tools

ralferoo

Quote from: krusty on 13:17, 15 October 21
    repeat RASTER_HEIGHT, loop

        ticker start @raster_line_duration
            ld a, (hl)
            out (c), a

            if {loop} != RASTER_HEIGHT
                inc hl
            endif
        ticker stop

        nop 64-@raster_line_duration

    endr
I think I need this in my life! I'm sick of writing code like this:

...
        ld hl,(palette+12)      ;5      L1+32

        defs line+1-32          ;       L1+ 1
        exx                     ;1      L1+ 2
        in a,(c)                ;4      L1+ 6
        xor c                   ;1      L1+ 7     ; check for edge
        call m,new_found_edge   ;3/5    L1+10   
        ld l,(hl)               ;2      L1+12     ; update counter
        exx                     ;1      L1+13

        ld a,#10                ;2      L1+15
        inc c                   ;1      L1+16 
        out (c),c               ;4      L1+20
        out (c),l               ;4      L1+24  ; colour 12
        out (c),a               ;4      L1+28
        inc c                   ;1      L1+29

        defs line+1-29          ;       L1+ 1
        exx                     ;1   L1+13...


ralferoo

Quote from: GUNHED on 21:08, 05 November 21
Can I call mine TASManian DEVil ??
Not CPC related, but TASM was Borland's Turbo Assembler on PC. About 1/10 the price of Microsoft's MASM, much faster and optionally compatible with its bugs.

ralferoo

Also, very timely seeing this thread. Last night, I encountered a particularly nasty bug in pasmo...


In some cases, where I had a value defines as "symbol equ -63"
Using that symbol with "ld de,symbol" assembled as "ld de,0". In the end, I had to change the value from -63 to 0-63 and suddenly it worked. Interestingly, this only affected 2 of about 20 such labels, the rest were done correctly.


In another case, I had "symbol_a equ -56" and "symbol_b equ -35+symbol_a" turned the "ld de,symbol_b" into something +ve.


What's weirder is that I can't understand how I was using this as my primary Z80 assembler for about 5 years and never noticed these problems before! But seemingly all of my old code does assemble correctly still.

krusty_benediction

Quote from: ralferoo on 01:12, 10 December 21Also, very timely seeing this thread. Last night, I encountered a particularly nasty bug in pasmo... In some cases, where I had a value defines as "symbol equ -63" Using that symbol with "ld de,symbol" assembled as "ld de,0". In the end, I had to change the value from -63 to 0-63 and suddenly it worked. Interestingly, this only affected 2 of about 20 such labels, the rest were done correctly. In another case, I had "symbol_a equ -56" and "symbol_b equ -35+symbol_a" turned the "ld de,symbol_b" into something +ve. What's weirder is that I can't understand how I was using this as my primary Z80 assembler for about 5 years and never noticed these problems before! But seemingly all of my old code does assemble correctly still.

so you can give a try to ours ;)But as I am the single user over the world, you may encounter bugs the first time you assemble your code. Would be happy to fix them.I'm using basm for the current demo screen I am coding and it works like a charm.I'm even able to assemble such piece of code:
font_char_address
    .current_char_relative_position = 0
    repeat NB_CHARS, char
        dw fonte + .current_char_relative_position*8
        .current_char_width = memory(fonte_width_table + ({char}-1))
        .current_char_width = max(1, ceil(.current_char_width/8))
        .current_char_relative_position = .current_char_relative_position + .current_char_width
    endr

norecess

#24
Quote from: krusty_benediction on 09:15, 10 December 21I'm even able to assemble such piece of code:
font_char_address
    .current_char_relative_position = 0
    repeat NB_CHARS, char
        dw fonte + .current_char_relative_position*8
        .current_char_width = memory(fonte_width_table + ({char}-1))
        .current_char_width = max(1, ceil(.current_char_width/8))
        .current_char_relative_position = .current_char_relative_position + .current_char_width
    endr
If your assembler is able to support that, that's pretty cool ! :)
But, in my opinion this goes too far.
When I need to generate such complex data, I prefer using a real IDE where I'm able to inspect generated data through a debugging session (from the IDE). The generator also outputs a human-readable text assembly file (eventually with a comment when required etc.). And finally, I can also create symbols for data that matters in the array.

Random examples as implemented in Sonic GX:

   ALIGN 2
Level2_UniqueGroundPositions:
Level2_UniqueGroundPosition_0:
   dw 0 + 32 ; Roof0
   dw 432 ; Ground0
   dw 0 + 32 ; Roof1
   dw 432 ; Ground1
   dw 0 + 32 ; Roof2
   dw 432 ; Ground2
Level2_UniqueGroundPosition_1:
   dw 0 + 32 ; Roof0
   dw 448 ; Ground0
   dw 0 + 32 ; Roof1
   dw 448 ; Ground1
   dw 0 + 32 ; Roof2
   dw 448 ; Ground2
Level2_UniqueGroundPosition_2:
   dw 0 + 32 ; Roof0
   dw 480 ; Ground0
   dw 0 + 32 ; Roof1
   dw 480 ; Ground1
   dw 0 + 32 ; Roof2
   dw 480 ; Ground2
Level2_UniqueGroundPosition_3:
   dw 0 + 32 ; Roof0
   dw 496 ; Ground0
   dw 0 + 32 ; Roof1
   dw 496 ; Ground1
   dw 0 + 32 ; Roof2
   dw 496 ; Ground2
Level2_UniqueGroundPosition_4:
   dw 0 + 32 ; Roof0
   dw 752 ; Ground0
   dw 0 + 32 ; Roof1
   dw 752 ; Ground1
   dw 0 + 32 ; Roof2
   dw 752 ; Ground2


Level2_MapPointers:
   db #80 + ROM_Level2_MapData
   db #80 + ROM_Tileset1_X0Y0
   db #80 + ROM_Tileset1_X1Y1
   db #B8 + ROM_Tileset1_X1Y0
   db #B8 + ROM_Tileset1_X0Y1
   db #B8 + ROM_Tileset1_TileOpcodeOffsets
   dw Tileset1_2MostUsedBytes
   db 0 ; HasVegetables 0=False 1=True
   dw Level2_UniqueGroundPositionIndices
   dw Level2_UniqueGroundPositions
   dw 0
   dw 0
   dw Level2_RingSliceIndices
   dw Level2_SpriteVisibility
   db 1
   db 1 + NbCharRows - 30
   dw _TILESET1_PALETTE
   db 1 ; 0=Fog 1=Water 2=Disabled (ColorCycling)
   dw 602 ; BossFightCameraTilePosX
   dw 0 ; wBossFightXOffset
   dw Music_BridgeZone_Start
   dw ManageSprites_Level23
   dw CollideSprites_Level23
   db 0 ; IsVertical
   db 1 ; HasBackgroundRasters
   db #80 + ROM_Level2_BigIndices
   dw Level2_PackedBigIndices
   db #80 + ROM_Level2_BigTileset
   dw ROM_Level2_BigTileset_ZX0

Powered by SMFPacks Menu Editor Mod