6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 6:21 pm

All times are UTC




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: get label address
PostPosted: Wed Sep 07, 2022 11:30 am 
Offline

Joined: Fri Jul 01, 2022 4:15 pm
Posts: 5
I'd like to create a kind of table for strings for printing purposes like this:
Code:
s1: .asciiz "string 1"
s2: .asciiz "string 2"
s3: .asciiz "string 3"


The table will be hard coded in rom and when I need one of those strings I'd like to move the address of a label to a zp location.
But I don't know how to get the address of a label.
Can someone give me a hint please?


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Wed Sep 07, 2022 11:44 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
Usually the value of the label is the address, so you just use the label name as a value:

Code:
LDX #0
again:
LDA s1,X
BEQ done
JSR print
INX
BNE again
done:

That's clumsy code, but you see what I mean. It should work, and it doesn't mind that s1 is a 16 bit address and therefore a 16 bit value, because "absolute, X" is a valid addressing mode.

To use indirect addressing you need to put s1, as a two byte value, into two successive bytes of zero page, which means you need to know what your assembler offers, as a way to get hold of each byte separately.

Code:
LDA #<s1
STA zp1
LDA #>s1
STA zp1+1
and now you can use
Code:
LDY#0
LDA (zp1),Y

in a loop rather like the one above.


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Wed Sep 07, 2022 3:55 pm 
Offline

Joined: Fri Jul 01, 2022 4:15 pm
Posts: 5
Thank you so much, that's exactly what I was looking for.
I had no idea about < >.


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Wed Sep 07, 2022 4:32 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
There's every chance I got them the wrong way around, so be vigilant!


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Wed Sep 07, 2022 5:39 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8510
Location: Midwestern USA
djenn wrote:
Thank you so much, that's exactly what I was looking for.
I had no idea about < >.

In assemblers that follow the MOS Technology syntax standard, < as an operator refers to the least significant byte (LSB) of a quantity, and > refers to the most significant byte (MSB). Hence the assembler would understand the instruction LDX #<$AB12 to mean LDX #$12 and would also understand LDY #>$AB12 to mean LDY #$AB. So if I write:

Code:
         ldx #<s1               ;LSB of string address
         ldy #>s1               ;MSB of string address
         jsr sprint             ;print string

...and if the address of S1 is $AB12, what I have effectively written is:

Code:
         ldx #$12               ;LSB of string address
         ldy #$ab               ;MSB of string address
         jsr sprint             ;print string

As a general rule, a label or symbol name may be substituted for a hard-coded numeric value, assuming the value represented by the label or symbol is of a magnitude that is allowed by the intended usage. Hence something such as LDA S1 would be acceptable—it would be treated as either zero page or absolute addressing, depending on the value of S1, whereas LDA #S1 would cause an error if S1 is greater than $FF, since immediate mode addressing is limited to an eight bit quantity.

Within the SPRINT subroutine, you could write something like this, assuming your strings are null-terminated (I'm not familiar with an .asciiz pesudo-op, but it sounds like something that would create a null-terminated string):

Code:
;sprint: print a character string
;
;   syntax: ldx #<string
;           ldy #>string
;           jsr sprint
;
;   String must be null-terminated.
;
sprint   stx zpptr01           ;set up ZP...
         sty zpptr01+1         ;pointer to string
         ldy #0                ;starting string index
;
sprint10 lda (zpptr01),y       ;get from string
         beq sprint20          ;end reached
;
         jsr putch             ;output character to somewhere
         iny                   ;bump index
         bne sprint10          ;get next
;
         inc zpptr01+1         ;move to next page &...
         bne sprint10          ;resume
;
sprint20 rts                   ;return to caller

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Thu Sep 08, 2022 5:46 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Thu Sep 08, 2022 1:58 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1385
Depending on how often you use some of the text strings in your code, there's also the option of creating a table and accessing your messages via a single numerical value, such as message #1. I use this method in my Monitor code as several of the short text messages are used by multiple routines. Being able to use a call such as:

Code:
SRCHTXT         LDA     #$08            ;Get msg " find text:"
                JSR     PROMPT          ;Send to terminal


is shorter overall, but then you need a way to define the address of each message. That's easy as well:

Code:
MSG_00  .DB     " cont?"
MSG_01  .DB     "(y/n)"
        .DB     $00
MSG_02  .DB     $0D,$0A
        .DB     "   "
MSG_03  .DB     " addr:"
        .DB     $00
MSG_04  .DB     " len:"
        .DB     $00
MSG_05  .DB     " val:"
        .DB     $00
MSG_06  .DB     " src:"
        .DB     $00
MSG_07  .DB     " tgt:"
        .DB     $00
MSG_08  .DB     " find txt:"
        .DB     $00
MSG_09  .DB     " find bin:"
        .DB     $00
MSG_0A  .DB     "not "
MSG_0B  .DB     "found"
        .DB     $00
MSG_0C  .DB     $0D,$0A
        .DB     "search- "
        .DB     $00
MSG_0D  .DB     $0D,$0A
        .DB     "(n)ext? "
        .DB     $00
MSG_0E  .DB     "SR:$"
        .DB     $00
MSG_0F  .DB     "SP:$"
        .DB     $00
;
MSG_TABLE       ;Message table: contains message addresses sent via the PROMPT routine
        .DW     MSG_00, MSG_01, MSG_02, MSG_03, MSG_04, MSG_05, MSG_06, MSG_07
        .DW     MSG_08, MSG_09, MSG_0A, MSG_0B, MSG_0C, MSG_0D, MSG_0E, MSG_0F


And the code that you call to send the message to a terminal:

Code:
;PROMPT routine: Send indexed text string to terminal. Index is contained in A Reg.
; String buffer address is stored in variable PROMPTL/PROMPTH.
PROMPT          ASL     A               ;Multiply by two for msg table index
                TAX                     ;Xfer to X Reg - index
                LDA     MSG_TABLE,X     ;Get low byte address
                LDY     MSG_TABLE+1,X   ;Get high byte address
;
;PROMPTR routine: takes message address in Y/A and prints via PROMPT2 routine
PROMPTR         STA     PROMPTL         ;Store low byte
                STY     PROMPTH         ;Store high byte
;
;PROMPT2 routine: prints message at address (PROMPTL) till null character found
PROMPT2         LDA     (PROMPTL)       ;Get string data (5)
                BEQ     HINEXIT         ;If null character, exit (borrowed RTS) (2/3)
                JSR     B_CHROUT        ;Send character to terminal (6)
                INC     PROMPTL         ;Increment low byte index (5)
                BNE     PROMPT2         ;Loop back for next character (2/3)
                INC     PROMPTH         ;Increment high byte index (5)
                BRA     PROMPT2         ;Loop back and continue printing (3)
;
HINEXIT         RTS                     ;And return to caller
;


Hope this helps...

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Thu Sep 08, 2022 7:56 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
If the sum total of your messages is less than 256 bytes, you can go even smaller:

floobydust wrote:
Depending on how often you use some of the text strings in your code, there's also the option of creating a table and accessing your messages via a single numerical value, such as message #1. I use this method in my Monitor code as several of the short text messages are used by multiple routines. Being able to use a call such as (note that the index is in X, not A):

Code:
SRCHTXT         LDX     #MNM_08         ;Get msg " find text:"
                JSR     PROMPT          ;Send to terminal


is shorter overall, but then you need a way to define the address of each message. That's easy as well:

Code:
MSG_00  .DB     " cont?"
MSG_01  .DB     "(y/n)"
        .DB     $00
MSG_02  .DB     $0D,$0A
        .DB     "   "
MSG_03  .DB     " addr:"
        .DB     $00
MSG_04  .DB     " len:"
        .DB     $00
MSG_05  .DB     " val:"
        .DB     $00
MSG_06  .DB     " src:"
        .DB     $00
MSG_07  .DB     " tgt:"
        .DB     $00
MSG_08  .DB     " find txt:"
        .DB     $00
MSG_09  .DB     " find bin:"
        .DB     $00
MSG_0A  .DB     "not "
MSG_0B  .DB     "found"
        .DB     $00
MSG_0C  .DB     $0D,$0A
        .DB     "search- "
        .DB     $00
MSG_0D  .DB     $0D,$0A
        .DB     "(n)ext? "
        .DB     $00
MSG_0E  .DB     "SR:$"
        .DB     $00
MSG_0F  .DB     "SP:$"
        .DB     $00

; Depending on your assembler, you may be able to generate these semi-automatically in a loop...
MNM_00  .EQU    0
MNM_01  .EQU    MSG_01-MSG_00
MNM_02  .EQU    MSG_02-MSG_00
MNM_03  .EQU    MSG_03-MSG_00
MNM_04  .EQU    MSG_04-MSG_00
MNM_05  .EQU    MSG_05-MSG_00
MNM_06  .EQU    MSG_06-MSG_00
MNM_07  .EQU    MSG_07-MSG_00
MNM_08  .EQU    MSG_08-MSG_00
MNM_09  .EQU    MSG_09-MSG_00
MNM_0A  .EQU    MSG_0A-MSG_00
MNM_0B  .EQU    MSG_0B-MSG_00
MNM_0C  .EQU    MSG_0C-MSG_00
MNM_0D  .EQU    MSG_0D-MSG_00
MNM_0E  .EQU    MSG_0E-MSG_00
MNM_0F  .EQU    MSG_0F-MSG_00


And the code that you call to send the message to a terminal, assuming that B_CHROUT preserves register X:

Code:
;PROMPT routine: Send indexed text string to terminal. Index is contained in X Reg.
PROMPT1         JSR     B_CHROUT        ;Send character to terminal (6)
                INX                     ;Increment byte index (2)
PROMPT          LDA     MSG_00,X        ;Get string data (4)
                BNE     PROMPT1         ;If null character, exit (borrowed RTS) (2/3)
;
HINEXIT         RTS                     ;And return to caller
;


Hope this helps...


If you do not need any concatenation as with MSG_0A, save another byte per message by setting the upper bit of the last character in each string instead of with a 0 byte to terminate.


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Thu Sep 08, 2022 2:06 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1385
Yes, as noted, there are other ways to send text strings. Nice approach BTW... I've used the high-bit set method before, back in the C64 days, as was common with much of that coding.

The CHROUT routine (the BIOS call) doesn't disturb any registers:

Code:
;Data Output A routine: puts the data in the A Reg into the xmit buffer, data in
; A Reg is preserved on exit. Transmit is IRQ driven/buffered with a size of 128 bytes.
;
CHROUT          PHY                     ;Save Y Reg (3)
OUTCH           LDY     OCNT_A          ;Get data output count in buffer (3)
                BMI     OUTCH           ;Check against limit, loop back if full (2/3)
;
                LDY     OTAIL_A         ;Get the buffer tail pointer (3)
                STA     OBUF_A,Y        ;Place data in the buffer (5)
                INC     OTAIL_A         ;Increment Tail pointer (5)
                RMB7    OTAIL_A         ;Strip off bit 7, 128 bytes only (5)
                INC     OCNT_A          ;Increment data count (5)
;
                LDY     #%00000100      ;Get mask for xmit on (2)
                STY     UART_COMMAND_A  ;Turn on xmit (4)
;
                PLY                     ;Restore Y Reg (4)
                RTS                     ;Return to caller (6)
;


However, I do have a large number of messages in my Monitor code, 74 messages, with a total of 1380 bytes, plus another 148 bytes for the message table. The largest message is 449 bytes in size, as it's the result of the Query command, which displays all of the commands in a single text string.

Code:
MSG_49  .DB     $0D,$0A
        .DB     "Memory Ops: "
        .DB     "[C]ompare, "
        .DB     "[D]isplay, "
        .DB     "[E]dit, "
        .DB     "[F]ill, "
        .DB     "[G]o Exec,",$0D,$0A
        .DB     "[H]ex Find, "
        .DB     "[I]nput Text, "
        .DB     "[M]ove, "
        .DB     "[T]ext Find",$0D,$0A,$0A
        .DB     "Register Ops: "
        .DB     "R,A,X,Y,S,P",$0D,$0A,$0A
        .DB     "Counter/Timer Ops: "
        .DB     ",= set ms|mult, "
        .DB     ".= exe ms, "
        .DB     "/= exe ms*mult",$0D,$0A
        .DB     "[B]enchmark clear/start, "
        .DB     "[Q]uit benchmark/display elapsed time",$0D,$0A,$0A
        .DB     "Macro: "
        .DB     "(= Init "
        .DB     ")= Run",$0D,$0A,$0A
        .DB     "CTRL[?]: "
        .DB     "[A]ssemble, "
        .DB     "[B]oot DOS/65, "
        .DB     "[D]isassemble, "
        .DB     "[E]dit EEPROM, "
        .DB     "[L]oad",$0D,$0A
        .DB     "[P]rogram, "
        .DB     "[Q]uery Cmds ,"
        .DB     "[R]eset, "
        .DB     "[S]ave, "
        .DB     "[T]ime/Date, "
        .DB     "[V]ersion",$0D,$0A
        .DB     "[Z]ero RAM/Reset",$0A
        .DB     $00
;


Yea, I know.... a large number of messages, but then again, the Monitor has a lot of functions (the Assembler part isn't completed yet) but it still uses just under 5KB of ROM space, from an allocated space of 6KB. The BIOS code and I/O space fit in an allocated space of 2KB and I still have some available space there too.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Fri Sep 09, 2022 8:28 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
Your longest message will continue to have to be done with (PROMPTL).

If you need to save space, you can create multiple message tables. With an appropriate naming convention, it is not very difficult to keep them straight:

Code:
    ldx     #M1N_08
    jsr     PROMPT1

    ldx     #M2N_09
    jsr     PROMPT2


You will have to give some back in the form of multiple "prompt" routines at 10 bytes each though.

The assembler in my monitor is a little over 1 KBytes. That figure does not include the strings for the instruction mnemonics shared with the disassembler. I am not used to coding for small size, so that is almost certain not to be the smallest possible implementation.


Top
 Profile  
Reply with quote  
 Post subject: Re: get label address
PostPosted: Thu Oct 20, 2022 2:41 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 411
Location: Minnesota
Of course you can also do away with any kind of "address of message" table altogether by simply starting at the beginning of the message table and counting down each time you reach the end of a message. When the count hits zero (allowing 256 messages, assuming the count is in X- or Y-) or becomes negative (limiting messages to 128), you've reached the start of the message you want to print.

This is of course slower than using an address table, but if speed is not a huge worry, a net size benefit if your message counting loop is smaller than an address table would be.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 41 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: