The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

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.

Obligatory Picture

[The future's so bright, I gotta wear shades]

Obligatory Contact Info

Obligatory Feeds

Obligatory Links

Obligatory Miscellaneous

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: https://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

https://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2024 by Sean Conner. All Rights Reserved.