6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 3:03 pm

All times are UTC




Post new topic Reply to topic  [ 40 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Functions in assembly
PostPosted: Fri Jul 04, 2014 12:25 am 
Offline
User avatar

Joined: Mon May 12, 2014 6:18 pm
Posts: 365
Hi! I just started with 6502 assembly and I find myself often trying to translate from C. When I make functions I feel like I should put temporary variables on the stack but this seems a little inconvenient so I tried using some zero-page memory as pseudo-registers. That means, though, that I would have to push copies of all of them on to the stack every time I call one function from another. Is that the standard way to do it? Here is some code I was working on. Is there a better way to do it?

Code:
  SCREEN = $5000
  STACK = $0100

  R0=$00 ;General pseduo registers
  R1=$01
  R2=$02
  R3=$03
  R4=$04

Test:
  ;Push arguments on to the stack
  LDA #$20 ;X
  PHA
  LDA #$15 ;Y
  PHA
  LDA #$00 ;COLOR
  PHA

  JSR DrawPixel
  ;Pop the three arguments off
  PLA
  PLA
  PLA

  JMP Done

; Draws a pixel to the 64x64 screen
; Expects X Y COLOR on the stack
DrawPixel:
  ;Push the pseudo registers in case the calling
  ;function was using them
  LDA R0
  PHA
  LDA R1
  PHA
  LDA R2
  PHA
  LDA R3
  PHA
  LDA R4
  PHA

  ;Copy arguments from stack to registers
  TSX
  INX ;Skip current stack pointer address
  INX ;Skip PC copy on stack
  INX
  INX ;Skip pseudo register copies
  INX
  INX
  INX
  INX
  LDA STACK,X
  STA R4 ;COLOR
  INX
  LDA STACK,X
  STA R0 ;Y
  INX
  LDA STACK,X
  STA R1 ;X

  ;Copy base address of video memory
  ;to R2-R3 which hold the final address
  LDA #<SCREEN
  STA R2
  LDA #>SCREEN
  STA R3

  ;Multiply Y times the width of the screen
  CalcY:
    LDA R0 ;Get Y argument
    BEQ CalcX ;Is it 0 yet?
    SEC
    SBC #$01 ;Subtract 1 from Y
    STA R0

    LDA R2 ;Load low byte of final address
    CLC
    ADC #$40 ;Add screen width
    STA R2
    BCC CalcY ;If overflowed add one to high byte
      LDA R3
      INC
      STA R3
    JMP CalcY

  ;Add X to the final address
  CalcX:
    LDA R2 ;Load low byte of final address
    CLC
    ADC R1 ;Add X argument to it
    STA R2
    BCC Write ;If overflowed add one to high byte
      LDA R3
      INC
      STA R3

  Write:
    LDA R4 ;Load color argument
    LDY #$00
    STA (R2),Y ;Write to final address

  ;Restore pseudo registers
  PLA
  STA R4
  PLA
  STA R3
  PLA
  STA R2
  PLA
  STA R1
  PLA
  STA R0

  RTS

Done:
  JMP Done


In C I would do something like this:

Code:
#define SCREEN_WIDTH 0x40
SCREEN[Y * SCREEN_WIDTH + X] = color;


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 1:15 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
Druzyek wrote:
Hi! I just started with 6502 assembly and I find myself often trying to translate from C. When I make functions I feel like I should put temporary variables on the stack but this seems a little inconvenient so I tried using some zero-page memory as pseudo-registers. That means, though, that I would have to push copies of all of them on to the stack every time I call one function from another. Is that the standard way to do it? Here is some code I was working on. Is there a better way to do it?

Without taking the time to figure out any of the graphics, I'll comment on some efficiency things.

Forth on the 6502 puts the data stack in ZP (zero page), and indexes them by X, so loading the top stack item is for example LDA 0,X, or from the address pointed to by that address, LDA (0,X) for example. Doing the same thing with the next two-byte cell would be LDA 2,X, from from the address pointed to by that cell, LDA (2,X). Having the separate stack for data solves various problems with trying to mix data and return addresses.

In your listing, if calling a routine will always be preceded by putting numbers on the stack from the same pseudo registers, why not just make the routine itself get them from there, skipping the overhead of moving things to and from the stack.

Next, in your
Code:
  ;Copy arguments from stack to registers
  TSX
  INX ;Skip current stack pointer address
  INX ;Skip PC copy on stack
  INX
  INX ;Skip pseudo register copies
  INX
  INX
  INX
  INX
  LDA STACK,X

just skip all the INX's and do LDA STACK+8,X. This assumes the stack was initialized at power-up with LDX#$FF, TXS, so you don't accidentally index into page 2.

Code:
    LDA R0 ;Get Y argument
    BEQ CalcX ;Is it 0 yet?
    SEC
    SBC #$01 ;Subtract 1 from Y
    STA R0

can be replaced with
Code:
    LDA R0
    BEQ CalcX
    DEC R0

and
Code:
      LDA R3
      INC
      STA R3

can be replaced with the single instruction INC R3,

On the 65c02 (ie, CMOS version, which has more instructions and addressing modes),
Code:
    LDY #$00
    STA (R2),Y ;Write to final address

can be replaced with the single instruction STA (R2). No Y needed.

Quote:
In C I would do something like this:

Code:
#define SCREEN_WIDTH 0x40
SCREEN[Y * SCREEN_WIDTH + X] = color;

After the internal building blocks are working and streamlined, you can use macros to hide the details without losing any control or efficiency. In your example, the macro invocation might look something like
Code:
    SCREEN  Y_,  SCREEN_WIDTH + X_,  color

(I put a trailing "_" after X and Y since the assembler probably won't allow a variable with the same name as processor registers.) I have an article on macros, particularly for program structures, at http://wilsonminesco.com/StructureMacros/index.html. I just finished another work project using them, and they have sure made it easier to keep control of the project and be more productive and get fewer bugs, without losing any of the benefits of assembly, because in almost all cases the macros assemble exactly the same thing I would by hand-- it's just that the source code is much more clear and maintainable. I do need to update the 6502 source-code file though to get the most recent improvements in.

_________________
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 04, 2014 2:31 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8144
Location: Midwestern USA
The 6502 and 65C02 are not well-suited languages like C and Forth because they lack instructions that can address the stack relative to the current stack pointer. The 6502 is further hobbled, as the only register that can be pushed and pulled is the accumulator. The 65C02 somewhat helps in this regard, as the .X and .Y registers can be pushed and pulled without using the accumulator as an intermediary. However, addressing stack frames is still a tedious and error-prone process with either MPU.

Forth's solution, as Garth noted, is to use zero page as the data stack, saving the hardware stack for return addresses and temporary register saves. Doing so takes advantage of the generally faster ZP instructions, as well as the useful indirect addressing modes. This scheme works well, but potentially eats up the most valuable 256 bytes in the memory map.

The few C compilers that have been written for the 65(c)02 have mostly resorted, like Forth, to using a software stack for parameter passing. The principle is similar, as are the limitations. There's no hardware stack pointer to keep track of the depth of the pushes in such an arrangement, so some memory has to be devoted to that task. Performance tends to suffer and the general design complicates recursiveness.

Bill Mensch evidently was thinking in terms of stack-intensive languages like C when he designed the 65C816, as he incorporated the very useful stack pointer relative addressing modes in the MPU's instruction set, along with instructions that can be used to generate a stack frame without clobbering the registers. These instructions greatly ease the pain of managing stack frames, as would be used to pass parameters to subroutines. The '816 is your best bet for C-like function calling.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 2:38 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8144
Location: Midwestern USA
GARTHWILSON wrote:
(I put a trailing "_" after X and Y since the assembler probably won't allow a variable with the same name as processor registers.)

I discovered that the Kowalski simulator's assembler has no objections to using register names as symbols. For example, LDA A is perfectly acceptable, assuming that A has been defined. Nevertheless, I habitually don't use register names as symbols to avoid any confusion. In my screen control code, I use the names like XPOS for the column, and YPOS for the row.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 3:40 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
Quote:
Forth's solution, as Garth noted, is to use zero page as the data stack, saving the hardware stack for return addresses and temporary register saves. Doing so takes advantage of the generally faster ZP instructions, as well as the useful indirect addressing modes. This scheme works well, but potentially eats up the most valuable 256 bytes in the memory map.

There is an advantage however, in that the technique makes a big reduction in the number of variables needed, especially of the type that are typically put in ZP; and, as with any stack operations, these pseudo-variables exist only when needed, and then there's an automatic type of garbage collection. Further, it's re-entrant, unlike the ZP variable usage in the Apple II, C64, etc. which didn't leave the user much available ZP space. So, in spite of the apparent disadvantage, I'd have to say that the data stack concept actually comes out ahead in the end.

The 65816 does have instructions that streamline stack operations, for example, your
Code:
  LDA #$20 ;X
  PHA
  LDA #$15 ;Y
  PHA

can be replaced with the single instruction PEA $2015, and the processor registers are not affected; and your

Code:
  LDA R0
  PHA
  LDA R1
  PHA

can be replaced with the single instruction PEI R0 if you can make R0 be at the first address after R1. Again, the processor registers are not affected.

Most of the 816's extra instructions can be synthesized on the 65c02, but not efficiently. Various subroutines and macros can be set up to do the job so at least the source code is relatively clean.

Although I would definitely encourage getting into the '816 eventually, I do not discount the goal of learning to be efficient on the 65c02 first. (BTW, the things that tend to make the '816 look scary are not the lions and tigers and bears they are made out to be. It's really pretty simple, and you can initially use it like a plain '02 and then gradually use more and more of the added power, at your own pace.)

As for translating however, I have found that languages don't often translate directly, and it makes for much more efficient code if you can just re-write it taking the approach preferred by the second language, which might be entirely different.

_________________
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 04, 2014 4:34 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8144
Location: Midwestern USA
GARTHWILSON wrote:
Code:
  LDA R0
  PHA
  LDA R1
  PHA

can be replaced with the single instruction PEI R0 if you can make R0 be at the first address after R1. Again, the processor registers are not affected.

If you can stand having the accumulator used, you can handle the endianess problem as follows:

Code:
         rep #%00100000        ;16 bit accumulator
         lda R0                ;loads R0 in .A & R1 into .B
         xba                   ;reverse endianess
         pha                   ;pushes R0 followed by R1

Quote:
As for translating however, I have found that languages don't often translate directly, and it makes for much more efficient code if you can just re-write it taking the approach preferred by the second language, which might be entirely different.

I agree with Garth. Concepts that have been "fleshed out" in higher level languages can be transferred to assembly language. However, the methods used in the source language may be awkward to implement, especially math (there's no algebraic precedence in assembly language). I never fell into that situation because I had learned assembly language before learning higher level languages. I do use C-like concepts, such as stack frames, but that's about it.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 4:48 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3346
Location: Ontario, Canada
GARTHWILSON wrote:
Forth on the 6502 puts the data stack in ZP (zero page), and indexes them by X
BigDumbDinosaur wrote:
The 6502 and 65C02 are not well-suited languages like C and Forth because they lack instructions that can address the stack relative to the current stack pointer.

BDD, your comments show you have a lot to learn about even the basics of Forth on 6502. Garth's post would be a good place to start. It's true as you say that the 6502 lacks stack-pointer-relative address modes but this is not a serious impediment. As Garth explained, it's X that addresses the data stack. As you know, addressing relative to X is well supported even on NMOS 6502.

I do agree the new '816 address modes are generally desirable, but in the Forth context there's not much need to index into the return stack. The data stack is much more likely to get indexed into; hence the use of X. Your remarks don't support your assertion that 6502 is ill suited to Forth.

GARTHWILSON wrote:
As for translating however, I have found that languages don't often translate directly, and it makes for much more efficient code if you can just re-write it taking the approach preferred by the second language, which might be entirely different.
BigDumbDinosaur wrote:
I agree with Garth.
At least on this point we are unanimous! :)

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 5:20 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
Code:
 ;Copy base address of video memory
  ;to R2-R3 which hold the final address
  LDA #<SCREEN
  STA R2
  LDA #>SCREEN
  STA R3

  ;Multiply Y times the width of the screen
  CalcY:
    LDA R0 ;Get Y argument
    BEQ CalcX ;Is it 0 yet?
    SEC
    SBC #$01 ;Subtract 1 from Y
    STA R0

    LDA R2 ;Load low byte of final address
    CLC
    ADC #$40 ;Add screen width
    STA R2
    BCC CalcY ;If overflowed add one to high byte
      LDA R3
      INC
      STA R3
    JMP CalcY

  ;Add X to the final address
  CalcX:
    LDA R2 ;Load low byte of final address
    CLC
    ADC R1 ;Add X argument to it
    STA R2
    BCC Write ;If overflowed add one to high byte
      LDA R3
      INC
      STA R3

  Write:
    LDA R4 ;Load color argument
    LDY #$00
    STA (R2),Y ;Write to final address


could also be written:

Code:

; load base address of video memory

    lda #<SCREEN
    ldx #>SCREEN

; calculate screen row

    ldy R0
    iny         ; add one as preparation for loop
calcy:
    clc         ; clear carry for addition(s)
ccy0:
    dey         ; another row ?
    beq calcx   ; b: no
    adc #40     ; offset to next row
    bcc ccy0    ; no overflow
    inx         ; update high byte of address
    bcs calcy   ; b: forced (and clear carry again)

; add screen column

calcx:
    adc R1      ; carry is clear at this point
    bcc write
    inx

write:
    sta R2      ; save pointer to screen location
    stx R3
    lda R4      ; get color value
    sta (R2),Y  ; store it (Y-reg is zero at this point)


Registers are your friends. Use them wisely.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 6:15 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Hi Druzyek
As a general point, idiomatic 6502 programming doesn't make use of the stack to pass parameters or for temporary storage. It's more usual either to use some area of zero page as scratchpad or to use zero page as globals, or a combination. For parameter passing, use the registers where possible or, for a reusable ABI, use a parameter block in memory and pass an address in the registers.

That is to say, C uses memory and the stack in a particular way, and that's not the idiomatic way to use memory and the stack in 6502. The 6502 method is more primitive, and takes more work and more bookkeeping. While it's possible to take the ideas and habits of C and try to apply them, it's probably a good idea to re-examine your assumptions and see if you can work more directly with the machine.

The only time a stack is required for parameters is when routines may be called recursively or must be re-entrant. In most programming, most routines don't get called in a nested way. Even when a problem is often handled by recursion, it's always possible to re-cast it as iteration or to use a state machine idiom. Remember that the very early computers lacked a stack, and a common early idea for calling subroutines - which were a distinct invention - was to write the return address into the first word of the routine so the routine could eventually jump back through it. Re-entrancy was not considered.

So, have a look at your programming tactics and re-examine the assumptions which you're using which you may have learnt from C or some other HLL. You'll probably do better in the long run!

Hope this helps
Ed

Edit: not to devalue what's been said earlier, of course.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 3:33 pm 
Offline
User avatar

Joined: Mon May 12, 2014 6:18 pm
Posts: 365
Quote:
Forth on the 6502 puts the data stack in ZP (zero page)

I see the advantages everyone has listed but wouldn't keeping track of your own stack pointer make pushing and popping a little slower? Probably not a deciding factor but it does seem like a little overhead.

Quote:
it makes for much more efficient code if you can just re-write it taking the approach preferred by the second language, which might be entirely different.

Right, I can see how writing it like C is a bad. I wanted to ask about the preferred way to do it in assembly. It seems from your posts that there are a couple of different ways to do it. I'm curious about what will work best once a program gets bigger and more complicated.

Quote:
It's more usual either to use some area of zero page as scratchpad or to use zero page as globals

I think this is the thing that is holding me up. I think for most things this would work but I am trying to predict now what I might need to do later so I don't have to redesign half way through. For example, I defined R0-R4 for the DrawPixel function to use. If I had a DrawLine function that calls DrawPixel I could define R5-R9 for it to use and for DrawRectangle R10-R14 since it will use DrawLine. To me this seems like I am using three times more of the zero page than necessary and wasting memory. How would you do it?

Thanks for all the hints about better code too.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 4:03 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Even if you did need 15 bytes of temporaries for your 3 nestable graphics routines, outside of those routines you could reuse those temporaries for other routines.

But in fact, there is probably some condensation possible.

DrawPixel needs X, Y and Colour. But Colour is a global, so it doesn't need to be copied from caller to callee.

Likewise, the X and Y which DrawPixel needs will initially be one of the X,Y pairs which DrawLine is called with. One of those pairs could perhaps be modified in place by DrawPixel, if that's OK for DrawRectangle. That is, call by name not call by value.

In general, if you have some large datatypes to be passed, which live in a not-too-large structure, you can use the base address of the structure as a global and pass in the index to the interesting datatype using the X or Y register.

In many cases we use subroutines only to factor our code into maintainable pieces - they don't need to pass data because the location of the data is already known.

If you look at floating point routines in BASIC interpreters, you generally find that one or two blocks of zero page are allocated as accumulators, so that operations will copy from the user's variables, operate using the user's variables, or write back to the user's variables. You don't need nearly so much zero page as you have operands in an expression. (I think this is right, but stand to be corrected.)

And outside the times that the interpreter is doing floating point expression evaluation, those blocks of zero page can be used for other purposes.

Hope this helps
Ed


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 4:56 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
Quote:
For example, I defined R0-R4 for the DrawPixel function to use. If I had a DrawLine function that calls DrawPixel I could define R5-R9 for it to use and for DrawRectangle R10-R14 since it will use DrawLine. To me this seems like I am using three times more of the zero page than necessary and wasting memory. How would you do it?



It happens that graphics programming is quite common on many platforms, including the 6502. So this question has been addressed lots of times, including by me.

Here's one approach:

DrawPixel needs X, Y and color. Three parameters. Okay, write a function to do that. If you want to use 'Rx' as the names of the zero page locations you will use, let's put:

color in R0
X in R1
Y in R2

Make DrawPixel a pure subroutine, in the sense that there is no function overhead of saving and restoring registers. If you still want it to be accessible as a function in contexts that do require saving and restoring registers, write three routines: SaveRegisters, DrawPixel, and RestoreRegisters and use them like this:

Code:
   SaveRegisters
   DrawPixel
   RestoreRegisters


The point is DrawPixel itself must be accessible without monkeying around saving and restoring memory locations.

DrawLine needs X1, Y1, X2, Y2 and color. Five parameters. BUT three of them are the same as DrawPixel. So we can re-use those same locations:

color in R0
X1 in R1
Y1 in R2
X2 in R3
Y2 in R4

"But", you might say, "I need to re-use R0-R2 for each pixel." And you'd be correct. "But," I'd say, "you don't need to save and restore them. Presumably you're using Bresenham's line drawing algorithm. Just update R1 and R2 directly within the main loop and call DrawPixel at each iteration. DrawPixel doesn't care how the values got into R1 and R2, just that they're there."

Code:
DrawLine:
   InitializeBrensenham
loop:
   DrawPixel
   UpdateBrensenham
   bcc loop         ; assuming "UpdateBrensenham" sets the carry if there's nothing more to do
   rts


DrawRectangle needs X1, Y1, X2, Y2 and color. The same as DrawLine, and for that reason it can use the same R0-R4 locations as DrawLine to store AND update them as needed within the DrawRectangle function. As with DrawPixel, DrawLine doesn't care how the values in R0-R4 got there, just that they're there.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 5:32 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
It is true that when programming the 6502 you must pay careful attention to how zero page memory is being used. I have found it a useful starting point to define eight bytes of zero page as "scratch memory":

Code:
Data0 .ds 2
Data1 .ds 2
Addr0 .ds 2
Addr1 .ds 2


Any "leaf" subroutine (that is, a subroutine which itself does not call any other subroutine) can freely use these locations for whatever purpose. It is guaranteed that they can't be stepped on by another subroutine , simply because no other subroutine is active at the same time.

Is that enough scratchpad memory? Not at all, but it is surprising how much just that little bit covers. Most leaf (or "level-0") subroutines can get by with them or perhaps one more address or data scratch location. I'm flexible; if a program needs that I'll provide it by altering the above definition.

What about subroutines which call leaf subroutines? This is where programmer attention pays off. If a subroutine calls only level-0 ("leaf" subroutines, it is a level-1 subroutine. Level-1 subroutines can have their own dedicated scratch memory. Again, it normally amounts to just a few bytes. Since only one level-1 subroutine is active at any given time, it can freely use "level-1 scratch" memory however it wishes.

The same goes for "level-2" subroutines. Beyond that, well, I rarely need to go beyond three levels, but it has happened and I have resorted to saving and restoring parts of zero page memory. But because they are exceptional situations they don't happen that often during execution, so the penalty isn't all that much.

The big question that needs to be answered using this approach is "how much scratch memory do I need at any level?" The answer is "as much as is used by the single subroutine at that level which uses the most". As a programmer you must be frugal in your use of scratch memory and only use as much as you really need at any level.

It also helps if the assembler you're using can assist you in this approach. Long ago I found a way to use the Merlin assembler's variable labels to keep track of each scratchpad area I was using. I wrote a macro that would add space to one whenever I needed it so I didn't have to manually do the bookkeeping. I've forgotten exactly how I did that, but when I wrote my own assembler I put in a named "common" pseudo opcode to make it easier to manage areas of memory for multiple users (subroutines).


Top
 Profile  
Reply with quote  
PostPosted: Fri Jul 04, 2014 7:52 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8428
Location: Southern California
Druzyek wrote:
Quote:
Forth on the 6502 puts the data stack in ZP (zero page)

I see the advantages everyone has listed but wouldn't keeping track of your own stack pointer make pushing and popping a little slower? Probably not a deciding factor but it does seem like a little overhead.

A little bit, but you also save some by not having to step over return addresses to handle data. I'm not sure I can illustrate this clearly with just text. I might have to find a way to diagram it. Say you're dealing with three data cells on a stack, A, B, and C, A being at the top. C is an input for subroutine 1, but before it uses it, it does some other stuff to get B and A and put them on the stack, then it calls subroutine 2 which needs A, B, and C. However, now there's a return address between B and C, and if you make subroutine 2 step over that to access C, now you can't use subroutine 2 with something else that calls it without a return address between B and C. It has no way of knowing which cells are return addresses and which ones are data unless you add more overhead. If you have a separate data stack, situations like this are automatically avoided.

_________________
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 04, 2014 11:43 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3346
Location: Ontario, Canada
Druzyek wrote:
wouldn't keeping track of your own stack pointer make pushing and popping a little slower?
The short answer is yes. Instead of PHA you could have STA 0,X then DEX, for example (minor variations are possible). So it is a little slower, although still very straight-forward and effective. And -- having two stacks drastically simplifies certain maneuvers, as Garth was explaining.

J :)

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


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

All times are UTC


Who is online

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