6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 5:24 am

All times are UTC




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Fri Jul 16, 2021 7:37 pm 
Offline

Joined: Mon Jun 08, 2020 7:42 pm
Posts: 22
Hi all, I am sending strings to the serial terminal (I am using an 8251 driven by the 6502) and I want to make my display routine a bit more compact by using a particular address to be loaded
with the start address of the string which is to be displayed when the routine is called, so that I can do something like:

Use particular address: "msgbase" to point to the start of the string
call the print routine which reads from msgbase which now points to the string start address

I am using vasm assembler.

At the moment I am doing the code below, but this loads string1 and would mean some repetition of the code for displaying string2 .
(I haven't shown the wait routine for the 8251 status check)

Code:
          jsr printmsg ; print message
         
end:      jmp end ; loops forever after printing

printmsg:
         ldx #0         
more:       lda string1,x   ;loads the start of string1, but I want to point to the fixed message address msgbase   
         beq end
         sta data8251
         inx
         jsr wait  ; check 8251 status
         jmp more      
         rts

msgbase: word  msgbase ; want this to point to start of the string to be displayed


What I need to do is
something similar to the principle of the lcd routine at: http://www.6502.org/mini-projects/optrexlcd/lcd.htm
but I can't get this to assemble:
Code:
          LDA #LDA #string1 ; I get the error 'operand doesn't fit into 8-bits' edit: my code starts at $FE00
          STA MSGBASE         ;store high byte of message address at the message base address used by the subroutine


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 16, 2021 7:57 pm 
Offline
User avatar

Joined: Tue Jul 17, 2018 9:58 am
Posts: 107
Location: Long Island, NY
Pointers are 16 bits, so you need additional notation to indicate which half of the pointer you are using.
#<string1 should be the low byte of the pointer
#>string1 should be the high byte


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 16, 2021 8:28 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8544
Location: Southern California
There are suggestions of efficient ways to do it in the "Inlining subroutine data" section (http://wilsonminesco.com/stacks/inlinedData.html) of the 6502 stacks treatise. You could inline the data itself, or just the address of the data. If you only use each string in one place in your code, you can put it right after the JSR, making that part of the code the shortest, but it runs a little more slowly since the subroutine then has to use the return address on the stack to find the data and then increment the return address past the data so the processor doesn't try to execute the data as code. A little faster but taking more program space in the main program is to load A and Y with the two bytes of the address of the string, and then call the subroutine.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 16, 2021 8:33 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
It's not a bad idea to load the A and Y registers with the upper and lower halves of the string address, and have the print routine set up the pointer, e.g.
Code:
    ...
example:
    lda #>message
    ldy #<message
    jsr strout
    ...

    ...
strout:
    sty ptr
    sta ptr+1
    ldy #0
    beq lp2
lp:
    jsr cout
    iny
lp2:
    lda (ptr),y
    bne lp
    rts
    ...

    ...
message:
    dcb "Hello",0
    ...

The #<message and #>message notation to break a 16-bit entity into its low and high halves is common, but not universal ... consult your assembler's documentation.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Fri Jul 16, 2021 8:46 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 16, 2021 8:45 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1385
My Monitor uses a couple ways to send a message to the (UART) console:
1- Use a message number (single byte value) which is used as an index to a message table containing the 16-bit addresses of each message (PROMPT)
2- As noted above, load the A/Y register pair with the message address (PROMPTR)

Also, I end all messages with a null character ($00)

Code is:

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
                BEQ     HINEXIT         ;If null character, exit (borrowed RTS)
                JSR     B_CHROUT        ;Send character to terminal
                INC     PROMPTL         ;Increment low byte index
                BNE     PROMPT2         ;Loop back for next character
                INC     PROMPTH         ;Increment high byte index
                BRA     PROMPT2         ;Loop back and continue printing
;
HINEXIT         RTS                     ;And return to caller


Note that the above uses CMOS instructions/addressing modes as the Y reg does not need to be loaded to $0. PROMPTL and PROMPTH are sequential page zero addresses that point to the actual address in memory that contains the message. B_CHROUT is the BIOS routine that sends a character to the UART for transmit. Using a single byte value as a message number becomes more memory efficient when the messages are used by multiple routines in the code.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 16, 2021 9:16 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1488
Location: Scotland
I've been using this, or variants of it for a long time:

Code:
; strout:
;       Output a zero terminated string to the host. The string is in-line with
;       the code and is used like:
;
;       jsr strout
;       .asciiz "Hello, world"
;  or
;       jsr strout
;       .byte   "Hello, world",13,10,0
;
; Note: This uses self-modifying code, so has to run in RAM. The main reason
;       is to not use up any page 0 locations as it's hard to find something
;       guaranteed to be free for everything.
;
;       Destroys A
;********************************************************************************

strout:

; Pull return address off the stack to use to get the data from

        pla
        sta     strX+1
        pla
        sta     strX+2

strout1:
        inc     strX+1
        bne     strX
        inc     strX+2
strX:
        lda     $FFFF           ; This word modified.
        beq     stroutEnd
        jsr     osWrch
        bra     strout1

; Push return address back onto the stack

stroutEnd:
        lda     strX+2
        pha
        lda     strX+1
        pha
        rts


This version isn't good for ROM code though, but it's not hard to change it to put the 2 address bytes in zero page then LDA (ptr), etc.

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 7:36 am 
Offline

Joined: Mon Jun 08, 2020 7:42 pm
Posts: 22
Thanks everybody for all the suggestions, I will take a read through all of the posts. I do like the ldy #<data / ldy #>data for low byte/high byte load
vasm assembler manual says it is accepted:

Code:
The parser understands a lo/hi-modifier to select low- or high-byte of a 16-bit word.
The character < is used to select the low-byte and > for the high-byte. It has to be the
first character before an expression.

Russell


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 9:03 am 
Offline

Joined: Mon Jun 08, 2020 7:42 pm
Posts: 22
[quote="barrym95838"]It's not a bad idea to load the A and Y registers with the upper and lower halves of the string address, and have the print routine set up the pointer, e.g.
Code:
    ...
example:
<edited by me>
lp2:
    lda (ptr),y
    bne lp
    rts
    ...

    ...
message:
    dcb "Hello",0
    ...

Thanks for that I get an assembly error when trying to use:
Code:
lda (ptr),y

Since my code resides from $FE00 - A convenience for loading from a rom emulator. I can move my code though (and there is ram at the lower addresses for the stack etc). Or I suppose I'll need to use the #< directive
I'll also have a look at the inline versions suggested above.
looking at: http://wilsonminesco.com/stacks/inlinedData.html
where is a PHX instruction in 6502 opcodes?
I see:
Code:
ISR:    PHA          ; (Later you may need to push Y also, with PHY, if the
        PHX          ; ISR uses Y anywhere. NMOS will have to go through A.)


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 10:22 am 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1488
Location: Scotland
rascalsailor wrote:
where is a PHX instruction in 6502 opcodes?
I see:
Code:
ISR:    PHA          ; (Later you may need to push Y also, with PHY, if the
        PHX          ; ISR uses Y anywhere. NMOS will have to go through A.)


It's a 65C02 instruction. You may need to tell your assembler/emulator to use 65C02 mode rather than strict 6502 mode.

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 10:40 am 
Offline

Joined: Mon Jun 08, 2020 7:42 pm
Posts: 22
drogon wrote:
rascalsailor wrote:
where is a PHX instruction in 6502 opcodes?
I see:
Code:
ISR:    PHA          ; (Later you may need to push Y also, with PHY, if the
        PHX          ; ISR uses Y anywhere. NMOS will have to go through A.)


It's a 65C02 instruction. You may need to tell your assembler/emulator to use 65C02 mode rather than strict 6502 mode.

-Gordon

Ah, ok - although I am using a genuine Rockwell 6502


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 11:31 am 
Offline

Joined: Mon Jun 08, 2020 7:42 pm
Posts: 22
drogon wrote:
I've been using this, or variants of it for a long time:

Code:
; strout:
;       Output a zero terminated string to the host. The string is in-line with
;       the code and is used like:
;
;       jsr strout
;       .asciiz "Hello, world"
;  or
;       jsr strout
;       .byte   "Hello, world",13,10,0
;
; Note: This uses self-modifying code, so has to run in RAM. The main reason
;       is to not use up any page 0 locations as it's hard to find something
;       guaranteed to be free for everything.
;
;       Destroys A
;********************************************************************************

strout:

; Pull return address off the stack to use to get the data from

        pla
        sta     strX+1
        pla
        sta     strX+2

strout1:
        inc     strX+1
        bne     strX
        inc     strX+2
strX:
        lda     $FFFF           ; This word modified.
        beq     stroutEnd
        jsr     osWrch
        bra     strout1

; Push return address back onto the stack

stroutEnd:
        lda     strX+2
        pha
        lda     strX+1
        pha
        rts


This version isn't good for ROM code though, but it's not hard to change it to put the 2 address bytes in zero page then LDA (ptr), etc.

-Gordon

Hi Gordon - I'm trying to understand this use of the stack to use for inlining the data. Could you explain a few things in this code, please?
The return address is stored at strX+1 and strX+2 right? But this has other code there it seems? What is the lda $FFFF doing? Is this loading from a byte of the IRQ vector?
Sorry a bit confused.
Russell


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 12:44 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1488
Location: Scotland
rascalsailor wrote:
drogon wrote:
I've been using this, or variants of it for a long time:

Code:
; strout:
;       Output a zero terminated string to the host. The string is in-line with
;       the code and is used like:
;
;       jsr strout
;       .asciiz "Hello, world"
;  or
;       jsr strout
;       .byte   "Hello, world",13,10,0
;
; Note: This uses self-modifying code, so has to run in RAM. The main reason
;       is to not use up any page 0 locations as it's hard to find something
;       guaranteed to be free for everything.
;
;       Destroys A
;********************************************************************************

strout:

; Pull return address off the stack to use to get the data from

        pla
        sta     strX+1
        pla
        sta     strX+2

strout1:
        inc     strX+1
        bne     strX
        inc     strX+2
strX:
        lda     $FFFF           ; This word modified.
        beq     stroutEnd
        jsr     osWrch
        bra     strout1

; Push return address back onto the stack

stroutEnd:
        lda     strX+2
        pha
        lda     strX+1
        pha
        rts


This version isn't good for ROM code though, but it's not hard to change it to put the 2 address bytes in zero page then LDA (ptr), etc.

-Gordon

Hi Gordon - I'm trying to understand this use of the stack to use for inlining the data. Could you explain a few things in this code, please?
The return address is stored at strX+1 and strX+2 right? But this has other code there it seems? What is the lda $FFFF doing? Is this loading from a byte of the IRQ vector?
Sorry a bit confused.
Russell


It's just a placeholder. Anything > 255 to force an LDA Absolute instruction. ($AD) The $FFFF is overwritten everytime it's called. Overwritten -> Self Modifying code. The LDA $FFFF gets turned into LDA $1234, or whatever the address of the string following the JSR strout is.

That's why this version isn't good for ROM - you can't change a ROM, however you could write the data to a pair of zero-page location and use that for real ROM code (My Ruby system is all RAM, so that's not a concern for me)

So the data of the string itself isn't on the stack, just the return address after the JSR instruction. The subroutine pulls this address off the stack then (adds 1) and uses it as a pointer to the data which is in-line with the program code. At the end of the print-string code, it pushes those 2 bytes back onto the stack - and executes the RTS. The CPU returns to the byte after the terminating zero byte of the string.

I do it this way to save a few bytes in my code - it also feels more intuitive to me - viz.

Code:
PRINT "Hello, World"


if BASIC or any similar programming language where the PRINT instruction is then followed by the data...

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 2:42 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
rascalsailor wrote:
Thanks for that I get an assembly error when trying to use:
Code:
lda (ptr),y

You may need to define (or "equate") ptr to a zero-page location before referencing it in this manner in your source, e.g.
Code:
    ...
ptr     equ 2       ; zp locations 2 and 3 hold a 16-bit address
    ...

    ...
    ldy #4
    sty ptr
    lda #5
    sta ptr+1
    lda (ptr),y     ; effective address is $0508
It's also possible that your assembler uses an alternate format to specify (zp),y addressing ... this notation is very common, but still not universal.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Last edited by barrym95838 on Sat Jul 17, 2021 3:14 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 3:06 pm 
Offline

Joined: Mon Jun 08, 2020 7:42 pm
Posts: 22
@Drogon: Thanks for that - so perhaps you could just reserve 3 bytes as:
Code:
.reserve 5
instead?

@barrym95838 - Yes I would probably need to do a zero page location.

I did find this which does the job too, see: http://6502.org/source/io/primm.htm

cheers
Russell


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 17, 2021 3:24 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1488
Location: Scotland
rascalsailor wrote:
@Drogon: Thanks for that - so perhaps you could just reserve 3 bytes as:
Code:
.reserve 5
instead?


3 not 5. Also you need to make sure the first byte is the correct LDA instruction. ie. $AD. you could do something like

Code:
    .byte    $AD,0,0


but putting the LDA in there explicitly make more sense to me.

Quote:
@barrym95838 - Yes I would probably need to do a zero page location.

I did find this which does the job too, see: http://6502.org/source/io/primm.htm


The last version on that page is the closest to my method using a ZP pointer rather than self modifying code.

Cheers,

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 15 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: