Merry Christmas - happy holidays - Happy new year!!!
Daryl
Unfortunately, I discovered macros declared with an ellipsis (variable number of parameters) no longer work. Assembly stops with the assembler complaining that the macro is being passed the wrong number of parameters. That shouldn't happen if an ellipsis is used in place of variables in the macro invocation.
2021 was a very busy year for me. 2022 looks like it will be even busier. I still intend to finish the 65816 support by adding the debugger (simulator), but I cannot provide an estimate of when it will be ready for first release.
Things seem to be working. However, I did run across another anomaly.
The .DS N and .RS N pseudo-ops are supposed to be synonyms for *=*+N, meaning they are supposed to advance the program counter N bytes without generating any object code. *=*+N is a common program idiom used to reserve dynamic storage space for runtime data. However, .DS N and .RS N are more convenient,¹ — less typing, especially if a lot of dynamic storage must be defined.
The anomaly is whereas *=*+N only advances the program counter N bytes, .DS N and .RS N do that and also generate null bytes through the area being reserved, which become part of the resulting program. It wouldn't be much of a problem if dynamic storage only took up a few bytes, but does become a potential problem if a lot of space is used.
For example, in my SCSI I/O routines, I define 512 and 1024 byte block buffers, e.g., .RS 512 and .RS 1024. That tacks several thousand useless nulls onto the total program size.
The assembler has the .DCB N,F pseudo-op, which advances the program counter N bytes and fills the intervening space with F. It appears the .DS and .RS pseudo-ops are behaving like .DCB, but with $00 hard-coded for F.
———————————————————— ¹I have a macro named res (reserve) that does the *=*+N thing.
I did some research and found that .RS and .DS act as you say. I have not altered that function since I started maintaining the source so it is acting as it was originally intended to act. I see no reason to change their behavior and worry that any change would impact others who use the assembler as it is written. If *=*+N does what you want it to do, then I recommend that you use that for your purpose.
I did some research and found that .RS and .DS act as you say. I have not altered that function since I started maintaining the source so it is acting as it was originally intended to act. I see no reason to change their behavior and worry that any change would impact others who use the assembler as it is written. If *=*+N does what you want it to do, then I recommend that you use that for your purpose.
It might be a good idea to edit the documentation for .DS and .RS to correctly describe their behavior.
Last edited by BigDumbDinosaur on Fri Jan 14, 2022 6:28 pm, edited 1 time in total.
The help can be compiled without doing the whole source, but its easier to just recompile the whole source - takes about 20 seconds at any rate, so not a problem.
The help can be compiled without doing the whole source, but its easier to just recompile the whole source - takes about 20 seconds at any rate, so not a problem.
Daryl
So, I forgot about the context selective help. To update that, it requires the source to be re-compiled.
I have updated the help documentation to better reflect the .DS & .RS operation. I also fixed a few typo's with the .ORG help. New version is 1.3.4.7 and can be found on my website. https://sbc.rictor.org/kowalski.html
The help can be compiled without doing the whole source, but its easier to just recompile the whole source - takes about 20 seconds at any rate, so not a problem.
Daryl
So, I forgot about the context selective help. To update that, it requires the source to be re-compiled.
I have updated the help documentation to better reflect the .DS & .RS operation. I also fixed a few typo's with the .ORG help. New version is 1.3.4.7 and can be found on my website. https://sbc.rictor.org/kowalski.html
In the process of getting from point A to point B, I needed to write some macros that would reduce the amount of code that has to be entered to access functions in my SCSI library. Below is one of those macros, which illustrates some of the things that can be done with the Kowalski assembler's macro processor.
Something worth noting is the Kowalski assembler has separate dictionaries for macro names and labels/symbols (there is a difference, by the way, between a label and a symbol). The separate dictionaries feature makes it possible to name a macro after the function it invokes without any namespace collisions. You can see that in the below macro.
;———————————————————————————————————————————————————————
;READ BLOCK(S) FROM SCSI DEVICE
;
;blkread *ID,*LBA,*NBLK,*BUF,<m1>,<m2>,<m3>,<m4>
;
; *ID — pointer to device bus ID
; *LBA — pointer to logical block address
; *NBLK — pointer to number of blocks to read
; *BUF — pointer to buffer
; <m1> — <m4> are addressing mode parameters:
;
; 'd' — Parameter is a direct page location containing a 32-bit pointer
; to an object, in standard little-endian format. It is assumed
; the direct page location is 4 contiguous bytes. An assembly-
; time error will occur if 'd' addressing is paired with any ad-
; dress greater than $FC.
;
; 'f' — Parameter is a 32-bit (“far”) address of an object. “Far” ad-
; dressing should not be used if the object is in the same bank
; as the program accessing it. “Near” addressing should be used
; instead.
;
; 'n' — Parameter is a 16-bit (“near”) address of an object assumed to
; be in the same bank as the program accessing it. If a 24- or
; 32-bit address is passed, the MSB or MSW will be ignored.
;
; 'r' — Parameter is ignored. The address of the object is passed in
; the index registers, with .X containing the LSW & .Y containing
; the MSW. The index registers will be set to 16 bits before be-
; ing pushed so as to satisfy the called function's stack frame
; requirements. It is recommended the parameter associated with
; register addressing be a literal zero so it is clear that it is
; not being used by the macro.
;
; Register addressing is limited to one parameter. If used more
; than once, an assembly-time error will occur.
;———————————————————————————————————————————————————————
;
blkread .macro ...
.rp =8 ;required number of parameters
.if @0 == .rp ;if required parameters were passed...
.np =.rp/2 ;number of address parameters
.ct .= .np ;loop counter
.xr .=0 ;register addressing flag
.rept .ct ;repeat following for each address parameter
.m .= {{@{.ct+.np}} | %00100000} ;down-case the addressing mode
.if .m == 'd' ;if direct page...
.if {@.ct} > $fc ;range-check address
.error ""+@0$+": 'DP' address out of range"
;
; ———————————————————————————————————————————————————————
; Note that assembly halts following an .error pseudo-op.
; ———————————————————————————————————————————————————————
;
.endif
pei {@.ct}+2 ;push MSW
pei {@.ct} ;push LSW
.else
.if .m == 'f' ;if “far” addressing...
.if {{@.ct} >> 16} > $ff ;range-check address
.error ""+@0$+": 'far' address out of range"
.endif
pea #{@.ct} >> 16 ;push MSW
pea #{@.ct} & $ffff ;push LSW
.else
.if .m == 'n' ;if “near” addressing...
.rept 2 ;execution bank is MSW
phk
.endr
per {@.ct} & $ffff ;push LSW
.else
.if .m == 'r' ;if register addressing...
.if .xr ;if already used...
.error ""+@0$+": 'r' addressing can only be used once"
.else
.xr .= 1 ;register addressing used
rep #%00010000 ;16-bit index
phy ;push MSW
phx ;push LSW
.endif
.else ;if an undefined addressing mode...
.error ""+@0$+": mode must be 'd', 'f', 'n' or 'r'"
.endif
.endif
.endif
.endif
.ct .= .ct-1 ;counter -1
.endr
.else
.error ""+@0$+": missing parameters"
.endif
.if .def(_SCSI_) ;if “_SCSI_” label defined...
jsl blkread ;use a “long” call
.else ;otherwise...
jsr blkread ;use a “short” call
.endif
.endm ;end of macro
I am writing a function that converts binary broken-down time to its ASCII equivalent, using a formatting mask. This is very similar to the date +%... options available in UNIX-like environments. My version is mostly POSIX-compliant, but that’s a discussion for another time.
Anyhow, the broken-down time is passed in a data table whose layout is almost identical to the tm structure of *NIX. Element offsets in the table are name tm_..., such as tm_wday for the day number of the week. As I was writing code, I mis-typed tm_wday as tm.wday. When I went to assemble my code, the assembler reported tm.wday as undefined. Once I had seen my typo, I was left a bit puzzled by this response.
Usually the assembler will complain about statement syntax when something in one of the fields is not to its liking, such as a misplaced comma or a label with a leading numeral. I would have expected tm.wday to trigger such an error. Since that didn't happen, it was apparent the assembler was treating tm.wday as a valid name. That led me to try assigning a value to tm.wday to see what would happen. The assembler accepted it:
The assembler documentation doesn't seem to indicate a dot is a valid character in a label or symbol. Evidently, it is.
This revelation means that long label/symbol names can be broken up into words using a dot, such as this.is.a.very.long.label, instead of the more common underscore, or something I heartily detest, use of upper-case letters at the start of each word, such as this: ThisIsAVeryLongLabel. Use of a dot instead of an underscore means one doesn't have shift to type an acceptable word separator.