Newbie question: accessing a 16-bit memory address

Programming the 6502 microprocessor and its relatives in assembly and other languages.
wstjohn
Posts: 19
Joined: 04 Mar 2007

Newbie question: accessing a 16-bit memory address

Post by wstjohn »

I have a two byte variable in zero page memory which contains the 16-bit address of a ROM table.
I need to access that memory location, but I'm confused on how to do it.
I can LDA , but how do I 'concatenate' the low and hi bytes of the address? Or is there a better way?
Thanks, here's my code:

PF0_addr .word ;zero page variable
...

SET_POINTER PF0_addr, TitleScreen_PF0 ;Macro storing addr of TitleScreen into PFO_addr
...

lda PF0_addr-1,x ;???????
sta PF0
...

TitleScreen_PF0
.byte #%00001100 ; ROM table
.byte #%00010010
...

MAC SET_POINTER
.POINTER SET {1}
.ADDRESS SET {2}

LDA #<.ADDRESS ; Get Lowbyte of Address
STA .POINTER ; Store in pointer
LDA #>.ADDRESS ; Get Hibyte of Address
STA .POINTER+1 ; Store in pointer+1

ENDM
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

I'm not sure exactly what you're trying to do, but if you don't need indexing and all you want to do is load the accumulator with the contents of the address pointed to by a pair of bytes in ZP, all you need is the one instruction LDA (ZP). It's a two-byte instruction, op code B2, with one byte of operand, and it takes 5 clocks to execute.

If you need indexing into the table and the table is no more than 256 bytes, put the index value in Y and use LDA (ZP),Y, again a 2-byte instruction, with op code B1. Then to step through the table, just increment or decrement Y with the single-byte, two-clock instructions INY or DEY, instead of having to modify the address in ZP.

If you know where the table is at compilation time so you don't even need to refer to the table's address in ZP, just use LDA abs,X, a 3-byte, 4-clock instruction with op code BD.
wstjohn
Posts: 19
Joined: 04 Mar 2007

Still not working...

Post by wstjohn »

Thanks for your help. This is what I tried...
I have a 2 byte pointer in ZP memory, and another var (ScreenFake) in ZP with an arbitrary value.
I also have a byte past ZP memory (Screen_PF1) with different data. In this example I explicitly set the address for testing purposes. In the real program I wont know it at compile time.
Anyway, the code runs, but only seems to get the LSB of the address, thus loading the data in ScreenFake, not Screen_PF1...
What am I doing wrong?
Thanks again.

Code:
;------------------------------------------------
; Addressing Test
;------------------------------------------------
processor 6502
include vcs.h
include macro.h
;------------------------------------------------
; Constants and Variables
;------------------------------------------------
SEG.U variables
ORG $80
PF1_pointer .word
ScreenFake = $AA
Screen_PF1 = $01AA

;------------------------------------------------
; START OF ROM
;------------------------------------------------
SEG Bank0
ORG $F000 ; 4k ROM start point
;-------------------------------------------------------------------------
; Game
;------------------------------------------------
Start
CLEAN_START ; Clear RAM and Registers

lda #%10001000
sta Screen_PF1
lda #%11111111
sta ScreenFake

SET_POINTER PF1_pointer, Screen_PF1
ldy #0
lda (PF1_pointer),Y
sta PF1

lda #$94
sta COLUPF

.mainLoop
jsr VERTICAL_BLANK
jsr DRAW
jsr WAIT_V_BLANK
jsr OVERSCAN
jmp .mainLoop

;-------------------------------------------------------------------------
; SUBROUTINES
;------------------------------------------------
;***** VERTICAL_BLANK *****
VERTICAL_BLANK
lda #0
sta VBLANK
VERTICAL_SYNC
lda #41 ;41 64-cycle ticks
sta TIM64T
RTS

;***** WAIT_V_BLANK *****
WAIT_V_BLANK
.waitForVBlank
lda INTIM
bne .waitForVBlank
RTS

;***** DRAW *****
DRAW
ldx #220
.drawLine
sta WSYNC
dex
bne .drawLine
RTS

;***** OVERSCAN *****
OVERSCAN
lda #%01000010
sta VBLANK
lda #35
sta TIM64T
.waitForOverscan
lda INTIM
bne .waitForOverscan
RTS

;------------------------------------------------
; Interrupt Vectors
;------------------------------------------------
echo [*-$F000]d, " ROM bytes used"
ORG $FFFA
.word Start ; NMI
.word Start ; RESET
.word Start ; IRQ
END
wstjohn
Posts: 19
Joined: 04 Mar 2007

PS

Post by wstjohn »

PS - I don't need indexing and all I want to do is load the accumulator with the contents of the address pointed to by a pair of bytes in ZP,
but this didnt work:

lda (PF1_pointer)

it told me it was an illegal addressing mode...
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

Quote:
Anyway, the code runs, but only seems to get the LSB of the address, thus loading the data in ScreenFake, not Screen_PF1...
What am I doing wrong?
You don't show your macro definitions, but it sounds like SET_POINTER is only taking care of one byte, and not putting the 1 (high byte of Screen_PF1's 1FF) at the next byte after the FF at PF1_pointer.
Quote:
it told me it was an illegal addressing mode...
It's standard for all CMOS 6502's. AFAIK, the NMOS one have not been made in two decades, but Western Design Center is making CMOS ones with no end in sight, with more instructions and addressing modes than the NMOS ones had. You might need to tell your assembler you're using CMOS by using the line "processor 65c02", if indeed you are. The only reason not to use CMOS is if you're using old hardware like the Commodore 64 which used the 6510 which was never avaialble in a CMOS version. With an NMOS 6502, your putting 0 in Y and using LDA (ZP),Y is the best way to do it.

Depending on whether the stack is getting initialized at 1FF at reset and whether there's a system running in the background that might eat up a lot of stack space, it might be a little dangerous to put Screen_PF1 in $01AA where it could get stepped on by the stack. Page 1, especially high in the page, is generally not a good place to put variables.

When you list code on this forum, in order to keep the spaces from getting gobbled up and everything shoved against the left margin, bracket the code with left-square-bracket CODE right-square-bracket (or just mark the block and then click "Code"), and uncheck the "disable BB code" box at the bottom.
wstjohn
Posts: 19
Joined: 04 Mar 2007

SET_POINTER macro

Post by wstjohn »

Garth, thanks for the replies. I'm just getting started and this is clearing a lot of things up.
I am coding for the Atari VCS, so I guess I will just use a zero offset when addressing like this.
Here is the SET_POINTER macro. I didn't write it, but it looks like it should write the low and high bytes...

Code: Select all

;-------------------------------------------------------
; SET_POINTER
; Original author: Manuel Rotschkar
;
; Sets a 2 byte RAM pointer to an absolute address.
;
; Usage: SET_POINTER pointer, address
; Example: SET_POINTER SpritePTR, SpriteData
;
; Note: Alters the accumulator, NZ flags
; IN 1: 2 byte RAM location reserved for pointer
; IN 2: absolute address

            MAC SET_POINTER
.POINTER    SET {1}
.ADDRESS    SET {2}

                LDA #<.ADDRESS  ; Get Lowbyte of Address
                STA .POINTER    ; Store in pointer
                LDA #>.ADDRESS  ; Get Hibyte of Address
                STA .POINTER+1  ; Store in pointer+1

            ENDM
wstjohn
Posts: 19
Joined: 04 Mar 2007

SET_POINTER works, but...

Post by wstjohn »

OK, I changed the address to $0AAA.
I did some tests and it looks like both the low and high bytes are getting set,
but LDA (PF1_pointer),#0 only goes to the low byte $0A and
LDA (PF1_pointer+1),#0 only goes to the high byte $AA.
So I still cant get the data from $0AAA...
User avatar
BitWise
In Memoriam
Posts: 996
Joined: 02 Mar 2004
Location: Berkshire, UK
Contact:

Re: SET_POINTER works, but...

Post by BitWise »

wstjohn wrote:
LDA (PF1_pointer),#0
LDA (PF1_pointer+1),#0
If LDA (PF1_pointer),Y is a valid memory pointer the LDA (PF1_Pointer+1),Y probably isn't. You need something like this

Code: Select all

LDY #0
LDA (PF1_pointer),Y ; Fetch first byte
STA XXX ; And do something with it
INY
LDA (PF1_pointer),Y ; Fetch following byte.
STA YYY ; ...
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
wstjohn
Posts: 19
Joined: 04 Mar 2007

Post by wstjohn »

BitWise,
I'm not sure I understand you. In my example, PF1_pointer is a 2 byte pointer to some location past zero page memory, say $FFAA.
The first byte contains the low byte of the address #$AA and the second byte contains the high byte of the address #$FF.
I need to use the pointer to somehow load the value at $FFAA into a register.

Can LDA (PF1_pointer), #0 do this?

Thanks for the input.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

When you do LDY #0, LDA (PF1_pointer),Y, the processor reads two bytes in ZP to get the 16-bit address at PF1_pointer, and adds the Y value to it to get the address of the one byte it will read into the accumulator. The Y being outside the parentheses means the Y value is not added until after the address is read from ZP. So LDA (anything),Y will always read two consecutive ZP bytes, and of course put one byte at the final resulting address into A.
Quote:
Can LDA (PF1_pointer), #0 do this?
LDA (PF1_pointer),Y can do it, all in one instruction if 0 is already in Y. The way you wrote it, although not a legal notation, is what LDA (PF1_pointer) on the 65c02 would do, without your needing to load 0 into Y first.
User avatar
BitWise
In Memoriam
Posts: 996
Joined: 02 Mar 2004
Location: Berkshire, UK
Contact:

Post by BitWise »

wstjohn wrote:
BitWise,
I'm not sure I understand you. In my example, PF1_pointer is a 2 byte pointer to some location past zero page memory, say $FFAA.
The first byte contains the low byte of the address #$AA and the second byte contains the high byte of the address #$FF.
I need to use the pointer to somehow load the value at $FFAA into a register.

Can LDA (PF1_pointer), #0 do this?

Thanks for the input.
So what you what is:

Code: Select all

DATAVAL EQU $FFAA
ZPPTR   EQU $80

 LDA #<DATAVAL ; Should evaluate to $AA
 STA ZPPTR
 LDA #>DATAVAL ; Should evaluate to $FF
 STA ZPPTR+1

 ; then either

 LDY #0
 LDA (ZPPTR),Y ; A contains byte at DATAVAL ($FFAA) address

 ; or

 LDX #0
 LDA (ZPPTR,X)  ; A contains byte at DATAVAL ($FFAA) address
As Garth points out the 65C02 has zero page indirect addressing mode (so you can drop the LDY #0 and change the final load to LDA (ZPPTR) ) but with earlier processors you must use index indirect or indirect indexed to access memory via a pointer on zero page.

The (),Y form is more useful if you are going to access several (usually contiguous) bytes from the same memory area.
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
wstjohn
Posts: 19
Joined: 04 Mar 2007

Post by wstjohn »

BitWise,
Yeah, that is exactly what I'm trying to do, and exactly the code I used.
But, what gets loaded into A is only the low byte, not both together!!!

Is this what should happen?

Code: Select all

pointer   .word
data = $9955
...
 LDA #<data
 STA pointer
 LDA #>data
 STA pointer+1 
...
;at this point I have verified that pointer contains #$55
;and pointer+1 contains #$99
...
;however, after this:
LDA  (pointer)
STA  PF0
;PF0 contains the data from $55, not from $9955

???
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

Are you using actual hardware, or a simulator that might have a bug in it?
wstjohn
Posts: 19
Joined: 04 Mar 2007

Post by wstjohn »

I'm using DASM to compile it and Stella to simulate the Atari VCS...
wstjohn
Posts: 19
Joined: 04 Mar 2007

Post by wstjohn »

I also used z26 and got the same result.
Post Reply