From: GaaK on

James M. Prange wrote:
> \<< @
> RCLF @ Get current flags, leave on stack.
> -55 SF @ Force last arguments off.
> 64 STWS @ Force wordsize.
use LCD\-> BYTES SWAP DROP (131*64=1098, 131*80=1370)
> "'LQ'" @
> STR\-> @ STR\-> compiles "'LQ'" to global name on 48SX/S,
> @ but errors out on other models.
> THEN @ Not 48SX/X.
> CASE @
> NOVAL TYPE 14 == @ NOVAL is type 19 on 49 series.
> THEN @
> 1. @ 48GX/G/G+.
> END @
> DROP @ Discard the Copyright string.
> 11 11 SUB "5" == @ will be "4" for 48gII or 49G.
VERSION command?... maybe... good for 50G
> THEN @
> 5. @ 50g.
> END @
> LCD\-> @
> SIZE @
> SWAP DROP @ Discard width.
> #50h == @ #50h on 49g+ or 50g, otherwise #40h.
....replace this
> THEN @
> 3. @ 49g+
> END @
> 1. @
> PVARS @ 1. PVARS errors out on 48gII, works on other 49
> @ series.
PVARS very very slow (49G full port 1).
Purge 'IOPAR', then 1 TRANSIO (force default 'IOPAR')
IOPAR HEAD (9600:49G, 115200:48gII)
> THEN @
> 4. @ 48gII
> ELSE @
> DROP2 @ Discard port variable list and
> @ available port capacity.
> 2. @ 49G
> END @ End of "inner" IFERR structure.
> END @ End of CASE structure.
> ELSE @
> DROP @ Discard 'LQ'.
> 0. @ 48SX/S.
> END @ End of "outer" IFERR structure.
> SWAP @ Move flags list down to level 1.
> STOF @ Restore original flags.
> \>> @

- GaaK -

From: James M. Prange on
John H Meyers wrote:
> On Sun, 31 Dec 2006 05:05:53 -0600, per James M. Prange:
> NOVAL TYPE 14 == @ NOVAL is type 19 on 49 series.
> Why not eliminate the chance of a variable
> (or even a library command) named NOVAL, e.g.:
> DUP SIZE 3 < @ Flag list is shorter on 48 series

Good point; I took the trouble to work around the possibility of a
global variable named LQ (or NOVAL) in the 48S*, but neglected the
possibility of a global variable named NOVAL (using the SysRPL
$>ID command, the S~N development library command, or by renaming
with the filer), or a library command named NOVAL, in later

But there could be a local variable named LQ, in which case "'LQ'"
STR\-> works on any model, or there could be a library command
named LQ on a 48S*, in which case "'LQ'" would error out unless
there were also a local variable with the same name, so that's not
bullet-proof after all.

Of course, an earlier model may very well have a variable or
library command that duplicates the name of a built-in command on
a later model.

Any particular reason for using DUP SIZE 3 <
instead of DUP SIZE 2 == ?

> Actually, if you want to rely on NOVAL,
> "{NOVAL" STR\-> 1 GET TYPE yields:
> 48G[X] ==> 14
> 49... ==> 19
> 48S[X] ==> 6

"NOVAL" STR\-> seems to do the same for me, at least once I
remembered to purge my global variable NOVAL.

> So you wouldn't need another "'LQ'" STR\->
> But just to warn of the potential for being fooled,
> all of the above are only *probable* results -- why?
> Consider that although VARS is generally a command,
> I have a *variable* (type 6) named VARS in my 48G[X]
> (for a 50-fold speed increase :),
> others have a ROMPTR (type 14) named VARS in their 48GX
> (same reason, when HACK library is installed),
> and the same can actually occur with any name at all,
> so likewise nothing is guaranteed about NOVAL.

And for that matter, a local name would take precedence over all
other names.

At least for now, I'll use the SYSEVAL equivalent of VERSTRING to
determine whether it a 48S* or 48G*

Come to think of it, any of the commands used in the program could
be superseded by variables or library commands that behave
differently, which means that any source code version can't be
absolutely reliable, but I'd hope that that wouldn't be the case
for such basic commands.

> Another (very minor):
> 11 11 SUB "5" == @ 11 DUP is 8 bytes shorter

Thanks! I overlooked an obvious code saving; actually, 8 bytes
shorter on a 48 series, 4 bytes shorter on a 49 series.

> It's not inconceivable that one "Code" object might actually
> be devised to work on all series and models, using only addresses
> in a range which is the same on all of these;

Certainly it's conceivable, but I'd want to start with a list of
which 48SX commands have never changed entry points, and I don't
know of one offhand, so I'd have to make my own. Of course the
zints would have to be changed to reals too. What I've done so far
is mostly meant as a "proof of concept" that there are indeed ways
to distinguish the hardware, and I really don't care to go that
far. Actually, I'd guess that it may well be easier using SysRPL
or assembler language.

> I would also
> not mind if it returned more than one result, such as:
> ROM series for entry points (0/1 for 48 vs 49),

I'd separate the 48S* from the 48G* as well.

> keyboard (0/1/2/3 for 48S/48G/49G/later)
> screen dimensions (perhaps a sublist), etc.,
> all returned in one list would be okay (and expandable),
> which are individually more important results
> for any programs that could use such generalized tests.

I see your point, but note that all of the above can be determined
by division into categories of 48S*, 48G*, 49G, and 49g+/50g.
Well, except for differences in the entry point/command set of the
49G depending on the ROM revision. Splitting 49g+/50g into 2
categories gives additional information on the external I/O and
power supply differences.

Then too, I expect that often a programmer would be interested in
only one characteristic, such as the screen size, or whether it's
an ARM-based model, or which external I/O ports are available, so
wouldn't need such a large program.

> More optional results?

Well, where would we stop adding to the list of characteristics?

> o Highest port number?

Or in the 48SX/GX, which ports are free?

Your question of how to determine whether it was running on an
emulator is a very good one. Of course, ideally it wouldn't
matter, but the emulations are less than perfect, so sometimes it
does matter. It seems to me that it would be a matter of trying
something that works differently on the emulator than it does on a
real calculator, but without crashing either one.

I had thought to use the same test that I used for distinguishing
a 49G from a 49g+, which depends on a Saturn+ opcode, and was very
surprised to find that the Saturn+ opcode actually worked on the
emulated 49g+, so I was wrong about that; the emulator can indeed
use at least some of the Saturn+ opcodes.

Digging into the source code for the emulator available from
( gives information on which opcodes are currently
emulated there, but the emulator included with Debug4x seems to be
different, and I don't know where to find source code for that

> o [nominate, petition, vote (early and often :)]

And if that doesn't work, sneak in a vote after the polls have
closed. ;-)

Seriously, I'd think that the categories 48S*, 48G*, 49G,
49g+, 48gII, and 50g, plus the version letter for the 48 series
and revision number for the 49 series would be most appropriate.

If someone needs to know which (memory) ports are available, and
how much capacity is left in them, then that can be checked
separately with PVARS.

Well, except for the SD card; it's existence could be checked by
trying to store a dummy object there; if the STO succeeds the card
is present and the dummy object can be purged, or if it errors out
with "Object In Use" then the card is present, or if it errors out
with "No SD card inserted" then of course the card isn't present.
The state of flag -55 determines whether last arguments need to be
dealt with. But I suspect that finding it's available capacity
would require SysRPL (or lowel level) code. But one could at least
determine whether the card has "enough" capacity by trying to
store an object in it.

Happy New Year, everyone!

From: James M. Prange on
Veli-Pekka Nousiainen wrote:
> "James M. Prange" <jmprange(a)> wrote in message
> news:e024e$45979995$4267eaf1$10204(a)123.NET...
> X
>>> So the Bronze goes to Tim "Joker" Wessman
>>> and you release your code and we'll see who is titled to GOLD & Silver
> X
>>> Both versions (the expanded, too) should go to the
>> Well, maybe, but as John noted, often a shorter test, such as for
>> the screen size, the availability of a "mass storage" port, or
>> which I/O ports are available, will suffice.
>>> in the Programming section of both 48 and 49:
>>> The code is _exactly_ the same for all models
>>> *and* UserRPL compatible.
>> Well, the *source code* is the same for all models, so will work
>> from an "ASCII" ("Text") transfer, but it will compile to 3
>> different program objects, depending on whether it's entered on a
>> 48SX/S, a 48GX/G/G+, or a 49 series, thus binary transfers from
>> one of these groups to another won't work.
> Source code compatibility on an interpreted language is enough, IMO
> I think that the purpose was
> 1) to show off some programming skills

Well yes, I do rather like to show off once in a while.

But it's also a learning experience. Someone is almost certain to
point out something that doesn't work in all cases, or ways to do
something using less bytes or execution time.

> 2) to show that it's possible in UserRPL and benefit on it

Yes, it may well be easier in SysRPL or assembly language, but I
think that UserRPL is more powerful than many give it credit for,
and making some code conditional on the hardware can be useful
even in UserRPL.

> eg. the final code will find it's way to the

Maybe, but mostly as a demonstration. I doubt that many would need
that much information, but they could choose whichever code they
wanted for their particular tests.

Happy New Year!

From: James M. Prange on
GaaK wrote:
> James M. Prange wrote:
>> \<< @
>> RCLF @ Get current flags, leave on stack.
>> -55 SF @ Force last arguments off.
>> 64 STWS @ Force wordsize.
> use LCD\-> BYTES SWAP DROP (131*64=1098, 131*80=1370)

Excellent! That not only mean that 64 STWS isn't needed, but any
zint with fewer than 15 digits or any real takes fewer bytes than


>> 1. @
>> IFERR @
>> PVARS @ 1. PVARS errors out on 48gII, works on other 49
>> @ series.
> PVARS very very slow (49G full port 1).
> Purge 'IOPAR', then 1 TRANSIO (force default 'IOPAR')
> IOPAR HEAD (9600:49G, 115200:48gII)

You have a good point, but if I did anything to IOPAR, I'd want to
restore everything about it to exactly the way that it originally
was, which opens a real can of worms; whether IOPAR originally
existed in the home directory, and if so, its original contents
and order within the directory. I've done that before so I know
that it's possible, but it would take more bytes and would also be

I expect that I'll simply try storing a dummy object to port 1
instead. I expect that it will take a few more bytes than the
PVARS method, but should be reasonably fast no matter what's
stored in port 1 of a 49G.

>> THEN @
>> 4. @ 48gII
>> ELSE @
>> DROP2 @ Discard port variable list and
>> @ available port capacity.
>> 2. @ 49G
>> END @ End of "inner" IFERR structure.


But I'll think that I'm expected at my niece's house for a few
glasses of sparkling grape juice soon, so I'll walk over there now
that the rain's slowed a bit. Well, at least it's not snow or
freezing rain, so I consider it good weather for New Year's Eve.

Happy New Year to all!

From: John H Meyers on
On Sun, 31 Dec 2006 11:09:21 -0600, GaaK wrote:

> Purge 'IOPAR', then 1 TRANSIO (force default 'IOPAR')

OPENIO CLOSEIO also creates default IOPAR when not present
(OPENIO creates it; CLOSEIO is to shut down the UART again :)