Friday, January 19, 2024
Complicating code
I recently added an .OPT
directive to my 6809 assembler.
This allows me to add options to a source file instead of having to always specify them on the command line.
I originally did this to support unit testing and I feel it's a nice addition.
When the test backend is enable, all the memory of the emulated 6809 is marked as non-readable, non-writable, non-executable. As the code is assembled, memory used by instructions are switched to “readable, executable” and memory used by data becomes “readable, writable” (easy, because of an early decision to have separate functions to write instructions vs. data). If you reference memory outside of the addresses used by the source code being assembled, you have to specify the permissions of said addresses.
; The set up for our tests. ; We should only read from these locations .opt test prot r,ECB.beggrp,ECB.beggrp + 1 .opt test prot r,$112 ; We can read and write to these locations .opt test prot rw,$0E00,$0E00 + 1023 ; Set the stack for our tests .opt test stack $FF00 ; Initialize some VM memory .opt test memw,ECB.beggrp,$0E00 .opt test memb,$112,2
You really only need these for memory locations defined outside the source code. In the example above, the memory referenced is defined by the Color Computer and not by the code being tested, so they need to be initialized. And because the system only supports 65,536 bytes of memory, we can easily (on modern systems) assign permissions per byte.
So this all works and is great at finding bugs.
But then I thought—I can use .OPT
to supress warnings as well.
If I assemble the following code:
;************************************************************************** ; frame_buffer set frame buffer address (GPL3+) ;Entry: A - MSB of frame buffer ;Exit: D - trashed ;************************************************************************** frame_buffer ldb PIA0BC ; wait for vert. blank bpl frame_buffer .now stx ,--s ; save X ldx #SAM.F6 ; point to framebuffer address bits .setaddress clrb ; reset B lsla ; get next address bit rolb ; isolate it stb b,x ; and set the SAM F bit leax -2,x ; point to next F bit register cmpx #SAM.F0 ; more? bhs .setaddress ; more ... puls x,pc ; return
I get
frame_buffer.asm:9: warning: W0002: symbol 'frame_buffer.now' defined but not used
The subroutine has effectively two entry points—frame_buffer
will wait until the next vertical blanking interrupt before setting the address,
while frame_buffer.now
will set it immediately.
The former is good if you are using a double-buffer system for graphics,
while the later is fine for just switching the address once.
Given that my assembler will warn on unused labels,
this means I'll always get this error when including this code.
I can supress that warning by issuing a -nW0002
on the command line,
but then this will miss other unused labels that might indicate an actual issue.
I wanted to have something like this:
.opt * disable W0002 frame_buffer ... .now ... puls x,p .opt * enable W0002
We first disable the warning for the code fragment, then afterwards we enable it again. I coded this all up, but it never worked. It worked for other warnings, but not this particular one.
The assembler is a classic two-pass assembler, but not all warnings are issued during the passes, as can be seen here (using a file that generates every possible warning with debug output enabled):
[spc]lucy:~/source/asm/a09/misc>../a09 -ftest -d -o/dev/null warn.asm warn.asm: debug: Pass 1 warn.asm:2: warning: W0010: missing initial label warn.asm:10: warning: W0008: ext/tfr mixed sized registers warn.asm:11: warning: W0001: label 'a_really_long_label_that_exceeds_the_internal_limit_its_quite_l' exceeds 63 characters warn.asm:16: warning: W0001: label 'a_really_long_label_that_exceeds_the_internal_limit_its_quite_l' exceeds 63 characters warn.asm:21: warning: W0001: label 'another_long_label_that_is_good.but_this_makes_it_too_long_to_u' exceeds 63 characters warn.asm:23: warning: W0001: label 'another_long_label_that_is_good.but_this_makes_it_too_long_to_u' exceeds 63 characters warn.asm:36: warning: W0013: label 'a' could be mistaken for register in index warn.asm:37: warning: W0013: label 'b' could be mistaken for register in index warn.asm:38: warning: W0013: label 'd' could be mistaken for register in index warn.asm: debug: Pass 2 warn.asm:2: warning: W0003: 16-bit value truncated to 5 bits warn.asm:3: warning: W0004: 16-bit value truncated to 8 bits warn.asm:4: warning: W0005: address could be 8-bits, maybe use '<'? warn.asm:5: warning: W0006: offset could be 5-bits, maybe use '<<'? warn.asm:8: warning: W0005: address could be 8-bits, maybe use '<'? warn.asm:9: warning: W0007: offset could be 8-bits, maybe use '<'? warn.asm:11: warning: W0009: offset could be 8-bits, maybe use short branch? warn.asm:13: warning: W0011: 5-bit offset upped to 8 bits for indirect mode warn.asm:25: warning: W0012: branch to next location, maybe remove? warn.asm:26: warning: W0012: branch to next location, maybe remove? warn.asm:43: warning: W0017: cannot assign the stack address within .TEST directive warn.asm:42: debug: Running test test warn.asm:42: warning: W0014: possible self-modifying code warn.asm:42: warning: W0016: memory write of 00 to 0034 warn.asm:42: warning: W0015: : reading from non-readable memory: PC=0016 addr=F015 warn.asm:42: debug: Post assembly phases warn.asm:2: warning: W0002: symbol '.start' defined but not used [spc]lucy:~/source/asm/a09/misc>
You can see some are generated during pass 1,
some during pass 2.
The message “Running test test” happens after the second pass is done,
and the one I'm trying to supress,
W0002
,
at the very end of the program.
The .OPT
directives are processed during passes 1 and 2.
There's just no easy way to supress W0002
just for a portion of the code,
as I would have to carry forward that any labels defined between the disable and enable of W0002
should be exempt from the “no-label warning” check.
It's issues like these that complicate programs over time.
I was about to scrap the idea when I came up with a solution.
Each symbol in the symbol table has a reference count.
At the end of assembly,
there's code that goes through the symbol table and issues the warning if a label has a reference count of 0.
All I did was create another option,
.OPT * USES <label>
,
to increment the reference count of a label.
At the end of the day,
it works.
I'm not saying this is a “good” solution,
just “a” solution.