### Author Topic: Can someone explain Flags to me?  (Read 21756 times)

0 Members and 1 Guest are viewing this topic.

#### troflip

##### Re: Can someone explain Flags to me?
« Reply #15 on: June 12, 2019, 02:07:52 PM »
Code: [Select]
`(procedure (Btst flagEnum) ;Test a boolean game flag (& [gameFlags (/ flagEnum 16)] (>> \$8000 (mod flagEnum 16))))`
If the flag you pass in is 35, then this essentially becomes:

(procedure (Btst flagEnum)
;Test a boolean game flag
(& [gameFlags 2] (>> \$8000 3)) ; since 35 /16 is 2, and 35 mod 16 is 3
)

So it's selecting the 3rd bit in the 2nd gameFlags array index. It's basically just doing integer division and modulo to turn the flag number into an array index (0 to however big the array is) and bit position (0-15).

Check out my website: http://icefallgames.com

#### Doan Sephim

##### Re: Can someone explain Flags to me?
« Reply #16 on: June 12, 2019, 02:16:34 PM »
Thanks Troflip et al. I think I finally grasp what going on with this script and I'm very thankful for everyone for bearing with me. I kinda feel like the dumb kid in class asking all these questions, but everyone has been quite patient and accommodating, and for that I'm grateful.

#### troflip

##### Re: Can someone explain Flags to me?
« Reply #17 on: June 12, 2019, 02:17:50 PM »
Interestingly enough, if you're doing it this way in SCI0, this could end up eating up more memory, since the script code takes up heap space (I forget if that's true for SCI01 or not, but it's no longer the case for SCI1+ since script code isn't in the heap).

BTest, bSet, BClear functions themselves eat up 101 bytes. So that's a fixed cost right off the bat.
160 regular globals eat up 320 bytes, whereas [gameFlags 10] only eats up 20. So you're saving 300 bytes at a cost of 101 bytes. Still coming out ahead, except...

Except it takes 6 bytes of compiled code to call BTest or BSet.
But only 4 bytes (or possibly 5) to directly assign a global TRUE or FALSE.
And only 2 bytes (or possibly 3) to read from it.

So, read a flag 75 times in currently loaded script code and you've already lost your bytes advantage.

Check out my website: http://icefallgames.com

#### Doan Sephim

##### Re: Can someone explain Flags to me?
« Reply #18 on: June 12, 2019, 02:26:51 PM »
So, read a flag 75 times in currently loaded script code and you've already lost your bytes advantage.
When you say 75 times in currently loaded script, i assume that that means (from a general gameplay perspective) in a single room, but after each room, it's all disposed, correct? So generally speaking, it's a heap saver, but if I were to read the flag(s) 75 times in a single room, it would be heap-negative compared to normal.

Or does it mean 75 times at all?

#### troflip

##### Re: Can someone explain Flags to me?
« Reply #19 on: June 12, 2019, 02:33:10 PM »
Yes correct, but you need to include all the places in Main.sc or Game.sc or whatever too (scripts that are always loaded).
Check out my website: http://icefallgames.com

#### Doan Sephim

##### Re: Can someone explain Flags to me?
« Reply #20 on: June 12, 2019, 02:39:06 PM »
Yes correct, but you need to include all the places in Main.sc or Game.sc or whatever too (scripts that are always loaded).
I see, so if I have any of those procedure already called in the default scripts, those will always be adding towards that 75-time limit.

Even so, it seems likely to me, that 75 is a pretty high number of calls for average use, so I think it will ultimately save.

#### Charles

##### Re: Can someone explain Flags to me?
« Reply #21 on: June 12, 2019, 02:46:40 PM »
Also considering the AddToScore procedure, which relies on the Btst/Bset/Bclr trio, let's round down to a hypothetical 50 flags being read in a single scene ( i.e. the current script, main.sc, game.sc, and whatever locales and regions are also included with the current script).  Is that a practical consideration?  50 flags read in a single game scene sounds more than sufficient, but I haven't done compared many games.

The only code I've really looked through is QFG1, and I'm struggling to recall if it ever did more than 5 flag read/writes in a single scene.

#### NilG

##### Re: Can someone explain Flags to me?
« Reply #22 on: June 12, 2019, 05:59:25 PM »
One other question from me, too.  I implemented this for one of my globals and it seems to be working great after a couple of initial test runs.  However, when I compile, I get a bunch of "No effect on code" warnings for the new stuff, as in the attached.

The flag and procedures must be working, since the game's responding to events related to the flag.  I've edited the code and commented out the global for the time being while testing.  From the player standpoint, so far so good, but am I missing something?

#### troflip

##### Re: Can someone explain Flags to me?
« Reply #23 on: June 12, 2019, 06:55:23 PM »
Enclose the code in BTest in a return statement.

Check out my website: http://icefallgames.com

#### NilG

##### Re: Can someone explain Flags to me?
« Reply #24 on: June 12, 2019, 07:51:54 PM »
That did it, except for the "oldState" lines in Bset and Bclr.

I found that enclosing "oldState" in a return statement in Bset made that one disappear; doing the same in Bclr did not.

I noticed that oldState isn't even in parentheses in either line, which seems unusual.  Are these what's getting returned?  Seems like it to me, they're the new values getting assigned(?), but making sure.  Any thoughts why Bclr's oldState would still give the "no effect on code" warning?

EDIT:  Must have done something wrong the first time, went back and added the return statement to Bclr again and now no warning.
« Last Edit: June 12, 2019, 07:54:02 PM by NilG »

#### troflip

##### Re: Can someone explain Flags to me?
« Reply #25 on: June 12, 2019, 08:26:42 PM »
I should have said: enclose the *last* statement of the procedure in a return statement.
Check out my website: http://icefallgames.com

#### NilG

##### Re: Can someone explain Flags to me?
« Reply #26 on: June 12, 2019, 10:01:32 PM »
Regarding heap, it seems like memory usage is going up each time I replace (== z TRUE/FALSE) with (Btst z) or = with Bset/Bclr.

It's not a huge difference, but seems counterintuitive to replacing the variables with flags.  Is it plugging in the whole procedure rather than a pointer (or whatever the procedure equivalent would be) each time I reference the call, or is this unexpected?  Logically, it's working fine.  I'm just not sure if the more flag testing/setting I do (as opposed to variable work), the heavier the price should be.  Some variables can pop up a dozen times in a script, so just looking for the best overall path.

#### troflip

##### Re: Can someone explain Flags to me?
« Reply #27 on: June 13, 2019, 12:09:08 AM »
See my post a few posts up for an explanation of why this is. Basically, a procedure call takes up a few more bytes than assigning or testing a variable. You can disassemble the script to see exactly how much.

Code: [Select]
`(== z TRUE/FALSE)`
btw, why not use:
Code: [Select]
`z  ; instead of (== z TRUE)`
or

Code: [Select]
`(not z)  ; instead of (== z FALSE)`
« Last Edit: June 13, 2019, 12:12:45 AM by troflip »
Check out my website: http://icefallgames.com

#### NilG

##### Re: Can someone explain Flags to me?
« Reply #28 on: June 13, 2019, 05:06:14 AM »
Thanks, troflip, not sure how I managed to miss that entire chunk of conversation, but makes sense.

Honestly, I just didn't realize the shorthand was accepted or think to try it out.  I'll def keep that in mind now, though.

#### Kawa

##### Re: Can someone explain Flags to me?
« Reply #29 on: June 13, 2019, 05:33:44 AM »
Why would you just say z directly in a conditional expression instead of (== z TRUE)? For those playing along at home and don't know yet, this is because of a few things. First, TRUE and FALSE are just constants representing 1 and 0. Second, any non-zero value is "truthy". You'd think TRUE is the most truthy of all but it's just a number so it's equally truthy, this isn't Animal Farm. The expression in an if statement and such has to evaluate not to true but to truthy to execute, and that can be any expression whatsoever.
Code: [Select]
`(if (AskConsent "Are you sure you want to shoot that?") ...) ; will execute if AskConsent returns non-zero(if (Btst fShotTheThing) ...) ; will execute if the "shot the thing" flag is set(if (== z TRUE) ...) ; will execute if z is exactly 1(if z ...) ; will execute if z is 1 or higher`
By that same token, (if mover (mover dispose:)) only executes if mover is non-zero and assumes it's a valid reference to some instance.

To elaborate on Phil's post about procedure calls and byte counts: bytecode-wise, (if z ...) turns into the following:
Code: [Select]
`83 00      lal local0 //or 85 00 lat temp0, you get the point.31 08      bnt justAfterThat`Load a local or temp into the accumulator, branch if the acc is not truthy. Four bytes, maybe one more if the code in the if block is large enough. The branch goes down eight bytes because that's how tiny the block itself is.
Compare that with a procedure call:
Code: [Select]
`78         push1 // one parameter39 2a      pushi 2a // flag #4245 01 02   callb Btest 2 // call with two bytes worth of param data31 08      bnt justAfterThat`That's six bytes to execute (if (Btest 42) ...). So if you test the same flag a bunch of times, consider testing it once and storing it to a temp.
And finally, checking your own properties as above is four bytes again:
Code: [Select]
`63 12      pToa mover31 08      bnt moverWasNull`Noting that mover is either null or something far greater than 1.
Now, by "returns non-zero" in the first half of this post above, we actually mean "sets the accumulator register to a non-zero value before popping back to the caller". You should be able to figure out what that means by now if you didn't already know.

Page created in 0.048 seconds with 24 queries.