Jump to content
IGNORED

Can the DPC+ Stack Be Hacked and Turned Into Variables?


Random Terrain

Recommended Posts

...convert the DPC+ 256 stack locations into 256 variables?...

 

I'll take what I can get.

One more update!

 

If you do not want to use the DPC+ Stack then:

  • If the value is >= 0 then you can use Set/GetValue1 to save 256 values
  • If the value is >= 256 then you can also use Set/GetValue2 for a total of 512 values
  • If the value is >= 512 then you can also use Set/GetValue3 for a total of 768 values
SetValue3
    ; A = location 0-255
    ; Y = value
    sta DF0LOW
    lda #$0d
    sta DF0HI
    sty DF0WRITE
    RETURN
    
GetValue3
    ; A = location 0-255
    sta DF0LOW
    lda #$0d
    sta DF0HI
    lda DF0DATA
    ; A is the return value
    RETURN


You can view those in Stella's debugger on page $0D of the Cartridge RAM tab.

  • Like 1
Link to comment
Share on other sites

Thanks. I don't understand any of it, but I can link to it from the bB page for other people who might be able to use it.

Think of the values as files on your disk - if you want to edit your shopping list you must load it into memory, make changes, then save it back to disk. For these values you just load them into a variable (a-z, var0-var8), make changes, then save them back.

 

 

Imagine a game where you're a wizard and are wandering around collecting items for your spells and potions. Use these new values to hold counts of how many of each item you've collected. First define the values you want to keep track of:

 

const V1_Amber       = 0
const V1_BayLaurel   = 1
const V1_Coriander   = 2
const V1_GarlicClove = 3
const V1_Lavender    = 4
const V1_Oil         = 5
const V1_Quartz      = 6
const V1_Rosemary    = 7
const V1_Thyme       = 8
const V1_Toadstool   = 9

When a new game starts you want all the counts to be zero. You can do it like this:

 temp1 = SetValue1(#V1_Amber,       0)
 temp1 = SetValue1(#V1_BayLaurel,   0)
 temp1 = SetValue1(#V1_Coriander,   0)
 temp1 = SetValue1(#V1_GarlicClove, 0)
 temp1 = SetValue1(#V1_Lavender,    0)
 temp1 = SetValue1(#V1_Oil,         0)
 temp1 = SetValue1(#V1_Quartz,      0)
 temp1 = SetValue1(#V1_Rosemary,    0)
 temp1 = SetValue1(#V1_Thyme,       0)
 temp1 = SetValue1(#V1_ToadStool,   0)

An alternative way, which would be a little slower but use less ROM, would be to use a for-next loop. The item #s are 0 thru 9 so:

 for x = 0 to 9 : temp1 = SetValue1(x, 0) : next

this might work too, and would make the intent a bit clearer:

 for x = #V1_Amber to #V1_ToadStool : temp1 = SetValue1(x, 0) : next

As your wizard goes around collecting things you need to update your inventory.

FoundToadstool
 rem for this function:
 rem var0 is used for toadstool count
 var0 = GetValue1(#V1_ToadStool) : rem get the current count for toadstools
 if var0 < 255 then var0 = var0 + 1 : rem max toadstools we can have are 255
 temp1 = SetValue1(#V1_ToadStool, var0) : rem we are done changing count, so need to save it
 return

FoundGarlicBulb
 rem for this function:
 rem var0 is used for starting clove count
 rem var1 is used for # of found cloves
 rem var2 is used for ending clove count
 var0 = GetValue1(#V1_GarlicClove) : rem get the current count for garlic cloves
 var1 = (rand & 3) + 4 : rem garlic bulbs have a variable number of garlic cloves, for this demo we are using a random number 4-7
 var2 = var0 + var1 : add the number of just found cloves to our current balance
 if var2 < var0 then var2 = 255 : rem if var2 < var0 then the count exceeded the max of 255 that can be held in a byte
 temp1 = SetValue1(#V1_GarlicClove, var2): rem we are done changing count, so save it
 return

etc.

Now that we have items, we want to brew up a healing potion. Let's say that needs a bay laurel, 3 garlic cloves, oil and 2 thyme:

 function HealingPotion
   rem for this function:
   rem var0 is used for bay laurel count
   rem var1 is used for garlic clove count
   rem var2 is used for oil count
   rem var3 is used for thyme count

   rem get our ingredients
   var0 = GetValue1(#V1_Baylaurel)
   var1 = GetValue1(#V1_GarlicClove)
   var2 = GetValue1(#V1_Oil)
   var3 = GetValue1(#V1_Thyme)

   rem make sure we have enough
   if var0 < 1 then return 0 : rem do we have enough bay laurel?  A return value of 0 means no potion made
   if var1 < 3 then return 0 : rem do we have enough garlic clove?
   if var2 < 1 then return 0 : rem do we have enough oil?
   if var3 < 2 then return 0 : rem do we have enough thyme?

   rem subtract them from inventory
   var0 = var0 - 1 : rem bay laurel
   var1 = var1 - 3 : rem garlic clove
   var2 = var2 - 1 : rem oil
   var3 = var3 - 2 : rem thyme

   rem update(save) our inventory
   temp1 = GetValue1(#V1_Baylaurel, var0)
   temp1 = GetValue1(#V1_GarlicClove, var1)
   temp1 = GetValue1(#V1_Oil, var2)
   temp1 = GetValue1(#V1_Thyme, var3)

   return 1 : rem return value of 1 means potion was made

 

Any help?

  • Like 2
Link to comment
Share on other sites

When a new game starts you want all the counts to be zero. You can do it like this:

temp1 = SetValue1(#V1_Amber, 0)
temp1 = SetValue1(#V1_BayLaurel, 0)
temp1 = SetValue1(#V1_Coriander, 0)
temp1 = SetValue1(#V1_GarlicClove, 0)
temp1 = SetValue1(#V1_Lavender, 0)
temp1 = SetValue1(#V1_Oil, 0)
temp1 = SetValue1(#V1_Quartz, 0)
temp1 = SetValue1(#V1_Rosemary, 0)
temp1 = SetValue1(#V1_Thyme, 0)
temp1 = SetValue1(#V1_ToadStool, 0)

An alternative way, which would be a little slower but use less ROM, would be to use a for-next loop. The item #s are 0 thru 9 so:

for x = 0 to 9 : temp1 = SetValue1(x, 0) : next

this might work too, and would make the intent a bit clearer:

for x = #V1_Amber to #V1_ToadStool : temp1 = SetValue1(x, 0) : next

 


There's a way to have DPC+ fill memory that would be super fast. I've never used it myself as it wouldn't make sense to drop out of the fast C code, have the slow 6507 request DPC+ to fill memory, then return to the fast C code.

 

; prep the datafetcher
    ; set the starting location
    lda #V1_Amber 
    sta DF0LOW

    ; set the page.  Use $0f for V1_, $0e for V2_, or $0d for V3_ values
    lda #$0f 
    sta DF0HI


 ; set the parameters
    ; fill value (can fill any value 0-255 that you wish).  stored twice, probably so the "data fetcher" and "bytes to fill" match the copy function
    lda #0 
    sta PARAMETER
    sta PARAMETER

    ; data fetcher (the one used above in "prep the datafetcher")
    lda #0 
    sta PARAMETER

    ; how many bytes to fill
    lda #(V1_ToadStool - V1_Amber) + 1 ; same as lda #10, but makes meaning clearer on the range to be filled
    sta PARAMETER


 ; trigger the fill process
    ; 2 selects the "Fill function"
    lda #2 
    sta CALLFUNCTION

Note: code is untested

Link to comment
Share on other sites

imo it would be more intuitive for BASIC programming to expose the DPC stack transparently as a variable array that could be used interchangably with other variables instead of requiring compound functions:

 

g = f + DPC(0) + DPC(30)*r

 

It would take a lot of compiler code revision to make array variables work with regular vars like that, but not so much if they are just being exposed as an array and shuttled back and forth into low ram vars before participating in expression calcs:

 

g=DPC(50):DPC(51)=r

 

One thing to keep in mind is that BASIC has the concept of = being intuitive (there is no == or ===) so to even integrate "g=DPC(#myvar):DPC(5)=37" requires that a c programmer forget about ==. If you dig into the source code you will doubtless find batari's hooks to handle when = means equals and when = means ==; but it only means that to the c programmer, to the BASIC programmer c style functions can be confusing.

Link to comment
Share on other sites

I'm sure that will help after I get more sleep. I'll read it in detail tomorrow. Thanks.

It took longer before I could get to it. Thanks for trying to make it clearer. I don't use functions since they are too complicated for me to use. Speaking of complicated, I also quit using Nybble Variables because the code for one of my programs became so confusing that I quit working on it for several years. When I eventually yanked out the Nybble Variables, I could finally work on the program again. As long as I keep it simple and avoid things that are too hard to remember, I can get things done.

Link to comment
Share on other sites

  • 1 year later...

Is this faster than the regular way of using the stack?

 

I pull and push values to temp2 and temp3 every frame of the main loop the normal way..

stack 70
pull temp2 temp3

(increase or decrease temp2 and temp3 values)

and then

stack 72
push temp2 temp3

 

I tried to pull like this instead

asm
; A = location 0-255
LDA #<(STACKbegin+72)

sta DF0LOW

lda #$0f

sta DF0HI

lda DF0DATA

; A is the return value
STA temp2
end

 

..and push like this

asm

; Y = value
LDY temp2

; A = location 0-255
LDA #<(STACKbegin+72)

sta DF0LOW

lda #$0f

sta DF0HI

sty DF0WRITE
end

 

That seems to push and pull correct for temp2 but alot of other thing start to glitch, the players will teleport a couple pixels to the right, then back again etc.

If this isn't faster I will keep the regular way, but it would still be nice to know what's wrong with it?

Link to comment
Share on other sites

  • 10 months later...

Alternately, you can use macros. While straightforward and obvious, this can be useful if you want to avoid using functions.


macro _Set_DPC1
asm
; 1 = location 0-255
; 2 = value
ldy {2}
lda {1}
sta DF0LOW
lda #$0f
sta DF0HI
sty DF0WRITE
end
end

macro _Get_DPC1
asm
; 1 = location 0-255
; 2 = target variable
lda {1}
sta DF0LOW
lda #$0F
sta DF0HI
end
{2} = DF0DATA
end


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;Access DPC+ stack using variables and/or hard coded index and value                          ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


temp1 = 2 ;index of set call
temp2 = 9 ;value to store

temp3 = 2 ;index of get call
; temp4 return value

callmacro _Set_DPC1 temp1 temp2 ;Usage:  callmacro _Set_DPC1 index value
callmacro _Get_DPC1 temp3 temp4 ;Usage:  callmacro _Get_DPC1 index target
_sc1 = temp4 ;use score to show returned value from _Get_DPC1 macro **(we stored that in temp4)

;Alternately, you can use hardcoded values or constants to interact with the DPC+ stack.
callmacro _Set_DPC1 3 8   ; Store 8 at index 3
callmacro _Get_DPC1 3 temp5   ; Store value at index 3 into temp5
_sc2 = temp5 ;use score to show returned value from _Get_DPC1 macro **(we stored that in temp5)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...