3

I've been fooling around with the Merlin 8 (v2.58) assembler on my Apple IIe and ran into a small snag.

Basically, I would like to load the address of a block of data into a two-byte pair in zero page. The data block might represent ASCII text, or a lookup table or just about anything. In my case, I want to do this as a way of passing the pointer to a subroutine.

I've written a short "Hello World" program that shows an example of what I am trying to do. Please note, this question isn't about the best way to print text or taking advantage of possible convenience features or Macros within Merlin. I just wrote this code as an example of what I am trying to do.

         ORG   $0900

STRLO    EQU   $08
STRHI    EQU   $09

CHROUT   EQU   $FDED

START    LDA   PTR        ;DATA PTR LSB TO STRLO
         STA   STRLO
         LDA   PTR+1      ;DATA PTR MSB TO STRHI
         STA   STRHI
         JSR   PRINT      ;PRINT
         RTS              ;ALL DONE

PRINT    LDY   #$00
:CONT    LDA   (STRLO),Y
         BEQ   :DONE
         JSR   CHROUT
         INY
         JMP   :CONT
:DONE    RTS

DATA     ASC   "HELLO RCSE!",00
PTR      DA    DATA

If you look at the listing, there is a subroutine labeled PRINT that expects an address to be already loaded into two zero page registers STRLO and STRHI. As it's name implies, PRINT will simply start spitting out characters starting at the address in STRLO/STRHI until a null terminator is encountered. This means the calling thread is responsible for loading the address of the data to be printed into STRLO/STRHI.

This is where things seem overly complicated, which is a sure sign I am missing something simple. Merlin allows a convenient mechanism to define and label my data block (in this example my data is simply a short string) like this:

DATA    ASC    "HELLO RCSE!",00

What I would like to know is the correct way to load the address of this data into STRLO/STRHI before calling PRINT. My first try was to use the DATA label directly like this:

START    LDA   DATA
         STA   STRLO
         LDA   DATA+1
         STA   STRHI

but this results in the loading the first two bytes of the data, not the address of the data.

My second attempt to was to try adding the immediate mode prefix # to my LDA instructions, like this:

START    LDA   #DATA
         STA   STRLO
         LDA   #DATA+1
         STA   STRHI

but that resulted in simply loading 0 (zero) and 1 (one) into STRLO/STRHI.

Ultimately, the only thing I have figured out is that I can get the address by defining a second label to serve as a pointer to the data (as shown in my code). This works, but it seems clunky and makes me feel like I am simply missing the correct syntax to work with the address of DATA directly.

In modern terms, having to define a second label to serve as a pointer to the actual label has a bad smell, not to mention it doubles the number of labels for things like string tables, etc. Can anyone shed some light on this for me?

3 Answers 3

6

In assembler, a label is just a number representing an address. On the 6502, addresses are 16 bits, but the accumulator can only contain 8 bits at a time. What you need is to extract the high and low halves of the address as distinct immediate operands, so that you can store them in the zero-page pointer location.

I'm not familiar with this particular assembler's capabilities, but often there is a prefix character that you can use to extract the high and low halves of an address or other value. In relatively modern assemblers, these would be:

LDA #<DATA  ; load low half of address
STA STRLO   ; store it to ZP
LDA #>DATA  ; load high half of address
STA STRHI   ; store it to ZP

From the WDC W65C816S datasheet: enter image description here

In an assembler that supports performing arithmetic at assembly time, you could also write (DATA DIV 256) and (DATA MOD 256), or whatever the local syntax is for those operators. Modern assemblers often support / and % respectively.

4
  • 1
    Most assemblers (64tass, ACME, ca65) treat #< and #> as byte-selection operators. Merlin treats them as shift+mod operators. For 8-bit code the effect is the same, but it can surprise you with 16-bit code. Your DATA DIV 256 example might throw an error if you attempted that with a 24-bit constant for an 8-bit immediate instruction.
    – fadden
    Commented Mar 30, 2020 at 4:39
  • @fadden The question was in the context of the 6502 specifically. I referred to the '816 datasheet as it was the one place I knew I could find those "standard" modifiers documented.
    – Chromatix
    Commented Mar 30, 2020 at 11:32
  • @fadden Additionally, as both the OP's example and the above table suggest, immediate operands are in fact truncated to fit the available data size, without generating an error.
    – Chromatix
    Commented Mar 30, 2020 at 13:49
  • "Truncating to fit available data size" is assembler-specific. The datasheet recommends, but does not define, assembler behavior. For example, in ca65, LDA #DATA / 256 will work, but LDA #DATA / 16 will not ("range error"). Merlin is aggressive when it comes to truncating expression results, more so than the other popular 65xx cross-assemblers.
    – fadden
    Commented Mar 30, 2020 at 14:44
4

Immediate mode constant allow the use of modifiers to select high/low byte of an address.

From the Merlin manual:

6.4  Immediate Data

For  those opcodes such as LDA, CMP, etc., which  accept  immediate
data  (numbers  as  opposed  to  the  contents  of  addresses)  the
immediate mode is signaled by preceding the expression with a  "#".
An example is LDX #3 which would load the X register with the value
3 rather than the contents of address 3.  In addition:

     #<expression   Produces the low byte of the expression.

     #>expression   Produces the high byte of the expression.

     #expression    Also produces the low byte  of  the  expression
                    (the 6502 does not accept 2-byte data).

     #/expression   Produces the high byte of the expression and is
                    an optional syntax.

The recommended syntax is  "<"  and  ">"  for  low  and  high  byte
respectively.   Whatever  syntax  you use, be consistent, use it in
every case so your reader won't think there  is  something  special
about the "one case" where you specify "<" for low byte.

So a proper (and common) solution might be:

START    LDA   #<DATA     ;DATA PTR LSB TO STRLO
         STA   STRLO
         LDA   #>DATA     ;DATA PTR MSB TO STRHI
         STA   STRHI
2

Apparently, the correct syntax is as follows:

START    LDA   #<DATA
         STA   STRLO
         LDA   #>DATA
         STA   STRHI

The #< and #> seem to indicate the the assembler that we are going for the LSB and MSB of the address for the DATA, not the DATA itself.

I'll leave the question open in case someone wants to elucidate on the why/what/wherefore.

2
  • 2
    # tells it to use the immediate-mode instruction, e.g. your LDY #$00. < and > shift by 0 and 8 bits, respectively, then discard all but the low 8 bits. Merlin is pretty aggressive about implicit mod operations, so you could write the first one as LDA #DATA. Other assemblers might complain that DATA doesn't fit in an 8-bit operand, so it's probably wise to make #< a habit.
    – fadden
    Commented Mar 30, 2020 at 4:45
  • See, what is weird is I tried #DATA and only got a zero, NOT the LSB of the address. So I dismissed the idea of immediate mode as the solution. but everything I read confirms that, yes, #DATA should be the LSB. I'm happy to use #< and #> but I don't really understand why #DATA wasn't working. Operator Error? lol.
    – Geo...
    Commented Mar 30, 2020 at 15:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .