Druzyek wrote:
I can't figure out how the macros in the simulator work. I downloaded 1.2.12 and tried this code:
Code:
.START Main ;start execution at Main
;Console i/o address for simulator
io_clrscr .SET $E000
io_out .SET $E001
io_in .SET $E004
;Simulator I/O macros
clrscr: .MACRO
STA io_clrscr
.ENDM
putc: .MACRO ...
.IF %0==1
LDA %1
.IF .PARAMTYPE(%1)==1
.ERROR "Type 1"
.ENDIF
.ENDIF
STA io_out
.ENDM
;Global variables
var1 .SET $0041
;Beginning of functions
.ORG $8000
Main:
LDA #'A'
nonsensical ——— > STA 'Y' <——— nonsensical
LDA #'B'
STA var1
putc var1
putc 'Y'
It stops assembling on line 17 where the .PARAMTYPE is saying "ERROR E030: Missing label. ROW 17, FILE" What is it talking about?
I commented those lines out and got it to print "CBA" like I expected. However, when I change the last line to putc #'Y', the simulator gives an "Encountered an improper argument" error message and stays stuck/crashed on the assembling screen. Any ideas?
Let's start out by picking apart your code.
The
.SET directive should not be used to define constants such as the number of seconds in a minute or the address of a function in ROM (ROM doesn't change as the program is running and there have been 60 seconds in a minute for many centuries
). For example,
io_clrscr .SET $E000 should be written as
io_clrscr = $E000. Use
.SET (or its equivalent, which is
.=) only to define variables that are to be reassigned later on.
.SET is mostly of value in macros, but can be used anywhere a declaration must be redefined as assembly progresses.
At the start of any program to be assembled by the Kowalski assembler a setup directive should declare the target processor and whether or not your source code is case-sensitive. I use the following in my source code:
Code:
.opt proc65c02,caseinsensitive
The above declares the (Rockwell) 65C02 as the target MPU, and that mnemonics, labels and symbols are not case-sensitive (recommended).
Labels and symbols must always start in the first column. Appending a colon to a label is optional; I don't do it, as it serves no purpose in the way in which I structure my source code.
Mnemonics must start in at least the second column, which is also true with assembler directives (pseudo-ops). Due to the way in which the assembler parses source code lines, it thinks anything that starts at the first column is a label or a symbol and generates a diagnostic if a mnemonic or pseudo-op is in the first column. Use tabs or spaces to align your fields as you type in code.
STA 'Y' is ambiguous at best. As assembly progresses,
'Y' will be converted to
$59. So
STA 'Y' is the same as writing
STA $59.
I recommend that you place an
.END pseudo-op on the last line of your source file. If you do so you can type in notes after
.END without having to mark them as comments, which is handy for things such as revision tables or discussion of an algorithm.
The diagnostic you are getting is telling you that when the assembler attempted to expand the
putc #'Y' macro invocation, it couldn't figure out what you meant with
#'Y'.
#'Y' appears to be a single argument to
putc, but
# is seen as an addressing mode symbol and is not a legal argument to a macro unless surrounded by single quotes. What could work is
putc '#','Y', which appears to the macro as two single character arguments. Your macro would have to be defined with two parameters, not one. I'll come back to this in a minute
Arguments to macro calls must be symbols, labels or quoted literal characters. Here's a simple macro example:
Code:
char ='A' ;assigns ASCII value of "A" to char, which is $41
;
;
; macro definition...
;
putchr .macro .c ;.c is the parameter
lda #.c ;load arg &...
jsr putch ;call console display function
.endm ;end of macro definition
;
;
; macro call...
;
putchr char ;outputs "A" to console
putchr 'Z' ;outputs "Z" to console
The label
putchr becomes the macro's name and is used to invoke the macro. Note that since the macro definition is declared with one parameter (
.c) one and only one argument must be passed with the macro invocation or else the assembler will complain and halt. It is not necessary to check the argument count in such a case, as the assembler will enforce it for you.
In the above example,
.c is a local variable known only to the macro. Each invocation of the macro will create
.c on the fly and assign the invocation argument to it. At the completion of macro expansion
.c will cease to exist until the next invocation of the macro.
Macros may defined in any of three ways:
- No parameters.
No parameters are defined in the macro and the macro must be invoked without arguments.
- Fixed parameters.
The macro is defined with a fixed number of parameters. The correct number of arguments must be passed when the macro is invoked.
- Variable parameters.
The macro is defined with .macro ..., where the ellipsis indicates that a variable number of parameters are expected. The body of the macro uses runtime variables that are prefixed with % to process the parameter list according to whatever rules you formulate.
Assembler internal variables that exist while a macro is being expanded are:
Code:
%0 — number of arguments passed with macro invocation
%1 — first argument
%2 — second argument
%N — Nth argument
%0$ — name of the macro
You can also refer to an argument within a loop. For example, if the local variable
.ct has been defined (for example,
.ct .= 1) within the body of the macro,
%.ct will refer to a specific argument. .ct could be incremented (
.ct .= .ct + 1) or decremented (
.ct .= .ct - 1) with each pass of the loop.
If the argument passed to the macro is a character string, you can determine that with the
.paramtype pseudo-op. For example:
Code:
.if .paramtype (%1) == 2
which would evaluate true if the first argument to the macro is a literal character string, e.g., prints "Testing" (not a recommended usage, incidentally). A
.paramtype of 1 would result with something like
putchr 'A'.