Jump to content
Sign in to follow this  
nanochess

IntyBASIC compiler v1.0 wish list and 'contrib' dir

Recommended Posts

 

I guess it has been already suggested, but a READMYMIND statement would be great!. It should not be too complicated because my brain is really simple. you would have read one or two neurones maximum.

 

More seriously , i didn't have time yet to really use your basic and i didn't follow in details the evolution , but i plan to use it as soon as i have complete my current colecovision projects.

 

It would be nice, if not here yet , To have a SAY command to drive the Intellivoice.

 

Be careful not to combine the MAKEGAME and READMYMIND statements!! :o Lest it results in a rip in the space-time continuum!!!

  • Like 1

Share this post


Link to post
Share on other sites

Another wish - the music player is fantastic, but the syntax does not allow for flats or sharps. Some music is just damned impossible to get right without half-tones. Yeah, I could POKE myself, but it would be cool to just add # or b to a note.

 

And I realize # is already taken - too bad, break some old games for my selfish desires :P I don't quite understand its purpose to be honest - I just use the S notation for sustaining a note(s). Do they do different things?

Share this post


Link to post
Share on other sites

Another wish - the music player is fantastic, but the syntax does not allow for flats or sharps. Some music is just damned impossible to get right without half-tones. Yeah, I could POKE myself, but it would be cool to just add # or b to a note.

 

And I realize # is already taken - too bad, break some old games for my selfish desires :P I don't quite understand its purpose to be honest - I just use the S notation for sustaining a note(s). Do they do different things?

 

You can use sharps! use the symbol # for it, like C4#

 

The S notation indicates to keep playing the previous note, note that this is different from putting the same note again, specially with piano.

Share this post


Link to post
Share on other sites

 

You can use sharps! use the symbol # for it, like C4#

 

The S notation indicates to keep playing the previous note, note that this is different from putting the same note again, specially with piano.

 

Oh! The a manual fix is needed :)

 

 

Identifiers for notes: Note C, D, E, F, G, A, B followed by octave (2-6), also

available C7

Optionally add # symbol for sustain.

 

And I guess you don't need flats, as that's just the sharp of the note below, if my grade 8 music lessons are still correct.

  • Like 1

Share this post


Link to post
Share on other sites

 

And I guess you don't need flats, as that's just the sharp of the note below, if my grade 8 music lessons are still correct.

 

For the most part, save for a few oddities like B# = C, and Cb = B.

Share this post


Link to post
Share on other sites

For the most part, save for a few oddities like B# = C, and Cb = B.

Yes, the only ones for which the sharp/flats coincide are the notes surrounding the black keys on a piano keyboard.

 

dZ.

Share this post


Link to post
Share on other sites

Music sounds SO much better with semitones. Especially with so much classical music written in unusual keys. It's very frustrating to have near-perfect pitch perception, but have lost the skills to transcribe it.

 

I must say, I've always had a lot of respect for programmers of yore, for the sheer technical challenge of 30 years ago. But I'm appreciating more and more the artistic side as well.

Share this post


Link to post
Share on other sites

 

Oh! The a manual fix is needed :)

 

 

And I guess you don't need flats, as that's just the sharp of the note below, if my grade 8 music lessons are still correct.

 

My mistake! :grin:

Share this post


Link to post
Share on other sites

I already asked for a true/false datatype likesomething:

a.boolean=false

 

maybe also a way to "destroy" variables not needed.

 

like:

a=32 ' (equ to address 0x144)

a=a+32

if a=64 then gosub dosomething

destroy a '(a is now undefined at the space 0x144 is free)

b=100' (new variable created, now using space 0x144)

 

maybe we could also have local variables like in Pascal, which only lives inside procedures (gosub).

 

Maybe also functions could be usefull like. (also like pascal)

 

i dont know if i did something wrong but i did something like this:

 

define 0,1,spr 'sprite bitmap

 

later in the code i did

 

define 10,1,grp 'create some other cards.

 

then my sprite was reset to be a block of 8x 255 bitmap.

 

dont take this the wrong way, already a big fan of intybasic :) working on my 2nd game :)

  • Like 2

Share this post


Link to post
Share on other sites

I already asked for a true/false datatype likesomething:

a.boolean=false

 

maybe also a way to "destroy" variables not needed.

 

like:

a=32 ' (equ to address 0x144)

a=a+32

if a=64 then gosub dosomething

destroy a '(a is now undefined at the space 0x144 is free)

b=100' (new variable created, now using space 0x144)

 

maybe we could also have local variables like in Pascal, which only lives inside procedures (gosub).

 

Maybe also functions could be usefull like. (also like pascal)

 

 

That's starting to sound a little like register allocation. Well... at least lifetime analysis, even if directly assisted with declarations like "destroy."

 

In classic BASIC, all variables are global. It's perfectly legal to GOSUB somewhere and expect a variable to be created by a subroutine and then reference it from the caller.

 

This idea introduces the concept of "scope" to variable names, which is foreign to the original BASIC and most interpreted 8-bit Microsoft BASICs. (Not to say that BASIC didn't eventually get the idea of scoping variables. Look up the SUB keyword in TI Extended BASIC. I don't know if Microsoft wrote that extension or not, but I'm told they wrote the original TI BASIC.)

 

The thing is, scope alone isn't enough. To allocate different variables to the same RAM at compile time, you have to do a control flow analysis to determine which subroutines might be active (and therefore their local variables) at the same time, which is why I draw parallels to register allocation. You have to do liveness analysis to build an interference graph to know which variables can occupy the same location at the same time.

 

The main difference is granularity and the size of the resource you're allocating. With register allocation, you're managing machine registers, and the granularity is "instructions." With subroutine local variables, the granularity is "subroutines" and the resource you're managing is RAM.

 

As I recall, the P Machinery framework uses game phases (may have the term wrong) to segment variables into local state that will or won't be live at one time, making better use of the Intellivision RAM that way. It's coarser grain, but it works with AS1600's macro framework. I'm sure dZ can tell you much more.

Edited by intvnut

Share this post


Link to post
Share on other sites

The thing is, scope alone isn't enough. To allocate different variables to the same RAM at compile time, you have to do a control flow analysis to determine which subroutines might be active (and therefore their local variables) at the same time, which is why I draw parallels to register allocation. You have to do liveness analysis to build an interference graph to know which variables can occupy the same location at the same time.

Or you could define local/lexical variables in the stack. ;)

 

 

As I recall, the P Machinery framework uses game phases (may have the term wrong) to segment variables into local state that will or won't be live at one time, making better use of the Intellivision RAM that way. It's coarser grain, but it works with AS1600's macro framework. I'm sure dZ can tell you much more.

That's right. P-Machinery at its core is a State Machine, treating a game as a series of states (or phases, as you put it). You have Main States, delineating the phases of the game, like Title Screen, Level Menu, Game Level Play, Game Over, Credits, etc. Each Main State can be divided into Sub-States, which are specific functional aspects or events of each level.

 

For instance a Title Screen may have an Initialization Sub-State where you draw the screen and set up the music, followed by a Wait or Play Sub-State where you just wait for input from the user before switching to the next state.

 

Being event-driven, you just switch states on an appropriate event (e.g., the user pressed a key while on the Title.Wait state, so your input event handler just sets the machine state to LevelMenu.Init to draw the menu; the framework does the rest).

 

The point is that each of the Main States are mutually exclusive, so any memory used within a state may not apply to the others. This includes 8-bit and 16-bit RAM as well as GRAM blocks. This is done by defining "RAM Segments" that overlap in memory.

 

In P-Machiner v2.0, I take this a step further by treating each Main State as an individual Game Module. The paradigm is that the modules are self-contained sub-programs with their own scope. Obviously, using only a macro veneer I cannot control the actual scope, so it's all done with a wink and a handshake: There's a Global RAM Segment that applies to all modules, and each module gets its own RAM Segment that extends from the end of the Global Segment to the end of available RAM.

 

(Of course, this is still Assembly Language so symbols are still very much global; this means you can't really declare two variables with the same name. You get to reuse the allocated memory space, not name space, sorry.)

 

Moreover, P-Machinery v2.0 supports defining common RAM Segments and a sort of inheritance for modules. For instance, suppose that you have a variable that you need available on the entire game program, across all states (perhaps high score points or something like that). You can define that as part of the Global RAM Segment.

 

However, what if you have a block of RAM that is needed in only two modules, Game Play and Game Over? You just create a common RAM Segment and have both states "inherit" from it. The end result is that both of those states will start their variable allocations from the end of their common segment, allowing you to safely use those variables.

 

Your memory allocation would look something like this:

 

                Available RAM
|---------------------------------------------|
     Global
|--------------|
                     Title
               |----------------|
                         Menu
               |----------------------|

                Common
               |------|
                             Game Play
                      |-----------------------|
                        Game Over
                      |----------|

The underlying framework keeps track of allocated memory per module/segment, similar to how Joe's CART.MAC does. The output looks like this:

 

DropZone:p-mach_drv dz$ ./assemble.sh 
ASSEMBLING SOURCE...
 
ROM USAGE:
    ==============================================================
        Segment        Type       Size       Used      Available  
    ==============================================================
    ROM Seg #0        16-bit        8K         1258     6934 words
    ROM Seg #1        16-bit        8K            0     8225 words
    ROM Seg #2        16-bit       16K            0    16350 words
    ROM Seg #3        16-bit        4K            0     3840 words
    ROM Seg #4        16-bit        4K            0     3840 words
    ROM Seg #5        16-bit        2K            0     2048 words
    ==============================================================
             TOTAL:   16-bit       42K         1258    41237 words
    ==============================================================
 
RAM USAGE:
    ==============================================================
        Segment        Type       Size       Used      Available  
    ==============================================================
    Global            16-bit        7984         84     7900 words
                       8-bit         238         20      218 bytes
    --------------------------------------------------------------
    Title             16-bit        7900          0     7900 words
                       8-bit         218          0      218 bytes
    ==============================================================
             TOTAL:   16-bit        7984         84     7900 words
                       8-bit         238         20      218 words
    ==============================================================
 ERROR SUMMARY - ERRORS DETECTED 0
               -  WARNINGS       0
For more information, see my article on Shared RAM Segments in the programming forum.

 

-dZ.

 

 

(Damn! The forum software mangled my post! Fixed.)

Edited by DZ-Jay

Share this post


Link to post
Share on other sites

I've always hated so-called "boolean" types. They're trivially implemented on your own; just use 0 and 1 for false and true, respectively. And hell, you can set this up yourself with CONST if you *really* need the true/false syntax. Not to sound elitist, but anyone coding for a platform like this should at least be able to intuitively understand something like that. You're going to struggle when it comes to things like the STIC if you don't think in terms of binary logic like this. :)

Edited by freeweed

Share this post


Link to post
Share on other sites

I've always hated so-called "boolean" types. They're trivially implemented on your own; just use 0 and 1 for false and true, respectively. And hell, you can set this up yourself with CONST if you *really* need the true/false syntax. Not to sound elitist, but anyone coding for a platform like this should at least be able to intuitively understand something like that. You're going to struggle when it comes to things like the STIC if you don't think in terms of binary logic like this. :)

 

It's a bit more nuanced. Intuitively, we know that the complement of "False" is "True," and vice-versa. However, "NOT False (0)" is not the same as "True (1)".

 

But that's not all, when converted to machine language, comparisons are typically based on equivalence to zero or otherwise. This means that in reality, and for optimal performance, you have "False = 0; True = Everything Else". Using "True = -1" fulfills both sides: different from zero and the transitive complement of zero.

 

However, this is not sufficient because you enter into a situation where, say,

let x = 0
let y = 1

REM This is true:
if (x or y) then goto XXX

REM This is false:
if ((x or y) = TRUE) then goto XXX
 

Your compiler or runtime will have to do implicit conversion of every "true" value into the canonical "TRUE" (-1).

 

Leaving the programmer to deal with this on his own is just a cop-out. Ideally, you want the high-level language to abstract these technical nuances of the machine language, and to offer a consistent and coherent model for the programmer. Even better if it fits intuitively into the programmer's expectations.

 

You should be able to say:

let x = 1
let y = 0
let a = true

let b = (x or y)

REM This is true:
if (a = b) then XXX 

What you do not want to do is a half-assed, mix-n-match approach like PHP; where you end up with "truthy" and "falsy" values that are semantically equivalent but logically different from the real "true" and "false" values.

 

A better approach may be the Perl way: there is a canonical false and it is called "0". Anything else is true in the same way that intuitively we think of "not false is true." The programmer then will have to keep in mind that boolean expressions evaluate to either zero or not zero; which corresponds to false or true, respectively. This approach throws away the concept of symbolic "TRUE" and "FALSE" constants in favour of logical truth.

 

-dZ.

Edited by DZ-Jay

Share this post


Link to post
Share on other sites

I get what you're saying, but most practical examples people give to justify boolean types are just a muddled mess. Your last example is precisely what I mean by this. To me this is precisely a mix-and-match situation.

 

And yeah, 1 vs -1 is another trick, with its own caveats. But it depends on what you want your pseudo-boolean to represent. Which is probably why I dislike most implementations - they're leading the programmer on, at times. Watching kids learn to program a lot of years ago, it was fascinating to see just how much bools can influence how a programmer thinks. And just how many goddamn flags these kids set for everything. Instead of tracking things in a bitmap, or actually, you know, using values to keep tabs on things. To me, with a RAM-limited system like the INTV, I just see bool types as a waste of memory. New programmers will use 20-30 variables just keeping track of state, because bools are "easy" and "logical" at first. Granted in a simple game you can get away with that, but it'll put handcuffs on anyone wanting to progress further.

 

When you describe a "better approach" in Perl, you describe pretty much how I think of these things. 0 (false) or !0 (not false). I just implement it with raw binary logic. And I can often fit 8 bools into a single variable, so :P

 

Plus I'm old and crusty. I learned without bools, and I'll die without bools.

Share this post


Link to post
Share on other sites

To each his own, I guess. I like to build abstractions in my programs because it frees my mind to concentrate on "more important" stuff like game logic. Others, like you, may feel so comfortable working at lower levels that it may not seem important to abstract these concepts.

 

What I preach is to use abstractions with the understanding of what they actually do under the bonnet. That way you can decide what to do in a particular situation, and won't be tripped up when something counterintuitive happens that breaks your assumptions.

 

Regarding boolean flags, to me that's purely an implementation detail. Logically, game states and flags are either true or false, and ideally a programmer should be free of any cognitive burden regarding how they are stored. From a purely logical and semantic perspective, game objects have distinct attributes such as Is Visible, Is Active, Is In Motion, Has Hammer, Can Kill, etc.; and the programmer should be able to deal with them as such.

 

If the compiler puts all these in separate 16-bit RAM cells, wasting precious limited memory, then who's fault is that? Forcing the programmer to repeatedly mask and test individual bits every time is the sort of boring boiler-plate sequence that is perfect for a programming language to take over.

 

Some compilers even detect boolean typed variables and group them all up into a single status bitmap and apply bitwise operations when testing individual flags. I believe Perl does this, maybe even C. Why should the programmer care?

 

Now, this may be an onerous request for IntyBASIC, but given that it aims to be powerful, easy, and approachable to new comers, it may be worth exploring.

 

-dZ.

Share this post


Link to post
Share on other sites

I saw a mention of the stack... How big is IntyBasic's stack, and how can it be accessed?

 

It would be nice if multiple DEFINE statements automatically pushed to a queue. I have a game concept in mind (nanochess knows what i'm talking about) that is only problematic with IntyBasic due to the amount of sprite cards it would require.

 

This is still doable, as long as I keep only current state's cards in memory. For instance (for the player sprite):

Stand = 1 frame (constant)

Walk = 3 frames

Attack = 3 frames

Jump = 3 frames

Death = 3 frames

 

... x2 because this is a 16-pixel sprite. Now obviously that's basically all the cards I've got, but if I keep separate states so that when the sprite goes from walking to attacking it just uses a DEFINE statement to swap the cards, it might be doable.

 

However, this would also apply to enemy sprites, which would need to have cards swapped out depending on the enemy's current state. This frame/state monitoring could be very complicated, especially since it won't be done in Assembly. It would be much easier if I could just call DEFINE and trust that multiple statements would be queued for execution on subsequent frames.

 

By the way, how much additional processing time is taken up by a DEFINE statement? Does it essentially gobble up all the spare cycles of a frame?

  • Like 1

Share this post


Link to post
Share on other sites

I'd have to think this through, but given that you have to use a WAIT after every DEFINE, you could be in for some serious overhead to be doing this dynamically.

 

I can't remember if DEFINE can use variables or if it needs constant values. If it can use variables, and I'm reading you right, couldn't you essentially create a queue with some clever card labeling and incremented variables? If it can't, then I can see your desire here.

Share this post


Link to post
Share on other sites

Or you could define local/lexical variables in the stack. ;)

 

 

I thought about it briefly. However, the Intellivision doesn't offer an efficient way to access values relative to the current stack pointer. You only really need to put local variables on a stack to handle recursion.

 

EDIT: If you don't have recursion, and can therefore express the call graph as a DAG, then you can use a longest-path algorithm to the DAG to come up with a suitable allocation in memory, now that I think of it. Just set the edge weights for edges exiting a procedure to the number of local variable slots. The longest path from the root (ie. the main program) to a given procedure then gives the starting offset in memory that procedure should use for its variables. That allows you to statically map local variables to RAM in a way in which they'll never conflict.

 

If you do have recursion, then local variables on the cycle in the call graph will need to be allocated on a stack somehow.

 

 

I saw a mention of the stack... How big is IntyBasic's stack, and how can it be accessed?

 

It's likely not very big, but it's also not asked to hold very much. It mostly just handles addresses for GOSUB/RETURN, and captures a few registers during interrupts.

 

Graphic tiles get stored in GRAM, and the Intellivision has 64 8x8 tiles of GRAM.

Edited by intvnut

Share this post


Link to post
Share on other sites

Some compilers even detect boolean typed variables and group them all up into a single status bitmap and apply bitwise operations when testing individual flags. I believe Perl does this, maybe even C. Why should the programmer care?

 

I sincerely doubt Perl automatically packs boolean variables. After all, Perl gave us "0 but true". Under the hood, Perl SVs look pretty involved. C stores _Bool in bytes when they're stored in memory. C++ allows the implementation to specialize vector<bool> to be a bitmap, but the programmer has to ask for vector<bool>.

 

Boolean types are nice in strongly typed languages when they work properly. My experience in C++ has been a little iffy. Mainly, it guarantees AND does what I expect if I throw non-0 values around to mean "true". true & true = true, but 0x55 & 0xAA = 0. If I am packing bits, having a 0/1 value means all I have to do is shift and OR. But really, it's hard for me to get too excited about the debate. I did just fine before C added _Bool.

Edited by intvnut

Share this post


Link to post
Share on other sites
If the compiler puts all these in separate 16-bit RAM cells, wasting precious limited memory, then who's fault is that? Forcing the programmer to repeatedly mask and test individual bits every time is the sort of boring boiler-plate sequence that is perfect for a programming language to take over.

 

Some compilers even detect boolean typed variables and group them all up into a single status bitmap and apply bitwise operations when testing individual flags. I believe Perl does this, maybe even C. Why should the programmer care?

 

Whose fault? The language definition and/or compiler implementation. And it's not exactly unheard of for stupid decisions in that area ;) As for why the programmer should care, this is precisely why. Poorly thought out implementations of things like bools are what give people bad impressions about development environments. Right now, IntyBASIC is not exactly RAM-hungry (given its power). Imagine its image if boolean variables used a full byte - and because you know damned well that people are going to use dozens upon dozens all over the place, suddenly IntyBASIC is viewed as "too much overhead". Whose fault is this? Kinda everyone's, but it doesn't change the perception. From what I can tell, IntyBASIC is just as powerful as ASM, beyond a few edge cases. I hope it stays that way.

 

Mostly though, I view it this way: if it's a simple thing to implement efficiently? Of course do it! I can choose to use it or not. But I'd hate to see a lot of time and effort spent on something like this, which is something that is trivial for a programmer to implement themselves, over other features that are a lot less straightforward to write.

 

I'll also sheepishly admit that I absolutely love coding in an untyped language again. There's a freedom (and danger!) that I miss. I really don't want to see IntyBASIC become like this:

 

int var1

uint var2

bool flag1

float var3

char(5) string1

 

etc, etc. It's the same reason I'm hesitant to advocate scoped variables. Freedom and danger.

 

Of course I'm also a complete hypocrite here, because I'm already kinda wishing we had a 24 or 32 bit type, to abstract that math away from me. :P

Share this post


Link to post
Share on other sites

It's likely not very big, but it's also not asked to hold very much. It mostly just handles addresses for GOSUB/RETURN, and captures a few registers during interrupts.

 

While this is not exactly scientific, I can speak from experience that the stack seems to be somewhere in the range of 8-16 entries. Based on how long it took for me to blow it with unRETURNed GOSUBs. If I had to swear on a bible, I'd say it's probably 8, but I could be mistaken.

Share this post


Link to post
Share on other sites

 

While this is not exactly scientific, I can speak from experience that the stack seems to be somewhere in the range of 8-16 entries. Based on how long it took for me to blow it with unRETURNed GOSUBs. If I had to swear on a bible, I'd say it's probably 8, but I could be mistaken.

 

That sounds like a typical starting stack depth. Interrupts require 8 stack slots all to themselves. So if IntyBASIC sets aside 16 locations for stack, you get 8 for GOSUB/RETURN, and 8 for the interrupt handler. The number 8 is fixed by the EXEC's interrupt dispatcher, which is hardwired into the console.

 

(I say "starting stack depth", as folks often start with a small stack, and then rework things—either making the stack deeper or changing their program—when it turns out to not be deep enough. 32 is another common value to start with, at least for me, especially if I do a lot of PSHR/PULR. IntyBASIC seems to only use stack for GOSUB/RETURN in my limited experimentation.)

Edited by intvnut

Share this post


Link to post
Share on other sites

@freeweed, @intvnut:

 

The thing that gets lost in many of these debates (and I've seen it in the past) is that we're not talking about a generalized, multi-purpose programming language here; we're talking about a specialized implementation for making games--and not just making games, making Intellivision games, which have by definition specific platform constraints and assumptions.

 

Higher-level abstractions and syntactic language sugar may not be for everyone, I grant you that, but it does have its place in a platform that aims to be approachable to others with no Assembly Language experience.

 

That said, Boolean variable types may not be the answer for IntyBASIC, but there are other ways to offer an abstraction to object states and flags. At the very least, the language can take the burden of performing boring repeating patterns for something as common as testing flags.

 

Again, this doesn't require a general hammer for every single boolean operation, but a precise scalpel for a specific use case: that of object status flags.

 

In my games, the way I've done this is to define flags as a record, and in fact P-Machinery offers a "BITMAP" structure. I can define a bitmap like this:

HAND.EVENT      BITFLD  8
                ; Event Status
@@DiscUp        DFLAG
@@ActionUp      DFLAG
@@KeyUp         DFLAG
@@DiscDown      DFLAG
@@ActionDown    DFLAG
@@KeyDown       DFLAG
@@EventPort     DBITF   2
                ENDB

Where the macro DFLAG defines a bit-field of one bit (i.e., a flag), and the macro DBITF defines a bit-field of n-bits. The resulting bitmap looks like this:

+-+-+-+-+-+-+-+-+
|7|6|5|4|3|2|1|0|
+-+-+-+-+-+-+-+-+
 \ / ^ ^ ^ ^ ^ ^
  V  | | | | | |
  |  | | | | | `----> 0: DiscUp
  |  | | | | `------> 1: ActionUp
  |  | | | `--------> 2: KeyUp
  |  | | `----------> 3: DiscDown
  |  | `------------> 4: ActionDown
  |  `--------------> 5: KeyDown
  `-----------------> 6,7: EventPort

Testing a flag is then performed with macros that encapsulate the bit-wise operations needed. Something like this:

   ; Test whether we have disc events
   FlagIsSet(EVENT.Flags, (HAND.EVENT.DiscUp OR HAND.EVENT.DiscDown))

The point is that, if the patterns are common enough, a standard model can be devised and the language can then provide facilities for it. It may be hard work to come up with a good and useful model and implement it efficiently, but it is not a worthless effort if it makes programming games easier and more efficient.

 

I'd bet big money that most integer expressions used in IntyBASIC programs can be made more efficient if the programmer took low-level control and applied shortcuts based on assumptions and deep knowledge of his register use and control flow with a specialized implementation for each expression. How about IF/ELSE conditional branching?

 

Would anybody here advocate we remove IF/ELSE control structures or the ability for IntyBASIC to evaluate complex arithmetic expressions just because they can be costly in a generalized implementation? I'm sure you'll agree these are exceedingly useful abstractions that increase programmer productivity.

 

Anyway, I didn't mean to turn this into a philosophical debate. I just found myself with some free time on my hands for a couple of hours. :)

 

Cheers to all!

-dZ.

Edited by DZ-Jay

Share this post


Link to post
Share on other sites

Would anybody here advocate we remove IF/ELSE control structures or the ability for IntyBASIC to evaluate complex arithmetic expressions just because they can be costly in a generalized implementation? I'm sure you'll agree these are exceedingly useful abstractions that increase programmer productivity.

 

 

Of course not, because IF/ELSE substitutes for several non-intuitive assembler directives. A boolean, at its best, makes a variable more readable and (potentially, depending on implementation and assumptions) easier to check.

 

But I think you're reading me wrong. I am certainly *not* the guy advocating for deep knowledge of ASM here. In fact I'm avoiding it like the plague, partly because IntyBASIC is just so damned nice to work with and I'm trying to push its limits, and partly because to be honest I couldn't program much in depth in ASM these days. I'm just not that good anymore and my experience is waaaay behind me. I *need* high-level language constructs at this point. I just don't put variable types in the same category as logical constructs. Same reason I don't feel a burning desire for signed vs unsigned variables. Yes, it makes some things slightly easier for the programmer. I just don't find them essential. Whereas I couldn't code myself an IF/ELSE block in assembly if my life depended on it.

 

I *do*, however, enjoy this sort of discussion because it brings back a lot of forgotten programming mnemonics and tricks. And even this little side-track has given me a couple of ideas for some things I've been wresting with. So it's all useful discussion :)

  • Like 1

Share this post


Link to post
Share on other sites
That said, Boolean variable types may not be the answer for IntyBASIC, but there are other ways to offer an abstraction to object states and flags. At the very least, the language can take the burden of performing boring repeating patterns for something as common as testing flags.

 

Fair enough.

 

For many games, you do just want to maintain a collection of flags. "Player did this. Player did not yet do that. The door is open. The other door is closed." It is perhaps worth considering declaring some 'variables' to be of this flag nature. That would allow IntyBASIC to pack them tightly, and handle testing them directly. Storing a 1 bit flag in an 8 bit or 16 bit location is truly a waste.

 

The code generated by IntyBASIC for flag testing isn't awful, if you use bare constants. Consider this example:

.

FLAG = FLAG OR 2
FLAG = FLAG AND NOT 2
IF FLAG AND 2 THEN PRINT "SET" ELSE PRINT "CLEAR"

.

Not exactly the Shakespeare of code, that. But it gives us a simple base for examination. The code generated looks like this:

.

    ; FLAG = FLAG OR 2
    MVI V1,R0
    ANDI #65533,R0
    XORI #2,R0
    MVO R0,V1
    ; FLAG = FLAG AND NOT 2
    MVI V1,R0
    ANDI #65533,R0
    MVO R0,V1
    ; IF FLAG AND 2 THEN PRINT "SET" ELSE PRINT "CLEAR"
    MVI V1,R0
    ANDI #2,R0
    TSTR R0
    BEQ T1
... code to print "SET"
    B T2
T1:
... code to print "CLEAR"
T2:

.

That actually is not too bad for simple flag set/clear code and flag test code. (Although, the TSTR R0 isn't necessary as ANDI sets the necessary flag.)

 

The real problem is the bare number 2 in the original source. Good coding practice would try to hide these magic numbers behind names. That way you're not left asking yourself "What does FLAGS4 bit 64 do again? And what about FLAGS2 bit 128?" when you come back to a crufty bit of code. Really, you'd want to say things more like this:

.

     FLAG Door3Open = 0 : REM Door3Open is a flag variable, and it initializes to 0 (unset). It can be set or cleared, but can only hold 0 or 1.
...
     SET Door3Open
...
     CLEAR Door3Open
...
     IF Door3Open THEN ... ELSE ...

.

Replace "Door3Open" with whatever Boolean condition you're interested in.

 

I think that's what you're trying to get at. I think it's a valid idea to pursue in this context. Allow folks to declare Boolean state variables and provide primitives for setting them, clearing them and testing them. That is a common pattern in many games and could be very useful in an IntyBASIC context.

Edited by intvnut
  • Like 2

Share this post


Link to post
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...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...