MAD-ASSEMBLER 1.9.5
Tebe/Madteam (01.01.2013)
translated poorly to English by Phaeron using Google Translate (01.26.2013)
INTRODUCTION
Mad-Assembler (MADS) is a 32 bit application, written in Delphi. Most applications are written in C, so to be different I used Delphi 7.0 ;).
MADS is oriented toward users of QA, XASM, and FA. The syntax is borrowed from QA, some macro commands and syntax come from XASM, and SpartaDOS X (SDX) syntax is inherited from FA. Additional characters are allowed in label names. Also added are support for the 65816 CPU, macros, procedures, splitting across virtual memory banks, and nested label names.
The number of labels and macros are limited only by the memory in your PC. Specifically, you can have 2147483647 (INTEGER) table entries. I'm sure this amount is sufficient. :)
Arithmetic operations are done with values of type INT64 (signed 64 bit), with the result represented with a value of type CARDINAL (unsigned 32 bit).A line can be up to 65535 bytes, which is also the length limit of a label name. However, I have not had the opportunity to check labels as long as poems. :)
With the free compiler Free Pascal Compiler, it is possible to compile MADS for other platforms, such as Linux, Mac, OS/2, etc. For more information on how to build, see the chapter on Compilation.
If your system can run a version of Free Pascal, you can use MADS.To compile MADS source, you can use the Delphi compiler, if Delphi 7.0 or later is installed.
Another, more multi-platform way is to use the compiler package from the Free Pascal Compiler (FPC), which can be downloaded from: http://www.freepascal.org/
Run the installer, and select the folder in which you installed FP. It is important not to use a directory name which contains '!' or other special characters, or else you will not be able to compile any files, and it should probably not be a standard path name. The command-line to start compilation could look like this (the case in the parameter names is important):fpc -Mdelphi -v mads.pas
DIFFERENCES AND SIMILARITIES BETWEEN MADS AND XASM
lda #' ' lda #" "
MADS distinguishes between single quotes (ATASCII coding) and double quotes (INTERNAL coding), whereas XASM treats both forms the same (ATASCII coding). Of course, the quote types are treated the same in DTA by MADS.
lda $2000,x+ -> lda $2000,x inx
lda $2000,x+2 -> lda $2002,x
USAGE
Syntax: mads source [switches] -b:address Generate binary file at specific address -c Label case sensitivity -d:label=value Define a label -f CPU command at first column -hc[:filename] Header file for CC65 -hm[:filename] Header file for MADS -i:path Additional include directories -l[:filename] Generate listing -m:filename File with macro definition -o:filename Set object file name -p Print fully qualified file names in listing and error messages -s Silent mode -t[:filename] List label table -x Exclude unreferenced procedures -vu Verify code inside unreferenced procedures -u Warn of unused labels
The default filenames are:
If no extension is specified for the source file, MADS will default to the extension .ASM.
Switches can be given in any order, preceded by '/' or '-', and case does not matter. Also, switches can be combined:mads -lptd:label=value -d:label2=value source.asm mads -l -p -t source mads source.asm -lpt mads.exe "%1" -ltpi:"d:\!atari\macro\" mads -i:"c:\atari\macros\" -c source.asm -lptBy default, after assembly MADS will save the output with the extension '.OBX', which can be changed with a batch file:
mads "%1" -o:%~n1.xexMore can be learned about the operators by doing "CALL /?" in Microsoft DOS.
-i:"c:\program files" -i:c:\temp -i:"d:\atari project"
D:\!Delphi\Masm\test.asm (29) ERROR: Missing .PROCNow just double-click the message line, and the cursor in the editor will move to the line with the error.
3 = bad parameters, assembling not started 2 = error occured 0 = no errorsWarnings do not change the value of the exit code.
The listing format is the same as XASM, except that the virtual memory bank is added before the address (if the bank is <>0). For more on virtual banks, see Memory banks.
3 4 = 01,9033 picture equ $9033 5 = 01,00A0 scr1 equ $a0 6 7 8 01,2000 EA main nop
As with XASM, the *.LAB file stores information about labels in the program.
There are three columns:
$FFF9 label for parameter in procedure defined by .PROC $FFFA label for array defined by .ARRAY $FFFB label for structured data defined by the pseudo-command DTA STRUCT_LABEL $FFFC label for SpartaDOS X symbol - SMB $FFFD label for macro defined by .MACRO directive $FFFE label for structure defined by .STRUCT directive $FFFF label for procedure defined by .PROC directive
Characters with special meanings in label names:
The numeric value after :: is the number of the macro call.
Mad-Assembler v1.4.2beta by TeBe/Madteam Label table: 00 0400 @STACK_ADDRESS 00 00FF @STACK_POINTER 00 2000 MAIN 00 2019 LOOP 00 201C LOOP::1 00 201C LHEX 00 0080 LHEX.HLP 00 204C LHEX.THEX 00 205C HEX 00 205C HEX.@GETPAR0.LOOP 00 2079 HEX.@GETPAR1.LOOP
I'm not sure if the file is OK, but Eru wanted something so here it is. :) It is useful for linking together ASM and CC65, or C ported to the Atari. Its contents can look like this (example file TEST.ASM):
#ifndef _TEST_ASM_H_ #define _TEST_ASM_H_ #define TEST_CPU65816 0x200F #define TEST_CPU6502 0x2017 #define TEST_TEXT6502 0x201F #define TEST_TEXT65816 0x2024 #endif
MACRO COMMANDS
REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS ADD, SUB ADB, SBB ADW, SBW PHR, PLR INW, INL, IND, DEW, DEL, DED MVA, MVX, MVY MWA, MWX, MWY CPB, CPW, CPL, CPDThe purpose of macro commands is to save time when writing programs and to shorten listings. Macro commands replace commonly used groups of instructions.
REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS
These macro commands are similar to the 6502 branch instructions BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS, but branch to the previously assembled instruction ("repeat"):
lda:cmp:req 20 -> lda 20 -> wait cmp 20 -> beq wait ldx #0 -> ldx #0 mva:rne $500,x $600,x+ -> loop lda $500,x -> sta $600,x -> inx -> bne loop
SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS
These macro commands are similar to the 6502 branch instructions BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS, but branch over the next assembled instruction ("skip"):
lda #40 -> lda #40 add:sta $80 -> clc scc:inc $81 -> adc $80 -> sta $80 -> bcc skip -> inc $81 -> skip
JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS
These macro commands are similar to the 6502 branch instructions BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS, but can target the entire 64KB address space ("jump"):
jne dest -> beq *+4 -> jmp destIf the distance is short (in the range -128 ... +127), MADS uses a regular BEQ, BNE, BPL, BMI, BCC, BCS, BVC, or BVS branch instruction.
ADD, SUB
These macro commands add or subtract from the accumulator without including the carry flag.
ADD -> CLC SUB -> SEC
-> ADC ... -> SBC ...
ADB, SBB
These macro commands add or subtract a value from a byte location in memory and store it either back
or to another location.
ADB SRC #$40 -> LDA SRC ADB A B C -> LDA A
-> CLC -> CLC
-> ADC #$40 -> ADC B
-> STA SRC -> STA C
SBB SRC #$80 -> LDA SRC SBB A B C -> LDA A
-> SEC -> SEC
-> SBC #$80 -> SBC B
-> STA SRC -> STA C
ADW, SBW
These macro commands add or subtract a value from a word location in memory and store it either back
or to another location.
ADW SRC #$40 -> CLC ADW A B C -> CLC
-> LDA SRC -> LDA A
-> ADC #$40 -> ADC B
-> STA SRC -> STA C
-> SCC -> LDA A+1
-> INC SRC+1 -> ADC B+1
-> STA C+1
ADW SRC #$40 SRC -> CLC
-> LDA SRC
-> ADC #$40
-> STA SRC
-> LDA SRC+1
-> ADC #$00
-> STA SRC+1
SBW SRC #$4080 -> SEC SBW A B C -> SEC
-> LDA SRC -> LDA A
-> SBC <$4080 -> SBC B
-> STA SRC -> STA C
-> LDA SRC+1 -> LDA A+1
-> SBC >$4080 -> SBC B+1
-> STA SRC+1 -> STA C+1
PHR, PLR
These macros are similar to the 6502 mnemonics PHA and PLA, but push or
pop the A, X, and Y registers to and from the stack.
PHR -> PHA PLR -> PLA
-> TXA -> TAY
-> PHA -> PLA
-> TYA -> TAX
-> PHA -> PLA
INW, INL, IND, DEW, DEL, DED
The macro commands INW, INL, and IND increment 16-bit (word), 24-bit (long), and 32-bit (dword) memory locations.
The macro commands DEW, DEL, and DED decrement 16-bit (word), 24-bit (long), and 32-bit (dword) memory locations. The accumulator is also changed after the decrement macro commands.inw dest -> inc dest -> inc dest -> bne skip -> sne -> inc dest+1 -> inc dest+1 -> skip -> dew dest -> lda dest -> lda dest -> bne skip -> sne -> dec dest+1 -> dec dest+1 -> skip dec dest -> dec dest
MVA, MVX, MVY
The macro commands MVA, MVX, MVY transfer bytes using the A, X, or Y registers, respectively.
OPT R+ can potentially reduce the code size from these macros by removing redundant instructions.
lda src -> mva src dst
sta dst ->
ldy $10,x -> mvy $10,x $a0,x
sty $a0,x ->
ldx #$10 -> mvx #$10 dst
stx dst ->
MWA, MWX, MWY
The macro commands MWA, MWX, MWY transfer words using the A, X, or Y registers, respectively.
OPT R+ can potentially reduce the code size from these macros by removing redundant instructions.
ldx <adr -> mwx #adr dst
stx dst ->
ldx >adr ->
stx dst+1 ->
mwa #0 $80 -> lda #0 mwy #$3040 $80 -> ldy <$3040
-> sta $80 -> sty $80
-> sta $81 -> ldy >$3040
-> sty $81
mwa ($80),y $a000,x -> lda ($80),y
-> sta $a000,x
-> iny
-> lda ($80),y
-> sta $a001,x
CPB, CPW, CPL, CPD
The macro commands CPB, CPW, CPL, and CPD perform comparison of
byte, word (16-bit), long (24-bit), and dword (32-bit) values, respectively.
cpw temp #$4080
bcc skip
cpd v0 v1
beq skip
PSEUDOCOMMANDS
IFT [.IF] expression ELS [.ELSE] ELI [.ELSEIF] expression EIF [.ENDIF] ERT ERT 'string'["string"] | ERT expression label EQU expression label = expression label SET expression label EXT type OPT [bcfhlmorst][+-] ORG [[expression]]address[,address2] INS 'filename'["filename"][*][+-value][,+-ofset[,length]] ICL 'filename'["filename"] DTA [abfghltv](value1,value2...)[(value1,value2...)] DTA [cd]'string'["string"] RUN expression INI expression END [.EN] SIN (centre,amp,size[,first,last]) RND (min,max,length) :repeat BLK N[one] X BLK D[os] X BLK S[parta] X BLK R[eloc] M[ain]|E[xtended] BLK E[mpty] X M[ain]|E[xtended] BLK U[pdate] S[ymbols] BLK U[pdate] E[xternal] BLK U[pdate] A[dress] BLK U[pdate] N[ew] X 'string' label SMB 'string' NMB RMB LMB #expression
These are mostly the same, although there are some differences with quoted strings. Both ' ' and " " are treated the same except in operands, where '' gives the ATASCII code of a character, and "" gives the INTERNAL code.
BLK
BLK N[one] X - set program counter to X without adding a header block BLK D[os] X - DOS block with header $FFFF or without header when the program counter is already set to X BLK S[parta] X - fixed address block with header $FFFA, set program counter to X BLK R[eloc] M[ain]|E[xtended] - relocatable block in MAIN or EXTENDED memory BLK E[mpty] X M[ain]|E[xtended] - relocatable block reserving MAIN or EXTENDED memory NOTE: the program counter is immediately raised by X bytes BLK U[pdate] S[ymbols] - update block for updating previous SPARTA or RELOC blocks with the addresses of SDX symbols BLK U[pdate] E[xternal] - update block for updating addresses of external labels ($FFEE header) NOTE: does not apply to SpartaDOS X, as this is a MADS extension BLK U[pdate] A[dress] - update block for addresses in RELOC blocks BLK U[pdate] N[ew] X 'string' - block declaring a new symbol 'string' in a RELOC block with address X. If the symbol name is prefixed with @, the address is in main memory and can be invoked by command.comFor more information on SpartaDOS X blocks, see SpartaDOS X file format and Programming SpartaDOS X.
The pseudo-command SET redefines a label, with similar effect as temporary labels that
begin with '?':
Declares a label as a SDX symbol. The symbol name can have a maximum of 8 characters.
BLK UPDATE SYMBOLS will direct the assembler to generate the correct update block
for symbols. Example:
Note: use labels for all symbols and declare before usage for correct operation!
Now, the ':' is correctly interpreted as :repeat.
All options controlled by OPT can be used anywhere in the listing, e.g. if it is
turned on at line 12 and off at line 20 then the listing will only contain lines 12 through 20.
If you use CodeGenie and 'OPT S+', the listing file is unnecessary as the listing is
printed in the lower pane (Output Bar).
Brackets [ ] are used to write a new header, which can be any length. Other
values following closing ']', separated by a comma, are the assembly address and the
address in the header.
The INS pseudo-command allows inclusion of an external binary file. The included
file does not have to be in the same directory as the main file being assembled. Search paths for
the file can be configurated using the /i switch (see assembly switches).
The pseudo-command ICL includes an additional source file in the assembly process.
The attached file does not have to be in the same directory as the main assembly file. Additional
paths are added to the MADS search path using the /i switch (see assembly switches).
These pseudo-commands and directives conditionally include lines based on the value of an expression.
DIRECTIVES
The directive .SYMBOL is equivalent to the pseudo-command SMB except that the symbol name does
not need to be specified, as it is implied to be label.
A .SYMBOL directive can be placed anywhere within a relocatable SDX block (BLK RELOC), unlike SMB.
Whenever there is a .SYMBOL directive, the following block update will be generated:
The defaults are: N=$0100, FILL=0.
The directive .SEGDEF defines a new segment LABEL at address ADDRESS and with length LENGTH. Read/write attributes can be assigned
to the segment (R-read, W-write, RW-read/write), as well as a virtual bank number BANK (default=0).
The .SEGMENT directive begins writing of code and data to the code segment LABEL. Exceeding the preset length of the
segment produces error message Segment LABEL error at ADDRESS.
The directive .ENDSEG ends writing to the current segment and returns to the main program block.
Using .ZPVAR with only an address will specify the first address to assign to the next variable (the
default address is $0080).
The address is automatically incremented by MADS and the warning message Access violations at address $xxxx
is generated if addresses are repeated. In the case of a zero page overflow, the error Value out of range is generated.
(Editor's note: Values are printed out as hexadecimal with a $ prefix.)
Store byte values in memory. ASCII strings can be specified by enclosing the string in either single or double quotes.
Example:
.HE 0 55 AA FF
This is an optional pseudo-op to mark the end of assembly. It can be placed before
the end of your source file to prevent a portion of it from being assembled.
CAUTION: Defining a label AFTER the use of a .DEF which references it can be dangerous, particularly if the .DEF is used in a .IF directive.
The permitted range of values is INDEX = <0..65535>.
These directives and pseudo commands can be used interchangeably to conditionally assemble
portions of code:
In these examples the parentheses or square brackets are required as otherwise the parameter
to the first .DEF directive would be the label label_name.AND.NOT.DEFlabel_name2 (spaces are ignored and periods are allowed in label names).
6502 CODE GENERATION DIRECTIVES
The #IF, #ELSE, and #END directives produce 6502 machine code for an IF conditional
statement around the designated program block and can be nested. All types are
acceptable (.BYTE, .WORD, .LONG, and .DWORD). It is possible to combine terms
using the .AND and .OR directives, but it is impossible to control the order of evaluation
with parentheses.
If the expression is non-zero (TRUE), the program block within the #IF is executed,
terminated by a JMP instruction to the next instruction after #END if there is an
#ELSE block.
The directives #WHILE and #END allow generation of 6502 machine code for a loop around
the given program block and can be nested. All types .BYTE, .WORD, .LONG, and .DWORD are
acceptable. Multiple terms can be connected with the .OR and .AND directives, but order
of evaluation cannot be controlled by parentheses.
The sequence of operations in the expansion of the #WHILE statement is as follows:
If the first evaluation of the expression produces zero, the program block will never be
executed and control passes to the next statement after #END.
The directive #CYCLE generates code to take a given number of cycles. The generated code does not
modify any memory or any general purpose register.
Unlike two-pass assemblers like QA and XASM, MADS
is a multi-pass assembler. Why?
However, MADS nicely generates LDA zp. This is the simplest benefit of multiple passes.
Another way to force zero page addressing is to use curly braces { }:
Therefore, MADS allows the following:
In summary:
Assembles at adres1, but loads to address adres2. The ORG
will always create a new block in the file, which adds an additional four bytes for the header of
the new block.
The directives .ADR and .LEN can then be used to copy the block to the correct address:
temp set 12
lda #temp
temp set 23
lda #temp
label SMB 'string'
pf smb 'PRINTF'
jsr pf
...
will make the SDX system insert the correct address into the JSR instruction.
cm smb 'COMTAB'
wp equ cm-1 (błąd !)
sta wp
Use this instead:
cm smb 'COMTAB'
sta cm-1 (ok !)
:repeat
Example:
:4 asl @
:2 dta a(*)
:256 dta #/8
ladr :4 dta l(line:1)
hadr :4 dta h(line:1)
A ':' specifies the number of times to repeat the line (in the case of a macro,
this specifies a macro parameter by number if it is in decimal).
The repeat count should be in the range <0..2147483647>.
When repeating a line with ':repeat', it is possible to use the loop counter in the repeated
line using a hash sign ('#') or the parameter :1.
.macro test
:2 lsr @
.endm
The ':' prefixed number in this case is interpreted as the second macro parameter.
To prevent this interpretation by MADS, add something after the ':'
that does nothing, such as the plus sign ('+').
.macro test
:+2 lsr @
.endm
OPT
The OPT pseudo-command allows options to be enabled or disabled during
assembly.
b+ enable bank sensitivity
b- disable bank sensitivity (default)
c+ use 65816 instruction set (16bit)
c- use 6502 instruction set (8bit) (default)
f+ emit file as a single block (useful for cartridges)
f- emit file as multiple blocks (default)
h+ write DOS executable file headers (default)
h- omit DOS executable file headers
l+ enable writing to listing file (LST)
l- disable writing to listing file (LST) (default)
m+ expand macros in listing
m- include only macro invocation in listing (default)
o+ enable writing to object file (OBX) (default)
o- disable writing to object file (OBX)
r+ enable optimization for MVA, MVX, MVY, MWA, MWX, MWY
r- disable optimization for MVA, MVX, MVY, MWA, MWX, MWY (default)
s+ print listing to screen
s- do not print listing to screen (default)
t+ enable SEP/REP tracking (65816 CPU)
t- disable SEP/REP tracking (65816 CPU) (default)
?+ labels beginning with '?' are local (MAE style)
?- labels beginning with '?' are temporary (default)
Example:
OPT c+ c - l + s +
OPT h-
OPT o +
ORG
The ORG pseudo-command sets a new assembly address and therefore a new position
for data in RAM.
adr assembles to ADR, setting the address in the header to ADR
adr,adr2 assembles to ADR, setting the address in the header to ADR2
[b($ff,$fe)] write $FFFE header (will generate 2 bytes)
[$ff,$fe],adr write $FFFE header, setting the address in the header to ADR
[$d0,$fe],adr,adr2 write $D0FE header, assemble at address ADR, set the address in the header to ADR2
[a($FFFA)],adr write SpartaDOS $FAFF header, set address in the header to ADR
Example:
opt h-
ORG [a($ffff),d'atari',c'ble',20,30,40],adr,adr2
Example:
opt h-f+
ORG [a(start), a(over-1)],$2000
start
nop
.ds 128
nop
over
INS 'filename'["filename"][*][+-value][,+-ofset[,length]]
* invert bytes
+-VALUE increase or decrease each byte by the value of the expression VALUE
+OFSET skip OFSET bytes at the beginning of the file (seek to OFSET)
-OFSET read OFSET bytes at the end of the file (seek to FileLength-OFSET)
LENGTH read LENGTH bytes from the file
If the LENGTH value is not specified, the default behavior is to read to the end.
ICL 'filename'["filename"]
DTA
The pseudo-command DTA defines typed data. If the type is not specified, byte data is assumed (b).
b byte data (8-bit)
a word data (16-bit)
v relocatable WORD data (16-bit)
l byte data (8-bit)
h byte data (8-bit)
t long data (24-bit)
e long data (24-bit)
f doubleword data (32-bit)
g doubleword data (32-bit) in reversed byte order (big-endian)
c ATASCII string, delimited by '' or ""; * at the end encodes inverse
video, e.g. dta c'abecadlo'*
d INTERNAL string, delimited by '' or ""; * at the end encodes inverse
video, e.g. dta d'abecadlo'*
Example:
dta 1 , 2, 4
dta a ($2320 ,$4444)
dta d'sasasa', 4,a ( 200 ), h($4000)
dta c 'file' , $9b
dta c'invers'*
SIN(centre,amp,size[,first,last])
where:
centre is a number which is added to every sine value
amp is the sine amplitude
size is the sine period
first,last define range of values in the table. They are optional.
Default are 0,size-1.
Example: dta a(sin(0,1000,256,0,63))
defines table of 64 words representing a quarter of sine with
amplitude of 1000.
RND(min,max,length)
This pseudo-command generates LENGTH pseudorandom values in the range <MIN..MAX>.
Example: dta b(rnd(0,33,256))
IFT, ELS, ELI, EIF
IFT [.IF] expression
ELS [.ELSE]
ELI [.ELSEIF] expression
EIF [.ENDIF]
.ALIGN N[,fill]
.ARRAY label index type [= default_value]
.ENDA, [.AEND]
.DEF label [= expression]
.ENUM label
.ENDE, [.EEND]
.ERROR [ERT] 'string'["string"] lub .ERROR [ERT] expression
.EXTRN label [,label2,...] type
.IF [IFT] expression
.ELSE [ELS]
.ELSEIF [ELI] expression
.ENDIF [EIF]
.IFDEF label
.IFNDEF label
.LOCAL label
.ENDL, [.LEND]
.LINK 'filename'
.MACRO label
.ENDM, [.MEND]
:[%%]parameter
.EXITM [.EXIT]
.NOWARN
.PRINT [.ECHO] 'string1','string2'...,value1,value2,...
.PAGES [expression]
.ENDPG, [.PGEND]
.PUBLIC, [.GLOBAL], [.GLOBL] label [,label2,...]
.PROC label
.ENDP, [.PEND]
.REG, .VAR
.REPT expression [,parameter1, parameter2, ...]
.ENDR, [.REND]
.R
.RELOC [.BYTE|.WORD]
.STRUCT label
.ENDS, [.SEND]
.SYMBOL label
.SEGDEF label address length [bank]
.SEGMENT label
.ENDSEG
.USING, [.USE] proc_name, local_name
.VAR var1[=value],var2[=value]... (.BYTE|.WORD|.LONG|.DWORD)
.ZPVAR var1, var2... (.BYTE|.WORD|.LONG|.DWORD)
.END
.EN
.BYTE
.WORD
.LONG
.DWORD
.OR
.AND
.XOR
.NOT
.LO (expression)
.HI (expression)
.DB
.DW
.DS expression
.BY [+byte] bytes and/or ASCII
.WO words
.HE hex bytes
.SB [+byte] bytes and/or ASCII
.CB [+byte] bytes and/or ASCII
.FL floating point numbers
.ADR label
.LEN label
.GET [index] 'filename'["filename"][*][+-value][,+-ofset[,length]]
.PUT [index] = value
.SAV [index] ['filename',] length
.SYMBOL label
BLK UPDATE NEW LABEL 'LABEL'
More on the declaration of SDX symbols is found in the section Defining symbols with SMB.
.ALIGN N [,fill]
The directive .ALIGN aligns the assembly address to the value N, using FILL as the memory preset value for
any needed fill. It is possible to align relocatable code provided that the FILL value is given.
Example:
.align
.align $400
.align $100,$ff
.REPT expression [,parameter1, parameter2, ...]
The directive REPT is the same as the :repeat syntax,
except that a block is repeated instead of a single line. The beginning of the block is defined
with the .REPT directive, which is followed by a repeat count in the range
<0..2147483647> and then optional parameters. Unlike macro parameters, the
parameters to .REPT are always calculated first and the result is substituted immediately (this
feature can be used to define new labels). The parameters specified in the .REPT block are used
like parameters to a .MACRO block. A .REPT block is ended using the .ENDR directive,
before which there should be no label.
Example:
.rept 12, #*2, #*3 ; a .REPT block can be combined with :rept
:+4 dta :1 ; :+4 to distinguish from repeat block parameter :4
:+4 dta :2
.endr
.rept 9, # ; define 9 labels label0..label8
label:1 mva #0 $d012+#
.endr
.PAGES [expression]
The directive .PAGES specifies that a piece of code delimited by a <.PAGES .. .ENDPG> block
should fit within a number of memory pages (the default is 1). If the program code exceeds the given
number of memory pages, the error Page error at ???? is generated.
Example:
org $4000
.pages $40
...
...
.endpg
.SEGDEF label address length [attrib] [bank]
.SEGMENT label
.ENDSEG
Example:
.segdef sdata adr0 $100
.segdef test adr1 $40
org $2000
nop
.cb 'ALA'
.segment sdata
nop
.endseg
lda #0
.segment test
ldx #0
clc
dta c'ATARI'
.endseg
adr0 .ds $100
adr1 .ds $40
.END
The directive .END can be used interchangeably with the directives .ENDP, .ENDM, .ENDS, .ENDA, .ENDL, .ENDR, .ENDPG, .ENDW,
and .ENDT.
.VAR var1[=value1],var2[=value2]... (.BYTE|.WORD|.LONG|.DWORD) [=address]
The directive .VAR is used to declare and initialize variables in the main program
block and in .PROC and .LOCAL blocks. MADS does not use the information in these variables in further
operations involving pseudo and macro commands. Acceptable types of variables are .BYTE, .WORD, .LONG,
and .DWORD, multiples of those types, and types declared by .STRUCT and .ENUM.
Example:
.var a,b , c,d .word ; 4 variables of type .WORD
.var a,b,f :256 .byte ; 3 variables each with a size of 256 bytes
.var c=5,d=2,f=$123344 .dword ; 3 .DWORD variables with values 5, 2, and $123344
.var .byte i=1, j=3 ; 2 variables of type .BYTE with values 1, 3
.var a,b,c,d .byte = $a000 ; 4 variables of type .BYTE with addresses $A000, $A001, $A002, $A003
.var .byte a,b,c,d = $a0 ; 4 variables of type byte, with the last variable 'D' having address $A0
; !!! in this form it is not possible to determine the address of variables
.proc name
.var .word p1,p2,p3 ; declare three variable of type .WORD
.endp
.local
.var a,b,c .byte
lda a
ldx b
ldy c
.endl
.struct Point ; new structure of type POINT
x .byte
y .byte
.ends
.var a,b Point ; declare structured variables
.var Point c,d ; equivalent to syntax 'label DTA POINT'
Declared variables are physically allocated at the end of the block, as determined by the directives
.ENDP, .ENDL, or .END. The exception is that in a .PROC block, variables declared with .VAR are
always allocated in front of the .ENDP directive even if the .VAR statements are within nested
.LOCAL blocks.
.ZPVAR var1, var2... (.BYTE|.WORD|.LONG|.DWORD) [=address]
The directive .ZPVAR is used to declare zero page variables in the main
program block and in .PROC and .LOCAL blocks. Attempting to assign to or initialize a variable
of this type will generate the warning message Uninitialized variable.
MADS does not use the information in these variables in further operations involving
pseudo and macro commands. Acceptable variable types are .BYTE, .WORD., .LONG, .DWORD,
multiples of those types, and types declared by .STRUCT and .ENUM:
Example:
.zpvar a b c d .word = $80 ; 4 variables of type .WORD starting at address $0080
.zpvar i j .byte ; two more byte variables starting at address $0080+8
.zpvar .word a,b ; 2 variables of type .WORD
; !!! in this form it is not possible to determine the addresses of the variables
.struct Point ; declare new structure POINT
x .byte
y .byte
.ends
.zpvar a,b Point ; declare structured variables
.zpvar Point c,d ; equivalent to syntax 'label DTA POINT'
The zero-page variable will be assigned addresses only at the end of the block in which it was declared, at
.ENDP, .ENDL, or .END. The exception is a .PROC block where variables declared with .ZPVAR are assigned
at the .ENDP directive even if variables are declared within nested .LOCAL blocks.
Example:
.zpvar = $40
.PRINT [.ECHO]
Prints on screen parameter values or string expressions delimited by ' ' or " " quotes:
Example:
.print "End: ",*,'..',$8000-*
.echo "End: ",*,'..',$8000-*
.ERROR
[ERT] 'string'["string"] | .ERROR [ERT] expression
The directive .ERROR and pseudo-command ERT are equivalent and
stop assembly, displaying a message given as a parameter delimited by ' ' or " ".
If the parameter is a boolean expression, then assembly will stop when the expression is true (with User error):
Example:
ert "halt" ; ERROR: halt
.error "halt"
ert *>$7fff ; ERROR: User error
.error *>$7fff
.BYTE, .WORD, .LONG, .DWORD
These directives are used to determine the allowable types for parameters in a procedure
declaration. They can also be used in place of the pseudo command DTA for
data definition.
Example:
.proc test (.word tmp,a,b .byte value)
.byte "atari",5,22
.word 12,$FFFF
.long $34518F
.dword $11223344
.DB
Define data of type BYTE. Equivalent to the pseudo command DTA B or the .BYTE directive.
.DW
Define data of type WORD. Equivalent to the pseudo command DTA A or the .WORD directive.
.DS expression
This directive has been adapted from MAC'65 and reserves uninitialized memory. This
is equivalent to the pseudo command ORG *+expression. The .DS directive cannot
be used in relocatable code like ORG can.
purpose: reserves space for data without initializing then space to any particular value(s).
usage: [label] .DS expression
Using ".DS expression" is exactly equivalent of using "ORG *+expression". That is, the label
(if it is given) is set equal to the current value of the location counter. Then then value
of the expression is added to then location counter.
Example: BUFFERLEN .DS 1 ;reserve a single byte
BUFFER .DS 256 ;reserve 256 bytes
.BY [+byte] bytes and/or ASCII
Example:
.BY +$80 1 10 $10 'Hello' $9B
will generate:
81 8A 90 C8 E5 EC EC EF 1B
Values in .BY statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.
.WO words
Stores words in memory. Multiple words can be entered.
Values in .WO statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.
.HE hex bytes
Store hex bytes in memory. This is a convenient method to enter strings of hex bytes, since it does not require the use of the '$' character. The bytes are still separated by spaces however, which I feel makes a much more readable layout than the 'all run together' form of hex statement that some other assemblers use.
.SB [+byte] bytes and/or ASCII
This is in the same format as the .BY pseudo-op, except that it will convert all bytes into ATASCII screen codes before storing them. The ATASCII conversion is done before any constant is added with the '+' modifier.
Values in .SB statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.
.CB [+byte] bytes and/or ASCII
This is in the same format as the .BY pseudo-op, except that the last character on the line will be EOR'ed with $80.
Values in .CB statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.
.FL floating point numbers
Stores 6-byte BCD floating point numbers for use with the OS FP ROM routines.
Values in .FL statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.
.EN
The directive .EN is equivalent to the pseudo command END and ends assembly.
.ADR label
The directive .ADR returns the address of LABEL before the assembly address is changed (you
can put LABEL between parentheses or square brackets). For example:
org $2000
.proc tb,$1000
tmp lda #0
.endp
lda .adr tb.tmp ; = $2000
lda tb.tmp ; = $1000
.LEN label
The directive .LEN returns the length in bytes of a block declared with .PROC, .ARRAY, .LOCAL,
or .STRUCT. The label is the name of the .PROC, .ARRAY, .LOCAL, or .STRUCT block (the label
name can be within parentheses or square brackets):
label .array [255] .dword
.enda
dta a(.len label) ; = $400
.proc wait
lda:cmp:req 20
rts
.endp
dta .len wait ; = 7
.DEF label [= expression]
The directive .DEF is used to check for the presence of the LABEL definition or
to define it. If the label is defined it returns 1 or TRUE, otherwise
it returns 0 or FALSE. It is possible to put the name
LABEL between parentheses or square brackets:
ift .not(.def label)
.def label
eif
This unary operator tests whether the following label has been defined yet, returning TRUE or FALSE as appropriate.
.IFDEF label
The directive .IFDEF is equivalent to .IF .DEF LABEL.
Example:
.ifdef label
jsr proc1
.else
jsr proc2
.endif
.IFNDEF label
The directive .IFNDEF is equivalent to .IF .NOT .DEF LABEL.
Example:
.ifndef label
clc
.else
sec
.endif
In the following example, the .IFNDEF (.IF) block will be processed and the label defined
only when the block is first encountered. If there are any errors associated with their definition,
this will only be reported when a reference to one of them is attempted, resulting in the
error Undeclared label LABEL_NAME.
.ifndef label
.def label
lda #0 ; this will only be generated once and not be reassembled
temp = 100 ; label TEMP is defined only once during assembly
.endif
.NOWARN
The directive .NOWARN disables warning messages for the current line being assembled.
Example:
.nowarn .proc temp ; warning not generated: 'Unreferenced procedure TEMP'
.endp
.USING, [.USE]
The directive .USING (.USE) allows an additional scope to be specified for label lookup.
.USING (.USE) is valid in the current namespace.
Example:
.local move
tmp lda #0
hlp sta $a000
.local move2
tmp2 ldx #0
hlp2 stx $b000
.endl
.endl
.local main
.use move.move2
lda tmp2
.use move
lda tmp
.endl
.GET [index] 'filename'... [.BYTE, .WORD, .LONG, .DWORD]
This is equivalent to the pseudo command INS (similar syntax), except
that the file is loaded into memory instead of included in assembly. This directive
allows the specified file to be loaded into memory and for its bytes to be referred to
as a one-dimensional array.
Example:
.get 'file' ; load the file into a MADS array
.get [5] 'file' ; load the file into an array starting at index 5
.get 'file',0,3 ; load the file into an array of size 3
lda #.get[7] ; load the value of element 7 of the file array
adres = .get[2]+.get[3]<<8 ; use bytes 2 and 3 of the file array as an address
With the help of the directives .GET, .PUT a module for Theta Music Composer (TMC)
can be read and relocated. The MADS makro in the directory ../EXAMPLES/MSX/TMC_PLAYER/tmc_relocator.mac
accomplishes this.
The permitted range of values is INDEX = <0..65535>. The values read by .GET are of type BYTE.
.PUT [index] = value
The directive .PUT stores a value of type BYTE into a one-dimensional array in
MADS memory. This is the same array in which the directive .GET reads a file.
Example:
.put [5] = 12 ; store the value 12 into MADS memory at element 5
.SAV [index] ['filename',] length
The directive .SAV saves the buffer used by the directives .GET and .PUT
to an external file or inserts it into the current assembly output.
Example:
.sav ?length ; add elements [0..?length-1] to current assembly output
.sav [200] 256 ; add elements [200..200+256-1] to current assembly output
.sav [6] 'filename',32 ; save elements [6..6+32-1] to file FILENAME
The permitted range of values for INDEX = <0..65535>.
.OR, .AND, .XOR, .NOT
These directives are equivalent to the logical operators || (.OR), && (.AND), ^ (.XOR), ! (.NOT).
.LO (expression), .HI (expression)
These directives are equivalent to '<' (low byte) and '>' (high byte), respectively.
.IF, .ELSE, .ELSEIF, .ENDIF
.IF [IFT] expression
.ELSE [ELS]
.ELSEIF [ELI] expression
.ENDIF [EIF]
Example:
.IF .NOT .DEF label_name
label_name = 1
.ENDIF
.IF [.NOT .DEF label_name] .AND [.NOT .DEF label_name2]
label_name = 1
label_name2 = 2
.ENDIF
#IF type expression [.OR type expression] [.AND type expression]
#ELSE
#END
#WHILE type expression [.OR type expression] [.AND type expression]
#END
#CYCLE #N
#IF type expression [.OR type expression] [.AND type expression]
The #IF directive is somewhat equivalent to the IF statement of higher-level languages (C, Pascal).
#if .byte label>#10 .or .byte label<#5
#end
#if .byte label>#100
#else
#if .byte label<#200
#end
#end
#if .byte label>#100 .and .byte label<#200 .or .word lab=temp
#end
#if .byte @
#end
#WHILE type expression [.OR type expression] [.AND type expression]
The #WHILE directive is equivalent to the WHILE statement in higher-level languages (C, Pascal).
#while .byte label>#10 .or .byte label<#5
#end
#while .byte label>#100
#while .byte label2<#200
#end
#end
#while .byte label>#100 .and .byte label<#200 .or .word lab=temp
#end
#CYCLE #N
#cycle #17 ; pha 3 cycle
; pla 4 cycle
; pha 3 cycle
; pla 4 cycle
; cmp $00 3 cycle
---------
17 cycle
Assembling into zero page
org $00
lda tmp+1
tmp lda #$00
A two-pass assembler does not know the value of the TMP label and will assume that it is
16-bit (WORD type) and generate LDA abs.
org $00
lda.w tmp+1
tmp lda #$00
Three mnemonic extensions are allowed:
.b[.z]
.w[.a][.q]
.l[.t]
meaning BYTE, WORD, and LONG (TRIPLE). The last one generates a
24-bit value for 65816 long addressing (seldom used). For more information 6502 and 65816
CPU mnemonics, see Mnemonics.
dta {lda $00},$80 ; lda $80
Either will work with MADS, but the last pass will do the trick for us. :) The next problem is
putting this code on your computer. Loading directly into page zero will probably work if the
target area is within $80..$FF, but below that the OS is unlikely to survive.
org $20,$3080
lda tmp+1
tmp lda #$00
This assembles at address $0020, but with a load address of $3080. Of course, moving
the code to the correct address ($0020 in our example) is now the responsibility of the programmer.
org adres1,adres2
1
2 org $2000
3
4 FFFF> 2000-200D> A9 00 lda #0
5 2002 EA nop
6
7 0060 .local temp, $60
8
9 0060 BD FF FF lda $ffff,x
10 0063 BE FF FF ldx $ffff,y
11
12 .endl
13
14 2009 A5 60 lda temp
15 200B AD 03 20 lda .adr temp
16
In this example the block TEMP will assemble with the new address = $60
and be placed in memory at address $2003. After the block end directive (.ENDL, .ENDP, .END),
assembly will resume at the previous assembly address plus the length of the block,
which in this case is $2009.
ldy #0
copy mva .adr(temp),y temp,y+
cpy #.len temp
bne copy
For more information on the directives, see .ADR and .LEN.
TYPES
MADS allows the declaration of two types of data: structured (.STRUCT), and enumerated (.ENUM).
STRUCTURED TYPES, STRUCTURES
If you have programmed in C, you have probably already encountered structures. Overall, a MADS structure defines an array of virtual, one-dimensional fields of varying .BYTE, .WORD, .LONG, or .DWORD types or multiples thereof. They are virtual because they exist only in memory during assembly.The fields of the structure contain information about offsets from the beginning of the structure.
Structure declaration directives:
name .STRUCT .STRUCT name .ENDS [.SEND] [.END]
.STRUCT name x .word ; lda #name.x = 0 y .word ; lda #name.y = 2 z .long ; lda #name.z = 4 v .dword ; lda #name.v = 7 q :3 .byte ; lda #name.q = 11 .ENDS ; lda #name = 14 (length)Each line defines a field by name and type (.BYTE, .WORD, .LONG, or .DWORD). The field name may be preceded by whitespace. Between the .STRUCT and .ENDS directives, CPU mnemonics may not be used. Attempting to do so or having other invalid characters will result in a Improper syntax or Illegal instruction error.
In summary, the label name contains information about the total length of the structure (in bytes). The other labels describing the fields contain information about the offsets to each field from the beginning of the structure.
Structure declarations cannot be nested, but previously declared structures can be nested in other ones (declaration order does not matter). For example:.STRUCT temp x .word y .word v .byte z .word .ENDS .STRUCT test tmp temp .ENDS lda #temp.v lda #test.tmp.x lda #test.tmp.zWhat are structures useful for? Suppose you have a table of different types, where you can read each table field with a predetermined offset value. If a field is added to the table, or the table is modified in any other way, the program code will have to be updated to use new field offsets. Defining the table using a structure means that offsets can be determined by the structure definition, which are then automatically updated even if the structure of the table is changed.
Another example of structure usage can be found in the section on External symbols, Using external symbols with structures (.STRUCT).
Structured data can be defined by assigning a new label with the pseudo-command DTA and a specific structure name, or just with the structure name without the pseudo-command. The result is that the virtual structure definition is turned into actual reserved memory.
label DTA struct_name [count] (data1,data2,data3...) (data1,data2,data3...) ... label struct_name
COUNT specifies a number in the range <0..COUNT>, which defines the maximum element index in a one-dimensional array and thus the amount of memory reserved.
Examples of structure and structured data declarations:;-----------------------; ; structure declaration ; ;-----------------------; .STRUCT temp x .word y .word v .byte z .word .ENDS ;---------------; ; defining data ; ;---------------; data dta temp [12] (1,20,200,32000) (19,2,122,42700) data2 dta temp [0] data3 temp // shorter equivalent to DATA2The value in square brackets must be a value between <..2147483647>, which defines the maximum value of a one-dimensional array index and thus the amount of memory reserved for the structured data.
After the square brackets an optional list of initializer values (in parentheses) may follow. Otherwise, the field values default to zero. However, if the initializer list is shorter than the number of declared fields, the remaining fields are initialized to the previous value given for those fields:
data dta temp [12] (1,20,200,32000)Such a declaration will result in all fields being initialized to the values 1,20,200,32000, not just the first element data[0].
If the list of initializers is longer than the number of elements, the error Constant expression violates subrange bounds will result.
To refer to fields in the structured data, use its name, followed by an index in square brackets and the field name after a dot:lda data[4].y ldx #data[0].v
Forgetting the brackets with an index in the syntax label[index] results in the error Undeclared label.
ENUMERATED TYPES, ENUMERATION
These directives are used with enumerations:
name .ENUM .ENDE [.EEND] [.END] Example: .enum portb rom_off = $fe rom_on = $ff .ende .enum test a ; a=0 b ; b=1 c = 5 ; c=5 d ; d=6 .ende
Enumerations are declared using the directives .ENUM and .ENDE. The name of the enumeration is required and an error will be generated otherwise. Enumeration names may not be the same as mnemonics or pseudo-commands, which will produce a Reserved word error.
The label values are automatically assigned starting with a default value of 0 and incrementing by 1. You can define the value of each label directly or have them be automatically set.Enumerated labels are referenced using this syntax:
enum_name (field)or directly like with .LOCAL and .PROC blocks, with a dot between the enumeration name and the field name:
lda #portb(rom_off) dta portb.rom_on, portb.rom_offEnumerations can be used for field declarations in structures (.STRUCT) and variable declarations (.VAR):
bank portb // allocate variable BANK of size 1 byte .var bank portb // allocate variable BANK of size 1 byte .struct test a portb b portb .endsThe size of an enumeration is dependent upon the maximum value of its labels:
.enum EState DONE, DIRECTORY_SEARCH=$ff, INIT_LOADING, LOADING .endeIn this example, the enumeration "EState" will have a size of two bytes (WORD).
The size of an enumeration can be checked with the .LEN directive (equivalent to SIZEOF), where the result will be a value in the range 1..4 (1=BYTE, 2=WORD, 3=LONG, 4=DWORD):
.print .len EState
ARRAYS
name .ARRAY index type [= default_value] .ARRAY name count type [= default_value] .ENDA [.AEND] [.END]Available types are .BYTE, .WORD, .LONG, and .DWORD.
INDEX specifies the maximum permitted value of the array index range [0..INDEX]. This value can be a constant or an expression in the range <0..65535>. If INDEX is omitted, the range is determined by the number of input values.
CPU mnemonics cannot be used between .ARRAY and .ENDA, and attempting to do so or having other illegal characters will result in the error Improper syntax. The array index used for initialization can be specified along with initializer values. A new array index is set by placing it in square brackets at the beginning of a new line, i.e. [expression]. Additional indices can be supplied, separated by a colon (':'). The array values then follow after a equals sign ('='):.array tab .byte ; define array TAB with an unspecified number of elements 1,3 ; [0]=1, [1]=3 5 ; [2]=5 [12] = 1 ; [12]=1 [3]:[7]:[11] = 9,11 ; [3]=9, [4]=11, [7]=9, [8]=11, [11]=9, [12]=11 .enda
This facility may seem strange and of limitd use, but it is occasionally useful, such as for declaring lookup tables for translating the scan code of a pressed key or between ATASCII and INTERNAL code.
.array TAB [255] .byte = $ff ; allocate 256 bytes [0..255] with initial value $FF [63]:[127] = "A" ; assign new values TAB[63]="A", TAB[127]="A" [21]:[85] = "B" [18]:[82] = "C" [58]:[122] = "D" [42]:[106] = "E" [56]:[120] = "F" [61]:[125] = "G" [57]:[121] = "H" [13]:[77] = "I" [1] :[65] = "J" [5] :[69] = "K" [0] :[64] = "L" [37]:[101] = "M" [35]:[99] = "N" [8] :[72] = "O" [10]:[74] = "P" [47]:[111] = "Q" [40]:[104] = "R" [62]:[126] = "S" [45]:[109] = "T" [11]:[75] = "U" [16]:[80] = "V" [46]:[110] = "W" [22]:[86] = "X" [43]:[107] = "Y" [23]:[87] = "Z" [33]:[97] = " " [52]:[180] = $7e [12]:[76] = $9b .enda
In this example, an array TAB is created with indices [0..255] and 256 .BYTEs in size, pre-initialized to $FF. The array elements are then set to translate keyboard scan codes (both upper and lowercase, ignoring case) to INTERNAL.
The colon (':') is used to separate array indices. Another example is to center a string:org $bc40 .array txt 39 .byte [17] = "ATARI" .enda
In summary, the .ARRAY directive allows creation of a one-dimensional array of values with a specified type.
To refer to an array:lda tab,y lda tab[23],x ldx tab[200]If the index given in square brackets exceeds the maximum index of the array, the error message Constant expression violates subrange bounds is reported.
MACROS
Macros help perform repetitive tasks by automating them. They are only kept in memory during assembly unless invoked. With their aid, MADS can push and pop parameters off a software stack for a procedure declared with the directive .PROC and switch extended memory banks with the option BANK SENSITIVE (OPT B+).
Macro pseudo-commands and directives:
name .MACRO [arg1, arg2 ...] ['separator'] ["separator"] .MACRO name [(arg1, arg2 ...)] ['separator'] ["separator"] .EXITM [.EXIT] .ENDM [.MEND] :[%%]parameter :[%%]label
.macro SetColor val,reg lda :val sta :reg .endmAt the end of the macro declaration, the argument separator and the argument parsing mode can be supplied (unchanged for single quotes, split into parameters and addressing modes for double quotes).
The default separators for macro argument parsing are a comma (',') and a space (' ').
test #12 200 <30 test .macro " " .endmThis TEST macro is declared with a space as the separator using ", which then causes the macro parameter to be divided into two parts, addressing mode and argument.
#12 -> addressing mode '#' argument 12 200 -> addressing mode ' ' argument 200 <30 -> addressing mode '#' argument 0 (calculated expression value of "<30") test '#' 12 ' ' 200 '#' 0NOTE #1: The sign operators '<', '>' are evaluated before parameters are passed to a macro, with the result substituted as the parameter.
NOTE #2: If the macro parameter is the loop counter '#' or '.R' (!!! the single character '#' or the directive '.R', and not an expression involving one of them !!!) the value of the loop counter is substituted as the macro parameter.
This property can be used to create iterated label names like "label0", "label1", "label2", "label3"... ::32 find # find .macro ift .def label:1 dta a(label:1) eif .endmIn this example, the address of each numbered label is written (if it is defined).
.EXITM [.EXIT]
End the macro. This terminates the macro invocation.
.ENDM [.MEND]
The directive .ENDM or .MEND ends the current macro definition. The .END directive cannot
be used as for the .LOCAL, .PROC, .ARRAY, .STRUCT, and .REPT directives.
:$2 nop :+2 nop :%10 nopParameter :0 (%%0) has special meaning and contains the number of parameters passed. This can be used to check if the required number of parameters was passed to a macro:
.IF :0<2 || :0>5 .ERROR "Wrong number of arguments" .ENDIF IFT %%0<2 .or :0>5 ERT "Wrong number of arguments" EIF
Example macro:
.macro load_word lda <:1 sta :2 lda >:1 sta :2+1 .endm test ne test eq .macro test b%%1 skip .endm
Macros are called by name, with arguments separated by default with commas (',') or spaces (' ').
The maximum number of parameters is limited only by PC memory. If the number of parameters passed
is fewer than the number of parameters in the macro, the missing parameters are set to the value
-1 ($FFFFFFFF). This property can be used to test if a parameter has been passed,
but it is easier to use parameter zero (%%0).
macro_name [Par1, Par2, Par3, 'Par4', "string1", "string2" ...]
A parameter can be a value or a string delimited by either single quotes ('') or double quotes. ("").
All label definitions within a macro are local.
If the assembler does not find a label within the macro, it will then look in the local scope (if there was a .LOCAL directive), then in the procedure (if a procedure is currently being defined), and then finally in the main program.Examples of macro calls:
macro_name 'a',a,>$a000,cmp ; the default separator ',' macro_name 'a'_a_>$a000_cmp ; the declared separator '_' macro_name 'a' a >$a000 cmp ; the default separator ' 'Macros can be called from other macros as well as called recursively. In the latter case, care should be taken to avoid causing stack overflow in MADS. MADS is protected against infinite recursion and will stop with an error once the nesting depth reaches 4095 (with Infinite recursion).
An example of a macro will overflow the MADS stack:
jump .macro jump .endmExample program that passes parameters to pseudo-procedures, from ..\EXAMPLES\MACRO.ASM:
org $2000 proc PutChar,'a'-64 ; call macro PROC, with as parameters proc PutChar,'a'-64 ; the name of a procedure to call by JSR proc PutChar,'r'-64 ; and a single argument (INTERNAL character code) proc PutChar,'e'-64 proc PutChar,'a'-64 proc Kolor,$23 ; call another procedure to change the background color ;--- loop jmp loop ; endless loop to show the effect ;--- proc .macro ; declare PROC macro push =:1,:2,:3,:4 ; call PUSH macro to push arguments onto the satck ; =:1 calculates the memory bank jsr :1 ; jump to procedure (procedure name is the first parameter) lmb #0 ; Load Memory Bank, setting bank to 0 .endm ; end of PROC macro ;--- push .macro ; declare PUSH macro lmb #:1 ; set up virtual memory bank .if :2<=$FFFF ; if passed argument is less than or equal to $FFFF lda <:2 ; put it on the stack sta stack lda >:2 sta stack+1 .endif .if :3<=$FFFF lda <:3 sta stack+2 lda >:3 sta stack+3 .endif .if :4<=$FFFF lda <:4 sta stack+4 lda >:4 sta stack+5 .endif .endm * ------------ * ; KOLOR procedure * PROC Kolor * * ------------ * lmb #1 ; set virtual bank to 1 ; label definitions are now local to this bank stack org *+256 ; stack for KOLOR procedure color equ stack Kolor ; code for KOLOR procedure lda color sta 712 rts * -------------- * ; PUTCHAR procedure * PROC PutChar * * -------------- * lmb #2 ; set virtual bank to 1 2 ; label definitions are now local to this bank stack org *+256 ; stack for PUTCHAR procedure char equ stack PutChar ; code for PUTCHAR procedure lda char sta $bc40 scr equ *-2 inc scr rtsOf course, this example uses a software stack, whereas with the 65816 the hardware stack could be used instead. Because the defined variables are each local to a particular bank, procedure calls can be created with similar structure and function to those of higher-level languages.
However, it is simpler and more efficient to use the procedures allowed by MADS, which are declared with .PROC. For more information on procedure declaration and operations, see Procedures.
PROCEDURES (.PROC)
MADS adds the ability to use procedures with parameters. This feature resembles the familiar mechanisms of high-level languages and is just as easy for a programmer to use.The built-in MADS macros (@CALL.MAC, @PULL.MAC, @EXIT.MAC) provide a software stack of 256 bytes, the same size as the hardware stack, a way to pop from the software stack, and to save and restore parameters when calling other procedures. MADS supports recursive procedure calls.
The programmer is not involved in this mechanism and can focus on his program, only needing to define the appopriate labels and include the needed macros when assembling the program.
Also, the software stack can be omitted and arguments passed using a more classical method with CPU registers (.REG directive) or with variables (.VAR directive).Another feature of .PROC procedures is that it is possible to omit them during assembly if they are not referenced. This produces the warning message Unreferenced procedure ????. They can be removed during assembly by specifying the -x 'Exclude unreferenced procedures' command-line parameter to MADS.
All labels defined in a .PROC procedure are local but can also be accessed globally, which is uncommon in other programming languages. It is possible to define a global label from Within a .PROC procedure (see Global labels). To access labels within a procedure from outside of it, address it using a dot ('.'):lda test.pole .proc test pole nop .endpIf a referenced label is not found within a .PROC procedure, MADS will then look for in enclosing scopes until the global scope is reached. To directly address a global label from within a .PROC procedure (or any other scope) prefix the label name with a colon (':'). For procedures that use a software stack, MADS requires three specific globally defined labels (the stack location, the stack pointer, and the address of the procedure variables):
If these labels are undefined and a ..PROC procedure with a software stack is used, MADS assumes the following default values: @PROC_VARS_ADR = $0500, @STACK_ADDRESS = $0600, and @STACK_POINTER = $FE.
For procedures using a software stack, MADS also requires the declaration of macros with specific names. Declarations of these macros are included with MADS in the following files:Procedure declaration directives:
name .PROC [(.TYPE PAR1 .TYPE PAR2 ...)] [.REG] [.VAR] .PROC name [,address] [(.TYPE PAR1 .TYPE PAR2 ...)] [.REG] [.VAR] .ENDP [.PEND] [.END]
name .PROC ( .WORD par1 .BYTE par2 ) name .PROC ( .BYTE par1,par2 .LONG par3 ) name .PROC ( .DWORD p1,p2,p3,p4,p5,p6,p7,p8 )
Additionally, by using the .REG or .VAR directives, parameters can be passed to MADS procedures by CPU registers (.REG) or variables (.VAR). The directive specifying the parameter passing convention is placed at the end of the .PROC procedure declaration.
Example procedure declarations using CPU registers:name .PROC ( .BYTE x,y,a ) .REG name .PROC ( .WORD xa .BYTE y ) .REG name .PROC ( .LONG axy ) .REG
The .REG directive requirse that the parameter names are made up of the letters 'A', 'X', 'Y', or a combination thereof. These refer to the names of the CPU registers and affect the order in which they are used. The number of parameters passed is limited by the number of CPU registers, so at most three bytes can be passed to the procedure. The advantage of this method is speed and lower memory usage.
Example procedure declarations using variables:name .PROC ( .BYTE x1,x2,y1,y2 ) .VAR name .PROC ( .WORD inputPointer, outputPointer ) .VAR name .PROC ( .WORD src+1, dst+1 ) .VARWith .VAR, the parameter names indicate the variable names which will be loaded with the passed parameters. This method is slower than .REG but still faster than software stack methods.
Procedures are exited in the usual way, using the RTS command. Adding the RTS instruction at the end of each code path is the responsibility of the programmer, not the assembler.
As with a .LOCAL block, a new address can be specified for assembling a .PROC block:.PROC label,$8000 .ENDP .PROC label2,$a000 (.word ax) .reg .ENDPFor procedures that use the software stack, MADS invokes the macro @EXIT at the end of the procedure, whose task is to modify the software stack pointer @STACK_POINTER. This is necessary for proper software stack operation. Users can write their own @EXIT macro, or use the one included with MADS (file ..\EXAMPLES\MACROS\@EXIT.MAC), which currently looks like this:
.macro @EXIT ift :1<>0 ift :1=1 dec @stack_pointer eli :1=2 dec @stack_pointer dec @stack_pointer els pha lda @stack_pointer sub #:1 sta @stack_pointer pla eif eif .endm
The macro @EXIT should not alter the contents of CPU registers if you want to return a result from a .PROC procedure using CPU registers.
A procedure is called by name (the same as for a macro), followed by any provided parameters, separated by a commas (',') or spaces (' '). It is not possible to use other separators.
If the type of a parameter is different than the type used in the procedure declaration, an Incompatible types error is reported.If the number of parameters passed is different than the number of parameters in the procedure declaration, the result is an Improper number of actual parameters error. The exception is procedures which use parameters passed by CPU register (.REG) or variable (.VAR), in which case the extra parameters are assumed to be already loaded into the correct registers or variables.
There are three ways to pass parameters:
Example procedure calls:
name @ , #$166 , $A400 ; with the software stack name , @ , #$3f ; with .REG or .VAR name "(hlp),y" "tab,y" ; with .VAR or the stoftware stack (the software stack uses the X register)
When MADS encounters a procedure call that uses the software stack, it executes the macro @CALL. However, if the procedure does not use the software stack, a plain JSR PROCEDURE is used instead of the @CALL macro.
To the @CALL macro, MADS passes parameters computed based on the procedure declaration, breaking each parameter into three components: addressing mode, parameter type, and parameter value.@CALL_INIT 3\ @PUSH_INIT 3\ @CALL '@','B',0\ @CALL '#','W',358\ @CALL ' ',W,"$A400"\ @CALL_END PROC_NAMEHere, the @CALL macro pushes the contents of the accumulator, then the value $166 (358 dec), then the value at the address $A400. For more information on how parameters are passed to the macro (and the importance of '' and ""), see Calling macros.
Parameters passed using the accumulator ('@') should always be the first parameter passed to the procedure. If it is used elsewhere, the accumulator is already modiied by that point (this restriction is imposed by the @CALL macro). Of course, this can be lifted with a custom version of the @CALL macro. With procedures that use .REG or .VAR, a '@' parameter can be used in any position.
The end of a .PROC procedure is marked by an RTS. After the procedure call, MADS invokes the @EXIT macro to modify the @STACK_POINTER, which is necessary for proper operation of the software stack. The number of bytes passed to the procedure is passed as a parameter to the macro, which then subtracts that number of bytes from the software stack.(Editor's note: This is known as a caller-pops convention, since the caller both pushes and pops the parameters off the stack, and is common in C implementations.)
Adding an RTS instruction at the end of every code path in the procedure is the responsibility of the programmer, and not the assembler.
@stack_address equ $400 @stack_pointer equ $ff @proc_vars_adr equ $80 name .PROC (.WORD par1,par2) lda par1 clc adc par2 sta par1 lda par1+1 adc par2+1 sta par1+1 .endp icl '@call.mac' icl '@pull.mac' icl '@exit.mac'At the time of declaration, MADS automatically defines these parameters by assigning values based on @PROC_VARS_ADR. In the preceding example, MADS will define the parameters as PAR1 = @PROC_VARS_ADR and PAR2 = @PROC_VARS_ADR + 2.
The programmer uses these parameters by the names given in the procedure declaration, similarly as with higher-level languages. In MADS, it is possible to access procedure parameters externally, which is unusual in higher-level languages. For instance, PAR1 can be read as follows:
lda name.par1 sta $a000 lda name.par1+1 sta $a000+1This copies the two bytes at PAR1 to addresses $A000 and $A000+1. Of course, this can only be done after completion of this particular procedure. Remember that each procedure has parameters stored at the same area addressed by @PROC_VARS_ADR, so with each new procedure call the parameter area at <@PROC_VARS_ADR .. @PROC_VARS_ADR + $FF> changes.
If a procedure is declared using .REG type parameters, the programmer should remember to preserve or use parameters before they are modified by the procedure code. With .VAR type parameters, this is not an issue because the parameters are saved in specific memory locations where they can always be read.
LOCAL AREA
The main purpose of a local area in MADS is to create a new scope for labels. All labels defined in a .LOCAL area are local. However, you can still refer to a local label globally, which is uncommon in other programming languages. It is possible to define a global label within a .LOCAL area (see Global labels). If the assembler cannot find a label within a .LOCAL area, MADS will then look in the global scope. To refer to global labels directly from a .LOCAL area, prefix the label name with a colon (':').Local scope directives:
[name] .LOCAL [,address] .LOCAL [name] [,address] .ENDL [.LEND] [.END]
Declares a local scope named name with the directive .LOCAL. The local scope name is not required and can be omitted. Local scope names cannot be the same as mnemonics and pseudo-commands, and attempting to use a reserved name will result in a Reserved word error.
After the name of the local scope (or the .LOCAL directive), a new assembly address can be provided. After the end of the block (.ENDL), the assembly address reverts to the previous address plus the length of the block.
label .local,$4000 .endl .local label2,$8000 .endl .local .endl .local label3 .endlAll definitions within a .LOCAL block are local. To refer to a global label with the same name as a local name, prefix the name with a colon (':'):
lab equ 1 .local lab equ 2 lda #lab ldx #:lab .endlIn this example, the A register is set to 2, while the X register is set to 1.
If the assembler cannot find a label in a .LOCAL scope, it will then check an enclosing macro (if one is being processed), then the enclosing procedure (if there is one), and finally the main program at global scope.
Within a local scope, all label definitions are qualified by the local scope's name. To reach labels defined in another local scope, both the name of the local scope and the label must be supplied:lda #name.lab1 ldx #name.lab2 .local name lab1 = 1 lab2 = 2 .endl
A dot ('.') is used to address labels within a .LOCAL block.
Local scopes can be nested, as well as placed within procedures declared using the .PROC directive. Local scopes are cumulative, i.e. there may be multiple scopes of the same name and all symbols in those scopes will belong to a common namespace.
The .ENDL directive ends a local scope.
Local scope declaration example:org $2000 tmp ldx #0 <------------- label in global scope | lda obszar.pole <--- | reference to local scope | | .local obszar | | local scope declaration | | lda tmp <--- | | | | | lda :tmp | | <--- reference to global scope | | tmp nop <--- | definition in local scope | pole lda #0 <--- <--- definition in local scope | lda pole <----------------- reference within local scope .endl end of local scope
SYNTAX
MADS accepts the same syntax as QA and XASM. However, it is stricter with comments placed at the end of a line (comments should be preceded the appropriate symbol) and more lenient with whitespace and CPU instruction mnemonics without arguments, i.e.:asl -> asl @ lda # -> lda #0MADS will accept no whitespace separating the mnemonic and operand, provided that the operand does not start with a '@', which is used in the names of labels, or '%' and ':', which are used to denote numbered macro parameters (%%number, :number), e.g.:
lda$44 lda# lda(80),y
* this is a comment ; this is a comment lda #0 ; this is a comment dta 1 , 3 * BAD COMMENT, WILL BE MISINTERPRETED org $2000 + 1 BAD COMMENT, WILL BE MISINTERPRETED nop // this is a comment // this is a comment dta 1,2, /* comment */ 3,4 lda /* comment */ #0 /* ... this is a multi-line comment ... */ /************************************ this is also a multi-line comment *************************************/
The multi-line comment signs '/* */' and the end-line comment sign '//' can be used without restrictions.
Multiple lines can be spliced into a single line using '\', e.g.:
lda 20\ cmp 20\ beq *-2 lda 20 \ cmp 20 \ beq *-2 lda #0 \lop sta $a000,y \ iny \ bne lop ; comments only at the end of this line
If there is no space after the '\' character, a mnemonic or other string can be interpreted as a label, so make sure to treat '\' as the beginning of a new line.
MADS stops processing the line once either at the end of the chain or when a comment is reached, so comments can be placed only at the end of the line.
WARNING!!! Putting a '\' at the end of a line tells MADS to splice the current line with the next line. For example:lda\ #\ 12In this case, we get the result 'LDA #12'.
XASM already provided the ability to combine two mnemonics using a ':'.
In MADS, this is extended to combine any number of MADS-known mnemonics:
lda:cmp:req 20
lda:iny:sta:iny $600,y
(Editor's note: All mnemonics in a combined statement are processed with the same argument. This is fine when some of the instructions only have implied addressing. However, it can lead to accidents with instructions that have both implied and memory forms. For instance, "STA:INC:RNE pixels,X+" will assemble as STA pixels,X / INX / INC pixels,X / INX / BNE *.)
EXPRESSIONS
The term expression means a series of operators and operands (arguments) that specifies the order of operations, i.e. type and order of evaluation. A compound expression is an expression with two or more operators. Operators that affect only one operand are called unary, and operators with two arguments are called binary. Expressions are evaluated in the order determined by priority of each operator in the direction specified by the precedence of each operator.MADS accepts numbers in decimal, hexadecimal, binary, ATASCII, and INTERNAL format.
-100 -2437325 1743
$100 $e430 $000001 0x12 0xa000 0xaabbccdd
%0001001010 %000000001 %001000
'a' 'fds' 'W'*
"B" "FDSFSD" "."*Only the first charcater of ATASCII or INTERNAL coding is significant. A '*' after the closing quote specifies inverse characters. Additionally, there are two possible operations '+' and '-' for strings, which increase or decrease the values of the quoted characters.
"FDttrteSFSD"-12 'FDSFdsldksla'+2
Binary operators: + Addition - Subtraction * Multiplication / Division % Remainder & Bitwise and | Bitwise or ^ Bitwise xor << Arithmetic shift left >> Arithmetic shift right = Equal == Equal (same as =) <> Not equal != Not equal (same as <>) < Less than > Greater than <= Less or equal >= Greater or equal && Logical and || Logical or Unary operators: + Plus (does nothing) - Minus (changes sign) ~ Bitwise not (complements all bits) ! Logical not (changes true to false and vice versa) < Low (extracts low byte) > High (extracts high byte) ^ High 24bit (extracts high byte) = Extracts memory bank : Extracts global variable value Operator precedence: first [] (brackets) + - ~ < > (unary) * / % & << >> (binary) + - | ^ (binary) = == <> != < > <= >= (binary) ! (unary) && (binary) last || (binary)
LABELS
Labels may be defined in the program with local or global scope, depending on the location where they are defined. Additionally, labels may be defined as temporary, also with local or global scope.
Examples of label definitions:
?name EQU $A000 ; defining a temporary global label name = * ; defining a global label name2=12 ; defining a global label @?name EQU 'a'+32 ; defining a global label name: equ 12 ; defining a non-global label that does not start at the beginning of the line name: = 'v' ; defining a non-global label that does not start at the beginning of the line
Unlike QA/XASM, a question mark ('?') and at-sign ('@') can be used in label names.
Using a dot ('.') in a label name is allowed but not recommended. The dot is reserved for extended mnemonics, assembler directives, and addressing new MADS structures.A dot ('.') at the beginning of a label name suggests that it is an assembler directive, and a question mark ('?') at the beginning signifies a temporary label that can be changed several times during assembly.
To ensure clarity when using anonymous labels, their use is limited only to jumps no more than 10 instances away.
The character '@' is reserved for anonymous labels, and there must be a sign
character indicating either forward direction ('+') or backwards direction ('-').
Additionally, you can specify the number of anonymous labels in the range [1..9].
Labels defined within a macro (.MACRO), procedure (.PROC),
or local scope (.LOCAL) are local labels by default and do not
need additional markup.
Each definition outside of a macro (.MACRO), procedure (.PROC),
or local scope (.LOCAL) is global.
The .DEF directive normally defines local labels, but the ':' at the beginning
of the label indicates a global label instead. Using the syntax .DEF :label
allows definition of global labels within a local scope.
@+[1..9] ; forward
@-[1..9] ; backward
@ dex ---- -------
bne @+ | -- |
stx $80 | | |
@ lda #0 | -- |
bne @- --- |
bne @-1 ---------
Local labels
EQU
=
To access global labels, i.e. those defined outside of a macro (.MACRO),
procedure (.PROC), or local scope (.LOCAL), use the
':' operator:
lp ldx #0 ; define global label LP
test
test
test .macro
lda :lp ; the ':' prefix causes reference to the global label LP
sta lp+1 ; reference to the local label LP in the macro
lp lda #0 ; definition of local label LP in the macro
.endm
In this example, there are two definitions of labels with the same name (LP),
but each has a different value and a different scope.
Global labels
EQU
=
Or with .DEF directive syntax:
.DEF :label [= expression]
lab equ * lab2 equ $4000 ?tmp = 0 ?tmp += 40 .proc name .def :?nazwa = $A000 .def :nazwa=20 .local lok1 .def :@?nazw = 'a'+32 .endl .endpExamples of defining a global temporary labels include the macro @CALL (in ..\EXAMPLES\MACROS\@CALL.MAC), which defines the temporary label ?@STACK_OFFSET. This is later used by other macros called called by the macro @CALL, and is used to optimize access to parameters on the stack.
@CALL .macro .def ?@stack_offset = 0 ; definition of temporary global lavel ?@stack_offset ... ... @CALL_@ .macro sta @stack_address+?@stack_offset,x .def ?@stack_offset = ?@stack_offset + 1 ; modify ?@stack_offset label .endm
A temporary label has the property that its value can be changed many times during assembly. Normally, attempting to redefine a label results in a Label declared twice error, which does not occur with a temporary label.
The lifetime of a temporary label depends on the scope in which it is defined. Temporary labels may have local scope (Local labels) or global scope (Global labels).
A temporary label is created by placing a '?' at the beginning:?labelTemporary labels should not be used to name procedures (.PROC), macros (.MACRO), local scopes (.LOCAL), structures (.STRUCT), or arrays (.ARRAY). A temporary label is defined using one of the following equivalent pseudo commands:
EQU =Additionally, they can be modified using recognizable C operators:
-= expression += expression -- ++These assignment operators only work with temporary labels, and attempting to use them for other types of labels results in a Improper syntax error message. Examples of using temporary labels:
?loc = $567 ?loc2 = ?loc+$2000 lda ?loc sta ?loc2 ?loc = $123 lda ?loc
Option OPT ?+ tells MADS that labels starting with '?' should be interpreted as local labels as MAE does. By default, labels starting with '?' are interpreted by MADS as temporary labels (Temporary labels).
An example of using MAE-style temporary labels:opt ?+ org $2000 local1 ldx #7 ?lop sta $a000,x dex bpl ?lop local2 ldx #7 ?lop sta $b000,x dex bpl ?lop
SPARTADOS X
Reproduced from Serious Magazine, author Qcyk/Dial.
A file itself is only a collection of bytes, lots of numbers that can mean both everything and nothing at the same time if you do not know how to interpret them. For this reason, most files are equipped with a variety of headers which store information about what the file contains and are necessary to read them. This includes binary executables loaded under DOS. After all, DOS is a program and like everyone else has a right to expect a certain structure in its data.
Traditional binary files, recognized by all DOSes for Atari XL/XEs, are built of blocks where each block has its own header. There are two types of headers:
1. dta a($ffff),a(str_adr),a(end_adr) 2. dta a(str_adr),a(end_adr)
str_adr - address at which the first byte of data will be loaded
end_adr - address at which the last byte of data will be loaded
The first block in the file header must be $ffff, followed by other blocks. Each block header should of course be followed by this amount of data:
(end_adr-str_adr)+1
That's enough of a refresher. The developers of SpartaDOS X have kept the above standard, while adding several new types of headers. The file is still divided into blocks, except that now there are a lot more types of blocks. Here they are:
dta a($fffa),a(str_adr),a(end_adr)
This is the same as a $ffff block - it does not matter which you use. However, $fffa will clearly indicate that the program is designed for SDX - another DOS cannot read the file.
dta a($fffe),b(blk_num),b(blk_id) dta a(blk_off),a(blk_len)
blk_num - block number in the file. Each relocatable block should have its own number. Because the load addresses of blocks are not known, blocks are just identified by number. They may be in the range 0-7, except that in practice they are usually numbered from 1 upwards.
blk_id - bits 1-5 are the memory type, indicating where the block is to be loaded. I have encountered two values:
$00 - main memory $02 - extended memoryAlso, bit 7 indicates no data block if set. In this case, SDX loads nothing, but still reserves memory.
blk_off - block base address, which is simply the address at which the code was assembled. Thsi is necessary when relocating addresses referring to the contents of the block.
blk_len - the length of the block. There should be as much data following as indicated by the header, unless the block is only a reserved area in which case there is no data.
When writing relocatable code, several limitations imposed by the idea of "relocatable code" must be kept in mind. All addresses referring to program areas must be updated during load, so sequences such as this cannot be used:lda <something ldx >something ... something equ * ... Instead, use something like this, for example: lda _something ldx _something+1 ... _something dta a(something) ... something equ *
dta a($fffd),b(blk_num),a(blk_len)
blk_num - number of block containing the reference targets
blk_len - update block length (without the header). This is ignored.
Addresses are updated by adding the difference between the address at which the relocatable block was loaded and the value of blk_off (where the block was originally assembled). This can be illustrated as follows:
ADR=ADR+(blk_adr-blk_off)
The payload of an update block contains pointers to addresses and special commands. Numbers between $00-$fb are offsets from the last updated location. This location is stored as an address in an update pointer. This pointer can be updated through special functions invoked by values greater than $fb:
$fc marks the end of the update block,
$fd,a(ADDR) directly updates the address ADDR. Thus, the update pointer is set to ADDR, which is used as the base for the next offset,
$fe,b(blk_num) sets the update pointer to the base address of the block specified by blk_num, which is then used for the next offset,
$ff increases the update pointer by $fa (without updating an address).
dta a($fffb),c'SMB_NAME',a(blk_len)
SMB_NAME - procedure symbol name (or array, system registry, etc.) Eight characters in ATASCII code,
blk_len - as in a $fffd block.
After the header, there is a sequence of offsets to locations of addresses to update - the same as in a $fffd block. Addresses are updated by adding the address of the procedure denoted by the symbol to the existing address. This allows use in programs of procedures whose addresses are unknown, such as procedures added by other applications running in the SDX environment. Also, system procedures must be used this way, as they have different addresses in different versions of Sparta.
dta a($fffc),b(blk_num),a(smb_off) dta c'SMB_NAME'
blk_num - number of the block in which the procedure is defined. This means that the procedure must be defined in a relocatable block.
smb_off - procedure offset in the block, which is an offset from the start of the block (the first byte is 0) plus the value of blk_off of the block. Basically, the address at which the procedure was assembled.
SMB_NAME - symbol name being defined for the procedure.
Block types $fffb, $fffc, and $fffd are not kept in memory. The system uses them only during program load.
The syntax for handling SpartaDOS X programs, was taken from FastAssemblera by the author Marka Goderskiego. Below is a quote from the manual that came with FA. MADS can now assemble source files in *.FAS format without any major problems. Relocatable commands always have two-byte arguments; it is impossible to relocate 3-byte arguments (65816).
The most important innovation in SDX is the ability for developers to write relocatable code. Since the MOS 6502 does not have relative addressing (except for short branches), the ICD developers created a way to do so using special program blocks. The process is based on loading blocks and then updating addresses within the blcok using special update blocks. It is enough to add the value of memlo to correct addresses, but what addresses to correct, and which ones to leave? That's the point of a special block that contains (specially coded) offsets to those addresses. Therefore, an UPDATE ADDRESS operation must be applied to each RELOC block before running the program. UPDATE ADDRESS must also be performed on any block which refers to SPARTA commands or vectors.
Another innovation is the introduction of symbols. Some of the SDX service procedures are defined by name! These names always have 8 letters (like filenames). Instead of using arrays of vectors or jumps (like in the OS), use symbols defined with SMB. After loading a block or blocks, SDX loads symbol blocks and updates symbol addresses the same way as with relocatable addresses in the program. Symbols can refer to items in either RELOC or SPARTA blocks.
Programmers can define custom symbols to replace the ones in SDX or completely new ones for use by other programs. This is done by the UPDATE NEW block. It should be kept in mind that new symbols must be stored in a RELOC block.
The number of RELOC and EMPTY blocks is limited to 7 by SDX.
Such blocks can be combined into chains:
blk sparta $600 ... blk reloc main ... blk empty $100 main ... blk reloc extended ... blk empty $200 extendedThis means that commands for these blocks can refer to all blocks in the chain.
The chain is not interrupted by updating addreses or symbols, but is ended by the definition of a new symbol and by other block types, e.g. DOS.
Note: The chain only makes sense if all blocks are loaded into the same memory, or when a program switches to the appropriate memory references.
Note: Commands and vectors in RELOC and EMPTY blocks should not refer to SPARTA blocks! This may cause an error when the user loads the program using the LOAD command and uses it after a long time. While RELOC and EMPTY are safe, you never know what is in memory where a SPARTA block was last loaded!
Equally dangerous is the references to RELOC and EMPTY blocks by SPARTA blocks (for the same reason as above), but during the installation of overlays (*.sys) using INSTALL this is sometimes necessary and therefore acceptable. You can also invoke a SPARTA block (through $2E2) to run immediate, then discard it.
Note: Addresses can collide between SPARTA blocks and RELOC/EMPTY addresses! FA recognizes references to other blocks by address, assuming a PC for RELOC and EMPTY blocks of $1000, so for mixed programs SPARTA blocks should be below $1000 (e.g. $600) or above the last relocatable block, $4000 using being enough. This error is not detected by the compiler!
RELOCATABLE CODE
Relocatable code is code without a fixed address, such that when loaded into a computer it has to work regardless of the load address. SpartaDOS X (SDX) provides a relocation facility for Atari XL/XE code, of which more than be read in the section Programming SpartaDOS X.A basic limitation of SDX relocatable code is that only WORD addresses are relocated and there is no support for 65816 code. MADS provides the ability to generate code both in SDX format and in a non-SDX format that removes the aforementioned limitations.
The MADS relocatable code format is similar to that of SDX, as there are main blocks and then blocks with additional address relocation information. MADS uses a simpler update block format, without the "compression" used by SDX.
Advantages of MADS relocatable code:
Limitations of MADS relocatable code:
A MADS relocatable block is created using the directive:
.RELOC [.BYTE|.WORD]
A block for updating a relocatable MADS block is created using the BLK pseudo-command:
BLK UPDATE ADDRESS
After the .RELOC directive, it is possible to specify a relocation block type (.BYTE, .WORD),
with the default being .WORD type. Type .BYTE is for blocks that must be placed
in zero page (instructions will use zero page), which MADS will assemble to address $0000.
Type .WORD means that MADS will assemble the block starting at address $0100 and that it
can be placed anywhere in memory (except for zero page).
The .RELOC block produces a header like the famous DOS header, further expanded by 10 bytes to a total of 16 bytes:
HEADER .WORD = $FFFF START_ADDRESS .WORD = $0000 END_ADDRESS .WORD = FILE_LENGTH-1 MADS_RELOC_HEADER .WORD = $524D UNUSED .BYTE = $00 CONFIG .BYTE (bit0) @STACK_POINTER .WORD @STACK_ADDRESS .WORD @PROC_VARS_ADR .WORD
MADS_RELOC_HEADER | always $524D, corresponding to the characters 'MR' (M-ADS R-ELOC) |
FILE_LENGTH | the length of the relocatable file without the 16-byte header |
CONFIG | currently only bit 0 is used, where bit0=0 means relocatable blocks are assembled starting at $0000, and bit0=1 means blocks are assembled at $0100. |
The last 6 bytes contain information about the values of labels needed for the operation of the software stack, @STACK_POINTER, @STACK_ADDRESS, and @PROC_VARS_ADR, if used in relocatable blocks. If the individual .RELOC blocks were assembled with different values of these labels, linking them together will produce an Incompatible stack parameters warning. If the software stack labels are not used, the values are zero.
The pseudo-command .RELOC switches MADS to relocatable code generation mode, taking into account the operand sizes of 6502 and 65816 instructions. In this mode, it is impossible to use the ORG, LMB, NMB, and RMB psuedo-instructions or the .DS directive. It is impossible to exit MADS relocatable code generation mode, but it is possible to have more than one .RELOC block.
Using the .RELOC directive will also increase the MADS virtual bank counter, and thus create a local scope not visible to other blocks. For more information on virtual banks, see the section Virtual memory banks (OPT B-).
At the end of a .RELOC block it is necessary to generate an update block, using the same BLK syntax for SDX relocatable blocks ("BLK UPDATE ADDRESS"). The encoding format for this block is different than for SDX and is as follows:
HEADER WORD ($FFEF) TYPE CHAR (B-YTE, W-ORD, L-ONG, D-WORD, <, >) DATA_LENGTH WORD DATA WORD [BYTE]
HEADER | always set to $FFEF |
TYPE | data type is stored in bits 0..6 and specifies the type of addresses to update, where "<" means the lower address byte and ">" means the upper address byte. |
DATA_LENGTH | number of 2-byte data items (addresses) to modify |
DATA | offsets for updating the relocatable block, where at each address a value of type TYPE is read and then updated to the final address |
The exception is an update block for address high bytes ">", where for such a block an extra BYTE is stored for each address (low byte of address being modified). To adjust the high bytes, the high byte has to be read from the WORD address in the DATA, added to the current relocation address, and then added to the low byte in the BYTE field of the DATA. The newly calculated byte is then store back at the WORD address in the DATA.
External symbols indicate that the represented variables and procedures are stored somewhere outside, beyond the current program. The location need not be specified, only the names and the types. Since the translation of instructions to appropriate machine code depends on the type of data represented by a symbol, the assembler needs to know the size of the data used.
Currently there is no support for manipulating external symbols of type '^' (highest byte).External symbosl may be used in both relocatable .RELOC blocks as well as usual DOS ones.
External symbols are declared using the pseudo-instruction EXT or the .EXTRN directive:label EXT type label .EXTRN type .EXTRN label1,label2,label3... typeAn update block for referencing external symbols is produced using the BLK psuedo-instruction:
BLK UPDATE EXTERNALNOTE: This will write symbol names used by the program..
External symbols of value type can also be defined (.BYTE, .WORD, .LONG , .DWORD):
name EXT .BYTE
label_name EXT .WORD
.EXTRN label_name .WORD
wait EXT .PROC (.BYTE delay)
External symbol declarations of procedure (.PROC) type default to .WORD, and attempts to
reference the label will be interpreted by MADS as an attempt to call the procedure.
For more about procedure calls, see Procedures.
External symbols can be useful when assembling part of a program separately from the rest of it. In this case, there are often references to other procedures and variables are defined elsewhere, and only the type and not value are known. With the help of external symbols, such a program can be assembled without the full procedure and variable definitions.
Another use for external symbols is for "plugins", or external programs connected to the main program to add additional functionality. These are types of libraries, using the procedures of the main program and expanding its capabilities. Creating such a plugin requires determining what procedures the main program provides (their name + parameters and type), and a procedure to read the file from the external symbols, which then attaches the plugins to the main program.
The following is the format of the file header produced by BLK UPDATE EXTERNAL for external symbols of type B-YTE, W-ORD, L-ONG, and D-WORD:
HEADER WORD ($FFEE) TYPE CHAR (B-YTE, W-ORD, L-ONG, D-WORD, <, >) DATA_LENGTH WORD LABEL_LENGTH WORD LABEL_NAME ATASCII DATA WORD .. .. ..
HEADER | always set to $FFEE |
TYPE | bits 0..6 contain the type of data to modify |
DATA_LENGTH | number of 2-byte data items (addresses) to modify |
LABEL_LENGTH | symbol name length in bytes |
LABEL_NAME | the symbol name, coded in ATASCII |
DATA | offset data for the relocation process. At each indicated address here, the value of type TYPE is read and then based on the actual value of the symbol. |
Public symbols make variables and procedures available to other blocks in the rest of the relocatable assembly program. With public symbols, you can refer to variables and procedures from libraries.
Public symbols can be used in relocatable .RELOC blocks as well as usual DOS blocks.
MADS automatically detects whether a public label is a variable, constant, or procedure defined by .PROC, and does not need any additional information as for external symbols.
Public symbols are declared using the following directives:.PUBLIC label [,label2,...] .GLOBAL label [,label2,...] .GLOBL label [,label2,...]The directives .GLOBAL and .GLOBL have been added for compatibility with other assemblers. They are identical in meaning to .PUBLIC directive. Update blocks for public symbols are created using the BLK pseudo-command:
BLK UPDATE PUBLIC
Below is the header format produced by BLK UPDATE PUBLIC:
HEADER WORD ($FFED) LENGTH WORD TYPE BYTE (B-YTE, W-ORD, L-ONG, D-WORD) LABEL_TYPE CHAR (C-ONSTANT, V-ARIABLE, P-ROCEDURE, A-RRAY, S-TRUCT) LABEL_LENGTH WORD LABEL_NAME ATASCII ADDRESS WORDMADS automatically selects the appropriate type for public labels:
If the symbol is of structure (.STRUCT) type, this additional information is saved (the type of the structure, name of the structure, and number of elements of the structure):C-ONSTANT label is not subject to relocationV-ARIABLE label is subject to relocationP-ROCEDURE procedura defined by .PROC, subject to relocationA-RRAY array defined by .ARRAY, subject to relocationS-TRUCT structure defined by .STRUCT, not subject to relocation
STRUCT_LABEL_TYPE CHAR (B-YTE, W-ORD, L-ONG, D-WORD) STRUCT_LABEL_LENGTH WORD STRUCT_LABEL_NAME ATASCII STRUCT_LABEL_REPEAT WORDIf the symbol is of array (.ARRAY) type, this additional information is saved (maximum element index, declared element type):
ARRAY_MAX_INDEX WORD ARRAY_TYPE CHAR (B-YTE, W-ORD, L-ONG, D-WORD)If the symbol is of procedure (.PROC) type, this additional information is saved, regardless of whether the procedure does or does not declare parameters:
PROC_CPU_REG BYTE (bits 00 - regA, 01 - regX, 10 - regY) PROC_TYPE BYTE (D-EFAULT, R-EGISTRY, V-ARIABLE) PARAM_COUNT WORD
.REG symbols of the procedure are then included, of PARAM_COUNT count:
PARAM_TYPE CHAR (B-YTE, W-ORD, L-ONG, D-WORD) ... ...
.VAR symbols of the procedure are included next along with their names, where PARAM_COUNT specifies the total length of the data:
PARAM_TYPE CHAR (B-YTE, W-ORD, L-ONG, D-WORD) PARAM_LENGTH WORD PARAM_NAME ATASCII ... ...
HEADER | always set to $FFED |
LENGTH | number of symbols stored in the update block |
TYPE | type of symbol data: B-YTE, W-ORD, L-ONG, D-WORD |
LABEL_TYPE | symbol type: V-ARIABLE, C-ONSTANT, P-ROCEDURE, A-RRAY, S-TRUCT
For type P, additional stored information: PROC_CPU_REG, PROC_TYPE, PARAM_COUNT, PARAM_TYPE For type A, additional stored information: ARRAY_MAX_INDEX, ARRAY_TYPE For type S, additional stored information: STRUCT_LABEL_TYPE, STRUCT_LABEL_LENGTH, STRUCT_LABEL_NAME, STRUCT_LABEL_REPEAT |
LABEL_LENGTH | public symbol name length in bytes |
LABEL_NAME | public symbol name stored as ATASCII |
ADDRESS | address assigned to the symbol in the relocatable .RELOC block. This value is relocated by adding its current assembly address. |
PROC_CPU_REG | information about the .REG CPU register usage records for a procedure |
PROC_TYPE | type of procedure: |
PARAM_COUNT | information about the number of register-passed parameters (.REGs) or total length of type and name data for variable-passed parameters (.VARs) |
PARAM_TYPE | parameter types, recorded using the characters 'B', 'W', 'L', 'D' |
PARAM_LENGTH | parameter name length (.VAR) |
PARAM_NAME | parameter name coded as ATASCII (.VAR) |
.LINK 'filename'The directive .LINK requires as a parameter the filename of the file to link. Only Atari DOS files are accepted, not SDX files.
If the file load address is different than $0000, it means that the file does not contain relocatable code, but may include update blocks for external and public symbols. The .LINK directive accepts files at any address, but only those starting at $0000 are subject to relocation. More information on how to construct such a file is included in the section Relocatable blocks (.RELOC).
The .LINK directive allows linking of relocatable and non-relocatable code. MADS automatically relocates the file based on all three types of update blocks (ADDRESS, EXTERNAL, PUBLIC).
There is no limit to the address at which a file can be relocated.If a block being relocated requires the MADS software stack, the labels @STACK_POINTER, @STACK_ADDRESS, and @PROC_VARS_ADR are automatically updated based on the .RELOC block header. It is necessary that the main program and the .RELOC blocks operate on the same software stack.
MNEMONICS
LDA LDX LDY STA STX STY ADC AND ASL SBC JSR JMP LSR ORA CMP CPY CPX DEC INC EOR ROL ROR BRK CLC CLI CLV CLD PHP PLP PHA PLA RTI RTS SEC SEI SED INY INX DEY DEX TXA TYA TXS TAY TAX TSX NOP BPL BMI BNE BCC BCS BEQ BVC BVS BITA mnemonic extension can be placed after a dot '.' for LDA, LDX, LDY, STA, STX, and STY:
.b or .z BYTE .a or .w or .q WORD e.g.: lda.w $80 ; AD 80 00 lda $80 ; A5 80
ASO RLN LSE RRD SAX LAX DCP ISB ANC ALR ARR ANE ANX SBX LAS SHA SHS SHX SHY NPO CIM
Available 65816 opcodes
STZ SEP REP TRB TSB BRA COP MVN MVP PEA PHB PHD PHK PHX PHY PLB PLD PLX PLY RTL STP TCD TCS TDC TSC TXY TYX WAI WDM XBA XCE INA DEA BRL JSL JMLIt is possible to use XASM-style a:, z:, and r: mnemonic extensions:
XASM MADS lda a:0 lda.a 0 ldx z:0 lda.z 0 org r:$40 org $40,*Mnemonic extensions can be placed after a dot ('.') for LDA, LDX, LDY, STA, STX, and STY:
.b or .z BYTE .a or .w or .q WORD .t or .l TRIPLE, LONG (24bit) e.g.: lda.w #$00 ; A9 00 00 lda #$80 ; A9 80
The following commands cannot have their operand given in absolute addressing form (some assemblers do not require the '#' character, but MADS requires it):
SEP REP COP
PEA
Another exception is long indirect addressing mode, which is represented by square brackets [ ]. These brackets are used to group subexpressions, but if the assembler encounters the character '[' first it is interpreted as the start of long indirect addressing, and if 65816 mode is not enabled a Illegal adressing mode results. To "trick" the assembler, place the character '+' before the '['.
lda [2+4] ; lda [6] lda +[2+4] ; lda 6
CPU DETECTION
/* How to detect on which CPU the assembler code is running (This information is from Draco, the author of SYSINFO 2.0) You can test on plain 6502-Code if there is a 65c816 CPU, the 16-Bit processor avaible in some XLs as a turbo-board, avaible. Draco told me how to do this: First we make sure, whether we are running on NMOS-CPU (6502) or CMOS (65c02,65c816). I will just show the "official" way which doesn`t uses "illegal opcodes": */ org $2000 opt c+ DetectCPU lda #$99 clc sed adc #$01 cld beq DetectCPU_CMOS DetectCPU_02 ldx #<_6502 ldy #>_6502 jsr $c642 lda #0 rts DetectCPU_CMOS lda #0 rep #%00000010 ;reset Z bit bne DetectCPU_C816 DetectCPU_C02 ldx #<_65c02 ldy #>_65c02 jsr $c642 lda #1 rts DetectCPU_C816 ldx <_65816 ldy >_65816 jsr $c642 lda #$80 rts _6502 dta c'6502',$9b _65c02 dta c'65c02',$9b _65816 dta c'65816',$9b
org $2000 opt c+ ; 65816 enabled lda #0 inc @ ; increment accumulator cmp #1 bcc cpu6502 ; ultimate test for 65816 presence xba ; put $01 in B accu dec @ ; A=$00 if 65C02 xba ; get $01 back if 65816 inc @ ; make $01/$02 cmp #2 bne cpu6502 cpu65816 ldx <text65816 ldy >text65816 jsr $c642 rts cpu6502 ldx <text6502 ldy >text6502 jsr $c642 rts text6502 dta c'6502',$9b text65816 dta c'65816',$9b
MEMORY BANKS
For probably anyone who has ever worked with the 8-bit Atari architecture, the term "memory bank" is associated with extended memory, divided into 16KB size banks, and switched in the address range <$4000..$7FFF>. MADS can interpret banks this way (option OPT B+, Hardware memory banks), but by default they are interpreted as virtual banks (option OPT B-, Virtual memory banks). The following pseudo commands apply to banks:LMB #value NMB RMB
lmb #0 lmb #bank lmb #5 , $6500 ; only with OPT B+
nmb nmb $6500 ; only with OPT B+
rmb rmb $3500 ; only with OPT B+ rmb $8500 ; only with OPT B+
In MADS, the term "virtual memory bank" refers to any area designated by a newly defined label set to the current value of the bank counter (the default bank counter is zero). That is, a virtual memory bank is not necessary a memory area in the range <$4000..$7FFF>, but each label presents a code area of the program which has been assigned a code (bank counter value) in the range <$00..$FF> with appropriate pseudo-commands for use by the programmer (NMB, RMB, LMB).
The exceptions are .RELOC blocks, where the bank counter cannot be changed manually and is automatically updated by MADS, incrementing it for each instance of the .RELOC directive. Bank counter values are in the range <$0001..$FFF7>.
The programmer can read the bank counter value associated with a label using the equal operator '=':label ldx #=labelIn this example, the X CPU register is set to the bank counter value associated by MADS with the label LABEL. Another useful operator is the colon (':') placed at the beginning of the label name, which causes MADS to allow references to labels outside of the area specified by the current MADS bank counter. Sometimes this may cause problems, such as if there is more than one label with the same name in different local scopes or in areas with a different virtual bank number.
lmb #5 label5 nop lmb #6 label6 nop lda :label5In this example, without the operator ':' at the beginning of the label name in the instruction 'lda :label5' the error ERROR: Undeclared label LABEL5 (BANK=6) would result. Virtual memory banks can be used to index an array containing values for the PORTB register. This is the main use of the option OPT B+.
@BANK_ADD @BANK_JMPand requires label definitions with the names:
@TAB_MEM_BANKS @PROC_ADD_BANKThe @TAB_MEM_BANKS label specifies the address of an array with values to write to the PORTB register for switching extended memory banks. You can take advantage of pre-written expanded memory bank detection routines included with MADS, in ..\EXAMPLES\PROCEDURES\@MEM_DETECT.ASM. The label @PROC_ADD_BANK is used by the macro @BANK_ADD and defines the address of the code for switching extended memory banks. The programmer can read the bank counter value associated with a label using the '=' operator:
label ldy #=labelIn this example, the Y register is set to the memory bank value associated with the label LABEL by MADS. If the MADS bank counter = 0:
An example of the use of MADS bank sensitive mode can be found in the file ..\EXAMPLES\XMS_BANKS.ASM. In this example, the program code is located in two different extended memory banks and runs as if it were a single program.
v1.9.5
- added pseudo-command SET to redefine labels, with a similar effect as temporary labels that begin with '?', e.g.:
temp set 12 lda #temp temp set 23 lda #temp
- added ability to force addressing mode in XASM-style ('a:', 'z:'):
XASM MADS lda a:0 lda.a 0 ldx z:0 lda.z 0
- added ability to specify a new code relocation address in XASM-style ('r:'):
XASM MADS org r:$40 org $40,*
- improved performance of '-x Exclude unreferenced procedures' switch; .VAR variables are not allocated for unused procedures
- extended single-line syntax for :rept loops so that the loop counter can now be used as a parameter :1 (%%1):
line0 line1 line2 line3 ladr :4 dta l(line:1) hadr :4 dta h(line:1)
- added a warning message when unstable illegal 6502 opcodes are used, i.e. CIM
- added new functionality to pseudo-opcodes RUN and INI to retain the previous assembly address when assembling to $2E0 (RUN), $2E2 (INI)
- added support for anonymous labels @, @+[1..9] (forward), @-[1..9] (backward); for clarity, their use is limited only to conditional branches no more than 10 instances away:
@ dex ---- ------- bne @+ | -- | stx $80 | | | @ lda #0 | -- | bne @- --- | bne @-1 ---------
- extended directives #IF and #WHILE to allow arguments declared by .VAR, whereas previously only a variable was allowed:
.var temp .word #if temp>#2100 #end #if .word temp>#2100 #end
v1.9.4
- dodana normalizacja ścieżek dla plików, tak aby działały pod Unixami, znaki '\' zamieniane są na '/'
- poprawione przekazywanie dyrektyw jako parametrów do procedur i makr, dyrektywy nie były rozpoznawane przy włączonym przełączniku -c (case sensitive)
- poprawione działanie .USE [.USING]
- dodana informacja w postaci ostrzeżenia (WARNING) o etykiecie powodującej nieskończoną ilość przebiegów asemblacji (INFINITE LOOP)
- dodany zapis dwóch bajtów nagłówka FF FF dla pliku zawierającego blok o adresie ładowania $FFFF
- komentarze po mnemonikach nie wymagających argumentu zostaną potraktowane jako błąd, wyjątkiem jest łączenie rozkazów w stylu xasm poprzez znak ':', np.:
pla $00 -> ERROR: Extra characters on line pha:pla $00 -> OK
- rozszerzona składnia makr o możliwość używania parametrów w postaci nazw a nie tylko wartości numerycznych-decymalnych, np.:
.macro SetColor val,reg lda :val sta :reg .endm .macro SetColor2 (arg1, arg2) lda #:arg1 sta :arg2 .endm
- naprawione definiowanie etykiet dla n/w sytuacji, pierwsza etykieta nie zostanie zignorowana
temp label = 100
v1.9.3
- poprawione przetwarzanie bloków .PROC, które w pewnych okolicznościach mogły zostać pominięte podczas asemblacji
- poprawiony zapis BLK EMPTY dla plików SDX jeśli zastosowaliśmy deklarację takiego bloku przez .DS
- poprawki dotyczące testowania końca linii
- dodane dyrektywy .FILESIZE, .SIZEOF jako odpowiednik dotychczasowej dyrektywy .LEN
- rozszerzona składnia dla pól struktury .STRUCT, np.:
.struct name .byte label0 .byte :5 label1 label2 .byte label3 :2 .word .ends
v1.9.2
- możliwość określenia adresu dla .ZPVAR = $XX
- usprawnione odwołania do etykiet wyliczeniowych .ENUM, np. enum_label(field0, field1)
- dodana możliwość generowania bloku dla symboli zewnętrznych BLK UPDATE EXTRN dla plików DOS-a, poprzednio tylko dla plików .RELOC, np.:
.extrn vbase .word org $2000 lda #$80 sta vbase+$5d blk update extrn
- dodany komunikat błędu "Could not use NAME in this context" w przypadku rozkazów odwołań do bloków .MACRO, .ENUM, .STRUCT
- poprawiony błąd który uniemożliwiał użycie 'EQU' w nazwie etykiety
- dodana dyrektywa .CB +byte,....., ostatni bajt ciągu znakowego zapisywany jest w inwersie
- dodana obsługa segmentów poprzez dyrektywy .SEGDEF, .SEGMENT, .ENDSEG
- dodana nowa dyrektywa #CYCLE #N generująca kod 6502 o zadanej liczbie cykli N
- dodana obsługa nielegalnych rozkazów CPU 6502, przykład w pliku ..\examples\test6502_illegal.asm
- uaktualnione pliki konfiguracyjne dla Notepad++ '..\syntax\Notepad++'
- poprawiony zapis pliku LST
- naprawiona alokacja pamięci dla zmiennych strukturalnych, rozszerzona składnia dla .STRUCT
.struct LABEL x,y,z .word // wiele zmiennych tego samego typu w jednej linii .byte a,b .ends .enum type a=1,b=2 .ende .struct label2 x type type y .ends
v1.9.0
- naprawiony zapis linii z komentarzem /* */ do pliku listingu *.LST, poprzednio takie linie nie były zapisywane
- poprawka dla etykiet deklarowanych z linii komend -d:label, poprzednio takie etykiety widziane były tylko w pierwszym przebiegu
- w przypadku addytywności bloków .LOCAL tylko pierwszy adres z takich bloków jest zapisywany
- poprawki dotyczące parsowania makr, poprzednio etykiety zaczynające się od END mogły zostać zinterpretowane jako pseudo rozkaz END
- poprawka odczytu dla pustego pliku relokowalnego, poprzednio występował błąd 'Value out of range'
- poprawki dla .USING (.USE)
v1.8.8 - 1.8.9
- uaktualniony silnik duchów programowych ..\EXAMPLES\SPRITES\CHARS o duchy 8x24
- w przypadku braku podania rozszerzenia pliku i braku istnienia takiego pliku dla ICL 'filename' zostanie domyślnie przyjęte rozszerzenie *.ASM ICL 'filename.asm'
- poprawione działanie komentarzy /* */ w blokach .MACRO i .REPT
- usunięty błąd uniemożliwiający poprawną asemblację bloku #IF, #WHILE dla wyrażeń łączonych przez .OR, .AND
- przełączniki w linii komend mogą być poprzedzone tylko znakiem '-', poprzednio także '/' jednak były problemy z działaniem tego znaku na MacOSX
- poprawiony zakres działania dyrektywy .USING, dla aktualnej przestrzeni nazw i kolejnych zawierających się w tej przestrzeni nazw
v1.8.6 - 1.8.7
- usprawnione rozpoznawanie komentarzy /* */ w wyrażeniach
- domyślny adres dla .ZPVAR ustawiony na $0080, poprzednio $0000
- dodana nowa dyrektywa .ELIF jako krótszy odpowiednik dyrektywy .ELSEIF
- rozszerzone działanie dyrektywy .LEN o możliwość podania jako parametru nazwy pliku, zwracana jest wówczas długość takiego pliku
- usprawnione działanie dyrektywy .DEF w wyrażeniach warunku .IF (.IFDEF, .IFNDEF)
v1.8.5
- dodane makro relokujące moduły RMT ...\EXAMPLES\MSX\RMT_PLAYER_RELOCATOR\
- dodany test składni dla nie asemblowanych procedur .PROC gdy aktywny jest przełącznik -x "Exclude unreferenced procedures"
- poprawione działanie przełącznika "-d:label[=value]", podanie wartości dla etykiety jest teraz opcjonalne, domyślnie mads przypisze wartość 1
- dyrektywy .DS i .ALIGN nie spowodują alokacji zmiennych zdefiniowanych przez .VAR
- alokacja zmiennych .VAR przed nowym blokiem ORG nie nastąpi jeśli blok ORG znajduje się w bloku .LOCAL lub .PROC
- poprawione łamanie wierszy znakiem '\' w ciągach ograniczonych nawiasami ()
- usunięty błąd powodujący relokowanie adresu dla wyrażenia dyrektywy .ERROR (ERT)
- usunięte zauważone błędy przy parsowaniu parametrów linii komend
- usunięte zauważone błędy dotyczące optymalizacji długości kodu makro rozkazów MVA, MWA itp.
- poprawiony kod realizujący zagnieżdżanie bloków .PROC
- poprawiony kod realizujący działanie pseudo rozkazów warunku IFT ELI ELS EIF
- dodany komunikat "'#' is allowed only in repeated lines" dla przypadków użycia licznika pętli # (.R) poza pętlą
- usunięty błąd powodujący błędne alokowanie zmiennych zadeklarowanych przez dyrektywę .VAR podczas wykonywania makra
- w celu ujednolicenia składni odwołania do etykiet typów wyliczeniowych możliwe są tylko poprzez znak kropki '.', poprzednio także przez '::'
- możliwe krótsze odwołania do typów wyliczeniowych enum_label(fields), np. :
.enum typ val0 = 1 val1 = 5 val2 = 9 .ende lda #typ(val0|val2) ; == "lda #typ.val0|typ.val2"
- rozszerzona składnia dyrektywy .SAV, np.:
.sav 'filename',offset,length .sav 'filenema',length .sav [offset] 'filename',offset2,length .sav length .sav offset,length
- rozszerzona składnia dyrektywy .ARRAY, w przypadku braku podania maksymalnego indeksu tablicy zostanie on obliczony na podstawie ilości wprowadzonych elementów, elementy można wprowadzać bez konieczności poprzedzenia ich indeksem [expression], np.:
.array temp .byte 1,4,6 ; [0..2] = 1,4,6 [12] = 9,3 ; [12..13] = 9,3 [5]:[8] = 10,16 ; [5..6] = 10,16 ; [8..9] = 10,16 0,0,\ ; [14..17] = 0,0,1,1 1,1 .enda ; 18 elementów, TEMP [0..17]
- dodana możliwość alokacji zmiennej typu strukturalnego przy pomocy dyrektyw .VAR i .ZPVAR, np.:
.struct Point x .byte y .byte .ends .var a,b,c Point .zpvar Point f,g,i
- dodana możliwość alokacji zmiennej typu wyliczeniowego przy pomocy dyrektyw .VAR i .ZPVAR, np.:
.enum Boolean false = 0 true = 1 .ende .var test Boolean .zpvar Boolean test
- dodana możliwość deklaracji pól struktury przy pomocy typów wyliczeniowych, np.:
.enum EState DONE, DIRECTORY_SEARCH, INIT_LOADING, LOADING .ende .struct SLoader m_file_start .word m_file_length .word m_state EState .ends
v1.8.3 - 1.8.4
- nowy silnik duchów programowych z minimalnymi wymaganiami pamięci, bez dodatkowych buforów pamięci obrazu ...EXAMPLES\SPRITES\CHARS_NG
- nowa wersja pakera Huffmana (kompatybilna z Free Pascal Compiler-em, "fpc -MDelphi sqz15.pas") i dekompresora Huffmana SQZ15 ...EXAMPLES\COMPRESSION\SQUASH
- poprawiony kod generowany dla rozkazów MVP, MVN, PEA, BRA (CPU 65816)
- dodane nowe rozkazy BRL, JSL, JML (CPU 65816), jako odpowiedniki rozkazów długich skoków BRA, JSR, JMP
- blok aktualizacji etykiet zewnętrznych (external) został rozszerzony o zapis młodszego i starszego bajtu adresu takiej etykiety
- poprawione działanie dyrektywy .USE (.USING), działa niezależnie od przestrzeni nazw w której zostanie użyta
- usunięty błąd, który powodował w pewnych sytuacjach pomijanie asemblacji bloku #IF, #WHILE
- dodana możliwość definiowania zmiennych poprzez dyrektywę .DS lub pseudo rozkaz ORG przed blokiem .RELOC
- dodana dodatkowa forma składni dla dyrektywy .VAR, z tym że dla takiego przypadku nie ma możliwości określenia adresu umiejscowienia zmiennych w pamięci
.VAR .TYPE lab1 lab2 lab3 .TYPE lab4 .TYPE lab5 lab6 ... .var .byte a,b,c .dword i j
- dodana możliwość definicji pojedyńczych zmiennych typu strukturalnego w krótszy sposób aniżeli dotąd przez DTA
.struct @point x .byte y .byte .ends pointA @point ; pointA dta @point [0] <=> pointA dta @point pointB @point ; pointB dta @point [0] <=> pointB dta @point points dta @point [100]
- dodana nowa dyrektywa .ZPVAR umożliwiająca automatyczne przydzielenie miejsca zmiennym na stronie zerowej
.ZPVAR TYPE label1, label2 label3 = $80 ; LABEL1=$80, LABEL2=LABEL1+TYPE, LABEL3=LABEL2+TYPE .ZPVAR label4, label5 TYPE ; LABEL4=LABEL3+TYPE, LABEL5=LABEL4+TYPE .print .zpvar
- poprawione działanie dyrektywy .ERROR i pseudo rozkazu ERT, możliwe jest umieszczenie dodatkowych informacji w wierszu podobnie jak dla .PRINT (.ECHO) np.:
ERT *>$6000 , 'BUUU przekroczyliśmy zakres pamięci o ' , *-$6000 , ' bajtów'
- dodana możliwość zagnieżdżania bloków procedur .PROC, ten sam kod może być wywoływany z różnymi parametrami np.:
.proc copySrc (.word src+1) .var .proc ToDst (.word src+1, dst+1) .var .endp ldy #0 src lda $ffff,y dst sta $ffff,y iny bne src rts .endp copySrc.ToDst #$a080 #$b000 copySrc #$a360
- dodane nowe dyrektywy .ENUM i .ENDE (.EEND)
.enum dni_tygodnia poniedzialek = 1 wtorek, sroda = 5, czwartek = 7 piatek sobota niedziela .ende ift dzien==dni_tygodnia::wtorek .print 'wtorek' eif
- rozszerzona funkcjonalność komentarzy wieloliniowych /* */ o możliwość umieszczania ich gdziekolwiek
lda #12+ /* komentarz */ 23
- umożliwiona relokacja adresów definiowanych dyrektywą .DEF
.reloc .def label=* lda label
- dodana możliwość użycia znaków { } do oznaczenia bloku (z wyjątkiem bloków .MACRO), znak '{','}' zostaje rozpoznany na początku nowego wiersza, np.:
#while .word ad+1<=#$bc40+39 { ad sta $bc40 inw ad+1 } .proc lab { .local temp2 { } .array tab [255] .long {} }
v1.8.2
- zniesione ograniczenie długości pliku dla pseudo rozkazu INS (poprzednio długość wczytywanego pliku ograniczona była do 65536 bajtów)
- dodany komunikat błędu 'The referenced label ... has not previously been defined properly' w przypadku etykiet, które nie zostały zdefiniowane do końca, np. tylko w pierwszym przebiegu wartością nieokreśloną
- dodana nowa dyrektywa .ECHO jako odpowiednik dyrektywy .PRINT, dodatkowo informacje generowane przez .PRINT (.ECHO) zapisywane są teraz także w listingu *.LST
- dodana nowa dyrektywa .ALIGN pozwalająca na wyrównanie do zadanego zakresu pamięci, dodatkowo można określić wartość jaką wypełnić pamięć
[label] .ALIGN N[,fill]
- dodany nowy przełącznik -U (Warn of unused labels)
1.8.1
- rozszerzone działanie znaku backslash '\', umieszczenie go na końcu wiersza oznacza kontynuację aktualnego wiersza od nowego wiersza, np.:
macro_temp \ _____________________________________parametr1_________________________________________________\ _____________________________________parametr2_________________________________________________\ _____________________________________parametr3_________________________________________________ lda\ #____________________________________label________________________________________\ +__________________________________expression___________________________________
- zmienione testowanie niekończącego wywoływania się makr po którym wystąpi błąd 'Infinite loop'
- naprawiony zapis etykiet do pliku *.LAB, błąd powstał po dodaniu addytywności obszarów LOCAL
- poprawione działanie pseudo rozkazu SIN (kod zapożyczony z XASM)
- poprawione rozpoznawanie dyrektyw przy włączonym przełączniku -C (Case sensitive)
- usprawniony odczyt bloków .REPT (wskazanie prawidłowej linii z błędem) i .MACRO
- zablokowane użycie .VAR w bloku .REPT
- umożliwione zagnieżdżanie oraz wielokrotne uruchamianie (poprzez makra) pętli .REPT i :repeat (poprzednio występował komunikat 'Use .REPT directive')
- umożliwione przekazywanie parametrów do bloku .REPT, np.
.REPT 10, # label:1 ; LABEL0, LABEL1, LABEL2 ... LABEL9 .ENDR .REPT 5, $12,$33,$44,$55,$66 dta :1,:2,:3,:4,:5 ; $12,$33,$44,$55,$66 dta :5,:4,:3,:2,:1 ; $66,$55,$44,$33,$12 .ENDR
1.7.9 - 1.8.0
- poprawiony błąd w opisie przełącznika -F, poprzednio 'Label at first column', prawidłowy opis to 'CPU command at first column'
- przepisana od nowa obsługa dyrektywy .DS i opcji OPT F+ (dodana możliwość użycia bloków RUN i INI)
- przepisana od nowa obsługa opcji OPT ?+ (etykiety lokalne w standardzie MAE)
- dodana możliwość upublicznienia w blokach PUBLIC tablic zadeklarowanych przez .ARRAY oraz deklaracji struktur .STRUCT
- dyrektywa generująca kod 6502 dla decyzji .TEST zastąpiona została przez dyrektywę #IF, dyrektywa .ENDT przez #END, dodatkowo możliwe jest użycie dyrektywy #ELSE np.:
# if .byte i>#8 .and .byte i<#200 # else #if .word j = #12 #end # end
- dyrektywa generująca kod 6502 dla iteracji .WHILE zastąpiona została przez dyrektywę #WHILE, dyrektywa .ENDW przez #END, np.:
lda 20 -> lda 20 # while .byte @=20 -> wait cmp 20 # end -> sne -> jmp wait
- dyrektywy #IF i #WHILE akceptują dwa dodatkowe operatory '==' i '!='
- dodana dyrektywa .EXITM jako odpowiednik .EXIT
- dodana dyrektywa .FI jako odpowiednik .ENDIF
- dodana dyrektywa .IFDEF jako krótszy odpowiednik dyrektyw .IF .DEF
- dodana dyrektywa .IFNDEF jako krótszy odpowiednik dyrektyw .IF .NOT .DEF
- umożliwione zostało definiowanie makr w obszarze procedury .PROC, podsumowując aktualnie dopuszczalne jest zdefiniowanie makra w obszarze .LOCAL i .PROC
- wystąpienie jakiegokolwiek ostrzeżenia podczas asemblacji nie zmieni kodu wyjścia (exit_code=0), zmiana podyktowana potrzebą kompatybilności z linuxowym makefile
- ujednolicony sposób deklaracji etykiet lokalnych i globalnych, "białe znaki" przed nazwą etykiety nie wymuszą zdefiniowania takiej etykiety jako globalnej, umożliwi to tylko dyrektywa .DEF :LABEL
- poprawione makra @CALL.MAC i @CALL_2.MAC, zmienna tymczasowa globalna ?@stack_offset modyfikowana jest teraz przez dyrektywę .DEF
- rezygnacja z opcji -E (Eat White spaces), aktualnie jest ta opcja zawsze włączona
- poprawione wyświetlanie numeru linii z błędem w aktualnie wykonywanym makrze
- skrócone nazwy etykiet tworzonych podczas wykonywania makr (łatwiejsza ich identyfikacja w pliku *.LAB)
- poprawione działanie opcji OPT H-
- dodane nowe makro rozkazy INL (increse LONG), IND (increse DWORD), DEL (decrese LONG), DED (decrese DWORD)
- dodane nowe makro rozkazy CPB (compare BYTE), CPW (compare WORD), CPL (compare LONG), CPD (compare DWORD)
- usprawnione i rozszerzone działanie dyrektyw #TEST i #WHILE w oparciu o kod generowany przez makro rozkazy CPB, CPW, CPL, CPD, dyrektywy #TEST i #WHILE dla wyrażeń '=#0' i '<>#0' generują najkrótszy kod wynikowy
- dodana optymalizacja długości generowanego kodu dla makro rozkazów MWA, MWX, MWY
- dodana nowa opcja OPT R optymalizująca kod makro rozkazów MWA, MWX, MWY, MVA, MVX, MVY ze względu na zawartość rejestrów, np.:
opt r- opt r+ mva #0 $80 -> lda #$00 -> lda #0 mva #0 $81 -> sta $80 -> sta $80 lda #$00 -> sta $81 sta $81 ->
- rozszerzona funkcjonalność dyrektywy .DEF o możliwość przypisania wartości nowo deklarowanej etykiecie, np.:
.def label = 1
- rozszerzona funkcjonalność dyrektywy .DEF o możliwość zdefiniowania etykiety globalnej niezależnie od aktulnego obszaru lokalnego, np.:
.def :label
- umożliwiona została addytywność obszarów .LOCAL, tzn. może istnieć wiele obszarów lokalnych o tej samej nazwie, symbole zawarte w takich obszarach należeć będą do wspólnej przestrzeni nazw, np.:
.local namespace .proc proc1 .endp .endl .local namespace .proc proc2 .endp .endl
1.7.8
- dodane dyrektywy .MEND, .PGEND, .REND jako odpowiedniki .ENDM, .ENDPG, .ENDR
- obecnie deklaracja makra musi kończyć się dyrektywą .ENDM lub .MEND (poprzednio dopuszczalne było użycie dyrektywy .END)
- poprawiony sposób wykonywania makr dzięki czemu umożliwione zostało wykonanie dyrektywy .ENDL z poziomu wykonywanego makra
- poprawione zauważone błędy dotyczące starszych bajtów relokowanego adresu oraz bloku aktualizacji symboli publicznych
- dodana nowa dyrektywa .USING (.USE) pozwalająca określić ścieżkę poszukiwań dla nazw etykiet
- poprawione działanie dyrektyw .LOCAL, .DEF, których błędne działanie objawiało się w szczególnych przypadkach
- poprawione działanie makro rozkazów skoków (SNE, RNE itp.), których błędne działanie objawiało się w szczególnych przypadkach
- rozszerzona składnia dyrektywy .TEST (kod 6502 dla warunku) o dowolną ilość wyrażeń połączonych przez .OR lub .AND (brak możliwości zmiany piorytetu wartościowania przy pomocy nawiasów), np.:
.test .byte k>#10+1 .or .word j>#100 .and .word j<#105 .or .byte k<=#5 ... ... .endt
- rozszerzona składnia dyrektywy .WHILE (kod 6502 dla pętli) o dowolną ilość wyrażeń połączonych przez .OR lub .AND (brak możliwości zmiany piorytetu wartościowania przy pomocy nawiasów), np.:
.while .byte k>#4 .and .byte k<#39 ... ... .endw
1.7.6 - 1.7.7
- dodany nowy przełącznik -B:ADDRESS umożliwiający asemblacje od zadanego adresu
- dodany nowa opcja OPT F+- pozwalająca tworzyć bloki ciągłej pamięci (przydatne dla cartów)
- dodana obsługa parametrów typu .LONG i .DWORD przekazywanych do procedur .PROC typu .VAR (poprzednio akceptowanymi typami parametrów był tylko .BYTE i .WORD)
- dodana nowa dyrektywa .FL realizująca zapis liczb rzeczywistych REAL w formacie FP Atari, np.:
pi .fl 3.1415926535897932384626433832795 ; 40 03 14 15 92 65 tb .fl 0.5 12.34 -2.30 0.00002 tb .fl 0.5, 12.34, -2.30, 0.00002
- umożliwiony został zapis wartości innych typów niż tylko .BYTE w bloku .ARRAY
- dodana obsługa typów wielokrotnych dla .STRUCT, poprzednio takie typy były akceptowane jednak pamięć nie była właściwie dla nich rezerwowana, np.:
.struct test x :200 .byte y :999 .long .ends buf dta test [0]
- poprawione błędy dotyczące generowania kodu relokowalnego zauważone przez Laoo, np.:
.reloc lda temp temp .long $aabbcc
- błąd 'Addres relocation overload' wystąpi teraz tylko gdy wyrażenie będzie dotyczyć więcej niż jednej etykiety relokowalnej, poprzednio każde wyrażenie z udziałem etykiety relokowalnej powodowało wyświetlenie tego komunikatu błędu
- blok aktualizacji symboli plublicznych rozszerzony został o możliwość przekazywania stałych różnych typów B-YTE, W-ORD, L-ONG, D-WORD, poprzednio przekazywanym typem był tylko W-ORD
- zmienione działanie dyrektywy .VAR w blokach .LOCAL znajdujących się w bloku .PROC, zmienne takie zawsze odkładane są na końcu bloku przed dyrektywą .ENDP, w pozostałych przypadkach na końcu bloku .LOCAL przed dyrektywą .ENDL
- umożliwiona została relokowalność kodu generowanego przez dyrektywy .WHILE i .TEST
- poprawione działanie testowania wartości typu .WORD w kodzie generowanym przez dyrektywy .WHILE i .TEST
- dodana nowa dyrektywa .ADR zwracająca adres etykiety przed zmianą adresu asemblacji
- dodana nowa dyrektywa .LEN zwracająca długość bloków zdefiniowanych przez .PROC i .ARRAY
- poprawione działanie operacji dzielenia, mnożenia i modulo, poprzednio błędnie był interpretowany piorytet dla tych operacji
- komentarze z końca linii nie poprzedzone znakiem komentarza będą powodować wystąpienie błędu 'Unexpected end of line'
- dodana możliwość przypisania zmiennej pól zdefiniowanych przez strukture, np.:
@point .struct x .byte y .byte .ends a @point b @point c @point
- rozszerzona składnia .STRUCT o możliwość dodania nowych pól bez definiowania nazwy pola, np.:
.struct @id id .word .ends .struct @mem @id adr .word .ends
- rozszerzona składnia makro rozkazu MWA o możliwość użycia adresowania pośredniego strony zerowej postindeksowanego Y, np.:
mwa ($80),y $a000,x mwa $bc40,y ($f0),y mwa ($80),y ($82),y
- rozszerzona składnia dyrektywy .EXTRN, obecnie możliwe jest zapowiedzenie większej ilości etykiet różnych typów w jednym wierszu, zapowiedzenie procedury .PROC w takim wierszu musi znajdować się na jego końcu, np.:
.extrn a,b,c,d .byte x y z .word line .proc(.byte x,y) .reg
- rozszerzona składnia dyrektywy .VAR, obecnie możliwe jest zadeklarowanie większej ilości etykiet różnych typów w jednym wierszu oraz przypisanie im adresu od którego zostaną odłożone w pamięci, np.:
.var x y z .byte bit :2 .dword = $80
- rozszerzona składnia dla parametrów procedur przekazywanych przez zmienne .VAR, możliwe jest podanie przesunięcia np.:
move .proc (.word src+1,dst+1) .var src lda $ffff dst sta $ffff .endp
- dodana nowa dyrektywa .NOWARN wyłączająca wyświetlenie ostrzeżenia dla aktualnie asemblowanego wiersza, np.:
.nowarn PROCNAME
- dodane nowe makro rozkazy PHR, PLR, realizujące odkładanie i zdejmowanie wartości rejestrów z udziałem stosu sprzętowego, np.:
PHR -> PHA PLR -> PLA TXA TAY PHA PLA TYA TAX PHA PLA
- dodane nowe makro rozkazy ADB, SBB realizujące dodawanie i odejmowanie wartości typu .BYTE, np.:
ADB $80 #12 $b000 -> lda $80 clc adc #12 sta $b000 SBB #200 $a000 -> lda #200 sec sbc $a000 sta $a000
- dodana możliwość użycia składni C dla liczb szestnastkowych, np.:
lda 0x2000 ldx #0x12 temp = 0x8000
1.7.5
- dyrektywa .DS w blokach relokowalnych SDX RELOC i MADS RELOC deklaruje od teraz pusty blok
- dodany nowy przełącznik -F, który umożliwia umieszczanie rozkazów CPU i pseudo rozkazów od pierwszej kolumny w wierszu
- przepisane od nowa procedury odczytu bloków .MACRO, .REPT oraz procedura realizująca dzielenie wiersza przy pomocy znaku '\'
- dodane nowe pseudo rozkazy ADW, SBW realizujące dodawanie i odejmowanie wartości typu WORD dla CPU6502, np.:
adw hlp #40 ; hlp=hlp+40 adw hlp #20 pom ; pom=hlp+20
- rozszerzone działanie dyrektywy .DEF o możliwość zdefiniowania etykiety, np.: .DEF label
- zwiększona liczba przebiegów dla deklaracji etykiet przez EQU dla pewnych szczególnych przypadków
1.7.4
- naprawione działanie dyrektywy .PRINT, dotąd mogła nie wyświetlić wartości etykiet zaczynającej się na literę 'A','B','C','D','E','F','G','H','L','T','V'
- zablokowane działanie dyrektywy .DS w blokach .RELOC i SDX oraz naprawione jej działanie z instrukcją warunkową .IF (IFT)
- usprawnione przeszukiwanie ścieżek dostępu -i:path (można odwoływać się do podkatalogów tam zawartych)
- w przypadku wystąpienia błędów podczas asemblacji wyświetlane są one wszystkie a nie tylko pierwszy z błędów
- poprawione zauważone błędy, m.in. użycie makra w pliku .RELOC mogło spowodować w pewnych sytuacjach zapis błędnej informacji o relokownych adresach
- uproszczony został sposób kończenia procedur wykorzystujących stos programowy MADS-a, nie ma potrzeby używania dyrektywy .EXIT, a dyrektywa .ENDP nie powoduje już dodatkowych działań na stosie programowym
- dodana nowa dyrektywa .SYMBOL jako odpowiednik bloku aktualizacji BLK UPDATE NEW SYMBOL 'SYMBOL', dyrektywę .SYMBOL można użyć w dowolnym miejscu programu
- dodane automatyczne wywoływanie bloków aktualizacji (ADDRESS, EXTERNAL, PUBLIC, SYMBOL) dla .RELOC i SDX
- dodane nowe dyrektywy .BY, .WO, .HE, .EN, .SB (zapożyczone z MAE)
- dodany nowy przełącznik OPT ?- (domyślnie) etykiety ze znakiem zapytania (?labels) traktowane są jako etykiety tymczasowe, OPT ?+ etykiety ze znakiem zapytania (?labels) traktowane są jako lokalne i tymczasowe, nazwą obszaru lokalnego jest ostatnio użyta etykieta bez znaku zapytania
- dodane dyrektywy .LEND, .PEND, .AEND, .WEND, .TEND, .SEND jako odpowiedniki dyrektyw .ENDL, .ENDP, .ENDW, ENDW, .ENDT, .ENDS
- dodane nowe dyrektywy .GLOBAL i .GLOBL jako odpowiednik (zamiennik) dyrektywy .PUBLIC
- dodana optymalizacja skoków warunkowych JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS, jeśli jest taka możliwość wybierany jest skok krótki typu BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS
- dodany nowy domyślny separator znak spacji dla przekazywanych parametrów do .PROC, .MACRO, dotąd był to tylko znak przecinka
- usprawnienia dotyczące przekazywania parametrów do makr i procedur, np. paramatrem makra może być dyrektywa zwracająca wartość wyrażenia lub symbol licznika pętli '#'
:12 makro #
- dodana możliwość użycia znaku spacji jako separatora dla .VAR, .EXTRN, np.
.EXTRN a b c d .word .VAR i = 1 j = 2 .byte .VAR a b c d .byte
- rozszerzona składnia dla .VAR umożliwiająca zaincjowanie zmiennych stałą, np.:
.var i = 10 j = 12 .byte .var a , b = 2 .byte
- dodane nowe dyrektywy .WHILE, .ENDW pozwalające na automatyczne wygenerowanie kodu dla pętli WHILE, np.:
ldx #$ff .while .word adr < #$bc40+40*24 stx $bc40 adr: equ *-2 inw adr .endw
- dodane nowe dyrektywy .TEST, .ENDT pozwalające na automatyczne wygenerowanie kodu dla warunku, np.:
.test .byte (@>=#'a') .test .byte (@<=#'z') .endt .endt
1.7.3
- dodana możliwość zmiany adresu asemblacji .PROC lub .LOCAL bez zmiany adresu ładowania
- usunięto optymalizację kodu dla makro rozkazów MWA itp., która mogła powodować w szczególnych przypadkach zapętlenie się MADS-a
- dodane dyrektywy .REG, .VAR pozwalające określić sposób przekazywania parametrów do procedur (.REG przez rejestry CPU, .VAR przez zmienne)
- dodana dyrektywa .VAR pozwalająca na deklarację zmiennych w blokach .PROC, .LOCAL, zadeklarowane zmiennne są fizycznie odkładane na końcu takiego bloku
- rozszerzona składnia dla dyrektywy .EXTRN, np. EXTRN label1,label2,label3... TYPE
- jesli brak deklaracji etykiet dla stosu programowego MADS-a, przyjmowane są domyślne wartości @PROC_VARS_ADR=$0500, @STACK_ADDRESS=$0600, @STACK_POINTER=$FE
- dodany repeat_counter #, który można używać zamiennie z dyrektywą .R
- wystapi błąd '^ not relocatable' przy próbie relokacji rozkazu 'lda ^label'
- dodana obsługa symboli publicznych dla stałych (C-ONSTANT) w blokach PUBLIC
- poprawiona relokowalnosc dla tablic .ARRAY, danych stworzonych przez .STRUCT, parametrów przekazywanych do procedur przez stała #
v1.7.2
- przepisana na nowo obsługa pseudo rozkazów REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS, SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS
- poprawione działanie dyrektywy .LINK dla bloków o stałych adresach
- poprawione testowanie słów zarezerwowanych (można używać nazw zarezerwowanych dla 65816 gdy używamy tylko 6502)
- zmiany w listingu, wyświetla informacje o numerze banku tylko gdy bank > 0
- dodana obsługa makro rozkazów MWA, MWX, MWY, MVA, MVX, MVY, ADD, SUB, INW, DEW (do ich obsługi nie są już potrzebne makra)
v1.7.1
- dodana możliwość używania nazw mnemoników 65816 w trybie pracy 6502, w trybie 65816 wystąpi już błąd Reserved word
- poprawione działanie pseudo rozkazów skoków SCC, RNE itp. w makrach
- usprawnione wykonywanie wielu makr rozdzielonych znakiem dwukropka ':'
v1.7.0
- usunięty błąd, który powodował zbyt mała liczbę przebiegów asemblacji
- dodana obsługa pseudo rozkazów JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS (makra nie są już potrzebne do ich obsługi)
v1.6.9
- rozszerzona składnia dla .ARRAY, .PUT
- dodany pseudo rozkaz EXT pozwalający na deklaracje etykiety external
- dodane makra JEQ, JNE, JPL, JMI, JCC, JCS
- dodane dyrektywy .PAGES i .ENDPG
- dodana dyrektywa .END zastepujaca inne dyrektywy .END?
- przełącznik -H zastąpiony został przez -HC (generuje plik nagłówkowy dla CC65)
- dodany nowy przełącznik -HM generujący plik nagłówkowy dla MADS-a z sortowaniem na etykiety typu CONSTANTS, VARIABLES, PROCEDURES
- dodana nowa dyrektywa .RELOC generująca kod relokowalny w formacie MADS-a
v1.6.8
- dodana nowa dyrektywa .PUT oraz rozszerzona składnia dla dyrektywy .GET (../EXAMPLES/MSX/MPT_PLAYER/MPT_RELOCATOR.MAC , ../EXAMPLES/MSX/TMC_PLAYER/TMC_RELOCATOR.MAC)
- dodana obsługa pseudo rozkazów XASM-a REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS, SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS
- dodana możliwość łączenia dowolnej liczby znanych MADS-owi mnemoników przy pomocy znaku ':' (styl XASM-a), np.:
lda:cmp:req 20 ldx:ldy:lda:iny label
v1.6.6 - 1.6.7
- źródło MADS-a kompatybilne z Free Pascal Compiler, po kompilacji możliwe jest jego używanie na innych platformach systemowych, jak np. Linux, Mac OS, OS/2 itp.
- od teraz MADS sam dobiera odpowiednią liczbę przebiegów asemblacji, przełącznik '/3' nie jest już potrzebny
- poprawiony i rozbudowany został mechanizm przekazywania parametrów do MADS-a (rozdział 'Przełączniki assemblera')
- poprawione zostało wywołanie makra w linii rozdzielanej znakiem '\' oraz usprawnione rozpoznawanie i wykonywanie linii rozdzielanych znakami '\'
- poprawiony błąd, w którym MADS mylił dyrektywę .ENDM z pseudorozkazem IFT
- poprawione działanie instrukcji warunkowych .ELSEIF, .ELSE
- poprawione testowanie poprawności instrukcji warunkowych w makrach
- obsługa procedur .PROC została rozbudowana o nowe makra i mechanizmy, dzięki którym podobna jest w działaniu jak i łatwości użycia do procedur z języków wyższego poziomu
- dla procedur .PROC z zadeklarowanymi parametrami potrzebna jest teraz dodatkowa deklaracja @PROC_VARS_ADR
- brak ograniczeń w liczbie parametrów przekazywanych do procedur, jedynym ograniczeniem jest dostępna pamięć
- dodany nowy przełącznik /d:label=value pozwalający zdefiniować nową etykietę MADS-a z poziomu linii poleceń
- dodany nowy przełącznik /x "Exclude unreferenced procedures" pozwalający pominąć podczas asemblacji nie używane w programie procedury zadeklarowane dyrektywą .PROC
- nowa opcja OPT T+ (track sep, rep) śledząca zmiany rozmiaru rejestrów A,X,Y dokonywane przez rozkazy SEP, REP (CPU 65816)
- nowe biblioteki w katalogu ..\EXAMPLES\LIBRARIES
- w deklaracji obszaru lokalnego .LOCAL nie jest wymagane podanie nazwy obszaru
- nowe operatory '-=', '+=', '++', '--' pozwalające zmniejszyć/zwiększyć wartość etykiety tymczasowej, np.:
?label -- -> ?label=?label-1 ?lab ++ -> ?lab=?lab+1 ?temp += 3 -> ?temp=?temp+3 ?ofset -= 5 -> ?ofset=?ofset-5
- rozszerzona o znak przecinka składnia deklaracji parametrów procedur, np.:
.proc nazwa (.byte a,b,c .word d,e) .endp