6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Tue Sep 24, 2024 2:27 pm

All times are UTC




Post new topic Reply to topic  [ 27 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Memory Management
PostPosted: Mon Dec 24, 2012 4:00 pm 
Offline
User avatar

Joined: Wed May 30, 2012 7:45 pm
Posts: 58
Location: Dallas, TX
In my NES project, I have reached a point where I really need to wrap my brain around 6502 memory management.
For game objects (collections of 8 x 8 pixel sprites) this seems crucial.

If I were using C, I could just create a struct object that stored all the attributes of an object and edit it accordingly.
Since there doesn't seem to be any innate data structures, I am kind of on my own.

The way that I can see that Video RAM works, is that each sprite is written directly to memory in a series of 4 bytes:

1) horizontal coords
2) tile to be used from CHR Memory
3) video attributes
4) vertical coords

For example, Link from Zelda is 4 8 x 8 sprites into 1 object. If I store him in VR at $0200, his memory footprint would be $0200 - $020F (16 bytes)

In essence, all my code would need to know is that Link "starts" at $0200. If I had a pointer to $0200, my code could take care of the rest.

Here is the part that eludes me:
Say $0200 - $020F will always store my main character. But I also have more ephemeral sprite objects.

It seems at runtime that I wouldn't always know where the object would be stored at. In any modern language I could have some sort of list that allows non linear adding and removing of objects. But I wouldn't know where
to begin with when using 6502 ASM.

In short, I need a better approach here. Using pointers to the first byte is a start, but this assumes that I always know the size of the object. This isn't too difficult to understand, but it heavily relies on the data being where it is supposed to be.

Am I missing something obvious here? Would implementing a linked list of some sort take care of the non-linear access I need?
Do I need non-linear access? Would the overhead of such a data structure be worth the convenience?
Thanks.

_________________
http://www.thestarrlab.com


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Mon Dec 24, 2012 7:08 pm 
Offline

Joined: Tue Nov 18, 2003 8:41 pm
Posts: 250
I am far from an expert but I'll make a couple of observations.

First you should peruse the forums at Nesdev if you haven't already.
(which, unfortunately, seems to be down at this moment)

Lot's of exactly this sort of stuff get's discussed extensively there.
Various approaches get offered and pros and cons bashed out by
people that seem to know what they're doing (that are intimately familiar
with NES). Though you might have to do some digging to find some of it.


You're going to have to make your own structures.

One common way to get more mileage out of an 8 bit pointer
is to keep the stuff under the pointer in seperate lists.
That is to say, instead of having one list, each entry of which
has say four succesive bytes, you keep four lists one for each
of the four and use the same pointer for all four lists.

You can then work with lists of pointers.

That may be more complication than solution but it's something
to think about that as a newbie you may not have thought of yet.


Last edited by bogax on Mon Dec 31, 2012 7:25 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Mon Dec 24, 2012 9:10 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8392
Location: Midwestern USA
Johnny Starr wrote:
In my NES project, I have reached a point where I really need to wrap my brain around 6502 memory management...

Memory management concepts are independent of the microprocessor type. If you understand the concept of indirection and the organization of a block of RAM into tables then you are well on your way to doing in assembly language what a struct does in C. The key is knowing how to define the fields (aka "members") in terms that the assembler will understand:

Code:
01547    ;LOGGED SCSI DEVICES TABLE
01548    ;
01549    ;                  ——————————————————————————————————————
01550    ;  slot offset ——> $00  $01  $02  $03  $04  $05  $06  $07
01551    ;                  ——————————————————————————————————————
01552    ;                   |    |    ^——————————————^    ^————^
01553    ;                   |    |        capacity         block
01554    ;                   |    |                         size
01555    ;                   |    +———> type
01556    ;                   +————————> flag
01557    ;
01558    ;
01559    ;   field sizes (description order)...
01560    ;
01561      0002             s_sdbsiz =s_word               ;block size
01562      0001             s_sdflag =s_byte               ;flag
01563      0004             s_sdcap  =s_dword              ;capacity in blocks
01564      0001             s_sdtype =s_byte               ;type
01565    ;
01566    ;
01567    ;   slot & table sizes...
01568    ;
01569      0003             sdslotex =ex2_x3               ;slot size computation factor...
01570    ;
01571    ;   ———————————————————————————————————————————————————————————————————————
01572    ;   SDSLOTEX is used to atomically define the size of the device table slot
01573    ;   so bit shifts can be used to compute a slot address for a SCSI ID.  The
01574    ;   above definition should be changed with caution.  See additional notes
01575    ;   in the SSLOTADR function.
01576    ;   ———————————————————————————————————————————————————————————————————————
01577    ;
01578      0008             s_sdslot =1 << sdslotex        ;slot
01579      0040             s_sd_tab =s_sdslot*n_scsiid    ;table
01580    ;
01581    ;
01582    ;   field offsets...
01583    ;
01584      0000             o_sdflag =0                    ;device flag...
01585    ;
01586    ;   xx000xxx
01587    ;   ||   |||
01588    ;   ||   +++———> ANSI version
01589    ;   |+—————————> 1: supports sync transfer
01590    ;   +——————————> 1: device enumerated
01591    ;
01592      0001             o_sdtype =o_sdflag+s_sdflag    ;device type...
01593    ;
01594    ;   x00xxxxx
01595    ;   |  |||||
01596    ;   |  +++++———> ANSI device type
01597    ;   +——————————> 1: device recognizes 32 bit LBAs
01598    ;
01599      0002             o_sdcap  =o_sdtype+s_sdtype    ;capacity in blocks
01600      0006             o_sdbsiz =o_sdcap+s_sdcap      ;block size

The above code (assembler listing) defines the SCSI device table in my POC unit. Although it is syntactically different in assembly language than in C, the principles that are used to construct the table are readily equivocated to the principles used to define structs in C. In most languages, structs are referred to as records and the structs' members are fields, however, I'll use C terminology to avoid confusing the issue.

The above table could be thought of as nested structs. The table is a struct whose members are structs, whose members are bytes, words or long words that define such things as the device type, its capacity in blocks, the block size, and so forth. Some of the members are bit fields, a concept with which you also should be familiar from C. So you can see that the SCSI device table is a good analogy to a struct in C.

As in C, your source code has to explicitly define the nature of each struct and its corresponding members. The table definition starts by defining inner struct member sizes in bytes. Unlike C, where various data types are defined by name (e.g., int, float, char, etc.), no such thing exists in assembly language. It's the programmer's responsibility to correctly utilize a member definition by not trying to store string data into a numeric, store an integer into a a string, etc. The only characteristic that you can assign to a member is its name and its size. The parts of your program that work with these definitions have to be careful to not violate the sizes, as the MPU will simply overwrite following areas (aka the dreaded "buffer overflow" problem that has long plagued C).

The size definitions for byte, word, etc., are elsewhere defined in an .INCLUDE file. Here's an excerpt from that file, as displayed in the assembler listing output:

Code:
00671    ;ATOMIC CONSTANTS
00672    ;
00673    ;
00674    ;   data type sizes...
00675    ;
00676      0001             s_byte   =1                    ;byte
00677      0002             s_word   =2                    ;word (16 bits)
00678      0003             s_wword  =3                    ;wide word (24 bits)
00679      0003             s_xword  =s_wword              ;extended word (24 bits)
00680      0004             s_dword  =4                    ;double word (32 bits)
00681      0008             s_lword  =8                    ;long word (64 bits)
00682      0100             s_rampag =$0100                ;65xx RAM page
00683    ;
00684      0008             s_bbyte  =8                    ;bits in a byte
00685    ;
00686    ;
00687    ;   MPU register sizes..
00688    ;
00689      0001             s_mpudbx =s_byte               ;data bank
00690      0002             s_mpudpx =s_word               ;direct page
00691      0001             s_mpupbx =s_byte               ;program bank
00692      0002             s_mpupcx =s_word               ;program counter
00693      0002             s_mpuspx =s_word               ;stack pointer
00694      0001             s_mpusrx =s_byte               ;status
00695    ;
00696    ;
00697    ;   derivations...
00698    ;
00699      0008             s_bcdfp  =s_lword              ;floating point number
00700      0002             s_ptr    =s_word               ;pointer
00701      0004             s_dptr   =s_ptr*2              ;double pointer
00702      1000             s_ramblk =s_rampag*16          ;RAM block
00703    ;
00704    ;
00705    ;   data type sizes in bits...
00706    ;
00707      0004             s_bnybbl =s_bbyte/2            ;nybble
00708      0008             s_bibyte =s_bbyte              ;byte
00709      0010             s_bword  =s_word*s_bbyte       ;word
00710      0018             s_bwword =s_wword*s_bbyte      ;wide word
00711      0018             s_bxword =s_xword*s_bbyte      ;extended word
00712      0020             s_bdword =s_dword*s_bbyte      ;double word
00713      0040             s_blword =s_lword*s_bbyte      ;long word
00714      0040             s_bbcdfp =s_bcdfp*s_bbyte      ;floating point number

The general procedure is to first define the smallest members, such as the device type. Next is to have the assembler compute the aggregate size of the members used to define one SCSI device, hence creating the struct size and definition that catalogs a device. As there are eight possible SCSI devices (narrow bus), eight of these structs will act as the members of the struct that represents the entire table, something the assembler also can do for you. The table size is computed from the size of a device struct multiplied by the number of devices. Incidentally, the expression s_sdslot = 1 << sdslotex means the same thing as it would in C—the << operator means left-shift, in this case sdslotex bits.

Like the definition of a struct in C, the above code merely defines a "template" for the device table structure. It doesn't actually allocate memory for it. That's the job of the following code excerpted from a different .INCLUDE file:

Code:
;ABSOLUTE SCSI WORKSPACE
;
scdevtab =scsiwspc             ;active device table
scmiswrk =scdevtab+s_sd_tab    ;temporary workspace
scsimesg =scmiswrk+s_miswrk    ;message workspace

When the above is run through the assembler, the following is the result (excerpt from the ROM listing file):

Code:
02361    ;ABSOLUTE SCSI WORKSPACE
02362    ;
02363      0110             scdevtab =scsiwspc             ;active device table
02364      0150             scmiswrk =scdevtab+s_sd_tab    ;temporary workspace
02365      017C             scsimesg =scmiswrk+s_miswrk    ;message workspace

The scsiwspc value is an address that has been atomically defined elsewhere as the start of absolute (non-zero page) storage for SCSI variables. So scdevtab is set to whatever the address is in scsiwspc, $0110 in this case, and in following code that refers to the SCSI device table, scdevtab indicates the start in memory of the table. This may seem redundant, but it allows me to move SCSI workspace to a different location in RAM if needed without having to edit references in other parts of the program. All I have to do is change the value of scsiwspc and everything else will follow at assembly time.

The next definition after scdevtab, which is scmiswrk, is also automatically handled by the assembler, as the latter will add the value s_sd_tab to the device table's start address, producing the address $0150. If you look back at the device table definition, you'll see where s_sd_tab has been defined. s_sd_tab, in turn, was calculated from device structs' member sizes, which were defined from atomic data size defintions (word, etc.). This sequence illustrates an important assembly language concept, which is that of never hard-coding this sort of stuff. Only atomic definitions like word sizes are hard-coded, as they are irreducible in nature (they're innate to the machine architecture). If I add another member to my device struct definition and have soft-coded the table size, it will automatically be changed when the program is reassembled. As a consequence, the position in RAM of the other workspace locations, such as scmiswrk, will likewise be automatically changed.

Now, in order to access a specific data object in the device table, it is necessary to know its absolute address. There are a number of ways to go about determining this: using look-up tables or computing it on the fly as the program runs. Both methods have their advantages and disadvantages. Look-up tables generally result in faster acting code, but consume RAM. Computing on the fly is slower but more memory-efficient if more than a few objects are involved. In the case of the SCSI device table it's almost a wash, as there are only eight table entries.

A look-up table for the device table would contain eight word-size (2 byte) entries, arrange in ascending order:

Code:
sdlutab  ,word scdevtab+s_sdslot*0 ;device $00 slot address
         .word scdevtab+s_sdslot*1 ;device $01 slot address
         .word scdevtab+s_sdslot*2 ;device $02 slot address
         .word scdevtab+s_sdslot*3 ;device $03 slot address
         .word scdevtab+s_sdslot*4 ;device $04 slot address
         .word scdevtab+s_sdslot*5 ;device $05 slot address
         .word scdevtab+s_sdslot*6 ;device $06 slot address
         .word scdevtab+s_sdslot*7 ;device $07 slot address

The .word pseudo-op defines a two byte value in little-endian format. To get the address of any device table slot, code like the following will work. Simply load the accumulator with the SCSI device ID ($00-$07):

Code:
;   return device table slot address in .X/.Y (LSB/MSB)...
;
         asl a                 ;double SCSI ID for word-size index
         tax
         lda sdlutab,x         ;get slot address LSB
         ldy sdlutab+s_byte,x  ;get slot address MSB
         tax
         ...program continues...

You would then store .X and .Y in contiguous zero page locations and use indirect-Y addressing to access a specific field in the slot corresponding to the chosen SCSI device. Note that even though we know that any given slot address is two bytes, I used the atomic definition s_byte to get the MSB. Doing so tends to keep typos from being converted into bugs. If I had meant to hard-code 1 instead of soft-coding with s_byte but accidentally typed 2, the assembler would have generated wrong code. On the other hand, if I had mistyped the name of the s_byte variable, the assembler would have complained and halted assembly.

Incidentally, this type of stuff tends to be easier with the 65C816 and its 16 bit registers. As the '816 can handle word-size data in one register, computing the slot address on the fly produces small code. At the high clock speeds at which POC runs, execution time isn't enough of a factor to mitigate in favor of the look-up table approach, and ROM space was tight. However, with the 65(c)02, tables will definitely be faster.

Quote:
For game objects (collections of 8 x 8 pixel sprites) this seems crucial.

Sprite definitions are just a bunch of equal-sized objects in RAM, same as the SCSI device table slots. Just make your table size such that it can store the maximum number of sprites defs that are expected during run-time. It's a lot easier than trying to dynamically adjust the table size as the program runs.

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


Last edited by BigDumbDinosaur on Thu Dec 27, 2012 5:53 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Wed Dec 26, 2012 1:51 pm 
Offline
User avatar

Joined: Wed May 30, 2012 7:45 pm
Posts: 58
Location: Dallas, TX
BDD,

Thank you for that wealth of information. I can clearly see that I have much to learn here. I'm afraid I have been spoiled in higher-level programming for much too long as well. Once I get past the syntax, I'm sure the core concepts will click with me though.

All the 6502 books I have are from the late 70s or early 80s. It would be nice to have an ASM 65* for C programmers book :P

Thanks.

_________________
http://www.thestarrlab.com


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Wed Dec 26, 2012 2:27 pm 
Offline
User avatar

Joined: Mon Apr 23, 2012 12:28 am
Posts: 760
Location: Huntsville, AL
It's not that I juuuust like assembly language programming, but that some of the concepts employed in a high level language like C are much better understood and appreciated if they've ever been implemented, as BDD has, at the assembler level. I think that most young engineers and programmers would benefit from having at least one class where the emphasis is on low level, high-wire, no net programming of a microprocessor in assembler. The high level language concepts would be better understood and appreciated, but there would also be a better understanding of how the computer does its work.

_________________
Michael A.


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Thu Dec 27, 2012 2:42 pm 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 674
You probably don't need a fully dynamic linked list setup. On systems like the NES, the maximum number of sprites you're going to be dealing with is small enough to keep a fixed-size array around. Each entry in the array can be enabled or disabled, and keeping track of which ones are disabled (available for allocation) can be done with simple link pointers holding the 8-bit offset of the next available array entry.

I don't know too many specifics about the NES graphics hardware, but you should be thinking more along the lines of fixed allocation pools like this if you need greater dynamism.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Thu Dec 27, 2012 6:35 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8392
Location: Midwestern USA
White Flame wrote:
You probably don't need a fully dynamic linked list setup. On systems like the NES, the maximum number of sprites you're going to be dealing with is small enough to keep a fixed-size array around. Each entry in the array can be enabled or disabled, and keeping track of which ones are disabled (available for allocation) can be done with simple link pointers holding the 8-bit offset of the next available array entry.

I don't know too many specifics about the NES graphics hardware, but you should be thinking more along the lines of fixed allocation pools like this if you need greater dynamism.

From what I gathered in reading Johhny's original post, I think he understands that part of it. It seems his area of doubt is in how to create such structures in assembly language. The "reverse transition" from a language of abstraction, such as C, to assembly language can be initially daunting, as C insulates the programmer from what actually happens in the system. This is the same sort of problem that BASIC programmers trip over when introduced to assembly language. All those language conveniences are suddenly gone.

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


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Thu Dec 27, 2012 7:21 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8514
Location: Southern California
Quote:
The "reverse transition" from a language of abstraction, such as C, to assembly language can be initially daunting, as C insulates the programmer from what actually happens in the system. This is the same sort of problem that BASIC programmers trip over when introduced to assembly language. All those language conveniences are suddenly gone.

Slightly off-topic, but I'll bite. Although I have always agreed with this, the magnitude still continues to impress me more and more now decades later, and I am realizing the need to keep it in mind and modify the way I write articles and perhaps posts. It might be good if there were kind of a primer on 6502 assembly language for those who started out in higher-level languages like C and BASIC. We have plenty of stuff on beginning assembly, then on macros, and now even on using macros to create program structures in assembly, but we lack something that flows better.

My first exposure to programming was with a programmable calculator (TI-58c and then TI-59, with printer) which had keystroke programming which was in some ways like assembly language. And, since the display was not alphanumeric, single-stepping through a program to check or edit meant the display showed only the address and numeric opcodes and operands. Soon after I got it, I started the class in 6502 assembly language on the AIM-65 in 1982. Although there was a rudimentary assembler on the AIM-65, we were required to assemble our little programs by hand and punch in the machine language. I think it was the same semester I took a Fortran IV class, showing the huge contrast.

_________________
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  
 Post subject: Re: Memory Management
PostPosted: Thu Dec 27, 2012 9:53 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8392
Location: Midwestern USA
GARTHWILSON wrote:
Quote:
The "reverse transition" from a language of abstraction, such as C, to assembly language can be initially daunting, as C insulates the programmer from what actually happens in the system. This is the same sort of problem that BASIC programmers trip over when introduced to assembly language. All those language conveniences are suddenly gone.

Slightly off-topic, but I'll bite. Although I have always agreed with this, the magnitude still continues to impress me more and more now decades later, and I am realizing the need to keep it in mind and modify the way I write articles and perhaps posts. It might be good if there were kind of a primer on 6502 assembly language for those who started out in higher-level languages like C and BASIC. We have plenty of stuff on beginning assembly, then on macros, and now even on using macros to create program structures in assembly, but we lack something that flows better.

Interestingly enough, in his book Machine Language for Beginners, Richard Mansfield attempts to make the transition from BASIC to assembly language less painful by illustrating programming techniques that are equivalent to BASIC statements in what they do. I think the number one stumbling block, however, continues to be that of wrapping one's head around what happens at the machine level. A BASIC statement such as PRINT "This is a test." makes more sense in assembly language once the would-be programmer understands the concept of a character string as being nothing more than some contiguous bytes in RAM, along with some way to determine how many bytes are in the string.

I've never had a problem with this, but that's because when I started with programming, everything was machine code and it was essential to have a mental picture of what was going on inside the machinery in order to write a good program. I knew 6502 assembly language well before I learned C and BASIC (I also did COBOL at one time, but don't like to admit it :oops:). So creating and manipulating data structures in assembly language seem to be no big deal. POC V1's ROM is full of them, especially the tables for translating between raw machine code and assembly language in the M/L monitor.

Quote:
My first exposure to programming was with a programmable calculator (TI-58c and then TI-59, with printer) which had keystroke programming which was in some ways like assembly language...there was a rudimentary assembler on the AIM-65, we were required to assemble our little programs by hand and punch in the machine language. I think it was the same semester I took a Fortran IV class, showing the huge contrast.

FORTRAN could be though of as the immediate ancestor of BASIC (mathematically, at least—BASIC's string handling is more akin to what goes on in COBOL), so yes it would be quite a backward leap for the reasonably competent FORTRAN programmer to immerse himself in assembly language. Interestingly enough, I do a lot of business software development in the Thoroughbred Dictionary-IV environment, which is built on top of the Thoroughbred (formerly Concept Omega) Business BASIC T-code engine (itself written in ANSI C and runable on a wide variety of systems). I've often used Thoroughbred BASIC's very powerful mathematical functions to evaluate algorithms destined for use in assembly language programs, especially ones related to low level mass storage control. Unfortunately, Business BASIC is designed for business applications and is somewhat weak in Boolean functions (beyond basic comparisons, such as IF...THEN) and even weaker with bit manipulation, things that don't get a lot of use in most business software.

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


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Fri Dec 28, 2012 6:18 pm 
Offline
User avatar

Joined: Wed May 30, 2012 7:45 pm
Posts: 58
Location: Dallas, TX
I want to take a modular approach to my code so that I can separate logic and
routines into specific files - much the way it would be done in C.
So far, I can see this being done by using pointers and sub routines. If I
were to send in the start address, the module could alter the memory offsets
and initialize and dispose the game-objects respectively.
This would be a more, somewhat, object-oriented approach which would make me
very pleased.
I would like to consider using MACROs to facilitate a better way of sending
parameters, which leads me to have a few questions.
For instance, let's say we have a "goomba.asm" file that handles all goomba
game object behavior. I could initialize the video memory and send in the
start address:
Code:
   LDA #$00 ; LSB
   LDY #$02 ; MSB
   JSR INIT_GOOMBA
   ; in goomba.asm
ME = $00 ; identifier for zero page pointer
INIT_GOOMBA:
   STA ME  ; store pointer in zp
   STY ME+1  ; MSB follows
   LDY #$00 ; set Y back to 0
   LDA #$32 ; store tile 32 at start address
   STA (ME), Y
   RTS

Of course, much more would come into play here. Based on my
current understanding, the only way to send in the 16-bit address of the starting point is to use that whacky indirect-indexed-Y mode. I suppose it makes sense that the MSB comes second if you consider it like a Stack, but I digress...
I would like to have some input here. I am hoping that this
makes sense and is scalable.

Is there a way to use a MACRO to make this even clearer / easier?
Thanks again!

_________________
http://www.thestarrlab.com


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Fri Dec 28, 2012 7:23 pm 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 674
Don't use 16-bit variable pointers. For a sprite's identity use an 8-bit offset into some object tables.

Unwrap your structures so that each field of your "object" is in its own table. Instead of some array of struct Sprite {x, y, tile}, do this:
Code:
MAX_SPRITES = 32
spriteX: .db MAX_SPRITES
spriteY: .db MAX_SPRITES
spriteTile: .db MAX_SPRITES
...etc

Let's say the third slot in your object pool is available, then a hypothetical "jsr allocateSprite" could return 2 in the X register. Then you can modify spriteX,x or spriteY,x in order to change that object's position, all without ever using 16-bit pointers.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Fri Dec 28, 2012 7:43 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8514
Location: Southern California
Quote:
Based on my current understanding, the only way to send in the 16-bit address of the starting point is to use that whacky indirect-indexed-Y mode.

Note that the 65c02 has STA(ZP) also, without the Y.

Quote:
I suppose it makes sense that the MSB comes second if you consider it like a Stack,

Perhaps off-topic, but the 6502 puts the low byte first to improve performance. If there's an operand, the low byte is needed whether there's a high byte or not, and it can be fetching it while it's decoding the instruction to see if it even needs a high byte; and if it needs to index and add to an address operand, it needs to get started on the low byte first anyway, and it can do that while it's fetching the high byte.

_________________
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  
 Post subject: Re: Memory Management
PostPosted: Fri Dec 28, 2012 8:21 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8392
Location: Midwestern USA
White Flame wrote:
Don't use 16-bit variable pointers. For a sprite's identity use an 8-bit offset into some object tables.

Unwrap your structures so that each field of your "object" is in its own table. Instead of some array of struct Sprite {x, y, tile}, do this:
Code:
MAX_SPRITES = 32
spriteX:    .db MAX_SPRITES
spriteY:    .db MAX_SPRITES
spriteTile: .db MAX_SPRITES
...etc

Let's say the third slot in your object pool is available, then a hypothetical "jsr allocateSprite" could return 2 in the X register. Then you can modify spriteX,x or spriteY,x in order to change that object's position, all without ever using 16-bit pointers.

I think Johnny is trying to get some re-usability with his subs, which means he's probably going to have to pass 16 bit pointers. Of course, if static tables can be made to work they would ease the pain. Self-modifying code, which is what you are recommending, can work in conjunction with statically-defined tables, but is something that should be avoided. For example, what happens if the program is halted and then restarted from the top with the data tables having been modified?

However, I think we may have drifted off-topic. Johnny's query had to do with conceptualizing things he knows in C into assembly language, in particular, making the transition from structures (struct) to their machine code equivalents. It might be premature to offer coding solutions if he's not yet comfortable with the nuts-and-bolts of setting up assembly language data tables.

BTW, if you use code examples as above it might not be a bad idea to explain what a pseudo-op such as .db means. Someone who is just learning how to write assembly language programs and isn't completely comfortable with an assembler's syntax might not know what a pseudo-op means or what it produces at assembly time. Also, pseudo-ops can vary widely between assemblers despite the existence of published recommendations. I have no idea of the origin of.db, as no assembler I have worked with up until very recently supported such a pseudo-op. My UNIX cross-assembler doesn't know .db or .dw but does know .byte and .word, both of which were defined in the MOS Technology assembly language syntax standard. Ditto for such stalwarts as MADS and DEVPAK, which were widely used during the hey-day of Commodore eight bit machines.

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


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Fri Dec 28, 2012 9:05 pm 
Offline
User avatar

Joined: Wed May 30, 2012 7:45 pm
Posts: 58
Location: Dallas, TX
Thank you everyone for the input...

BDD,
I appreciate you understanding more specifically what I was asking. I do have a brief understanding of psuedo-op codes and am familiar with .db

I would like to understand more about ASM tables though.

Sorry if I am not sure how to ask my questions in a way that makes more sense globally.

I sort of get where White Flame was going in his example, but I don't really understand how or where the "tables" are stored...

This leads me to another question...

Would I even need indirect addressing to accomplish this? Couldn't I just send the address of the sprites first location and index accordingly?

_________________
http://www.thestarrlab.com


Top
 Profile  
Reply with quote  
 Post subject: Re: Memory Management
PostPosted: Sat Dec 29, 2012 12:05 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 395
Location: Minnesota
Code:
MAX_SPRITES = 32
spriteX:    .db MAX_SPRITES
spriteY:    .db MAX_SPRITES
spriteTile: .db MAX_SPRITES
...etc


Actually I think this example is a little bit off. It produces is a series of bytes all of value 32 ($20). The length of the series is three plus however many "etc" entries there are.

What White Flame is trying to do, I think, is not to create self-modifying "code". These are meant to be data tables, each 32 bytes long. Essentially they are variables, and as such can be modified at will without violating any Programming Rules.

Possibly the pseudo-op meant is ".ds" (for "Define Storage") or some other equivalent pseudo-op. What this does is store the current value of the program counter in the label and then advance the program counter by the value of the argument. No code is generated, but a series of labels can be assigned appropriate values without having to manually work out what those should be. Another common 6502-ish idiom for doing this sort of thing is:

Code:
MAX_SPRITES = 32
SPRITE_X = * ; current value of the program counter
SPRITE_Y = SPRITE_X + MAX_SPRITES
SPRITE_TILE = SPRITE_Y + MAX+SPRITES
...etc


but it's usually cleaner-looking and easier to understand as

Code:
MAX_SPRITES = 32
spriteX:    .ds MAX_SPRITES
spriteY:    .ds MAX_SPRITES
spriteTile: .ds MAX_SPRITES
...etc


As to how the value of the program counter gets set in the first place, that's the job of the ".org" pseudo-op. Most assemblers allow multiple uses of ".org". One common way to use that ability is to create multiple "blocks" of definitions - dividing up RAM memory with convenient labels for variable use, naming support chip memory locations, etc - and code.

What White Flame means to do is create what I call a "parallel" array. The bytes of a single entry are not stored consecutively but in different arrays, where each array is one "field" of the "structure". The advantage is that the bytes of any one structure are at a constant offset from the start of each array. Thus no indirection is needed if there are no more than 256 "fields" in a "structure". If you want to access the second "structure", you load the X- or Y- register with "1" and use absolute-indexed addressing to access the second element in each array.


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

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 12 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: