dmsc / fastbasic

FastBasic - Fast BASIC interpreter for the Atari 8-bit computers
GNU General Public License v2.0
135 stars 20 forks source link

Signed Integers with loops & memory #8

Open ukcroupier opened 4 years ago

ukcroupier commented 4 years ago

I know this probably isn't a bug and is working within the parameters of signed integers, but it is a problem that shows the need for an option to use unsigned integers.

I was testing some scrolling and hit a problem, I've condensed it down to the code in the attached file.

This drops memtop to reserve memory for the scroll, it then sets up the DL and then loops through memory and displays a character moving diagonally.

As long as PAGE<=64 everything works fine but if I wanted a larger area for my scrolled screen then it hits an issue with signed integers. If I set PAGE=68 the loop will have the following value:

FOR A=32014 TO -16640

This causes program flow to drop through the loop without executing it.

In order to work with memory (and therefore my scrolling screen) am I forced to use floating point to avoid issues with the sign flipping?

An option to use unsigned integers would solve this. Being able to select signed or unsigned as your default would be great. TEST.TXT

ukcroupier commented 4 years ago

I guess I could code stuff like this with offsets (as in attached file) but it's a bit of a pain.

TEST1.TXT

dmsc commented 4 years ago

Hi!

You are right, FOR loops with small signed integers is a mess - you run into problems easily.

I don't want to add unsigned integers to the language as the parser would be a lot bigger and slower, as you would need to parse expressions as signed and unsigned and select which works better, see:

This expression should be parsed as unsigned:

FOR A=1 TO 40000 : NEXT

But this expression should be parsed as signed:

X = -1
FOR A=1 TO 10000*X : NEXT

And both should include different code for the "FOR" and the "NEXT" statements!

The root cause is that FOR ends the iteration by doing a signed comparison, and that comparison can fail on unexpected ways.

My recommendation, don't ever use FOR loops. They tend to be slower and more complicated that WHILE and REPEAT loops, for example:

A=HI*256+270
E=(HI+PAGE-1)*256
REPEAT
    PAUSE 10
    POKE A-257,0
        POKE A,17
        A = A + 257
UNTIL A - E > 0

The above would work whenever the total range (END - START) is < 32768.

Another possibility, instead of an arbitrary "OFFSET" as you programmed, simply loop from 0 always, also would work whenever the range is < 32768:

O=HI*256+270
FOR A=0 TO (HI+PAGE-1)*256-O STEP 257
    PAUSE 10
    POKE O+A-257,0
    POKE O+A,17
NEXT A

Hope the above helps.

ukcroupier commented 4 years ago

Thanks for the advice, very useful.

But wouldn't it be fairly simple to make a compiler that only uses integers 0-65535 instead of signed? For games programming working without negatives would be easier than worrying about bugs involving signed integers.

The only time I've every needed a negative in games is for -1 and I can easily code around that.

dmsc commented 4 years ago

Hi!

But wouldn't it be fairly simple to make a compiler that only uses integers 0-65535 instead of signed? For games programming working without negatives would be easier than worrying about bugs involving signed integers.

The only time I've every needed a negative in games is for -1 and I can easily code around that.

I haven't tough of that, an unsigned only version could be easy to do. I modeled the signed-integer support on other integer BASIC of the time, like Microsoft BASIC, so I tough it would be less confusing.

You could ask other people opinions in the atariage (programming) forum :)

Have Fun!

dmsc commented 4 years ago

Another tough, how do you express negative STEP's in a FOR statement with unsigned only arithmetic?

Or if you have a "speed" variable, how do you express negative numbers there?

Have Fun!

ukcroupier commented 4 years ago

I'm sure some games would find negatives useful, but if it's easy to do then why not give us the option to compile with signed or unsigned.

I know I'm being a pain, but for me accessing memory without unusual things happening is the main issue, I can code around issues with negative numbers.

BTW: I'm only bugging you because I love BASIC (and FastBasic is awesome). I can code in C, Pascal, Assembler to varying degrees of competence (also PHP, COBOL, Pearl), but BASIC is what I love.

dmsc commented 4 years ago

Hi!

I'm sure some games would find negatives useful, but if it's easy to do then why not give us the option to compile with signed or unsigned.

I know I'm being a pain, but for me accessing memory without unusual things happening is the main issue, I can code around issues with negative numbers.

You are not being a pain :), your input is appreciated.

Problem is, maintaining another build (or two more, unsigned only and unsigned + FP) will be more work and a maintenance burden, so I won't jump to it immediately.

So, I will prefer to find another solution that would be simpler.

For example, I could add a "unsigned FOR loop" that would treat numbers as unsigned for the loop end... I don't like this syntax, but:

  FOR A=1 UP 40000 : .... do something ... : NEXT

  FOR A=40000 UP 1 : .... do something... : NEXT

BTW: I'm only bugging you because I love BASIC (and FastBasic is awesome). I can code in C, Pascal, Assembler to varying degrees of competence (also PHP, COBOL, Pearl), but BASIC is what I love.

Thanks!

Have Fun!

ukcroupier commented 4 years ago

I never considered having to maintain all those builds, yeah it's a lot of work.

The unsigned loop is a nice workaround, it's the kind of thing I'd do so it's probably a bad idea lol

Given the multiple ways of working around the problem you've shown, and the fact that it's only really an issue when crossing the 'signed bondry' I think you should probably invest your time doing something more important :)

dmsc commented 4 years ago

Hi!

I never considered having to maintain all those builds, yeah it's a lot of work.

The unsigned loop is a nice workaround, it's the kind of thing I'd do so it's probably a bad idea lol

Given the multiple ways of working around the problem you've shown, and the fact that it's only really an issue when crossing the 'signed bondry' I think you should probably invest your time doing something more important :)

Well, I really want to rewrite the FOR loop handling, as I don't like how it is currently implemented - the address of the loop variable, the step and the end value are all pushed to the stack and the value is updated on the NEXT statement.

This allows the FOR variable to be any expression - for example "FOR S(2*X+1)=0 TO 10", as the address is stored it does not needs reevaluation on each iteration. But, I think that this flexibility is not really needed, I could limit the parser to allow only a single variable, and optimize for that case.

That time, I will revisit all options to make the loops over "addresses" (unsigned) better.

Have Fun!