Search the Community
Showing results for tags 'variables'.
Found 2 results
Currently I'm working on completely rewriting the burger logic code in Chaotic Grill. I've run into an issue where the variable labels in Stella don't match the variables defined in a DASM "SEG.U vars" set. At first I thought it was a bug in Stella, but then I realized that my new title screen variables overlap with the main game variables and are defined later in the code base, which probably causes the issue. The main game variables are: - defined in a SEG.U vars segment - defined as: chefState ds 1 OR enemyY ds 4 The title screen variables are: - defined after the main game variables - defined outside the SEG.U segment - defined as chefState = $87 Now I realize I'm might be shooting myself in the foot by using two different methods to define variables... but I've only recently switched to using SEG.U with the main game variables and haven't gotten around to cleaning up the other areas of code that define their own variables. What are my options for "forcing" Stella to use a certain set of variable labels? Currently I'm just using the DASM built lst/sym files.
Local Variables for the Common Man I wrote a local variables library for TurboForth some years back. It was/is quite sophisticated; Forth words could have their own private, named variables. Nice. Just recently I find I'm interested in writing less code, not more. I find I'm more interested in what code I can do *without* rather than the code I need. This leads to very interesting thought-exercises. It is very interesting to strip code away and arrive at the simplest code you can come up with that still gets the job done. With that in mind, I recently took another look at local variables. For some folk, local variables in Forth are an anathema. I disagree. They reduce stack "juggling", or "stack traffic" where you are just juggling items on the stack to get them into the order you need them in. *Not* spending CPU cycles on juggling is getting more useful work done. Having named local variables in Forth (i.e. local variables that you can give any name to) is very nice, but we can really live without them. In assembly language on the TMS9900, we have 16 global "variables" - the registers. They are named R0 to R15. If we BLWP into a subroutine in a new workspace, we have 16 local variables, also called R0 to R15. The names are fixed, and we seem to get along with them just fine. So why not just do the same with local variables? With that in mind, I thought I would write something to be as economical as possible. I was very pleased with all the code that I *hadn't* written, so I thought I would share what I haven't written here. :-) The first problem to solve is where to put the local variables. We can't put them on the Forth data stack, because they would get in the way of other data that words are pushing/pulling to/from the stack. Imagine this word: : A ( -- ) B C D E ; Imagine that all of the words B C D and E use local variables. Furthermore, these words may internally call other words that also use local variables - maybe B calls Y and Y uses locals, and Y calls Z and Z uses locals. We need a locals stack. Well, a stack is just an area of memory with a pointer that points to the top of the stack: $ff00 value lsp \ locals stack pointer So, here's a VALUE (a type of variable) called 'lsp' (locals stack pointer). We're going to place our locals stack at >FF00 at the end of RAM. Now we need some words that can store values on the data stack. Let's implement three local variables, A, B and C. What are we going to call these words? Well, for storing data in the variables (that is, taking something off the stack and storing it in a local variable) how about >a >b and >c? The arrow before the variable name shows something "going into" the variable. It's a picto-gram. Similarly, for reading from a local variable, (reading from the variable and pushing onto the stack) how about a> b> and c>? The arrow shows something leaving the variable. Looks pretty good to me. So, each word that uses local variables can have three local variables, a,b, and c. That's 6 bytes. +-------+ lsp --> | A | 2 bytes +-------+ | B | 2 bytes +-------+ | C | 2 bytes +-------+ As can be seen, the locals stack pointer (lsp) is pointing to the top of the locals stack. So local variable A will be stored at the address in lsp, local variable B at lsp+2, and local variable C at lsp+6. Simple. Here's the code for writing to the local variables. Note the stack signatures. : >a ( n -- ) lsp ! ; : >b ( n -- ) lsp 2+ ! ; : >c ( n -- ) lsp 4 + ! ; And here's the code for reading from the local variables. Again, note stack signatures. : a> ( -- n ) lsp @ ; : b> ( -- n ) lsp 2+ @ ; : c> ( -- n ) lsp 4 + @ ; Now we need a word to make some space on the locals stack. Again, I'll use a pictogram: : lsp-> ( -- ) 6 +to lsp ; Here, the -> is pointing "upwards" on an imaginary number line, indicating that the word increases the lsp. And here's a word to decrease the local stack pointer: : <-lsp ( -- ) -6 +to lsp ; We're nearly done. All we need to do now is have some method of using the local variables in a Forth word. After some experimenting, the simplest approach I could come up with was to have a new word for : (which is used to create new words) that indicates that we want to create a new word, but with the special property of having access to local variables. For this, I chose :: (two colons). So instead of: : fred ( -- ) some clever code here ; We have: :: fred ( -- ) some clever code here ;; Both are identical, but the word created by :: has access to local variables. Here's the code: : :: : compile lsp-> ; That actually looks quite confusing, so let's break it down: The first colon means "hey, here comes a new word". The double colon is the name of the new word, so we have "hey, here comes a new word called ::" The third colon just includes the behaviour of : *in* the new word! So we have "hey, here comes a new word called :: and I want it do the same thing as : does, thanks." The "compile lsp->" part will ensure that when :: runs (when a new word with locals is being created) a reference to our lsp-> word will be compiled into *that* word (the word that is bing created). Hence, the locals stack will move down memory, and the word will get its own space for its locals at run-time. Finally, we need to terminate the definition, just like ; in a "regular" word. : ;; compile <-lsp [compile] ; ; immediate Again, we use : to create a new word called ;; and this word will compile a reference to <-lsp into the word under creation, thus re-claiming the locals stack space that the word will use at runtime. We then want the word to run the normal ; action to complete the word compilation. Well, ; is an immediate word so we use [compile] to override this behaviour. Then we terminate the ;; itself with ; and we make it immediate, so that it matches the behaviour of ; And voila. We're done. Look how much code it isn't: $ff00 value lsp \ locals stack pointer : >a ( n -- ) lsp ! ; : >b ( n -- ) lsp 2+ ! ; : >c ( n -- ) lsp 4 + ! ; : a> ( -- n ) lsp @ ; : b> ( -- n ) lsp 2+ @ ; : c> ( -- n ) lsp 4 + @ ; : lsp-> ( -- ) 6 +to lsp ; : <-lsp ( -- ) -6 +to lsp ; : :: : compile lsp-> ; : ;; compile <-lsp [compile] ; ; immediate We've just added the ability for Forth words to have true, stacked access to local variables, and it took us 188 bytes! Let's test it and see if it works: :: test2 ( n1 n2 n3 -- ) 3 * >c 3 * >b 3 * >a ." Test 2:" a> . b> . c> . cr ;; :: test1 ( n1 n2 n3 -- ) cr 2* >c 2* >b 2* >a a> b> c> test2 ." Test 1:" a> . b> . c> . cr ;; 1 2 3 test1 If you run this, you get the following output: Test 2: 6 12 18 Test 1: 2 4 6 Explanation of the code: We put 1 2 3 on the stack. Then we call test1. Test1 multiples the top of the stack (n3 in the stack signature) by 2, and stores it in local variable c. In doing so, it is removed from the stack. The next item on the stack (2) is multiplied by 2, and stored in b. Then the last item on the stack (1) is multipled by 2, and stored in a. We then get those stored values out of the local variables and back on to the stack, and call test2. Test2 does a similar thing: Takes the values off the stack, multiolying them by 3 and storing them in *its* local variables. Then it displays them. Then, control is returned to test1 just after the call to test2. Now, the local variables that were in test2 have gone, and the local variables that are in test1 are "back in scope" and we prove that by displaying them. Hence we have proved that we can nest calls to words to contain their own local variables and they work as expected and don't interfere with each other. And all in 188 bytes. Enjoy your Forth!