Complicating code

I recently added an .OPT directive to my 6809 assembler [1]. 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 [2] 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.

[1] https://github.com/spc476/a09

[2] /boston/2023/12/11.1

Gemini Mention this post

Contact the author