Jump to content
IGNORED

Hell's Halls - TI BASIC Cassette Game Dungeon Crawl Project


Recommended Posts

1 hour ago, sometimes99er said:

Oh, I thought it was said, that looking up line-numbers was a somewhat significant overhead !?
 

Well, I suppose I shouldn't say there is no performance overhead, but in the practical context and scale of BASIC timing with which I'm concerned, it's never been a substantive one in my testing. 

 

I was curious nonetheless to dig deeper, so I just did a long enough test to see a difference (20,000 loops), and the difference between

 

Z=1

Q=0

 

and

 

Z=1::Q=0

 

was 0.3ms, in that test. 

 

So while that's something, it still falls below my threshold of interest, in a language where a PRINT X takes 90ms.

 

But just because I don't care doesn't mean it isn't there! :)

  • Like 1
Link to comment
Share on other sites

4 hours ago, pixelpedant said:

Well, I suppose I shouldn't say there is no performance overhead, but in the practical context and scale of BASIC timing with which I'm concerned, it's never been a substantive one in my testing. 

 

I was curious nonetheless to dig deeper, so I just did a long enough test to see a difference (20,000 loops), and the difference between

 

Z=1

Q=0

 

and

 

Z=1::Q=0

 

was 0.3ms, in that test. 

 

So while that's something, it still falls below my threshold of interest, in a language where a PRINT X takes 90ms.

 

But just because I don't care doesn't mean it isn't there! :)

Ok. Thanks. ?
 
Often in XB you have / could have more statements on one line than just two ...
 

Edited by sometimes99er
Link to comment
Share on other sites

It does make a difference when you GOTO or GOSUB. From the thread "What are the impacts of line numbering in Basic/XB execution speed"

(Post #98)

Getting back on the subject, (at last) I made a ridiculous program of 1601 lines.

   1 X=X+1::GOSUB 2::GOTO 1

   2 RETURN

   3 !

   4 !

   (Lines 3=1599 are comments)

   1599 !

   1600 X=X+1::GOSUB 1601::GOTO 1600

   1601 RETURN

In 30 seconds:

RUN 1 loops 125 times

RUN 1600 loops 3421 times.

Note that there is 1 GOSUB and 1 GOTO per loop.

So there is definitely a speed advantage when going to a higher number line.

Obviously, the example program is a little extreme.  To fit 1600 lines into 24K, each line would average 15 bytes, and 6 of those are used for the line #, length byte and trailing zero.

In the real world a really long program might be 400 lines, which would average around 54 bytes of code per line, so this effect would not be so extreme.

I think this is true for Basic as well, but you might want to check to be sure.

I think this order is true for Basic as well. (i.e. the higher line numbers are found more quickly than the lower line numbers)

But you might want to check to be sure.

Edited by senior_falcon
Link to comment
Share on other sites

  • 3 weeks later...

Watched some of @pixelpedant's great videos yesterday.  Hell's halls is truly amazing in TI BASIC.

I took a look at the spreadsheet with Basic timing. My results (running on real iron) varied a little from those. I was testing CALL CHAR in loops, like this:

image.thumb.png.b9ebcbfdd3c6fffe5918d6574471600d.png

This takes just under 25 seconds. I then assigned the string constant to A$ before the loop and the execution time dropped to about 22.5 seconds. It appears a single loop iteration (i.e. running NEXT) takes something like 3.5 milliseconds (or an empty loop of 1000 iterations is around 3.5 seconds), so with 201 iterations we're talking about 0.7 seconds for the loop alone. With that CALL CHAR(32,A$) appears to take about 108 ms, somewhat less than what the very nice BASIC execution time spreadsheet indicates. Still the difference is only about 10% and falls in the margin of error. 

 

Anyway CALL CHAR is just very very slow at 100ms+ for a single character. Compared to CALL CLEAR (44ms according to the table) it is just miserably slow. CALL CLEAR does not have to deal with argument evaluation, which just goes to show how slow that is. The payload of CALL CLEAR is 768 writes to VDP memory, the payload of CALL CHAR is 8 writes... In practice processing of CALL CHAR probably contains a ton of VDP activity just to evaluate the arguments. I have earlier measured that the TMS9900 runs at around 0.13 MIPS or something like that, meaning that a single CALL CHAR is around 14 000 machine code instructions. Wow.

 

This makes me feel humbled and really just highlights how amazing great TI BASIC games like Hell's hall are.

Edited by speccery
  • Like 4
Link to comment
Share on other sites

On 6/30/2022 at 10:06 PM, pixelpedant said:

Well, I suppose I shouldn't say there is no performance overhead, but in the practical context and scale of BASIC timing with which I'm concerned, it's never been a substantive one in my testing. 

 

I was curious nonetheless to dig deeper, so I just did a long enough test to see a difference (20,000 loops), and the difference between

 

Z=1

Q=0

 

and

 

Z=1::Q=0

 

was 0.3ms, in that test. 

 

So while that's something, it still falls below my threshold of interest, in a language where a PRINT X takes 90ms.

 

But just because I don't care doesn't mean it isn't there! :)

So are you saying Line Numbers are faster then :: as there are several steps to go to next line number unlike :: that is only the next byte.

As the line is being processed it is already on the correct address for next byte with :: unlike a line number that has to be switched and that takes time.

 

As for PRINT is slow as it has way to many options so slow down the program.

This is why I made CALL HPUT(row,column,variable[,...])

Also why DISPLAY AT(row,column) was put into XB to speed it up, problem is they screwed up and used same code making it worse.

 

Link to comment
Share on other sites

On 7/1/2022 at 8:58 AM, senior_falcon said:

It does make a difference when you GOTO or GOSUB. From the thread "What are the impacts of line numbering in Basic/XB execution speed"

(Post #98)

Getting back on the subject, (at last) I made a ridiculous program of 1601 lines.

   1 X=X+1::GOSUB 2::GOTO 1

   2 RETURN

   3 !

   4 !

   (Lines 3=1599 are comments)

   1599 !

   1600 X=X+1::GOSUB 1601::GOTO 1600

   1601 RETURN

In 30 seconds:

RUN 1 loops 125 times

RUN 1600 loops 3421 times.

Note that there is 1 GOSUB and 1 GOTO per loop.

So there is definitely a speed advantage when going to a higher number line.

Obviously, the example program is a little extreme.  To fit 1600 lines into 24K, each line would average 15 bytes, and 6 of those are used for the line #, length byte and trailing zero.

In the real world a really long program might be 400 lines, which would average around 54 bytes of code per line, so this effect would not be so extreme.

I think this is true for Basic as well, but you might want to check to be sure.

I think this order is true for Basic as well. (i.e. the higher line numbers are found more quickly than the lower line numbers)

But you might want to check to be sure.

Why would higher line numbers be faster? 

All line numbers in Basic or XB are a 1 word value >0001 to >7FFF is valid and >8000 to >FFFF is not valid line numbers.

So are you saying line number >7FE0 is faster than line number >0032 ?

 

Also XB and Basic both have exactly the same set up for String Variables, but XB uses upper 24K for Numeric Variables for the number itself storage.

Not really much of an advantage considering X=9 takes 17 bytes in Basic or XB, with XB 32K having the FP 8 bytes in RAM instead of VDP.

Link to comment
Share on other sites

9 hours ago, speccery said:

Watched some of @pixelpedant's great videos yesterday.  Hell's halls is truly amazing in TI BASIC.

I took a look at the spreadsheet with Basic timing. My results (running on real iron) varied a little from those. I was testing CALL CHAR in loops, like this:

image.thumb.png.b9ebcbfdd3c6fffe5918d6574471600d.png

This takes just under 25 seconds. I then assigned the string constant to A$ before the loop and the execution time dropped to about 22.5 seconds. It appears a single loop iteration (i.e. running NEXT) takes something like 3.5 milliseconds (or an empty loop of 1000 iterations is around 3.5 seconds), so with 201 iterations we're talking about 0.7 seconds for the loop alone. With that CALL CHAR(32,A$) appears to take about 108 ms, somewhat less than what the very nice BASIC execution time spreadsheet indicates. Still the difference is only about 10% and falls in the margin of error. 

 

Anyway CALL CHAR is just very very slow at 100ms+ for a single character. Compared to CALL CLEAR (44ms according to the table) it is just miserably slow. CALL CLEAR does not have to deal with argument evaluation, which just goes to show how slow that is. The payload of CALL CLEAR is 768 writes to VDP memory, the payload of CALL CHAR is 8 writes... In practice processing of CALL CHAR probably contains a ton of VDP activity just to evaluate the arguments. I have earlier measured that the TMS9900 runs at around 0.13 MIPS or something like that, meaning that a single CALL CHAR is around 14 000 machine code instructions. Wow.

 

This makes me feel humbled and really just highlights how amazing great TI BASIC games like Hell's hall are.

The reason why a CALL CHAR(32,"007F41414141417F") is slower then CALL CHAR(32,A$) is the interpreter has to load the first one's string into a temp buffer.

Whereas the second only has to go to the address of the string and use it.

Anything using VDP is slower than RAM as you get wait cycles to use VDP unlike RAM. (Also you can only write 1 byte at a time unlike RAM)

  • Like 2
Link to comment
Share on other sites

9 hours ago, RXB said:

Why would higher line numbers be faster? 

All line numbers in Basic or XB are a 1 word value >0001 to >7FFF is valid and >8000 to >FFFF is not valid line numbers.

So are you saying line number >7FE0 is faster than line number >0032 ?

It's not the actual line number that matters, it's where it is in the program. The program example that I gave has 1601 lines. If you look at the program you will see that line 1 is exactly the same as line 1600 except for the line numbers, yet RUN 1 is much slower than RUN 1600. So yes, going to a higher line number in the program is faster than going to a lower line number in the program.

Link to comment
Share on other sites

10 hours ago, RXB said:

The reason why a CALL CHAR(32,"007F41414141417F") is slower then CALL CHAR(32,A$) is the interpreter has to load the first one's string into a temp buffer.

Whereas the second only has to go to the address of the string and use it.

Nope. It is because A$ is 2 bytes long and 007f41414141417F is 16 bytes long.

If you make the variable and the string the same length you will find that using the variable is a tiny bit slower. (about 1%)

The variable can be AAAAAAAAAAAAAAA$ or the string can be "FF"

  • Like 2
Link to comment
Share on other sites

1 hour ago, senior_falcon said:

It's not the actual line number that matters, it's where it is in the program. The program example that I gave has 1601 lines. If you look at the program you will see that line 1 is exactly the same as line 1600 except for the line numbers, yet RUN 1 is much slower than RUN 1600. So yes, going to a higher line number in the program is faster than going to a lower line number in the program.

I can find nothing in GPL or Assembly ROMs that would explain this difference.

The only possibility is the line number table but as it is sequential the only delay would be a GOTO to find that Line Number.

Do know why this could possibly be true?

Link to comment
Share on other sites

45 minutes ago, senior_falcon said:

Nope. It is because A$ is 2 bytes long and 007f41414141417F is 16 bytes long.

If you make the variable and the string the same length you will find that using the variable is a tiny bit slower. (about 1%)

The variable can be AAAAAAAAAAAAAAA$ or the string can be "FF"

First off they use the same string to be loaded at the character so no that would not make it slower.

An Assembly ROM routine finds the A$ in the Variable list and points to it. (FBSYMB is the routine in XB ROMs that does this.)

On the other hand, a string has to have a temporary VDP buffer of that string added to VDP tables and used,  this is mostly GPL with some Assembly too.

There are like 20 more instructions to be done in GPL for a STRING vs Assembly ROM for a String variable.

And longer variable names take longer to find and use as even you have even stated this one.

 

Can you show the GPL or Assembly code that would explain this difference you see?

 

 

Link to comment
Share on other sites

5 hours ago, Willsy said:

Presumably it's a linked list that has to be "walked" from the beginning to find a particular line number.

In TI BASIC a bit surprisingly it's not like that, there is no linked list. Just an array, with two 16-bit values per entry. One entry is the line number, the other pointer to the line's tokens. I guess it points to the length byte of the line. So in TI BASIC you don't need to do pointer chasing, and like someone pointed out - I forget who and in which thread - if you renumbered the lines of the program with a constant interval - such as 1 ? you could find any line in a GOTO situation in fixed time with a single lookup. I don't think TI BASIC does such a thing, but would be a good way to optimise.

 

Most other BASICs I've looked at (Commodore, Sinclair) don't have the line number table as an array of fixed length entries, but they store that information among the tokenised BASIC program code, so you need to traverse a linked list of the program lines to find anything.

 

Of course TI BASIC makes this also special in that the line number table is stored in the VDP memory... 

Edited by speccery
Link to comment
Share on other sites

8 hours ago, RXB said:

First off they use the same string to be loaded at the character so no that would not make it slower.

An Assembly ROM routine finds the A$ in the Variable list and points to it. (FBSYMB is the routine in XB ROMs that does this.)

On the other hand, a string has to have a temporary VDP buffer of that string added to VDP tables and used,  this is mostly GPL with some Assembly too.

There are like 20 more instructions to be done in GPL for a STRING vs Assembly ROM for a String variable.

And longer variable names take longer to find and use as even you have even stated this one.

Can you show the GPL or Assembly code that would explain this difference you see?

In post #59, you explained why using a string variable for the pattern was faster. I stated that the real reason it is faster is because A$ is 2 bytes long, and the string was 16 bytes long. This program shows that.


100 OPEN #1:"CLOCK" :: INPUT #1:A$,B$,C$
110 X$="FF"
120 FOR I=1 TO 1000 :: CALL CHAR(32,X$):: NEXT I
130 INPUT #1:D$,E$,F$ :: PRINT A$,D$:B$,E$,C$,F$

Try this with CALL CHAR(32,X$) and CALL CHAR(32,"FF")

Or you can change the program to use a 15 byte long variable name XXXXXXXXXXXXXX$=" and a 15 byte long pattern

Either way, when the string and the variable name are the same length, you will see that the direct string is a wee bit faster than using the variable.

 

For the GPL or Assembly code that would explain this, I will quote Mr. Spock's words to Scottie. "I note it, Mr. Scott, without necessarily understanding it."

Link to comment
Share on other sites

5 hours ago, senior_falcon said:

In post #59, you explained why using a string variable for the pattern was faster. I stated that the real reason it is faster is because A$ is 2 bytes long, and the string was 16 bytes long. This program shows that.



100 OPEN #1:"CLOCK" :: INPUT #1:A$,B$,C$
110 X$="FF"
120 FOR I=1 TO 1000 :: CALL CHAR(32,X$):: NEXT I
130 INPUT #1:D$,E$,F$ :: PRINT A$,D$:B$,E$,C$,F$

Try this with CALL CHAR(32,X$) and CALL CHAR(32,"FF")

Or you can change the program to use a 15 byte long variable name XXXXXXXXXXXXXX$=" and a 15 byte long pattern

Either way, when the string and the variable name are the same length, you will see that the direct string is a wee bit faster than using the variable.

 

For the GPL or Assembly code that would explain this, I will quote Mr. Spock's words to Scottie. "I note it, Mr. Scott, without necessarily understanding it."

Ran your demo but changed 1000 to 10000 and first run confirmed your statement run time 3 seconds after 10,000 loop.

 

Second run I changed to 110 X$="FFFFFFFFFFFFFFFF" and got 43:35 run time.

Then I changed CALL CHAR(32,X$) into CALL CHAR(32,"FFFFFFFFFFFFFFFF") and got 43:35 run time.

So no change at all after 10,000 loop guess I could test a 100,000 loop to see if something pops up.

 

So CALL CHAR(32,X$) and CALL CHAR(32,"FFFFFFFFFFFFFFFF") confirmed what I had already stated that string variables more usable than hard coded strings.

You have to re-write a XB program to change CALL CHAR(32,"FFFFFFFFFFFFFFFF") but X$="FFFFFFFFFFFFFFFF" is more fluid to use.

The advantage is still to use String Variables overall as more benifits exist for them then hard coded strings.

Link to comment
Share on other sites

OK took over 3 hours to do 100,000 loop and results are in:

 

X$="FFFFFFFFFFFFFFFF" took 3 hours 16 minutes and 5 seconds

CALL CHAR(32,"FFFFFFFFFFFFFFFF") took 3 hours 16 minutes and 6 seconds.

 

Thus String variable is not really a huge improvement at all, but is much more versatile and useful then hard coded strings.

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...