diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..eb80c63b --- /dev/null +++ b/404.html @@ -0,0 +1,474 @@ + + + +
+ + + + + + + + + + + + + +Here is the help provided by basm
.
Profile debug compiled: Sun, 20 Aug 2023 20:55:05 +0000
+
+Benediction ASM -- z80 assembler that mainly targets Amstrad CPC
+
+Usage: basm.exe [OPTIONS] [INPUT]
+
+Arguments:
+ [INPUT] Input file to read.
+
+Options:
+ --inline <INLINE> Z80 code is provided inline
+ -o, --output <OUTPUT> Filename of the output.
+ --db Write a db list on screen (usefull to get the value of an opcode)
+ --lst <LISTING_OUTPUT> Filename of the listing output.
+ --sym <SYMBOLS_OUTPUT> Filename of the output symbols file.
+ --sym_kind <SYMBOLS_KIND> Format of the output symbols file [default: basm] [possible values: winape, basm]
+ --basic Request a Basic header (the very first instruction has to be the LOCOMOTIVE directive).
+ --binary Request a binary header
+ --snapshot Generate a snapshot
+ -i, --case-insensitive Configure the assembler to be case insensitive.
+ -d, --directives-prefixed-by-dot Expect directives to by prefixed with a dot
+ -I, --include <INCLUDE_DIRECTORIES> Provide additional directories used to search files
+ -D, --define <DEFINE_SYMBOL> Provide a symbol with its value (default set to 1)
+ --m4 <TO_M4> Provide the IP address of the M4
+ -l <LOAD_SYMBOLS> Load symbols from the given file
+ --Werror Warning are considered to be errors
+ --progress Show a progress bar.
+ --list-embedded List the embedded files
+ --view-embedded <VIEW_EMBEDDED> Display one specific embedded file [possible values: inner://crtc.asm, inner://deexo.asm, inner://dzx0_fast.asm, inner://dzx0_standard.asm, inner://firmware/amsdos.asm, inner://firmware/casmng.asm, inner://firmware/gfxvdu.asm, inner://firmware/highkern.asm, inner://firmware/indirect.asm, inner://firmware/kernel.asm, inner://firmware/keymng.asm, inner://firmware/lowkern.asm, inner://firmware/machine.asm, inner://firmware/math6128.asm, inner://firmware/mathnot464.asm, inner://firmware/mathnot6xx.asm, inner://firmware/not464.asm, inner://firmware/scrpack.asm, inner://firmware/sound.asm, inner://firmware/txtvdu.asm, inner://ga.asm, inner://lz48decrunch.asm, inner://lz49decrunch.asm, inner://lz4_docent.asm, inner://opcodes_first_byte.asm, inner://pixels-routs.asm, inner://unaplib.asm, inner://unaplib_fast.asm]
+ -h, --help Print help
+ -V, --version Print version
+
+Still a Work In Progress assembler
+
Failure
+Not all directives have their synopsis, explanation, and examples
+Example:
+ org 0
+ LIST
+
+ ld a, 0
+ ; visible in the listing output
+
+ NOLIST
+
+ ld a, 1
+ ; Not visible in the listing ouput
+
Example:
+ org 0x1234
+
+ align 256
+ assert $ == 0x1300
+
+ align 256
+ assert $ == 0x1300
+
+ nop
+
+ align 128, 3
+ assert $ == 0x1300 + 128
+
Confine a memory area of 256 bytes maximum in such a way that it is always possible to navigate in the data by only modifying the low byte address (i.e INC L always works).
+ +;;
+; Confined directive is inspired by confine from rasm.
+; I guess confined is more ergonomic has it does not requires to manually specify the size of the confined area
+
+ org 0x0000
+
+ CONFINED
+ assert $ == 0
+ defs 128, 0xff
+ ENDCONFINED
+
+ CONFINED
+ assert $ == 256
+ defs 200, 0xff
+ ENDCONFINED
+
+ CONFINED
+ assert $ == 256 + 200
+ defs 20, 0xff
+ ENDCONFINED
+
On the code space ($), not physical space ($$)
+Example of code that assembles: +
+Example of code that fails: +
+; https://k1.spdns.de/Develop/Projects/zasm/Documentation/z71.htm
+ org 0x100
+
+label_100
+ nop
+label_101
+
+ assert $$ == 0x101
+ assert $ == 0x101
+
+ phase 0x200
+
+ assert $$ == 0x101
+ assert $ == 0x200
+
+label_200
+ nop
+label_201
+
+ dephase
+label_102
+
+ assert label_100 == 0x100
+ assert label_101 == 0x101
+ assert label_102 == 0x102
+ assert label_200 == 0x200
+ assert label_201 == 0x201
+
Synopsis: +
+Description: +Mark the memory between START and STOP as protected against write. Assembler fails when writting there. +On the code space ($), not physical space ($$)
+Example: +
+Description: +RANGE allows to define named portion of the memory, while SECTION allows to chose the portion of interest.
+Example: +
; sarcasm inspiration https://www.ecstaticlyrics.com/electronics/Z80/sarcasm/
+
+range $0080, $3FFF, code
+range $4000, $7FFF, data
+
+section code
+ ld hl, message_1
+ call print_message
+
+section data
+message_1: db "This is message #1.", $00
+
+section code
+ ld hl, message_2
+ call print_message
+
+section data
+message_2: db "This is message #2.", $00
+
+section code
+ print_message:
+ ld a, (hl)
+ or a
+ ret z
+ call 0xbb5a
+ inc hl
+ jr print_message
+
+ assert section_start("data") == 0x4000
+ assert section_length("data") == 0x4000
+ assert section_used("data") == 40
+
Description:
+When used with no argument, a bank corresponds to a memory area outside of the snapshot. All things read&write in memory are thus uncorrelated to the snapshot. +Sections do not apply in a bank.
+BANK page
is similar to WRITE DIRECT -1 -1 page
Synopsis:
+ +Example: +
; Set up a unique value in various banks
+ BANK 0xc0
+ org 0x4000 + 0
+ db 0xc0
+
+ BANK 0xc4
+ org 0x4000 + 1
+ db 0xc4
+
+
+ BANK 0xc5
+ org 0x4000 + 2
+ db 0xc5
+
+ BANK 0xc6
+ org 0x4000 + 3
+ db 0xc6
+
+
+ BANK 0xc7
+ org 0x4000 + 4
+ db 0xc7
+
+
+ BANKSET 0
+ assert memory(0x4000 + 0) == 0xC0
+
+ BANKSET 1
+ assert memory(0x4000 + 2) == 0xC5
+ assert memory(0x8000 + 3) == 0xC6
+ assert memory(0xC000 + 4) == 0xC7
+
Synopsis:
+ +Example: +
BANKSET 0
+
+ org 0x0000
+ db 1,2,3,4
+
+ org 0x4000
+ db 5,6,7,8
+
+ org 0x8000
+ db 9,10,11,12
+
+ org 0xc000
+ db 13, 14, 15, 16
+
+
+ BANKSET 1
+ org 0x0000
+ db 10,20,30,40
+
+ org 0x4000
+ db 50,60,70,80
+
+ org 0x8000
+ db 90,100,110,120
+
+ org 0xc000
+ db 130, 140, 150, 160
+
+
+ BANKSET 0
+ assert memory(0x0000) == 1
+ assert memory(0x4000) == 5
+ assert memory(0x8000) == 9
+ assert memory(0xc000) == 13
+
+ save "good_bankset_0_0.o", 0x0000, 4
+ save "good_bankset_0_1.o", 0x4000, 4
+ save "good_bankset_0_2.o", 0x8000, 4
+ save "good_bankset_0_3.o", 0xc000, 4
+
+ BANKSET 1
+ assert memory(0x0000) == 10
+ assert memory(0x4000) == 50
+ assert memory(0x8000) == 90
+ assert memory(0xc000) == 130
+
+ save "good_bankset_1_0.o", 0x0000, 4
+ save "good_bankset_1_1.o", 0x4000, 4
+ save "good_bankset_1_2.o", 0x8000, 4
+ save "good_bankset_1_3.o", 0xc000, 4
+
Description: +WRITE DIRECT is a directive from Winape that we have not fully reproduced. It's two first arguments need to be -1.
+Example: +
WRITE DIRECT -1, -1, 0xc0
+ org 0x4000+0
+ db 0xc0
+
+ WRITE DIRECT -1, -1, 0xc4
+ org 0x4000+1
+ db 0xc4
+
+
+ WRITE DIRECT -1, -1, 0xc5
+ org 0x4000+2
+ db 0xc5
+
+ WRITE DIRECT -1, -1, 0xc6
+ org 0x4000+3
+ db 0xc6
+
+
+ WRITE DIRECT -1, -1, 0xc7
+ org 0x4000+4
+ db 0xc7
+
+
+ BANKSET 0
+ assert memory(0x4000 + 0) == 0xC0
+
+ BANKSET 1
+ assert memory(0x4000 + 2) == 0xC5
+ assert memory(0x8000 + 3) == 0xC6
+ assert memory(0xC000 + 4) == 0xC7
+
Description:
+Assign an expression to a label. Assignement can be repeated several times.
+Synopsis: +
+Example: +
label = 1
+label =3
+.label=2
+
+ assert label == 3
+
+
+label = 5
+ assert label == 5
+
+label <<= 1
+ assert label == 10
+
Description: +Assign an expression to a label. Assignement cannot be repeated several times.
+Synopsis: +
+Example: +
label = 1
+label =3
+.label=2
+
+ assert label == 3
+
+
+label = 5
+ assert label == 5
+
+label <<= 1
+ assert label == 10
+
MAP VALUE
define a map counter to the required value.
#` is used to assign the value to a given label and increment it of the appropriate amount.
Example: +
; extract stolen here https://github.com/GuillianSeed/MetalGear/blob/master/Variables.asm#L10
+ map #c000
+
+GameStatus: # 1
+GameSubstatus: # 1
+ControlConfig: # 1
+ ; Bit6: 1=Enable music/Player control
+TickCounter: # 1
+WaitCounter: # 1
+TickInProgress: # 1
+ControlsTrigger: # 1
+ ; 5 = Fire2 / M, 4 = Fire / Space, 3 = Right, 2 = Left, 1 = Down, 0 = Up
+ControlsHold: # 1
+ ; 5 = Fire2 / M, 4 = Fire / Space, 3 = Right, 2 = Left, 1 = Down, 0 = Up
+Pause_1_F5_2: # 1
+TutorialStatus: # 1
+DemolHoldTime: # 1
+UnusedVar1: # 1
+DemoPlayId: # 1
+DemoDataPointer: # 2
+ ; Pointer to presaved demo controls
+SprShuffleOffset: # 1
+
+
+ assert GameStatus = 0xc000
+ assert GameSubstatus = (0xc000 + 1)
+ assert ControlConfig = (0xc000 + 1 + 1)
+ assert TickCounter = (0xc000 + 1 + 1+1)
+ assert DemolHoldTime = (0xc000 + 10)
+ assert SprShuffleOffset = (0xc000 + 15)
+
MAP
directive is probably easier to use
Example: +
; http://www.aspisys.com/asm11man.htm
+
+ org 0x100
+
+
+data set $
+ assert data == 0x100
+
+data1 setn data ; data1 could be modified
+data2 next data, 2 ; data2 cannot be modified
+data3 next data
+
+ assert data1 == 0x100
+ assert data2 == 0x101
+ assert data3 == 0x103
+ assert data == 0x104
+
Example: +
my_label = 1
+
+ ifndef my_label
+ fail "my_label must exist"
+ endif
+
+ undef my_label
+
+ ifdef my_label
+ fail "my_label must not exist"
+ endif
+
Example: +
; defb tests
+ org 0x200
+
+ defb 1, 2, 3, 4
+ defb "hello", ' ', "world"
+ defb $, $ ; should generate 2 different values
+ db "Hello world", 0
+
Example: +
+Description: +STR encodes string in AMSDOS format (i.e., adds 0x80 to the last char) and stores its bytes in memory.
+Example: +
org 0x1000
+ defb "hell"
+ defb 'o' + 0x80
+
+ org 0x2000
+ str "hello"
+
+ org 0x3000
+ db "Next one will be more complex"
+ db " \" et voila"
+ db " \" et voila"
+ db "\" et voila"
+
+ assert memory(0x1000) == memory(0x2000)
+ assert memory(0x1001) == memory(0x2001)
+ assert memory(0x1002) == memory(0x2002)
+ assert memory(0x1003) == memory(0x2003)
+
Example: +
org 0x100
+
+ charset "abcdefghijklmnopqrstuvwxyz", 0
+ charset "AB", 100
+ db "aA"
+ ASSERT memory(0x100) == 0x00
+ ASSERT memory(0x101) == 100
+
+ org 0x200
+ charset
+ db "aA"
+ ASSERT memory(0x200) == 'a'
+ ASSERT memory(0x201) == 'A'
+
Example: +
org 0x1000
+
+ ifndef toto
+ assert toto == $
+toto
+ assert pouet == 0x1002
+ dw pouet
+ endif
+pouet
+
Example: +
org 0x1000
+; 3 passes are needed there
+
+ ifused toto
+toto
+ ret
+ endif
+
+ call toto
+
+; test
+
Conditions can be nested.
+Example: +
org 0x100
+
+ if 0 == 1
+ fail "not reachable"
+ else ifdef toto
+ fail "not reachable"
+ else ifndef toto
+ print "reached"
+ db 1
+ else
+ fail "not reachable"
+ endif
+
Example: +
org 0x100
+
+ switch 3
+ ; one comment
+ case 1
+ db 1
+ break
+ case 3
+ db 3
+ ; another comment
+ case 4
+ db 4
+ break
+ case 5
+ db 5
+ default
+ db 6
+ endswitch
+
+
+
+ switch 5
+ case 1
+ db 1
+ break
+ case 3
+ db 3
+ case 4
+ db 4
+ break
+ case 5
+ db 5
+ default
+ db 6
+ endswitch
+
Example: +
; Takes inspiration from BRASS assembler
+
+ for count, 0, 10, 3
+ db {count}
+ endfor
+
+ for x, 0, 3
+ for y, 0, 3
+ db {x}*4 + {y}
+ fend
+ endfor
+
db 0
+ db 3
+ db 6
+ db 9
+
+ db 0*4 + 0
+ db 0*4 + 1
+ db 0*4 + 2
+ db 0*4 + 3
+
+ db 1*4 + 0
+ db 1*4 + 1
+ db 1*4 + 2
+ db 1*4 + 3
+
+ db 2*4 + 0
+ db 2*4 + 1
+ db 2*4 + 2
+ db 2*4 + 3
+
+ db 3*4 + 0
+ db 3*4 + 1
+ db 3*4 + 2
+ db 3*4 + 3
+
REPEAT AMOUNT [, COUNTER [, START]] + INNER LISTING +REND
+start
+ repeat 3, count
+ incbin 'AZERTY{{count}}.TXT'
+ rend
+
+
+ assert char(memory(start+0)) == 'A'
+ assert char(memory(start+1)) == 'Z'
+ assert char(memory(start+2)) == 'E'
+ assert char(memory(start+3)) == 'R'
+ assert char(memory(start+4)) == 'T'
+ assert char(memory(start+5)) == 'Y'
+ assert char(memory(start+6)) == 'U'
+ assert char(memory(start+7)) == 'I'
+ assert char(memory(start+8)) == 'O'
+ assert char(memory(start+9)) == 'P'
+
+ assert char(memory(start+10)) == 'Q'
+ assert char(memory(start+11)) == 'S'
+ assert char(memory(start+12)) == 'D'
+ assert char(memory(start+13)) == 'F'
+ assert char(memory(start+14)) == 'G'
+ assert char(memory(start+15)) == 'H'
+ assert char(memory(start+16)) == 'J'
+ assert char(memory(start+17)) == 'K'
+ assert char(memory(start+18)) == 'L'
+ assert char(memory(start+19)) == 'M'
+
+
+ assert char(memory(start+20)) == 'W'
+ assert char(memory(start+21)) == 'X'
+ assert char(memory(start+22)) == 'C'
+ assert char(memory(start+23)) == 'V'
+ assert char(memory(start+24)) == 'B'
+ assert char(memory(start+25)) == 'N'
+
The expression $i$ is evaluated after having generated the code of expression $i-1$. Take that into account if expressions use $.
+Example: +
; Glass inspiration http://www.grauw.nl/projects/glass/
+
+ iterate value, 1, 2, 10
+ add {value}
+ jr nz, @no_inc
+ inc c
+@no_inc
+ call do_stuff
+ iend
+
+
+ iterate value in [11, 12, 110]
+ add {value}
+ jr nz, @no_inc
+ inc c
+@no_inc
+ call do_stuff
+ iend
+
+do_stuff
+ ret
+
add 1
+ jr nz, no_inc1
+ inc c
+no_inc1
+ call do_stuff
+
+ add 2
+ jr nz, no_inc2
+ inc c
+no_inc2
+ call do_stuff
+
+ add 10
+ jr nz, no_inc3
+ inc c
+no_inc3
+ call do_stuff
+
+
+ add 11
+ jr nz, no_inc4
+ inc c
+no_inc4
+ call do_stuff
+
+ add 12
+ jr nz, no_inc5
+ inc c
+no_inc5
+ call do_stuff
+
+ add 110
+ jr nz, no_inc6
+ inc c
+no_inc6
+ call do_stuff
+
+do_stuff
+ ret
+
Example of standard macro: +
; rasm inspired
+ macro LDIXREG register,dep
+ if {dep}<-128 || {dep}>127
+ push BC
+ ld BC,{dep}
+ add IX,BC
+ ld (IX+0),{register}
+ pop BC
+ else
+ ld (IX+{dep}),{register}
+ endif
+ mend
+
+ LDIXREG H,200
+ LDIXREG L,32
+
Example of macro using raw arguments: +
macro BUILD_LABEL r#label
+{label}_first
+ endm
+
+
+ BUILD_LABEL "HERE"
+ BUILD_LABEL "THERE"
+
+ ifndef HERE_first
+ fail "macro error"
+ endif
+ ifndef THERE_first
+ fail "macro error"
+ endif
+
+ macro BUILD_CODE r#code
+ {code}
+ endm
+
+START_CODE1
+ BUILD_CODE "xor a"
+ BUILD_CODE "ld hl, 0xc9fb : ld (0x38), hl"
+END_CODE1
+
+START_CODE2
+ xor a
+ ld hl, 0xc9fb : ld (0x38), hl
+END_CODE2
+
+ assert END_CODE2 - START_CODE2 == END_CODE1 - START_CODE1
+ assert END_CODE2 - START_CODE2 == 7
+
+ assert memory(START_CODE1) == memory(START_CODE2)
+ assert memory(START_CODE1+1) == memory(START_CODE2+1)
+ assert memory(START_CODE1+2) == memory(START_CODE2+2)
+ assert memory(START_CODE1+3) == memory(START_CODE2+3)
+ assert memory(START_CODE1+4) == memory(START_CODE2+4)
+ assert memory(START_CODE1+5) == memory(START_CODE2+5)
+ assert memory(START_CODE1+6) == memory(START_CODE2+6)
+
Description:
+Structures allow to defined data blocs with semantic.
+In practice, they replace bunches of DEFB
, DEFW
directives and enforce checks at assembling (you cannot add more data than expected or forget some).
+If a label is used before the use of a struct, it is necessary to postfix it by :.
+Otherwise the assembler thinks the label is a macro or structure call.
Synopsis
+STRUCT <name>
+<filed1> DB|DW|STR|<other struct> [<value>]
+...
+<filedn> DB|DW|<other struct> [<value>]
+ENDSTRUCT
+
+
+
+[<label>:] <name> <arg1>, ... , <argn>
+
Standard example: +
struct color
+r db 1
+g db 2
+b db 3
+ endstruct
+
+ struct point
+x db 4
+y db 5
+ endstruct
+
+col0: color (void)
+pt0: point (void)
+
+col1: color 'a', 'b', 'c'
+pt1: point 'd', 'e'
+
+ struct colored_point
+col color 10, 20, 30
+pt point 10, 20
+ endstruct
+
+
+ colored_point (void)
+
Example using default values: +
;; Define a 3 fields structure
+ struct point
+xx db 4
+yy db 5
+zz db 6
+ endstruct
+
+ assert point == 3
+ assert point.xx == 0
+ assert point.yy == 1
+ assert point.zz == 2
+
+ point 1, 2 , 3
+ point ,,8
+ point 9
+
+
+; force values
+; : after label name allows to disambiguate parser that does not try to check if label is a macro (less errors/faster)
+my_point1: point 1, 2, 3
+
+; use all default values
+my_point2: point (void)
+
+; use default at the end
+my_point3: point 1
+
+; use default at the beginning
+my_point4: point ,,1
+
+
+p1: point 1, 2 , 3
+p2: point ,,8
+p3: point 9
+
+
+
+
+ struct triangle
+p1 point 1, 2 , 3
+p2 point ,,8
+p3 point 9 ; third point
+ endstruct
+
+ assert triangle == 9
+ assert triangle.p1 == 0
+ assert triangle.p2 == 3
+ assert triangle.p3 == 6
+
+
+my_triangle2: triangle [1, 2, 3], [4, 5, 6], [7, 8 , 9]
+
+
+ if 0
+
+
+my_triangle1: triangle
+
+my_triangle2: triangle [1, 2, 3], , [7, 8 , 9]
+ endif
+
Filenames are stored in a string. +These string can do expansion of formulas embedded in {}.
+basm embeds some files in its executable, they are access under the name "inner://" :
+Example: +
org 0x100
+
+
+ ld hl, CS_START
+ ld de, 0xc000
+ call aplib.depack
+ jp $
+
+CS_START
+ LZAPU
+INNER_START
+ defs 100
+INNER_STOP
+ LZCLOSE
+CS_STOP
+
+ assert INNER_STOP - INNER_START == 100
+ assert CS_STOP - CS_START < 100
+
+ include "inner://unaplib.asm" namespace "aplib"
+
INCBIN|BINCLUDE "fname" [[, SKIP], AMOUNT]
Fname can be build with variables.
+Limitations:
+Example: +
here
+ incbin "AZERTY.TXT", 2, 3
+there
+
+ assert peek(here) == 'E'
+ assert peek(here+1) == 'R'
+
+ assert there-here == 3
+
AZERTY.TXT
containing the text AZERTYUIOPQSDFGHJKLMWXCVBN
.
+INCLUDE|READ [ONCE] "<fname>" [AS|MODULE|NAMESPACE "<module>"]
Fname can be build with variables.
+Example with once: +
org 0x4000
+
+SIZE1_start
+ include once "include_once.asm"
+SIZE1_stop
+
+SIZE2_start
+ include once "include_once.asm"
+SIZE2_stop
+
+ assert (SIZE2_stop - SIZE2_start) == 0
+
Example with namespace: +
include "good_labels.asm" namespace "good"
+
+ ifndef good.outer1
+ fail "good.outer1 is undefined"
+ endif
+
+ ifdef outer1
+ fail "outer1 is defined"
+ endif
+
+ ifndef good.outer2.inner1
+ fail "good.outer2.inner1 is undedined"
+ endif
+
Files prefixed by inner://
are embedded by BASM
.
+Example:
+
include "inner://opcodes_first_byte.asm"
+
+ org 0x4000
+
+ db opcode_inc_l
+ inc l
+
+ assert memory(0x4000) == memory(0x4001)
+
In case of conditional assembling, inclusion are only done in the executed branch. This code always assemble as it never includes 'unknonw' file.
+ +Example: +
+SAVE "<fname>", [[[START], [SIZE]], AMSDOS|BASIC|TAPE]
SAVE "<fname>", START, SIZE, DSK, "<fname.dsk>" [, SIDE]
SAVE "<fname>", START, SIZE, HFE, "<fname.hfe>" [, SIDE]
SAVE "<fname>", START, SIZE, DISC, "<fname.hfe>"|"<dname.dsk>" [, SIDE]
Unimplemented
+TAPE option is not coded. +Other options are not intensively tested
+Example: +
org 0x4000
+ run $
+FIRST_ADDRESS
+ ld hl, txt
+loop
+ ld a, (hl)
+ or a
+ jp z, $
+
+ push hl
+ call 0xbb5a
+ pop hl
+ inc hl
+ jp loop
+
+txt
+.start
+ defb "Hello World!"
+ defb 0
+.stop
+LAST_ADDRESS
+
+
+ save "good_save_whole_inner.bin" ; Save binary without header
+ save "hello.bin", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, AMSDOS ; Save a binary with header
+ save "hello.bin", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, DSK, "hello.dsk" ; Save binary with header INSIDE a dsk
+
+ if BASM_FEATURE_HFE
+ save "hello.bin", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, HFE, "hello.hfe" ; Save binary with header INSIDE a hfe file
+ endif
+
+ save "good_save_txt.bin", txt.start, (txt.stop - txt.start) ; save text without header
+
+
+; cmd line to generate the binary with header
+; basm good_save.asm --binary -o run.bin
+; cmd line to put it in a dsk
+; dskmanager test.dsk format --format data42
+; dskmanager test.dsk add run.bin
+
Example: +
+Description: +Compute the execution duration of a block of code
+Synopsys: +
+Example 1: +
; http://mads.atari8.info/mads_eng.html
+
+ TICKER START count
+ WAITNOPS 3
+ TICKER STOP
+
+ assert count == 3
+
+ TICKER START count2
+ nop
+ TICKER STOP
+
+ assert count2 == 1
+
Example 2: +
TICKER START duration_varying_code
+ xor a
+ ld b, 1
+ TICKER STOP
+ assert duration_varying_code == 1 + 2
+ UNDEF duration_varying_code
+
+
+ TICKER START duration_varying_code
+ xor a
+ TICKER STOP
+ assert duration_varying_code == 1
+ UNDEF duration_varying_code
+
+ TICKER START duration_varying_code
+ TICKER STOP
+ assert duration_varying_code == 0
+ UNDEF duration_varying_code
+
+
+ assert duration(xor a) == 1
+ ;assert duration(xor a : xor a) == 2 ; Does not compile yet Could be a good idea
+
+
+ TICKER START duration_varying_code
+ WAITNOPS 64
+ TICKER STOP
+ assert duration_varying_code == 64
+ UNDEF duration_varying_code
+
+
+ TICKER START duration_stable_code
+ TICKER START duration_varying_code
+ out (c), c
+ TICKER STOP
+ WAITNOPS 64 - duration_varying_code
+ TICKER STOP
+ assert duration_stable_code == 64
+ UNDEF duration_varying_code
+
+
+ MACRO BUILD_STABLE_CODE duration, r#code
+ TICKER START .my_count
+ {code}
+ TICKER STOP
+ ASSERT {duration} >= .my_count
+ WAITNOPS {duration}-.my_count
+
+ IFDEF DEBUG_EXPECTED_DURATION
+ ASSERT .my_count == DEBUG_EXPECTED_DURATION
+ ENDIF
+ UNDEF .my_count
+ ENDM
+
+ DEBUG_EXPECTED_DURATION = 2
+ BUILD_STABLE_CODE 64, "xor a : xor a"
+
Generate a list of instructions that do not modify any registers or memory but is executed with the expected amount of nops. +(Currently it is synonym of NOP, but as soon as someone wants to provide clever rules to use less bytes, I'll implement them)
+ LOCOMOTIVE start
+10 REM Basic loader of binary exec
+20 REM yeah !!
+30 call {start}
+ ENDLOCOMOTIVE
+
+start
+ ld hl, txt
+.loop
+ ld a, (hl)
+ or a : jr z, .end
+ call #bb5a
+ inc hl
+ jr .loop
+.end
+ jp $
+
+txt
+ db "Hello world", 0
+
+ print "LOADER START IN ", {hex}start
+ save "LOADER.BAS",,,BASIC
+
Benediction ASsembler (BASM
in short) is a modern Z80 assembler.
+He has taken its inspiration from various Z80 assembler (Maxam/Winape, sjasmplus, rasm, vasm, BRASS, glass, zasm) as well as assemblers from other platforms (asm11, sarcasm).
+It is tailored for Amstrad CPC demomaking and has been successfully used to develop the Amstrad CPC demo Can Robots Take Control?.
+It has been still improved since and will serve for futur productions too.
The documentation is quite minimal at the moment, but included example code should be still valid and assembled propetly. +The user base being quite small, lots of bugs can remain. Do note hesitate to fill issues https://github.com/cpcsdk/rust.cpclib/issues or propose fixes.
+ld hl, de
).incbin 'file.asm
).ld a, opcode(xor a)
).MY_MACRO_WITH_TWO_ARGS 1, "string"
).db 5, my_function(3)
).BASM
uses as many passes as needed).BASM
that can be used by the users.An hello world representative of the functionalities of BASM
would be:
+
snainit "../cpclib-sna/src/cpc6128.sna" ; should be uneeded by properly specifying sna properties
+
+ org 0x4000
+ run $
+
+
+
+ ld hl, text_content
+loop
+ ld a, (hl)
+ or a
+ jp z, finished
+
+ call TXT_OUTPUT
+ inc hl
+ jp loop
+
+finished
+ jp $
+
+text_content
+ db "Hello, world!", 0
+ include "inner://firmware/txtvdu.asm"
+
Prefer to compile yourself basm
. But you can still download latest versions here:
Failure
+Continuous delivery system for Linux is broken. The executables are outdated of few years...
+MODULE
directive must be closed by ENDMODULE
REPEAT
counter is not accessible by using the variable counter
but {counter}
as in a MACRO
MACRO
using the label before the MACRO
directivebasm
can use an unlimited number of pass (warning there is not infinite loop check ATM), it can assemble code that would not be assembled with rasm
because labels have to be known at this momentDSK``, no support of
TAPEand `CPR
. HFE
is usable on Linux with the appropriate compilation option. AMSDOS
support is buggy ATM
SNA
should be okBASIC
tokens to create loaders that do not clear the screen when launchedWarning
+There may be still some subtle parser bugs, but it is possible to span instructions and directives on several lines by ending the previous line with \
BASM
is quite lax on the z80 format: it does not impose to start a label at the very first char of a line and does not force an instruction or directive to not start at the very first line of a char (behavior stolen to rasm
).
+As a consequence there can be ambiguities between labels and macros.
+If it fails in properly recognizing macros or label, you can guide it by suffixing label declaration by : or by using (void) for macros with no arguments.
outer1
+ jp outer2
+ jp outer2.inner1
+
+
+outer2
+ jp .inner1
+.inner1
+
+ ifndef outer1
+ fail "outer1 is wrongly undefined"
+ endif
+
+ ifndef .inner1
+ fail ".inner1 is wrongly undefined"
+ endif
+
+ ifndef outer2.inner1
+ fail "outer2.inner1 is wrongly undefined"
+ endif
+
module module1
+label1
+ jp module2.label1
+ endmodule
+
+
+
+ module module2
+label1
+ jp module3.module31.label1
+ endmodule
+
+ module module3
+ module module31
+label1
+ jp ::label1
+ endmodule
+ endmodule
+label1
+
Labels can be generated thanks to the content of other ones. +
+Here is the list of instructions used to validate BASM
:
; Test of assembling of z80 instructions.
+ LIST
+
+ org $0
+
+des equ $05
+n equ $20
+nn equ $0584
+
+ ; Documented instructions
+; ADC
+ adc a,(hl)
+ adc a,(ix + des)
+ adc a,(iy + des)
+ adc a,a
+ adc a,b
+ adc a,c
+ adc a,d
+ adc a,e
+ adc a,h
+ adc a,l
+ adc a,n
+
+ adc hl,bc
+ adc hl,de
+ adc hl,hl
+ adc hl,sp
+
+; ADD
+ add a,(hl)
+ add a,(ix + des)
+ add a,(iy + des)
+ add a,a
+ add a,b
+ add a,c
+ add a,d
+ add a,e
+ add a,h
+ add a,l
+ add a,n
+
+ add hl,bc
+ add hl,de
+ add hl,hl
+ add hl,sp
+
+ add ix,bc
+ add ix,de
+ add ix,ix
+ add ix,sp
+
+ add iy,bc
+ add iy,de
+ add iy,iy
+ add iy,sp
+
+; AND
+ and (hl)
+ and (ix + des)
+ and (iy + des)
+ and a
+ and b
+ and c
+ and d
+ and e
+ and h
+ and l
+ and n
+
+; BIT
+ bit 0,(hl)
+ bit 0,(ix + des)
+ bit 0,(iy + des)
+ bit 0,a
+ bit 0,b
+ bit 0,c
+ bit 0,d
+ bit 0,e
+ bit 0,h
+ bit 0,l
+
+ bit 1,(hl)
+ bit 1,(ix + des)
+ bit 1,(iy + des)
+ bit 1,a
+ bit 1,b
+ bit 1,c
+ bit 1,d
+ bit 1,e
+ bit 1,h
+ bit 1,l
+
+ bit 2,(hl)
+ bit 2,(ix + des)
+ bit 2,(iy + des)
+ bit 2,a
+ bit 2,b
+ bit 2,c
+ bit 2,d
+ bit 2,e
+ bit 2,h
+ bit 2,l
+
+ bit 3,(hl)
+ bit 3,(ix + des)
+ bit 3,(iy + des)
+ bit 3,a
+ bit 3,b
+ bit 3,c
+ bit 3,d
+ bit 3,e
+ bit 3,h
+ bit 3,l
+
+ bit 4,(hl)
+ bit 4,(ix + des)
+ bit 4,(iy + des)
+ bit 4,a
+ bit 4,b
+ bit 4,c
+ bit 4,d
+ bit 4,e
+ bit 4,h
+ bit 4,l
+
+ bit 5,(hl)
+ bit 5,(ix + des)
+ bit 5,(iy + des)
+ bit 5,a
+ bit 5,b
+ bit 5,c
+ bit 5,d
+ bit 5,e
+ bit 5,h
+ bit 5,l
+
+ bit 6,(hl)
+ bit 6,(ix + des)
+ bit 6,(iy + des)
+ bit 6,a
+ bit 6,b
+ bit 6,c
+ bit 6,d
+ bit 6,e
+ bit 6,h
+ bit 6,l
+
+ bit 7,(hl)
+ bit 7,(ix + des)
+ bit 7,(iy + des)
+ bit 7,a
+ bit 7,b
+ bit 7,c
+ bit 7,d
+ bit 7,e
+ bit 7,h
+ bit 7,l
+
+; CALL
+ call nn
+
+ call nz,nn
+ call z,nn
+ call nc,nn
+ call c,nn
+ call po,nn
+ call pe,nn
+ call p,nn
+ call m,nn
+
+; CCF
+ ccf
+
+; CP
+ cp (hl)
+ cp (ix + des)
+ cp (iy + des)
+ cp a
+ cp b
+ cp c
+ cp d
+ cp e
+ cp h
+ cp l
+ cp n
+
+ cpd
+ cpdr
+ cpir
+ cpi
+
+; CPL
+ cpl
+
+; DAA
+ daa
+
+; DEC
+ dec (hl)
+ dec (ix + des)
+ dec (iy + des)
+ dec a
+ dec b
+ dec c
+ dec d
+ dec e
+ dec h
+ dec l
+
+ dec bc
+ dec de
+ dec hl
+ dec ix
+ dec iy
+ dec sp
+
+; DI
+ di
+
+; DJNZ
+l1 djnz l1
+
+; EI
+ ei
+
+; EX
+ ex af,af'
+
+ ex de,hl
+
+ ex (sp),hl
+ ex (sp),ix
+ ex (sp),iy
+
+ exx
+
+; HALT
+ halt
+
+; IM
+ im 0
+ im 1
+ im 2
+
+; IN
+ in a,(c)
+ in b,(c)
+ in c,(c)
+ in d,(c)
+ in e,(c)
+ in h,(c)
+ in l,(c)
+
+ in a,(n)
+
+ ind
+ indr
+ ini
+ inir
+
+; INC
+ inc (hl)
+ inc (ix + des)
+ inc (iy + des)
+ inc a
+ inc b
+ inc c
+ inc d
+ inc e
+ inc h
+ inc l
+
+ inc bc
+ inc de
+ inc hl
+ inc ix
+ inc iy
+ inc sp
+
+; JP
+ jp nn
+
+ jp (hl)
+ jp (ix)
+ jp (iy)
+
+ jp nz,nn
+ jp z,nn
+ jp nc,nn
+ jp c,nn
+ jp po,nn
+ jp pe,nn
+ jp p,nn
+ jp m,nn
+
+; JR
+ jr $ + $22
+
+ jr nz,$ + $22
+ jr z,$ + $22
+ jr nc,$ + $22
+ jr c,$ + $22
+
+; LD
+ ld (bc),a
+ ld (de),a
+
+ ld (hl),a
+ ld (hl),b
+ ld (hl),c
+ ld (hl),d
+ ld (hl),e
+ ld (hl),h
+ ld (hl),l
+ ld (hl),n
+
+ ld (ix + des),a
+ ld (ix + des),b
+ ld (ix + des),c
+ ld (ix + des),d
+ ld (ix + des),e
+ ld (ix + des),h
+ ld (ix + des),l
+ ld (ix + des),n
+
+ ld (iy + des),a
+ ld (iy + des),b
+ ld (iy + des),c
+ ld (iy + des),d
+ ld (iy + des),e
+ ld (iy + des),h
+ ld (iy + des),l
+ ld (iy + des),n
+
+ ld (nn),a
+
+ ld (nn),bc
+ ld (nn),de
+ ld (nn),hl
+ ld (nn),ix
+ ld (nn),iy
+
+ ld (nn),sp
+
+ ld a,(bc)
+ ld a,(de)
+ ld a,(hl)
+ ld a,(ix + des)
+ ld a,(iy + des)
+ ld a,(nn)
+ ld a,a
+ ld a,b
+ ld a,c
+ ld a,d
+ ld a,e
+ ld a,h
+ ld a,l
+ ld a,n
+
+ ld b,(hl)
+ ld b,(ix + des)
+ ld b,(iy + des)
+ ld b,a
+ ld b,b
+ ld b,c
+ ld b,d
+ ld b,e
+ ld b,h
+ ld b,l
+ ld b,n
+
+ ld c,(hl)
+ ld c,(ix + des)
+ ld c,(iy + des)
+ ld c,a
+ ld c,b
+ ld c,c
+ ld c,d
+ ld c,e
+ ld c,h
+ ld c,l
+ ld c,n
+
+ ld d,(hl)
+ ld d,(ix + des)
+ ld d,(iy + des)
+ ld d,a
+ ld d,b
+ ld d,c
+ ld d,d
+ ld d,e
+ ld d,h
+ ld d,l
+ ld d,n
+
+ ld e,(hl)
+ ld e,(ix + des)
+ ld e,(iy + des)
+ ld e,a
+ ld e,b
+ ld e,c
+ ld e,d
+ ld e,e
+ ld e,h
+ ld e,l
+ ld e,n
+
+ ld h,(hl)
+ ld h,(ix + des)
+ ld h,(iy + des)
+ ld h,a
+ ld h,b
+ ld h,c
+ ld h,d
+ ld h,e
+ ld h,h
+ ld h,l
+ ld h,n
+
+ ld l,(hl)
+ ld l,(ix + des)
+ ld l,(iy + des)
+ ld l,a
+ ld l,b
+ ld l,c
+ ld l,d
+ ld l,e
+ ld l,h
+ ld l,l
+ ld l,n
+
+ ld a,i
+ ld i,a
+
+ ld a,r
+ ld r,a
+
+ ld bc,(nn)
+ ld de,(nn)
+ ld hl,(nn)
+ ld ix,(nn)
+ ld iy,(nn)
+ ld sp,(nn)
+
+ ld bc,nn
+ ld de,nn
+ ld hl,nn
+ ld ix,nn
+ ld iy,nn
+
+
+ ld sp,hl
+ ld sp,ix
+ ld sp,iy
+ ld sp,nn
+
+ ldd
+ lddr
+ ldi
+ ldir
+
+; NEG
+ neg
+
+; NOP
+ nop
+
+; OR
+ or (hl)
+ or (ix + des)
+ or (iy + des)
+ or a
+ or b
+ or c
+ or d
+ or e
+ or h
+ or l
+ or n
+
+
+; OUT
+ out (c),a
+ out (c),b
+ out (c),c
+ out (c),d
+ out (c),e
+ out (c),h
+ out (c),l
+ out (n),a
+
+ outd
+ otdr
+ outi
+ otir
+
+; POP
+ pop af
+ pop bc
+ pop de
+ pop hl
+ pop ix
+ pop iy
+
+; PUSH
+ push af
+ push bc
+ push de
+ push hl
+ push ix
+ push iy
+
+; RES
+ res 0,(hl)
+ res 0,(ix + des)
+ res 0,(iy + des)
+ res 0,a
+ res 0,b
+ res 0,c
+ res 0,d
+ res 0,e
+ res 0,h
+ res 0,l
+
+ res 1,(hl)
+ res 1,(ix + des)
+ res 1,(iy + des)
+ res 1,a
+ res 1,b
+ res 1,c
+ res 1,d
+ res 1,e
+ res 1,h
+ res 1,l
+
+ res 2,(hl)
+ res 2,(ix + des)
+ res 2,(iy + des)
+ res 2,a
+ res 2,b
+ res 2,c
+ res 2,d
+ res 2,e
+ res 2,h
+ res 2,l
+
+ res 3,(hl)
+ res 3,(ix + des)
+ res 3,(iy + des)
+ res 3,a
+ res 3,b
+ res 3,c
+ res 3,d
+ res 3,e
+ res 3,h
+ res 3,l
+
+ res 4,(hl)
+ res 4,(ix + des)
+ res 4,(iy + des)
+ res 4,a
+ res 4,b
+ res 4,c
+ res 4,d
+ res 4,e
+ res 4,h
+ res 4,l
+
+ res 5,(hl)
+ res 5,(ix + des)
+ res 5,(iy + des)
+ res 5,a
+ res 5,b
+ res 5,c
+ res 5,d
+ res 5,e
+ res 5,h
+ res 5,l
+
+ res 6,(hl)
+ res 6,(ix + des)
+ res 6,(iy + des)
+ res 6,a
+ res 6,b
+ res 6,c
+ res 6,d
+ res 6,e
+ res 6,h
+ res 6,l
+
+ res 7,(hl)
+ res 7,(ix + des)
+ res 7,(iy + des)
+ res 7,a
+ res 7,b
+ res 7,c
+ res 7,d
+ res 7,e
+ res 7,h
+ res 7,l
+
+; RET
+ ret
+
+ ret z
+ ret nz
+ ret c
+ ret nc
+ ret po
+ ret pe
+ ret p
+ ret m
+
+ reti
+ retn
+
+; RL
+ rl (hl)
+ rl (ix + des)
+ rl (iy + des)
+ rl a
+ rl b
+ rl c
+ rl d
+ rl e
+ rl h
+ rl l
+
+; RLA
+ rla
+
+; RLC
+ rlc (hl)
+ rlc (ix + des)
+ rlc (iy + des)
+ rlc a
+ rlc b
+ rlc c
+ rlc d
+ rlc e
+ rlc h
+ rlc l
+
+; RLCA
+ rlca
+
+; RLD
+ rld
+
+; RR
+ rr (hl)
+ rr (ix + des)
+ rr (iy + des)
+ rr a
+ rr b
+ rr c
+ rr d
+ rr e
+ rr h
+ rr l
+
+; RRA
+ rra
+
+; RRC
+ rrc (hl)
+ rrc (ix + des)
+ rrc (iy + des)
+ rrc a
+ rrc b
+ rrc c
+ rrc d
+ rrc e
+ rrc h
+ rrc l
+
+; RRCA
+ rrca
+
+; RRD
+ rrd
+
+; RST
+ rst $00
+ rst $08
+ rst $10
+ rst $18
+ rst $20
+ rst $28
+ rst $30
+ rst $38
+
+; SBC
+ sbc a,(hl)
+ sbc a,(ix + des)
+ sbc a,(iy + des)
+ sbc a,a
+ sbc a,b
+ sbc a,c
+ sbc a,d
+ sbc a,e
+ sbc a,h
+ sbc a,l
+ sbc a,n
+
+ sbc hl,bc
+ sbc hl,de
+ sbc hl,hl
+ sbc hl,sp
+
+; SCF
+ scf
+
+; SET
+label set 0,(hl)
+ set 0,(ix + des)
+ set 0,(iy + des)
+ set 0,a
+ set 0,b
+ set 0,c
+ set 0,d
+ set 0,e
+ set 0,h
+ set 0,l
+
+ set 1,(hl)
+ set 1,(ix + des)
+ set 1,(iy + des)
+ set 1,a
+ set 1,b
+ set 1,c
+ set 1,d
+ set 1,e
+ set 1,h
+ set 1,l
+
+ set 2,(hl)
+ set 2,(ix + des)
+ set 2,(iy + des)
+ set 2,a
+ set 2,b
+ set 2,c
+ set 2,d
+ set 2,e
+ set 2,h
+ set 2,l
+
+ set 3,(hl)
+ set 3,(ix + des)
+ set 3,(iy + des)
+ set 3,a
+ set 3,b
+ set 3,c
+ set 3,d
+ set 3,e
+ set 3,h
+ set 3,l
+
+ set 4,(hl)
+ set 4,(ix + des)
+ set 4,(iy + des)
+ set 4,a
+ set 4,b
+ set 4,c
+ set 4,d
+ set 4,e
+ set 4,h
+ set 4,l
+
+ set 5,(hl)
+ set 5,(ix + des)
+ set 5,(iy + des)
+ set 5,a
+ set 5,b
+ set 5,c
+ set 5,d
+ set 5,e
+ set 5,h
+ set 5,l
+
+ set 6,(hl)
+ set 6,(ix + des)
+ set 6,(iy + des)
+ set 6,a
+ set 6,b
+ set 6,c
+ set 6,d
+ set 6,e
+ set 6,h
+ set 6,l
+
+ set 7,(hl)
+ set 7,(ix + des)
+ set 7,(iy + des)
+ set 7,a
+ set 7,b
+ set 7,c
+ set 7,d
+ set 7,e
+ set 7,h
+ set 7,l
+
+; SLA
+ sla (hl)
+ sla (ix + des)
+ sla (iy + des)
+ sla a
+ sla b
+ sla c
+ sla d
+ sla e
+ sla h
+ sla l
+
+; SRA
+ sra (hl)
+ sra (ix + des)
+ sra (iy + des)
+ sra a
+ sra b
+ sra c
+ sra d
+ sra e
+ sra h
+ sra l
+
+; SRL
+ srl (hl)
+ srl (ix + des)
+ srl (iy + des)
+ srl a
+ srl b
+ srl c
+ srl d
+ srl e
+ srl h
+ srl l
+
+; SUB
+ sub (hl)
+ sub (ix + des)
+ sub (iy + des)
+ sub a
+ sub b
+ sub c
+ sub d
+ sub e
+ sub h
+ sub l
+ sub n
+
+; XOR
+ xor (hl)
+ xor (ix + des)
+ xor (iy + des)
+ xor a
+ xor b
+ xor c
+ xor d
+ xor e
+ xor h
+ xor l
+ xor n
+
+ ; Undocumented instructions
+; IN
+ in (c) ; DEFB $ED,$70
+ in f,(c) ; DEFB $ED,$70
+
+; OUT
+ out (c) ; DEFB $ED,$71
+ out (c),f ; DEFB $ED,$71
+
+; SLL
+ sll (hl)
+ sll (ix+des)
+ sll (iy+des)
+ sll a
+ sll b
+ sll c
+ sll d
+ sll e
+ sll h
+ sll l
+
+; IX and IY 8 bits halfs
+ add a,ixh
+ add a,ixl
+ add a,iyh
+ add a,iyl
+
+ adc a,ixh
+ adc a,ixl
+ adc a,iyh
+ adc a,iyl
+
+ and ixh
+ and ixl
+ and iyh
+ and iyl
+
+ cp ixh
+ cp ixl
+ cp iyh
+ cp iyl
+
+ dec ixh
+ dec ixl
+ dec iyh
+ dec iyl
+
+ inc ixh
+ inc ixl
+ inc iyh
+ inc iyl
+
+ ld a,ixh
+ ld b,ixh
+ ld c,ixh
+ ld d,ixh
+ ld e,ixh
+ ;ld h,ixh
+ ;ld l,ixh
+
+ ld a,ixl
+ ld b,ixl
+ ld c,ixl
+ ld d,ixl
+ ld e,ixl
+ ;ld h,ixl
+ ;ld l,ixl
+
+ ld a,iyh
+ ld b,iyh
+ ld c,iyh
+ ld d,iyh
+ ld e,iyh
+ ;ld h,iyh
+ ;ld l,iyh
+
+ ld a,iyl
+ ld b,iyl
+ ld c,iyl
+ ld d,iyl
+ ld e,iyl
+ ;ld h,iyl
+ ;ld l,iyl
+
+ ld ixh,a
+ ld ixh,b
+ ld ixh,c
+ ld ixh,d
+ ld ixh,e
+ ld ixh,ixh
+ ld ixh,ixl
+ ld ixh,n
+
+ ld ixl,a
+ ld ixl,b
+ ld ixl,c
+ ld ixl,d
+ ld ixl,e
+ ld ixl,ixh
+ ld ixl,ixl
+ ld ixl,n
+
+ ld iyh,a
+ ld iyh,b
+ ld iyh,c
+ ld iyh,d
+ ld iyh,e
+ ld iyh,iyh
+ ld iyh,iyl
+ ld iyh,n
+
+ ld iyl,a
+ ld iyl,b
+ ld iyl,c
+ ld iyl,d
+ ld iyl,e
+ ld iyl,iyh
+ ld iyl,iyl
+ ld iyl,n
+
+ or ixh
+ or ixl
+ or iyh
+ or iyl
+
+ sbc a,ixh
+ sbc a,ixl
+ sbc a,iyh
+ sbc a,iyl
+
+ sub ixh
+ sub ixl
+ sub iyh
+ sub iyl
+
+ xor ixh
+ xor ixl
+ xor iyh
+ xor iyl
+
+
+ end
+
To ease coding, several fake instructions are allowed by BASM
. It replaces them by the combination of true instructions.
Here is a subset of the possibilities.
+Failure
+Most accepted fake instructions are missing from the listing
+Example: +
assert $ == 0
+ assert $$ == 0
+
+ org 0x100
+ assert $ == 0x100
+ assert $$ == 0x100
+ nop
+ assert $ == 0x101
+ assert $$ == 0x101
+
+
+ org 0x200, 0x300
+ assert $ == 0x200
+ assert $$ == 0x300
+ nop
+ assert $ == 0x201
+ assert $$ == 0x301
+
Failure
+Need to document all functions
+assemble(str)
consider the string str
to be a list of instructions (no directives) and returns the list of bytes corresponding to the assembled version of the given string.
assert list_len(assemble(" nop")) == 1
+ assert list_len(assemble(" nop : nop ")) == 2
+ assert list_len(assemble("")) == 0
+
duration(instruction)
returns the number of nop of the instructionlist_sublist(list, start, end)
-> list: Return a new list from start until end not included load(fname) -> list of bytes
: return the bytes from the given file name org 0x4000
+ assert memory(label2) == 4
+
+label1
+ db 1, 2, 3
+
+label2
+ db 4, 5, 6
+
+ assert memory(label1) == 1
+ assert memory(label1+2) == 3
+
BASM
allows to define functions that can be used in any expression.
+The functions are fed with parameters and execute conditional directives as well as directives able to handle variables.
+They finish at the execution of the RETURN
directive.
Failure
+Better explain how to build function
+Example of the fibonacci function:
+ 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
+
Example of function to handle lists: +
; Skip the {start} first element of list {l}
+ FUNCTION SKIP, l, start
+ len = list_len({l})
+ if {start} < len
+ return list_sublist({l}, {start}, len)
+ else
+ return []
+ endif
+ ENDFUNCTION
+
+ ; Take the {amount} first element of list {l}
+ FUNCTION TAKE, l, amount
+ assert {amount} > 0
+ len = list_len({l})
+ start = 0
+ finish = start + min({amount}, len) ; seems to not work for un unknown reason
+ if {amount} > len
+ finish = len
+ else
+ finish = {amount}
+ endif
+ return list_sublist({l}, start, finish)
+ ENDFUNCTION
+
+ ; Reverse list {l}
+ FUNCTION REVERT, l
+ new = []
+ nb = list_len({l})
+ for idx, 0, nb-1
+ new = list_push(new, list_get({l}, nb-1-{idx}))
+ endfor
+ return new
+ ENDFUNCTION
+
+ assert list_len([1, 2, 3, 4]) == 4
+ assert list_sublist([1, 2, 3, 4], 0, 2) == [1,2]
+ assert list_sublist([1, 2, 3, 4], 0, 4) == [1, 2, 3, 4]
+
+
+ ; Various test to check appropriate behavior
+ assert SKIP([1, 2, 3, 4], 2) == [3, 4]
+ assert SKIP([1, 2, 3, 4], 5) == []
+
+ assert TAKE([1, 2, 3, 4], 2) == [1, 2]
+ assert min(4,5) == 4
+ assert TAKE([1, 2, 3, 4], 4) == [1, 2, 3, 4]
+ assert TAKE([1, 2, 3, 4], 10) == [1, 2, 3, 4]
+
+ assert REVERT([1, 2, 3, 4]) == [4, 3, 2, 1]
+ assert list_len(load("hello.sna")) == 4674
+
+ assert list_len(TAKE(load("hello.sna"), 8)) == 8
+
+
+
+ assert string_from_list(TAKE(load("hello.sna"), 8)) == "MV - SNA"
+
+ ; Write in memory 8 bytes from the given file
+ snapshot = load("hello.sna")
+ header_id = TAKE(snapshot, 8)
+ db header_id
+
+ ; Check that memory is correctly set
+ assert peek(0) == "M"
+ assert peek(7) == "A"
+
Makefile-like tool tailored to build Amstrad CPC project. +It embeds various cpclib tools such as basm, m4, img2cpc but can still execute external programs such as rasm, winape.
+The rules are described in a yaml
file. Check for example the test project in
Benediction CPC demo project builder
+
+Usage: bndbuilder [OPTIONS] [TARGET]...
+
+Arguments:
+ [TARGET]...
+ Provide the target(s) to run.
+
+Options:
+ -h, --help [<CMD>]
+ Show the help of the given subcommand CMD.
+
+ [default: bndbuild]
+ [possible values: img2cpc, basm, rm, bndbuild, xfer]
+
+ -V, --version
+ Print version
+
+ -f, --file <FILE>
+ Provide the YAML file for the given project.
+
+ [default: bndbuild.yml]
+
+ -w, --watch
+ Watch the targets and permanently rebuild them when needed.
+
Here is an example to build a dummy Amstrad CPC project and execute on the real machine thanks to the m4. +It is available in tests/dummy (the repository does not contains the external tools needed to properly build the project. It is straighforward to add them). +Successive calls to the build task do nothing as soon as no file has been modified. +It is also possible to watch the dependencies of a given task to automatically build it when they are modified. +This cannot be seen with the capture, but each time m4 command is launched, the project is send into the CPC machine (it takes several seconds however).
+ +The rules description file must respect the yaml
text file format.
+It is preferably named bndbuild.yml
but this can be overriden by the -f
argument.
+It contains list of rules.
+Each rule can have the following keys:
tgt
: to list the files build by the ruledep
: to list the files needed to build the rulecmd
: a command, or a list of commands, executed by the rule. Commands prefixed by -
can silently failhelp
: an optional help text to describe the rulephony
: an optional tag to express the rule does not generate anyfile (it is infered when the commands are not extern). Mainly serve for the --watch
argument.The commands are either included by the application (so limited to cpclib commands and os agnostic) or accessible externally (no limitation, but os dependent).
+They are:
+- basm
to launch basm assembler
+- img2cpc
to make image conversion
+- rm
to delete files
+- xfer
to transfer to the CPC with the M4 card
+- extern
to launch any command available on the machine
cpclib is a rust library that aims at helping to develop Amstrad CPC demos. +Maybe it could be usefull for other z80 platform or even games. +Most functionalities are still in beta state; +I have only implemented the subset I need for my current Amstrad CPC demo projects. +Several tools are provided in addition to library.
+There are more are less able to do:
+- assemble z80 source code.
+ - Mainly interesting for auto-generated code, not for handcrafted one.
+ - Not all opcodes are managed.
+ - Functionalities not available in other assemblers:
+ - Injection of basic source code (WIP)
+ - Function able to provided the opcode value of an instruction or its standard duration
+- manipulate .sna files
+ - Minimal support of chunks at the moment
+- convert images to CPC format. Usable for standard resolutions/modes
+- manipulate DSK (trying to mimick iDSK or dskmanager). Able to format and add files
+- communicate with cpcwifi board
+ - Replication of xfer utility.
+ - Only reset and run file have been coded at the moment
+ - In opposite to the original xfer tool, cpclib one is able to start snapshots V3 (there are simply converted as snapshot v2 on the fly)
+- create basic tokens from basic source (WIP)
+
cpclib is a rust library that aims at helping to develop Amstrad CPC demos. Maybe it could be usefull for other z80 platform or even games. Most functionalities are still in beta state; I have only implemented the subset I need for my current Amstrad CPC demo projects. Several tools are provided in addition to library.
There are more are less able to do:
- assemble z80 source code.\n - Mainly interesting for auto-generated code, not for handcrafted one.\n - Not all opcodes are managed.\n - Functionalities not available in other assemblers:\n - Injection of basic source code (WIP)\n - Function able to provided the opcode value of an instruction or its standard duration\n- manipulate .sna files\n - Minimal support of chunks at the moment\n- convert images to CPC format. Usable for standard resolutions/modes\n- manipulate DSK (trying to mimick iDSK or dskmanager). Able to format and add files\n- communicate with cpcwifi board\n - Replication of xfer utility.\n - Only reset and run file have been coded at the moment\n - In opposite to the original xfer tool, cpclib one is able to start snapshots V3 (there are simply converted as snapshot v2 on the fly)\n- create basic tokens from basic source (WIP)\n
"},{"location":"basm/","title":"BASM documentation - WIP","text":""},{"location":"basm/#basm","title":"BASM","text":"Benediction ASsembler (BASM
in short) is a modern Z80 assembler. He has taken its inspiration from various Z80 assembler (Maxam/Winape, sjasmplus, rasm, vasm, BRASS, glass, zasm) as well as assemblers from other platforms (asm11, sarcasm). It is tailored for Amstrad CPC demomaking and has been successfully used to develop the Amstrad CPC demo Can Robots Take Control?. It has been still improved since and will serve for futur productions too.
The documentation is quite minimal at the moment, but included example code should be still valid and assembled propetly. The user base being quite small, lots of bugs can remain. Do note hesitate to fill issues https://github.com/cpcsdk/rust.cpclib/issues or propose fixes.
"},{"location":"basm/#features-of-interest","title":"Features of Interest","text":"ld hl, de
).incbin 'file.asm
).ld a, opcode(xor a)
).MY_MACRO_WITH_TWO_ARGS 1, \"string\"
).db 5, my_function(3)
).BASM
uses as many passes as needed).BASM
that can be used by the users.An hello world representative of the functionalities of BASM
would be:
snainit \"../cpclib-sna/src/cpc6128.sna\" ; should be uneeded by properly specifying sna properties\n\n org 0x4000\n run $\n\n ld hl, text_content\nloop\n ld a, (hl)\n or a\n jp z, finished\n\n call TXT_OUTPUT\n inc hl\n jp loop\n\nfinished\n jp $\n\ntext_content\n db \"Hello, world!\", 0\n include \"inner://firmware/txtvdu.asm\"\n
"},{"location":"basm/#download-last-version","title":"Download last version","text":"Prefer to compile yourself basm
. But you can still download latest versions here:
Failure
Continuous delivery system for Linux is broken. The executables are outdated of few years...
"},{"location":"basm/#differences-with-rasm","title":"Differences with RASM","text":"MODULE
directive must be closed by ENDMODULE
REPEAT
counter is not accessible by using the variable counter
but {counter}
as in a MACRO
MACRO
using the label before the MACRO
directivebasm
can use an unlimited number of pass (warning there is not infinite loop check ATM), it can assemble code that would not be assembled with rasm
because labels have to be known at this momentDSK``, no support of
TAPEand `CPR
. HFE
is usable on Linux with the appropriate compilation option. AMSDOS
support is buggy ATM
SNA
should be okBASIC
tokens to create loaders that do not clear the screen when launchedHere is the help provided by basm
.
Profile debug compiled: Sun, 20 Aug 2023 20:55:05 +0000\n\nBenediction ASM -- z80 assembler that mainly targets Amstrad CPC\n\nUsage: basm.exe [OPTIONS] [INPUT]\n\nArguments:\n [INPUT] Input file to read.\n\nOptions:\n --inline <INLINE> Z80 code is provided inline\n -o, --output <OUTPUT> Filename of the output.\n --db Write a db list on screen (usefull to get the value of an opcode)\n --lst <LISTING_OUTPUT> Filename of the listing output.\n --sym <SYMBOLS_OUTPUT> Filename of the output symbols file.\n --sym_kind <SYMBOLS_KIND> Format of the output symbols file [default: basm] [possible values: winape, basm]\n --basic Request a Basic header (the very first instruction has to be the LOCOMOTIVE directive).\n --binary Request a binary header\n --snapshot Generate a snapshot\n -i, --case-insensitive Configure the assembler to be case insensitive.\n -d, --directives-prefixed-by-dot Expect directives to by prefixed with a dot\n -I, --include <INCLUDE_DIRECTORIES> Provide additional directories used to search files\n -D, --define <DEFINE_SYMBOL> Provide a symbol with its value (default set to 1)\n --m4 <TO_M4> Provide the IP address of the M4\n -l <LOAD_SYMBOLS> Load symbols from the given file\n --Werror Warning are considered to be errors\n --progress Show a progress bar.\n --list-embedded List the embedded files\n --view-embedded <VIEW_EMBEDDED> Display one specific embedded file [possible values: inner://crtc.asm, inner://deexo.asm, inner://dzx0_fast.asm, inner://dzx0_standard.asm, inner://firmware/amsdos.asm, inner://firmware/casmng.asm, inner://firmware/gfxvdu.asm, inner://firmware/highkern.asm, inner://firmware/indirect.asm, inner://firmware/kernel.asm, inner://firmware/keymng.asm, inner://firmware/lowkern.asm, inner://firmware/machine.asm, inner://firmware/math6128.asm, inner://firmware/mathnot464.asm, inner://firmware/mathnot6xx.asm, inner://firmware/not464.asm, inner://firmware/scrpack.asm, inner://firmware/sound.asm, inner://firmware/txtvdu.asm, inner://ga.asm, inner://lz48decrunch.asm, inner://lz49decrunch.asm, inner://lz4_docent.asm, inner://opcodes_first_byte.asm, inner://pixels-routs.asm, inner://unaplib.asm, inner://unaplib_fast.asm]\n -h, --help Print help\n -V, --version Print version\n\nStill a Work In Progress assembler\n
"},{"location":"basm/directives/","title":"Directives","text":"Failure
Not all directives have their synopsis, explanation, and examples
"},{"location":"basm/directives/#listing-related","title":"Listing related","text":""},{"location":"basm/directives/#list-nolist","title":"LIST, NOLIST","text":"Example:
org 0\n LIST\n\n ld a, 0\n ; visible in the listing output\n\n NOLIST\n\n ld a, 1\n ; Not visible in the listing ouput\n
"},{"location":"basm/directives/#memory-related","title":"Memory related","text":""},{"location":"basm/directives/#align","title":"ALIGN","text":"Example:
org 0x1234\n\n align 256\n assert $ == 0x1300\n\n align 256\n assert $ == 0x1300\n\n nop\n\n align 128, 3\n assert $ == 0x1300 + 128\n
"},{"location":"basm/directives/#confined","title":"CONFINED","text":"Confine a memory area of 256 bytes maximum in such a way that it is always possible to navigate in the data by only modifying the low byte address (i.e INC L always works).
CONFINED\n LISTING\nENDCONFINED\n
;;\n; Confined directive is inspired by confine from rasm.\n; I guess confined is more ergonomic has it does not requires to manually specify the size of the confined area\n\n org 0x0000\n\n CONFINED\n assert $ == 0\n defs 128, 0xff\n ENDCONFINED\n\n CONFINED\n assert $ == 256\n defs 200, 0xff\n ENDCONFINED\n\n CONFINED\n assert $ == 256 + 200\n defs 20, 0xff\n ENDCONFINED\n
"},{"location":"basm/directives/#org","title":"ORG","text":""},{"location":"basm/directives/#limit","title":"LIMIT","text":"On the code space ($), not physical space ($$)
Example of code that assembles:
org 0x100\n\n limit 102\n
Example of code that fails:
\n
"},{"location":"basm/directives/#phase-dephase","title":"PHASE, DEPHASE","text":"; https://k1.spdns.de/Develop/Projects/zasm/Documentation/z71.htm\n org 0x100\n\nlabel_100\n nop\nlabel_101\n\n assert $$ == 0x101\n assert $ == 0x101\n\n phase 0x200\n\n assert $$ == 0x101\n assert $ == 0x200\n\nlabel_200\n nop\nlabel_201\n\n dephase\nlabel_102\n\n assert label_100 == 0x100\n assert label_101 == 0x101\n assert label_102 == 0x102\n assert label_200 == 0x200\n assert label_201 == 0x201\n
"},{"location":"basm/directives/#protect","title":"PROTECT","text":"Synopsis:
PROTECT START, STOP\n
Description: Mark the memory between START and STOP as protected against write. Assembler fails when writting there. On the code space ($), not physical space ($$)
Example:
org 0x4000\n\n protect 0x8000, 0xbfff\n\n defb \"No memory issue\"\n
"},{"location":"basm/directives/#range-section","title":"RANGE, SECTION","text":"Description: RANGE allows to define named portion of the memory, while SECTION allows to chose the portion of interest.
Example:
; sarcasm inspiration https://www.ecstaticlyrics.com/electronics/Z80/sarcasm/\n\nrange $0080, $3FFF, code\nrange $4000, $7FFF, data\n\nsection code\n ld hl, message_1\n call print_message\n\nsection data\nmessage_1: db \"This is message #1.\", $00\n\nsection code\n ld hl, message_2\n call print_message\n\nsection data\nmessage_2: db \"This is message #2.\", $00\n\nsection code\n print_message: \n ld a, (hl)\n or a\n ret z\n call 0xbb5a\n inc hl\n jr print_message\n\n assert section_start(\"data\") == 0x4000\n assert section_length(\"data\") == 0x4000\n assert section_used(\"data\") == 40\n
"},{"location":"basm/directives/#bank","title":"BANK","text":"Description:
When used with no argument, a bank corresponds to a memory area outside of the snapshot. All things read&write in memory are thus uncorrelated to the snapshot. Sections do not apply in a bank.
BANK page
is similar to WRITE DIRECT -1 -1 page
Synopsis:
BANK [EXPRESSION]\n
Example:
; Set up a unique value in various banks\n BANK 0xc0\n org 0x4000 + 0\n db 0xc0\n\n BANK 0xc4\n org 0x4000 + 1\n db 0xc4\n\n BANK 0xc5\n org 0x4000 + 2\n db 0xc5\n\n BANK 0xc6\n org 0x4000 + 3\n db 0xc6\n\n BANK 0xc7\n org 0x4000 + 4\n db 0xc7\n\n BANKSET 0\n assert memory(0x4000 + 0) == 0xC0 \n\n BANKSET 1\n assert memory(0x4000 + 2) == 0xC5\n assert memory(0x8000 + 3) == 0xC6\n assert memory(0xC000 + 4) == 0xC7\n
"},{"location":"basm/directives/#bankset","title":"BANKSET","text":"Synopsis:
BANKSET EXPRESSION\n
Example:
BANKSET 0\n\n org 0x0000\n db 1,2,3,4\n\n org 0x4000\n db 5,6,7,8\n\n org 0x8000\n db 9,10,11,12\n\n org 0xc000\n db 13, 14, 15, 16\n\n BANKSET 1\n org 0x0000\n db 10,20,30,40\n\n org 0x4000\n db 50,60,70,80\n\n org 0x8000\n db 90,100,110,120\n\n org 0xc000\n db 130, 140, 150, 160\n\n BANKSET 0\n assert memory(0x0000) == 1\n assert memory(0x4000) == 5\n assert memory(0x8000) == 9\n assert memory(0xc000) == 13\n\n save \"good_bankset_0_0.o\", 0x0000, 4\n save \"good_bankset_0_1.o\", 0x4000, 4\n save \"good_bankset_0_2.o\", 0x8000, 4\n save \"good_bankset_0_3.o\", 0xc000, 4\n\n BANKSET 1\n assert memory(0x0000) == 10\n assert memory(0x4000) == 50\n assert memory(0x8000) == 90\n assert memory(0xc000) == 130\n\n save \"good_bankset_1_0.o\", 0x0000, 4\n save \"good_bankset_1_1.o\", 0x4000, 4\n save \"good_bankset_1_2.o\", 0x8000, 4\n save \"good_bankset_1_3.o\", 0xc000, 4\n
"},{"location":"basm/directives/#write-direct","title":"WRITE DIRECT","text":"Description: WRITE DIRECT is a directive from Winape that we have not fully reproduced. It's two first arguments need to be -1.
Example:
WRITE DIRECT -1, -1, 0xc0\n org 0x4000+0\n db 0xc0\n\n WRITE DIRECT -1, -1, 0xc4\n org 0x4000+1\n db 0xc4\n\n WRITE DIRECT -1, -1, 0xc5\n org 0x4000+2\n db 0xc5\n\n WRITE DIRECT -1, -1, 0xc6\n org 0x4000+3\n db 0xc6\n\n WRITE DIRECT -1, -1, 0xc7\n org 0x4000+4\n db 0xc7\n\n BANKSET 0\n assert memory(0x4000 + 0) == 0xC0 \n\n BANKSET 1\n assert memory(0x4000 + 2) == 0xC5\n assert memory(0x8000 + 3) == 0xC6\n assert memory(0xC000 + 4) == 0xC7\n
"},{"location":"basm/directives/#labels-related","title":"Labels related","text":""},{"location":"basm/directives/#set","title":"=, SET","text":"Description:
Assign an expression to a label. Assignement can be repeated several times.
Synopsis:
LABEL = EXPRESSION\n
Example:
label = 1\nlabel =3\n.label=2\n\n assert label == 3\n\nlabel = 5\n assert label == 5\n\nlabel <<= 1\n assert label == 10\n
"},{"location":"basm/directives/#equ","title":"EQU","text":"Description: Assign an expression to a label. Assignement cannot be repeated several times.
Synopsis:
LABEL = EXPRESSION\n
Example:
label = 1\nlabel =3\n.label=2\n\n assert label == 3\n\nlabel = 5\n assert label == 5\n\nlabel <<= 1\n assert label == 10\n
"},{"location":"basm/directives/#map","title":"MAP","text":"MAP VALUE
define a map counter to the required value.
#` is used to assign the value to a given label and increment it of the appropriate amount.
Example:
; extract stolen here https://github.com/GuillianSeed/MetalGear/blob/master/Variables.asm#L10\n map #c000\n\nGameStatus: # 1\nGameSubstatus: # 1\nControlConfig: # 1\n ; Bit6: 1=Enable music/Player control\nTickCounter: # 1\nWaitCounter: # 1\nTickInProgress: # 1\nControlsTrigger: # 1\n ; 5 = Fire2 / M, 4 = Fire / Space, 3 = Right, 2 = Left, 1 = Down, 0 = Up\nControlsHold: # 1\n ; 5 = Fire2 / M, 4 = Fire / Space, 3 = Right, 2 = Left, 1 = Down, 0 = Up\nPause_1_F5_2: # 1\nTutorialStatus: # 1\nDemolHoldTime: # 1\nUnusedVar1: # 1\nDemoPlayId: # 1\nDemoDataPointer: # 2\n ; Pointer to presaved demo controls\nSprShuffleOffset: # 1\n\n assert GameStatus = 0xc000\n assert GameSubstatus = (0xc000 + 1)\n assert ControlConfig = (0xc000 + 1 + 1)\n assert TickCounter = (0xc000 + 1 + 1+1)\n assert DemolHoldTime = (0xc000 + 10)\n assert SprShuffleOffset = (0xc000 + 15)\n
"},{"location":"basm/directives/#setn-next","title":"SETN, NEXT","text":"MAP
directive is probably easier to use
Example:
; http://www.aspisys.com/asm11man.htm\n\n org 0x100\n\ndata set $\n assert data == 0x100\n\ndata1 setn data ; data1 could be modified\ndata2 next data, 2 ; data2 cannot be modified\ndata3 next data\n\n assert data1 == 0x100\n assert data2 == 0x101\n assert data3 == 0x103\n assert data == 0x104\n
"},{"location":"basm/directives/#undef","title":"UNDEF","text":"Example:
my_label = 1\n\n ifndef my_label\n fail \"my_label must exist\"\n endif\n\n undef my_label\n\n ifdef my_label\n fail \"my_label must not exist\"\n endif\n
"},{"location":"basm/directives/#data-related","title":"Data related","text":""},{"location":"basm/directives/#byte-text-db-defb-dm-defm","title":"BYTE, TEXT, DB, DEFB, DM, DEFM","text":"Example:
; defb tests\n org 0x200\n\n defb 1, 2, 3, 4\n defb \"hello\", ' ', \"world\"\n defb $, $ ; should generate 2 different values\n db \"Hello world\", 0\n
"},{"location":"basm/directives/#word-dw-defw","title":"WORD, DW, DEFW","text":""},{"location":"basm/directives/#defs","title":"DEFS","text":"Example:
org 0x100\n\n defs 5\n defs 5, 8, 4\n defs 5, 8, 4, 1\n
"},{"location":"basm/directives/#str","title":"STR","text":"Description: STR encodes string in AMSDOS format (i.e., adds 0x80 to the last char) and stores its bytes in memory.
Example:
org 0x1000\n defb \"hell\"\n defb 'o' + 0x80\n\n org 0x2000\n str \"hello\"\n\n org 0x3000\n db \"Next one will be more complex\"\n db \" \\\" et voila\"\n db \" \\\" et voila\"\n db \"\\\" et voila\"\n\n assert memory(0x1000) == memory(0x2000)\n assert memory(0x1001) == memory(0x2001)\n assert memory(0x1002) == memory(0x2002)\n assert memory(0x1003) == memory(0x2003)\n
"},{"location":"basm/directives/#charset","title":"CHARSET","text":"Example:
org 0x100\n\n charset \"abcdefghijklmnopqrstuvwxyz\", 0\n charset \"AB\", 100\n db \"aA\"\n ASSERT memory(0x100) == 0x00\n ASSERT memory(0x101) == 100\n\n org 0x200\n charset\n db \"aA\"\n ASSERT memory(0x200) == 'a'\n ASSERT memory(0x201) == 'A' \n
"},{"location":"basm/directives/#conditional-directives","title":"Conditional directives","text":""},{"location":"basm/directives/#if-ifnot","title":"IF, IFNOT","text":""},{"location":"basm/directives/#ifdef-ifndef","title":"IFDEF, IFNDEF","text":"Example:
org 0x1000\n\n ifndef toto\n assert toto == $\ntoto\n assert pouet == 0x1002\n dw pouet\n endif\npouet\n
"},{"location":"basm/directives/#ifused","title":"IFUSED","text":"Example:
org 0x1000\n; 3 passes are needed there\n\n ifused toto\ntoto\n ret\n endif\n\n call toto\n\n; test\n
"},{"location":"basm/directives/#nested-conditions","title":"Nested conditions","text":"Conditions can be nested.
Example:
org 0x100\n\n if 0 == 1\n fail \"not reachable\"\n else ifdef toto\n fail \"not reachable\"\n else ifndef toto\n print \"reached\"\n db 1\n else\n fail \"not reachable\"\n endif\n
"},{"location":"basm/directives/#switch-endswitch","title":"SWITCH, ENDSWITCH","text":"Example:
org 0x100\n\n switch 3\n ; one comment\n case 1\n db 1\n break\n case 3\n db 3\n ; another comment\n case 4\n db 4\n break\n case 5\n db 5\n default\n db 6\n endswitch\n\n switch 5\n case 1\n db 1\n break\n case 3\n db 3\n case 4\n db 4\n break\n case 5\n db 5\n default\n db 6\n endswitch\n
"},{"location":"basm/directives/#code-duplication-directives","title":"Code duplication directives","text":""},{"location":"basm/directives/#for","title":"FOR","text":"FOR <variable> [, EXPRESSION]+\n LISTING\nENDFOR|FEND\n
Example:
; Takes inspiration from BRASS assembler\n\n for count, 0, 10, 3\n db {count}\n endfor\n\n for x, 0, 3\n for y, 0, 3\n db {x}*4 + {y}\n fend\n endfor\n
Corresponds to db 0\n db 3\n db 6\n db 9\n\n db 0*4 + 0\n db 0*4 + 1\n db 0*4 + 2\n db 0*4 + 3\n\n db 1*4 + 0\n db 1*4 + 1\n db 1*4 + 2\n db 1*4 + 3\n\n db 2*4 + 0\n db 2*4 + 1\n db 2*4 + 2\n db 2*4 + 3\n\n db 3*4 + 0\n db 3*4 + 1\n db 3*4 + 2\n db 3*4 + 3\n
"},{"location":"basm/directives/#while","title":"WHILE","text":"CPT=3\n\n while CPT > 0\n db CPT\nCPT=CPT-1\n wend\n
"},{"location":"basm/directives/#repeat","title":"REPEAT","text":"REPEAT AMOUNT [, COUNTER [, START]] INNER LISTING REND
start\n repeat 3, count\n incbin 'AZERTY{{count}}.TXT'\n rend\n\n assert char(memory(start+0)) == 'A'\n assert char(memory(start+1)) == 'Z'\n assert char(memory(start+2)) == 'E'\n assert char(memory(start+3)) == 'R'\n assert char(memory(start+4)) == 'T'\n assert char(memory(start+5)) == 'Y'\n assert char(memory(start+6)) == 'U'\n assert char(memory(start+7)) == 'I'\n assert char(memory(start+8)) == 'O'\n assert char(memory(start+9)) == 'P'\n\n assert char(memory(start+10)) == 'Q'\n assert char(memory(start+11)) == 'S'\n assert char(memory(start+12)) == 'D'\n assert char(memory(start+13)) == 'F'\n assert char(memory(start+14)) == 'G'\n assert char(memory(start+15)) == 'H'\n assert char(memory(start+16)) == 'J'\n assert char(memory(start+17)) == 'K'\n assert char(memory(start+18)) == 'L'\n assert char(memory(start+19)) == 'M'\n\n assert char(memory(start+20)) == 'W'\n assert char(memory(start+21)) == 'X'\n assert char(memory(start+22)) == 'C'\n assert char(memory(start+23)) == 'V'\n assert char(memory(start+24)) == 'B'\n assert char(memory(start+25)) == 'N'\n
"},{"location":"basm/directives/#iterate","title":"ITERATE","text":"ITERATE COUNTER, EXPR...\n INNER LISTING\nIEND\n
The expression $i$ is evaluated after having generated the code of expression $i-1$. Take that into account if expressions use $.
Example:
; Glass inspiration http://www.grauw.nl/projects/glass/\n\n iterate value, 1, 2, 10\n add {value}\n jr nz, @no_inc\n inc c\n@no_inc\n call do_stuff\n iend\n\n iterate value in [11, 12, 110]\n add {value}\n jr nz, @no_inc\n inc c\n@no_inc\n call do_stuff\n iend\n\ndo_stuff\n ret\n
Corresponds to: add 1\n jr nz, no_inc1\n inc c\nno_inc1\n call do_stuff\n\n add 2\n jr nz, no_inc2\n inc c\nno_inc2\n call do_stuff\n\n add 10\n jr nz, no_inc3\n inc c\nno_inc3\n call do_stuff\n\n add 11\n jr nz, no_inc4\n inc c\nno_inc4\n call do_stuff\n\n add 12\n jr nz, no_inc5\n inc c\nno_inc5\n call do_stuff\n\n add 110\n jr nz, no_inc6\n inc c\nno_inc6\n call do_stuff\n\ndo_stuff\n ret\n
"},{"location":"basm/directives/#code-and-data-generation-directives","title":"Code and data generation directives","text":""},{"location":"basm/directives/#macro","title":"MACRO","text":"Example of standard macro:
; rasm inspired\n macro LDIXREG register,dep\n if {dep}<-128 || {dep}>127\n push BC\n ld BC,{dep}\n add IX,BC\n ld (IX+0),{register}\n pop BC\n else\n ld (IX+{dep}),{register}\n endif\n mend\n\n LDIXREG H,200\n LDIXREG L,32\n
Example of macro using raw arguments:
macro BUILD_LABEL r#label\n{label}_first\n endm\n\n BUILD_LABEL \"HERE\"\n BUILD_LABEL \"THERE\"\n\n ifndef HERE_first\n fail \"macro error\"\n endif\n ifndef THERE_first\n fail \"macro error\"\n endif\n\n macro BUILD_CODE r#code\n {code}\n endm\n\nSTART_CODE1\n BUILD_CODE \"xor a\"\n BUILD_CODE \"ld hl, 0xc9fb : ld (0x38), hl\"\nEND_CODE1\n\nSTART_CODE2\n xor a\n ld hl, 0xc9fb : ld (0x38), hl\nEND_CODE2\n\n assert END_CODE2 - START_CODE2 == END_CODE1 - START_CODE1\n assert END_CODE2 - START_CODE2 == 7\n\n assert memory(START_CODE1) == memory(START_CODE2)\n assert memory(START_CODE1+1) == memory(START_CODE2+1)\n assert memory(START_CODE1+2) == memory(START_CODE2+2)\n assert memory(START_CODE1+3) == memory(START_CODE2+3)\n assert memory(START_CODE1+4) == memory(START_CODE2+4)\n assert memory(START_CODE1+5) == memory(START_CODE2+5)\n assert memory(START_CODE1+6) == memory(START_CODE2+6)\n
"},{"location":"basm/directives/#struct","title":"STRUCT","text":"Description: Structures allow to defined data blocs with semantic. In practice, they replace bunches of DEFB
, DEFW
directives and enforce checks at assembling (you cannot add more data than expected or forget some). If a label is used before the use of a struct, it is necessary to postfix it by :. Otherwise the assembler thinks the label is a macro or structure call.
Synopsis
STRUCT <name>\n<filed1> DB|DW|STR|<other struct> [<value>]\n...\n<filedn> DB|DW|<other struct> [<value>]\nENDSTRUCT\n\n[<label>:] <name> <arg1>, ... , <argn>\n
Standard example:
struct color\nr db 1\ng db 2\nb db 3\n endstruct\n\n struct point\nx db 4\ny db 5\n endstruct\n\ncol0: color (void)\npt0: point (void)\n\ncol1: color 'a', 'b', 'c'\npt1: point 'd', 'e'\n\n struct colored_point\ncol color 10, 20, 30\npt point 10, 20\n endstruct\n\n colored_point (void)\n
Example using default values:
;; Define a 3 fields structure\n struct point\nxx db 4\nyy db 5\nzz db 6\n endstruct\n\n assert point == 3\n assert point.xx == 0\n assert point.yy == 1\n assert point.zz == 2\n\n point 1, 2 , 3\n point ,,8\n point 9\n\n; force values\n; : after label name allows to disambiguate parser that does not try to check if label is a macro (less errors/faster)\nmy_point1: point 1, 2, 3\n\n; use all default values\nmy_point2: point (void)\n\n; use default at the end\nmy_point3: point 1\n\n; use default at the beginning\nmy_point4: point ,,1\n\np1: point 1, 2 , 3\np2: point ,,8\np3: point 9\n\n struct triangle\np1 point 1, 2 , 3\np2 point ,,8\np3 point 9 ; third point\n endstruct\n\n assert triangle == 9\n assert triangle.p1 == 0\n assert triangle.p2 == 3\n assert triangle.p3 == 6\n\nmy_triangle2: triangle [1, 2, 3], [4, 5, 6], [7, 8 , 9]\n\n if 0\n\nmy_triangle1: triangle\n\nmy_triangle2: triangle [1, 2, 3], , [7, 8 , 9]\n endif\n
"},{"location":"basm/directives/#data-loading-and-transformation-directives","title":"Data loading and transformation directives","text":"Filenames are stored in a string. These string can do expansion of formulas embedded in {}.
basm embeds some files in its executable, they are access under the name \"inner://\" :
"},{"location":"basm/directives/#lzapu-lz48-lz49","title":"LZAPU, LZ48, LZ49","text":"Example:
org 0x100\n\n ld hl, CS_START\n ld de, 0xc000\n call aplib.depack\n jp $\n\nCS_START\n LZAPU\nINNER_START\n defs 100\nINNER_STOP\n LZCLOSE\nCS_STOP\n\n assert INNER_STOP - INNER_START == 100\n assert CS_STOP - CS_START < 100\n\n include \"inner://unaplib.asm\" namespace \"aplib\"\n
"},{"location":"basm/directives/#incbin-binclude","title":"INCBIN, BINCLUDE","text":"INCBIN|BINCLUDE \"fname\" [[, SKIP], AMOUNT]
Fname can be build with variables.
Limitations:
Example:
here\n incbin \"AZERTY.TXT\", 2, 3\nthere\n\n assert peek(here) == 'E'\n assert peek(here+1) == 'R'\n\n assert there-here == 3\n
with AZERTY.TXT
containing the text AZERTYUIOPQSDFGHJKLMWXCVBN
."},{"location":"basm/directives/#include-read","title":"INCLUDE, READ","text":"INCLUDE|READ [ONCE] \"<fname>\" [AS|MODULE|NAMESPACE \"<module>\"]
Fname can be build with variables.
Example with once:
org 0x4000\n\nSIZE1_start\n include once \"include_once.asm\"\nSIZE1_stop\n\nSIZE2_start\n include once \"include_once.asm\"\nSIZE2_stop\n\n assert (SIZE2_stop - SIZE2_start) == 0\n
Example with namespace:
include \"good_labels.asm\" namespace \"good\"\n\n ifndef good.outer1\n fail \"good.outer1 is undefined\"\n endif\n\n ifdef outer1\n fail \"outer1 is defined\"\n endif\n\n ifndef good.outer2.inner1\n fail \"good.outer2.inner1 is undedined\"\n endif\n
Files prefixed by inner://
are embedded by BASM
. Example:
include \"inner://opcodes_first_byte.asm\"\n\n org 0x4000\n\n db opcode_inc_l\n inc l\n\n assert memory(0x4000) == memory(0x4001)\n
In case of conditional assembling, inclusion are only done in the executed branch. This code always assemble as it never includes 'unknonw' file.
truc equ 0:if truc:include'unknown':endif:nop\n
"},{"location":"basm/directives/#data-saving-and-export","title":"Data saving and export","text":""},{"location":"basm/directives/#export-noexport","title":"EXPORT, NOEXPORT","text":"Example:
org 0x4000\n\n NOEXPORT\n EXPORT toto\n\nlabel1\nlabel2\ntoto ; only this one is exported\n
"},{"location":"basm/directives/#save-write","title":"SAVE, WRITE","text":"SAVE \"<fname>\", [[[START], [SIZE]], AMSDOS|BASIC|TAPE]
SAVE \"<fname>\", START, SIZE, DSK, \"<fname.dsk>\" [, SIDE]
SAVE \"<fname>\", START, SIZE, HFE, \"<fname.hfe>\" [, SIDE]
SAVE \"<fname>\", START, SIZE, DISC, \"<fname.hfe>\"|\"<dname.dsk>\" [, SIDE]
Unimplemented
TAPE option is not coded. Other options are not intensively tested
Example:
org 0x4000\n run $\nFIRST_ADDRESS\n ld hl, txt\nloop\n ld a, (hl)\n or a\n jp z, $\n\n push hl\n call 0xbb5a\n pop hl\n inc hl\n jp loop\n\ntxt\n.start\n defb \"Hello World!\"\n defb 0\n.stop\nLAST_ADDRESS\n\n save \"good_save_whole_inner.bin\" ; Save binary without header\n save \"hello.bin\", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, AMSDOS ; Save a binary with header\n save \"hello.bin\", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, DSK, \"hello.dsk\" ; Save binary with header INSIDE a dsk\n\n if BASM_FEATURE_HFE\n save \"hello.bin\", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, HFE, \"hello.hfe\" ; Save binary with header INSIDE a hfe file\n endif\n\n save \"good_save_txt.bin\", txt.start, (txt.stop - txt.start) ; save text without header\n\n; cmd line to generate the binary with header\n; basm good_save.asm --binary -o run.bin \n; cmd line to put it in a dsk\n; dskmanager test.dsk format --format data42\n; dskmanager test.dsk add run.bin \n
"},{"location":"basm/directives/#debug-directives","title":"Debug directives","text":""},{"location":"basm/directives/#assert","title":"ASSERT","text":"ASSERT BOOLEAN_EXPRESSION [, PRINTABLE_EXPRESSION]*\n
"},{"location":"basm/directives/#print","title":"PRINT","text":"Example:
print \"Hello world\"\n\nworld = \"World\"\n print \"hello \", world\n
"},{"location":"basm/directives/#amstrad-cpc-related-directives","title":"Amstrad CPC related directives","text":""},{"location":"basm/directives/#ticker","title":"TICKER","text":"Description: Compute the execution duration of a block of code
Synopsys:
TICKER START variable\n instructions\nTICKER STOP\n
Example 1:
; http://mads.atari8.info/mads_eng.html\n\n TICKER START count\n WAITNOPS 3\n TICKER STOP\n\n assert count == 3\n\n TICKER START count2\n nop\n TICKER STOP\n\n assert count2 == 1\n
Example 2:
TICKER START duration_varying_code\n xor a\n ld b, 1\n TICKER STOP\n assert duration_varying_code == 1 + 2\n UNDEF duration_varying_code\n\n TICKER START duration_varying_code\n xor a\n TICKER STOP\n assert duration_varying_code == 1 \n UNDEF duration_varying_code\n\n TICKER START duration_varying_code\n TICKER STOP\n assert duration_varying_code == 0\n UNDEF duration_varying_code\n\n assert duration(xor a) == 1\n ;assert duration(xor a : xor a) == 2 ; Does not compile yet Could be a good idea\n\n TICKER START duration_varying_code\n WAITNOPS 64\n TICKER STOP\n assert duration_varying_code == 64\n UNDEF duration_varying_code\n\n TICKER START duration_stable_code\n TICKER START duration_varying_code\n out (c), c\n TICKER STOP\n WAITNOPS 64 - duration_varying_code\n TICKER STOP\n assert duration_stable_code == 64\n UNDEF duration_varying_code\n\n MACRO BUILD_STABLE_CODE duration, r#code\n TICKER START .my_count\n {code}\n TICKER STOP\n ASSERT {duration} >= .my_count\n WAITNOPS {duration}-.my_count\n\n IFDEF DEBUG_EXPECTED_DURATION\n ASSERT .my_count == DEBUG_EXPECTED_DURATION\n ENDIF\n UNDEF .my_count\n ENDM\n\n DEBUG_EXPECTED_DURATION = 2\n BUILD_STABLE_CODE 64, \"xor a : xor a\"\n
"},{"location":"basm/directives/#waitnops","title":"WAITNOPS","text":"Generate a list of instructions that do not modify any registers or memory but is executed with the expected amount of nops. (Currently it is synonym of NOP, but as soon as someone wants to provide clever rules to use less bytes, I'll implement them)
"},{"location":"basm/directives/#locomotive","title":"LOCOMOTIVE","text":" LOCOMOTIVE start\n10 REM Basic loader of binary exec\n20 REM yeah !!\n30 call {start}\n ENDLOCOMOTIVE\n\nstart\n ld hl, txt\n.loop\n ld a, (hl)\n or a : jr z, .end\n call #bb5a\n inc hl\n jr .loop\n.end\n jp $\n\ntxt\n db \"Hello world\", 0\n\n print \"LOADER START IN \", {hex}start\n save \"LOADER.BAS\",,,BASIC\n
"},{"location":"basm/directives/#snaset","title":"SNASET","text":""},{"location":"basm/syntax/","title":"Z80 Syntax","text":""},{"location":"basm/syntax/#general-syntax","title":"General syntax","text":"LABEL OPCODE1\n OPCODE2 : OPCODE3\n DIRECTIVE\n
Warning
There may be still some subtle parser bugs, but it is possible to span instructions and directives on several lines by ending the previous line with \\
BASM
is quite lax on the z80 format: it does not impose to start a label at the very first char of a line and does not force an instruction or directive to not start at the very first line of a char (behavior stolen to rasm
). As a consequence there can be ambiguities between labels and macros. If it fails in properly recognizing macros or label, you can guide it by suffixing label declaration by : or by using (void) for macros with no arguments.
outer1\n jp outer2\n jp outer2.inner1\n\nouter2\n jp .inner1\n.inner1\n\n ifndef outer1\n fail \"outer1 is wrongly undefined\"\n endif\n\n ifndef .inner1\n fail \".inner1 is wrongly undefined\"\n endif\n\n ifndef outer2.inner1\n fail \"outer2.inner1 is wrongly undefined\"\n endif\n
"},{"location":"basm/syntax/#module-handling","title":"module handling","text":" module module1\nlabel1\n jp module2.label1\n endmodule\n\n module module2\nlabel1 \n jp module3.module31.label1\n endmodule\n\n module module3\n module module31\nlabel1\n jp ::label1\n endmodule\n endmodule\nlabel1\n
would generate a binary similar to jp label1\nlabel1\n jp label2\nlabel2\n jp label3\nlabel3\n
"},{"location":"basm/syntax/#labels-generation","title":"Labels generation","text":"Labels can be generated thanks to the content of other ones.
value=5\nlabel{value+2}\n\n ifndef label7\n fail \"unable to build label with expression\"\n endif\n
"},{"location":"basm/syntax/#instructions","title":"Instructions","text":"Here is the list of instructions used to validate BASM
:
; Test of assembling of z80 instructions.\n LIST\n\n org $0\n\ndes equ $05\nn equ $20\nnn equ $0584\n\n ; Documented instructions\n; ADC\n adc a,(hl)\n adc a,(ix + des)\n adc a,(iy + des)\n adc a,a\n adc a,b\n adc a,c\n adc a,d\n adc a,e\n adc a,h\n adc a,l\n adc a,n\n\n adc hl,bc\n adc hl,de\n adc hl,hl\n adc hl,sp\n\n; ADD\n add a,(hl)\n add a,(ix + des)\n add a,(iy + des)\n add a,a\n add a,b\n add a,c\n add a,d\n add a,e\n add a,h\n add a,l\n add a,n\n\n add hl,bc\n add hl,de\n add hl,hl\n add hl,sp\n\n add ix,bc\n add ix,de\n add ix,ix\n add ix,sp\n\n add iy,bc\n add iy,de\n add iy,iy\n add iy,sp\n\n; AND\n and (hl)\n and (ix + des)\n and (iy + des)\n and a\n and b\n and c\n and d\n and e\n and h\n and l\n and n\n\n; BIT\n bit 0,(hl)\n bit 0,(ix + des)\n bit 0,(iy + des)\n bit 0,a\n bit 0,b\n bit 0,c\n bit 0,d\n bit 0,e\n bit 0,h\n bit 0,l\n\n bit 1,(hl)\n bit 1,(ix + des)\n bit 1,(iy + des)\n bit 1,a\n bit 1,b\n bit 1,c\n bit 1,d\n bit 1,e\n bit 1,h\n bit 1,l\n\n bit 2,(hl)\n bit 2,(ix + des)\n bit 2,(iy + des)\n bit 2,a\n bit 2,b\n bit 2,c\n bit 2,d\n bit 2,e\n bit 2,h\n bit 2,l\n\n bit 3,(hl)\n bit 3,(ix + des)\n bit 3,(iy + des)\n bit 3,a\n bit 3,b\n bit 3,c\n bit 3,d\n bit 3,e\n bit 3,h\n bit 3,l\n\n bit 4,(hl)\n bit 4,(ix + des)\n bit 4,(iy + des)\n bit 4,a\n bit 4,b\n bit 4,c\n bit 4,d\n bit 4,e\n bit 4,h\n bit 4,l\n\n bit 5,(hl)\n bit 5,(ix + des)\n bit 5,(iy + des)\n bit 5,a\n bit 5,b\n bit 5,c\n bit 5,d\n bit 5,e\n bit 5,h\n bit 5,l\n\n bit 6,(hl)\n bit 6,(ix + des)\n bit 6,(iy + des)\n bit 6,a\n bit 6,b\n bit 6,c\n bit 6,d\n bit 6,e\n bit 6,h\n bit 6,l\n\n bit 7,(hl)\n bit 7,(ix + des)\n bit 7,(iy + des)\n bit 7,a\n bit 7,b\n bit 7,c\n bit 7,d\n bit 7,e\n bit 7,h\n bit 7,l\n\n; CALL\n call nn\n\n call nz,nn\n call z,nn\n call nc,nn\n call c,nn\n call po,nn\n call pe,nn\n call p,nn\n call m,nn\n\n; CCF\n ccf\n\n; CP\n cp (hl)\n cp (ix + des)\n cp (iy + des)\n cp a\n cp b\n cp c\n cp d\n cp e\n cp h\n cp l\n cp n\n\n cpd\n cpdr\n cpir\n cpi\n\n; CPL\n cpl\n\n; DAA\n daa\n\n; DEC\n dec (hl)\n dec (ix + des)\n dec (iy + des)\n dec a\n dec b\n dec c\n dec d\n dec e\n dec h\n dec l\n\n dec bc\n dec de\n dec hl\n dec ix\n dec iy\n dec sp\n\n; DI\n di\n\n; DJNZ\nl1 djnz l1\n\n; EI\n ei\n\n; EX\n ex af,af'\n\n ex de,hl\n\n ex (sp),hl\n ex (sp),ix\n ex (sp),iy\n\n exx\n\n; HALT\n halt\n\n; IM\n im 0\n im 1\n im 2\n\n; IN\n in a,(c)\n in b,(c)\n in c,(c)\n in d,(c)\n in e,(c)\n in h,(c)\n in l,(c)\n\n in a,(n)\n\n ind\n indr\n ini\n inir\n\n; INC\n inc (hl)\n inc (ix + des)\n inc (iy + des)\n inc a\n inc b\n inc c\n inc d\n inc e\n inc h\n inc l\n\n inc bc\n inc de\n inc hl\n inc ix\n inc iy\n inc sp\n\n; JP\n jp nn\n\n jp (hl)\n jp (ix)\n jp (iy)\n\n jp nz,nn\n jp z,nn\n jp nc,nn\n jp c,nn\n jp po,nn\n jp pe,nn\n jp p,nn\n jp m,nn\n\n; JR\n jr $ + $22\n\n jr nz,$ + $22\n jr z,$ + $22\n jr nc,$ + $22\n jr c,$ + $22\n\n; LD\n ld (bc),a\n ld (de),a\n\n ld (hl),a\n ld (hl),b\n ld (hl),c\n ld (hl),d\n ld (hl),e\n ld (hl),h\n ld (hl),l\n ld (hl),n\n\n ld (ix + des),a\n ld (ix + des),b\n ld (ix + des),c\n ld (ix + des),d\n ld (ix + des),e\n ld (ix + des),h\n ld (ix + des),l\n ld (ix + des),n\n\n ld (iy + des),a\n ld (iy + des),b\n ld (iy + des),c\n ld (iy + des),d\n ld (iy + des),e\n ld (iy + des),h\n ld (iy + des),l\n ld (iy + des),n\n\n ld (nn),a\n\n ld (nn),bc\n ld (nn),de\n ld (nn),hl\n ld (nn),ix\n ld (nn),iy\n\n ld (nn),sp\n\n ld a,(bc)\n ld a,(de)\n ld a,(hl)\n ld a,(ix + des)\n ld a,(iy + des)\n ld a,(nn)\n ld a,a\n ld a,b\n ld a,c\n ld a,d\n ld a,e\n ld a,h\n ld a,l\n ld a,n\n\n ld b,(hl)\n ld b,(ix + des)\n ld b,(iy + des)\n ld b,a\n ld b,b\n ld b,c\n ld b,d\n ld b,e\n ld b,h\n ld b,l\n ld b,n\n\n ld c,(hl)\n ld c,(ix + des)\n ld c,(iy + des)\n ld c,a\n ld c,b\n ld c,c\n ld c,d\n ld c,e\n ld c,h\n ld c,l\n ld c,n\n\n ld d,(hl)\n ld d,(ix + des)\n ld d,(iy + des)\n ld d,a\n ld d,b\n ld d,c\n ld d,d\n ld d,e\n ld d,h\n ld d,l\n ld d,n\n\n ld e,(hl)\n ld e,(ix + des)\n ld e,(iy + des)\n ld e,a\n ld e,b\n ld e,c\n ld e,d\n ld e,e\n ld e,h\n ld e,l\n ld e,n\n\n ld h,(hl)\n ld h,(ix + des)\n ld h,(iy + des)\n ld h,a\n ld h,b\n ld h,c\n ld h,d\n ld h,e\n ld h,h\n ld h,l\n ld h,n\n\n ld l,(hl)\n ld l,(ix + des)\n ld l,(iy + des)\n ld l,a\n ld l,b\n ld l,c\n ld l,d\n ld l,e\n ld l,h\n ld l,l\n ld l,n\n\n ld a,i\n ld i,a\n\n ld a,r\n ld r,a\n\n ld bc,(nn)\n ld de,(nn)\n ld hl,(nn)\n ld ix,(nn)\n ld iy,(nn)\n ld sp,(nn)\n\n ld bc,nn\n ld de,nn\n ld hl,nn\n ld ix,nn\n ld iy,nn\n\n ld sp,hl\n ld sp,ix\n ld sp,iy\n ld sp,nn\n\n ldd\n lddr\n ldi\n ldir\n\n; NEG\n neg\n\n; NOP\n nop\n\n; OR\n or (hl)\n or (ix + des)\n or (iy + des)\n or a\n or b\n or c\n or d\n or e\n or h\n or l\n or n\n\n; OUT\n out (c),a\n out (c),b\n out (c),c\n out (c),d\n out (c),e\n out (c),h\n out (c),l\n out (n),a\n\n outd\n otdr\n outi\n otir\n\n; POP\n pop af\n pop bc\n pop de\n pop hl\n pop ix\n pop iy\n\n; PUSH\n push af\n push bc\n push de\n push hl\n push ix\n push iy\n\n; RES\n res 0,(hl)\n res 0,(ix + des)\n res 0,(iy + des)\n res 0,a\n res 0,b\n res 0,c\n res 0,d\n res 0,e\n res 0,h\n res 0,l\n\n res 1,(hl)\n res 1,(ix + des)\n res 1,(iy + des)\n res 1,a\n res 1,b\n res 1,c\n res 1,d\n res 1,e\n res 1,h\n res 1,l\n\n res 2,(hl)\n res 2,(ix + des)\n res 2,(iy + des)\n res 2,a\n res 2,b\n res 2,c\n res 2,d\n res 2,e\n res 2,h\n res 2,l\n\n res 3,(hl)\n res 3,(ix + des)\n res 3,(iy + des)\n res 3,a\n res 3,b\n res 3,c\n res 3,d\n res 3,e\n res 3,h\n res 3,l\n\n res 4,(hl)\n res 4,(ix + des)\n res 4,(iy + des)\n res 4,a\n res 4,b\n res 4,c\n res 4,d\n res 4,e\n res 4,h\n res 4,l\n\n res 5,(hl)\n res 5,(ix + des)\n res 5,(iy + des)\n res 5,a\n res 5,b\n res 5,c\n res 5,d\n res 5,e\n res 5,h\n res 5,l\n\n res 6,(hl)\n res 6,(ix + des)\n res 6,(iy + des)\n res 6,a\n res 6,b\n res 6,c\n res 6,d\n res 6,e\n res 6,h\n res 6,l\n\n res 7,(hl)\n res 7,(ix + des)\n res 7,(iy + des)\n res 7,a\n res 7,b\n res 7,c\n res 7,d\n res 7,e\n res 7,h\n res 7,l\n\n; RET\n ret\n\n ret z\n ret nz\n ret c\n ret nc\n ret po\n ret pe\n ret p\n ret m\n\n reti\n retn\n\n; RL\n rl (hl)\n rl (ix + des)\n rl (iy + des)\n rl a\n rl b\n rl c\n rl d\n rl e\n rl h\n rl l\n\n; RLA\n rla\n\n; RLC\n rlc (hl)\n rlc (ix + des)\n rlc (iy + des)\n rlc a\n rlc b\n rlc c\n rlc d\n rlc e\n rlc h\n rlc l\n\n; RLCA\n rlca\n\n; RLD\n rld\n\n; RR\n rr (hl)\n rr (ix + des)\n rr (iy + des)\n rr a\n rr b\n rr c\n rr d\n rr e\n rr h\n rr l\n\n; RRA\n rra\n\n; RRC\n rrc (hl)\n rrc (ix + des)\n rrc (iy + des)\n rrc a\n rrc b\n rrc c\n rrc d\n rrc e\n rrc h\n rrc l\n\n; RRCA\n rrca\n\n; RRD\n rrd\n\n; RST\n rst $00\n rst $08\n rst $10\n rst $18\n rst $20\n rst $28\n rst $30\n rst $38\n\n; SBC\n sbc a,(hl)\n sbc a,(ix + des)\n sbc a,(iy + des)\n sbc a,a\n sbc a,b\n sbc a,c\n sbc a,d\n sbc a,e\n sbc a,h\n sbc a,l\n sbc a,n\n\n sbc hl,bc\n sbc hl,de\n sbc hl,hl\n sbc hl,sp\n\n; SCF\n scf\n\n; SET\nlabel set 0,(hl)\n set 0,(ix + des)\n set 0,(iy + des)\n set 0,a\n set 0,b\n set 0,c\n set 0,d\n set 0,e\n set 0,h\n set 0,l\n\n set 1,(hl)\n set 1,(ix + des)\n set 1,(iy + des)\n set 1,a\n set 1,b\n set 1,c\n set 1,d\n set 1,e\n set 1,h\n set 1,l\n\n set 2,(hl)\n set 2,(ix + des)\n set 2,(iy + des)\n set 2,a\n set 2,b\n set 2,c\n set 2,d\n set 2,e\n set 2,h\n set 2,l\n\n set 3,(hl)\n set 3,(ix + des)\n set 3,(iy + des)\n set 3,a\n set 3,b\n set 3,c\n set 3,d\n set 3,e\n set 3,h\n set 3,l\n\n set 4,(hl)\n set 4,(ix + des)\n set 4,(iy + des)\n set 4,a\n set 4,b\n set 4,c\n set 4,d\n set 4,e\n set 4,h\n set 4,l\n\n set 5,(hl)\n set 5,(ix + des)\n set 5,(iy + des)\n set 5,a\n set 5,b\n set 5,c\n set 5,d\n set 5,e\n set 5,h\n set 5,l\n\n set 6,(hl)\n set 6,(ix + des)\n set 6,(iy + des)\n set 6,a\n set 6,b\n set 6,c\n set 6,d\n set 6,e\n set 6,h\n set 6,l\n\n set 7,(hl)\n set 7,(ix + des)\n set 7,(iy + des)\n set 7,a\n set 7,b\n set 7,c\n set 7,d\n set 7,e\n set 7,h\n set 7,l\n\n; SLA\n sla (hl)\n sla (ix + des)\n sla (iy + des)\n sla a\n sla b\n sla c\n sla d\n sla e\n sla h\n sla l\n\n; SRA\n sra (hl)\n sra (ix + des)\n sra (iy + des)\n sra a\n sra b\n sra c\n sra d\n sra e\n sra h\n sra l\n\n; SRL\n srl (hl)\n srl (ix + des)\n srl (iy + des)\n srl a\n srl b\n srl c\n srl d\n srl e\n srl h\n srl l\n\n; SUB\n sub (hl)\n sub (ix + des)\n sub (iy + des)\n sub a\n sub b\n sub c\n sub d\n sub e\n sub h\n sub l\n sub n\n\n; XOR\n xor (hl)\n xor (ix + des)\n xor (iy + des)\n xor a\n xor b\n xor c\n xor d\n xor e\n xor h\n xor l\n xor n\n\n ; Undocumented instructions\n; IN\n in (c) ; DEFB $ED,$70\n in f,(c) ; DEFB $ED,$70\n\n; OUT\n out (c) ; DEFB $ED,$71\n out (c),f ; DEFB $ED,$71\n\n; SLL\n sll (hl)\n sll (ix+des)\n sll (iy+des)\n sll a\n sll b\n sll c\n sll d\n sll e\n sll h\n sll l\n\n; IX and IY 8 bits halfs\n add a,ixh\n add a,ixl\n add a,iyh\n add a,iyl\n\n adc a,ixh\n adc a,ixl\n adc a,iyh\n adc a,iyl\n\n and ixh\n and ixl\n and iyh\n and iyl\n\n cp ixh\n cp ixl\n cp iyh\n cp iyl\n\n dec ixh\n dec ixl\n dec iyh\n dec iyl\n\n inc ixh\n inc ixl\n inc iyh\n inc iyl\n\n ld a,ixh\n ld b,ixh\n ld c,ixh\n ld d,ixh\n ld e,ixh\n ;ld h,ixh\n ;ld l,ixh\n\n ld a,ixl\n ld b,ixl\n ld c,ixl\n ld d,ixl\n ld e,ixl\n ;ld h,ixl\n ;ld l,ixl\n\n ld a,iyh\n ld b,iyh\n ld c,iyh\n ld d,iyh\n ld e,iyh\n ;ld h,iyh\n ;ld l,iyh\n\n ld a,iyl\n ld b,iyl\n ld c,iyl\n ld d,iyl\n ld e,iyl\n ;ld h,iyl\n ;ld l,iyl\n\n ld ixh,a\n ld ixh,b\n ld ixh,c\n ld ixh,d\n ld ixh,e\n ld ixh,ixh\n ld ixh,ixl\n ld ixh,n\n\n ld ixl,a\n ld ixl,b\n ld ixl,c\n ld ixl,d\n ld ixl,e\n ld ixl,ixh\n ld ixl,ixl\n ld ixl,n\n\n ld iyh,a\n ld iyh,b\n ld iyh,c\n ld iyh,d\n ld iyh,e\n ld iyh,iyh\n ld iyh,iyl\n ld iyh,n\n\n ld iyl,a\n ld iyl,b\n ld iyl,c\n ld iyl,d\n ld iyl,e\n ld iyl,iyh\n ld iyl,iyl\n ld iyl,n\n\n or ixh\n or ixl\n or iyh\n or iyl\n\n sbc a,ixh\n sbc a,ixl\n sbc a,iyh\n sbc a,iyl\n\n sub ixh\n sub ixl\n sub iyh\n sub iyl\n\n xor ixh\n xor ixl\n xor iyh\n xor iyl\n\n end\n
"},{"location":"basm/syntax/#fake-instructions","title":"Fake instructions","text":"To ease coding, several fake instructions are allowed by BASM
. It replaces them by the combination of true instructions.
Here is a subset of the possibilities.
Failure
Most accepted fake instructions are missing from the listing
ld hl, de\n jp hl\n jp ix\n jp iy\n push hl, de, bc\n pop bc, de, hl\n
"},{"location":"basm/syntax/#comments","title":"Comments","text":""},{"location":"basm/syntax/#expressions","title":"Expressions","text":""},{"location":"basm/syntax/#special-variables","title":"Special variables","text":"Example:
assert $ == 0\n assert $$ == 0\n\n org 0x100\n assert $ == 0x100\n assert $$ == 0x100\n nop\n assert $ == 0x101\n assert $$ == 0x101\n\n org 0x200, 0x300\n assert $ == 0x200\n assert $$ == 0x300\n nop\n assert $ == 0x201\n assert $$ == 0x301\n
"},{"location":"basm/syntax/#provided-functions","title":"Provided functions","text":"Failure
Need to document all functions
"},{"location":"basm/syntax/#z80-related-functions","title":"Z80 related functions","text":""},{"location":"basm/syntax/#assemble","title":"assemble","text":"assemble(str)
consider the string str
to be a list of instructions (no directives) and returns the list of bytes corresponding to the assembled version of the given string.
assert list_len(assemble(\" nop\")) == 1\n assert list_len(assemble(\" nop : nop \")) == 2\n assert list_len(assemble(\"\")) == 0\n
"},{"location":"basm/syntax/#duration","title":"duration","text":"duration(instruction)
returns the number of nop of the instruction ld a, opcode(inc e)\n ld a, opcode(dec e)\n
"},{"location":"basm/syntax/#amstrad-cpc-video-handling","title":"Amstrad CPC video handling","text":"list_sublist(list, start, end)
-> list: Return a new list from start until end not included load(fname) -> list of bytes
: return the bytes from the given file name org 0x4000\n assert memory(label2) == 4\n\nlabel1\n db 1, 2, 3\n\nlabel2\n db 4, 5, 6\n\n assert memory(label1) == 1\n assert memory(label1+2) == 3\n
"},{"location":"basm/syntax/#user-defined-functions","title":"User defined functions","text":"BASM
allows to define functions that can be used in any expression. The functions are fed with parameters and execute conditional directives as well as directives able to handle variables. They finish at the execution of the RETURN
directive.
FUNCTION [ARG1 [, ARGN]]\n INSTRUCTIONS\n RETURN VALUE\nENDFUNCTION\n
Failure
Better explain how to build function
Example of the fibonacci function:
function fibo nb\n if {nb} == 0\n return 0\n else if {nb} == 1\n return 1\n else\n return fibo({nb}-1) + fibo({nb}-2)\n endif\n\n endfunction\n\n assert fibo(0) == 0\n assert fibo(1) == 1\n\n assert fibo(5) == 5\n\n assert fibo(10) == 55\n
Example of function to handle lists:
; Skip the {start} first element of list {l}\n FUNCTION SKIP, l, start\n len = list_len({l})\n if {start} < len\n return list_sublist({l}, {start}, len)\n else\n return []\n endif\n ENDFUNCTION\n\n ; Take the {amount} first element of list {l}\n FUNCTION TAKE, l, amount\n assert {amount} > 0\n len = list_len({l})\n start = 0\n finish = start + min({amount}, len) ; seems to not work for un unknown reason\n if {amount} > len\n finish = len\n else\n finish = {amount}\n endif\n return list_sublist({l}, start, finish)\n ENDFUNCTION\n\n ; Reverse list {l}\n FUNCTION REVERT, l\n new = []\n nb = list_len({l})\n for idx, 0, nb-1\n new = list_push(new, list_get({l}, nb-1-{idx}))\n endfor\n return new\n ENDFUNCTION\n\n assert list_len([1, 2, 3, 4]) == 4\n assert list_sublist([1, 2, 3, 4], 0, 2) == [1,2]\n assert list_sublist([1, 2, 3, 4], 0, 4) == [1, 2, 3, 4]\n\n ; Various test to check appropriate behavior\n assert SKIP([1, 2, 3, 4], 2) == [3, 4]\n assert SKIP([1, 2, 3, 4], 5) == []\n\n assert TAKE([1, 2, 3, 4], 2) == [1, 2]\n assert min(4,5) == 4\n assert TAKE([1, 2, 3, 4], 4) == [1, 2, 3, 4]\n assert TAKE([1, 2, 3, 4], 10) == [1, 2, 3, 4]\n\n assert REVERT([1, 2, 3, 4]) == [4, 3, 2, 1]\n assert list_len(load(\"hello.sna\")) == 4674\n\n assert list_len(TAKE(load(\"hello.sna\"), 8)) == 8\n\n assert string_from_list(TAKE(load(\"hello.sna\"), 8)) == \"MV - SNA\"\n\n ; Write in memory 8 bytes from the given file\n snapshot = load(\"hello.sna\")\n header_id = TAKE(snapshot, 8)\n db header_id\n\n ; Check that memory is correctly set\n assert peek(0) == \"M\"\n assert peek(7) == \"A\"\n
"},{"location":"bndbuild/","title":"Bndbuild","text":""},{"location":"bndbuild/#synopsis","title":"Synopsis","text":"Makefile-like tool tailored to build Amstrad CPC project. It embeds various cpclib tools such as basm, m4, img2cpc but can still execute external programs such as rasm, winape.
The rules are described in a yaml
file. Check for example the test project in folder."},{"location":"bndbuild/#help","title":"Help","text":"
Benediction CPC demo project builder\n\nUsage: bndbuilder [OPTIONS] [TARGET]...\n\nArguments:\n [TARGET]...\n Provide the target(s) to run.\n\nOptions:\n -h, --help [<CMD>]\n Show the help of the given subcommand CMD.\n\n [default: bndbuild]\n [possible values: img2cpc, basm, rm, bndbuild, xfer]\n\n -V, --version\n Print version\n\n -f, --file <FILE>\n Provide the YAML file for the given project.\n\n [default: bndbuild.yml]\n\n -w, --watch\n Watch the targets and permanently rebuild them when needed.\n
"},{"location":"bndbuild/#example","title":"Example","text":"Here is an example to build a dummy Amstrad CPC project and execute on the real machine thanks to the m4. It is available in tests/dummy (the repository does not contains the external tools needed to properly build the project. It is straighforward to add them). Successive calls to the build task do nothing as soon as no file has been modified. It is also possible to watch the dependencies of a given task to automatically build it when they are modified. This cannot be seen with the capture, but each time m4 command is launched, the project is send into the CPC machine (it takes several seconds however).
"},{"location":"bndbuild/#format","title":"Format","text":"The rules description file must respect the yaml
text file format. It is preferably named bndbuild.yml
but this can be overriden by the -f
argument. It contains list of rules. Each rule can have the following keys:
tgt
: to list the files build by the ruledep
: to list the files needed to build the rulecmd
: a command, or a list of commands, executed by the rule. Commands prefixed by -
can silently failhelp
: an optional help text to describe the rulephony
: an optional tag to express the rule does not generate anyfile (it is infered when the commands are not extern). Mainly serve for the --watch
argument.The commands are either included by the application (so limited to cpclib commands and os agnostic) or accessible externally (no limitation, but os dependent). They are: - basm
to launch basm assembler - img2cpc
to make image conversion - rm
to delete files - xfer
to transfer to the CPC with the M4 card - extern
to launch any command available on the machine