Jump to content
IGNORED

FastBasic 4.6 cross-compiler tips, tricks, quirks, and oddities?


Recommended Posts

Posted (edited)

I've been experimenting quite a bit lately with the Windows hosted Fastbasic 4.6 crosscompiler. Targeting cart images makes it a breeze to test them out in Altirra, much less pointy-clicky than having to import a .XEX into a scratch .ATR or fooling with virtual filesystems. Hopefully this doesn't bite me someday!

 

DIMming large arrays and then starting some memory-hungry graphics modes like 11 or 9 results in unexpected behavior instead of returning to DOS or Memo Pad. Sometimes the display shows garbage, other times it's just black, still other times the (edit: emulated Atari) computer appears to hang. Of course calling DIM after setting up the graphics mode will reasonably bounce out.

 

Use BYTE arrays if you possibly can, unless being unable to use INC or DEC on array elements tips the CPU cycle balance back towards the default integer arrays.

 

Numeric expressions like ABS(CI% * (CR% + CR%)) + CI% are faster than ABS((CR% + CR%) * CI%) + CI% is faster than 2 * ABS(CI% * CR%) + CI% is faster than ABS(CI% * CR%) * 2 + CI%. Would CI% + Abs(CI% * (CR% + CR%)) make a difference? Who knows, the ship is already burning

 

Unlike in some other Basics, the COLOR statement is very fast. There's hardly any need at all to make min- or max-heaps of objects to be redrawn just to keep calls to COLOR to a bare minimum

 

I don't understand FILL TO at all. Maybe it just doesn't like GTIA modes?

 

EDIT: The floating point weirdness is probably at least partly due to the OS-B mathpack madness. It seems it's sensitive to whether ABS(x%) < ABS(y%) in x% +/- y%, x% * y%, maybe others

Edited by yetanothertroll
Going mad from the revelation
Link to comment
Share on other sites

How do I prevent or at least delay memory fragmentation issues with re-DIMming arrays? I have something like this in my main loop,

 

   If DecayMax <= DecayHighCount
    DecayMax = 4 + DecayHighCount + DecayHighCount / 4
   EndIf

   Dim DecayPixelX(DecayMax - 1) Byte, DecayPixelY(DecayMax - 1) Byte
   Dim DecayTarget(DecayMax - 1) Byte, DecayCurrent(DecayMax - 1) ' Byte

   DecayCount = 0
   DecayHighCount = 0

 

After a few repetitions of that, FB quits due to memory issues. I made certain to allocate those arrays last. I don't want to use CLR and lose all my other variables or have to save and reload them from disk or some memory block somewhere I have to manage myself. Is there a good way to erase individual arrays to try to get a nice contiguous block of free memory for reallocating them?

 

 

 

Link to comment
Share on other sites

Hi!

5 hours ago, yetanothertroll said:

How do I prevent or at least delay memory fragmentation issues with re-DIMming arrays? I have something like this in my main loop,

 

   If DecayMax <= DecayHighCount
    DecayMax = 4 + DecayHighCount + DecayHighCount / 4
   EndIf

   Dim DecayPixelX(DecayMax - 1) Byte, DecayPixelY(DecayMax - 1) Byte
   Dim DecayTarget(DecayMax - 1) Byte, DecayCurrent(DecayMax - 1) ' Byte

   DecayCount = 0
   DecayHighCount = 0

 

After a few repetitions of that, FB quits due to memory issues. I made certain to allocate those arrays last. I don't want to use CLR and lose all my other variables or have to save and reload them from disk or some memory block somewhere I have to manage myself. Is there a good way to erase individual arrays to try to get a nice contiguous block of free memory for reallocating them?

 

Well, due to space constraints, FastBasic does not implement any means to free blocks of memory.

 

My suggestion is to simply DIM the arrays at the start of the program with the maximum expected value for "DecayMax", it will be simpler and faster.

 

Have Fun!

 

  • Thanks 1
Link to comment
Share on other sites

Posted (edited)
1 hour ago, dmsc said:

Hi!

 

Well, due to space constraints, FastBasic does not implement any means to free blocks of memory.

 

My suggestion is to simply DIM the arrays at the start of the program with the maximum expected value for "DecayMax", it will be simpler and faster.

 

Have Fun!

 

That's actually going to complicate things for this particular project. If DecayMax doesn't change, do repeated calls to Dim still leak memory?

 

Thanks!

 

EDIT: Would DIY-ing something like an array deallocation proc be possible? How does the array allocator work currently? Which source files should I be looking for?

 

Edited by yetanothertroll
I just had an idea, a bad idea, a very very bad idea
Link to comment
Share on other sites

Posted (edited)
7 hours ago, tebe said:

you expect too high a level of abstraction

 

XLOS with Rapidus gives you the ability to manage memory

http://drac030.krap.pl/pl-specyfikacja.php

 

No problem, now that I know that's what's actually going on. I'll figure something out for this project and then think about how to design a malloc/free/realloc like system with the minimum possible overhead.

 

Two bytes each for the start and end of the dynamic memory arena plus another two bytes for each memory chunk? If memory is managed on two or four byte boundaries, the low order bits can flag freed/in use and such. If a flag is kept for whether free has ever been used, blocks can be allocated quickly with no need to step through the block list looking for a previously freed block. Contiguous free blocks can be merged at this stage. I'm not actually expecting anyone to actually add all this, this is merely a thought experiment lol

Edited by yetanothertroll
Double checking to whom I'm replying 🤦🏻‍♂️
Link to comment
Share on other sites

Hi!

23 hours ago, yetanothertroll said:

That's actually going to complicate things for this particular project. If DecayMax doesn't change, do repeated calls to Dim still leak memory?

Yes

 

I still do not understand why you need to change the size of the array. The 8-bit computers have a small amount of memory available, so you need to plan ahead how to use each byte :) , setting the size to a large value should work.

 

23 hours ago, yetanothertroll said:

 

EDIT: Would DIY-ing something like an array deallocation proc be possible? How does the array allocator work currently? Which source files should I be looking for?

Works the same as other 8-bit BASICs, it simply has a base pointer and a top pointer. When you DIM an array or allocate a string, the top pointer is used as the address of the new allocation, and the top pointer is moved up.

 

There are BASICs that support garbage collection - when the top pointer reach the top of available memory, all the objects are compacted and the top pointer recalculated. But that means that existing strings and arrays change address, this means that you can't store the address to use later, and this breaks a lot of programs that store data or machine code in strings. Implementing this in FastBasic also has another complication - that at runtime there is no information on the type of variables, so you don't know which variables are pointer types (arrays or strings) to correctly adjust the values at GC - this is extra complicated because string-arrays are actually an array of pointers.

 

An idea would be to transform each allocated block into a structure with: 2 bytes for the block size, 2 bytes for the address of the variable that points to the block, n bytes for the actual data.  This transforms the area into a linked-list, because you can walk the allocations by doing "next_ptr = ptr + ptr->size", and you can check which variable holds your address by looking at "ptr->var".

 

So, a FastBasic with GC would need:

 

- Changing the "alloc_array" code (at src/interp/clearmem.asm) to allocate 4 extra bytes, and store at the beginning of each block the block size.

- Change all the "alloc_array" callers to store the address of the variable on which the value will be stored to the block.

- On allocation failure at "err_nomem", call the GC, which does:

  - Set "ptr" and "new_ptr" to the address of the first block.

  - Iterate through the list of blocks, until "ptr" is equal to topmem.

   - For each block, check if *(ptr->var) contains the address of the block, if not the block is free to reuse.

   - If the block was used:

     - Adjust *(ptr->var) with new_ptr (the address of the block after the move).

     - Move the block data from ptr to new_ptr.

     - Set "new_ptr" to "new_ptr + ptr->size"

   - Set "ptr" to "ptr + ptr->size" to go to next block.

  - At the end, set topmem to the value of new_ptr (this is the end of memory).

 

Does not seems that difficult.

 

Have Fun!

 

 

Link to comment
Share on other sites

Posted (edited)
1 hour ago, dmsc said:

Hi!

Yes

 

I still do not understand why you need to change the size of the array. The 8-bit computers have a small amount of memory available, so you need to plan ahead how to use each byte :) , setting the size to a large value should work.

 

Works the same as other 8-bit BASICs, it simply has a base pointer and a top pointer. When you DIM an array or allocate a string, the top pointer is used as the address of the new allocation, and the top pointer is moved up.

 

There are BASICs that support garbage collection - when the top pointer reach the top of available memory, all the objects are compacted and the top pointer recalculated. But that means that existing strings and arrays change address, this means that you can't store the address to use later, and this breaks a lot of programs that store data or machine code in strings. Implementing this in FastBasic also has another complication - that at runtime there is no information on the type of variables, so you don't know which variables are pointer types (arrays or strings) to correctly adjust the values at GC - this is extra complicated because string-arrays are actually an array of pointers.

 

[ scary stuff elided ]
 

Does not seems that difficult.

 

Have Fun!

 

 

A full mark and sweep GC? No thanks. If I wanted random two second freezes while the GC churns all the RAM, I'd just fire up Logo! I was thinking more along the lines of the old C malloc routines, which can reuse previously freed blocks without any GC sweep. Also, if an array is Dim'ed already, if it's Dim'ed again, the metadata for the previous size, address, etc., if any, are still available to the second call to Dim, yes?

 

EDIT: I forgot to mention why I might want to expand certain arrays, with or without preserving their contents. 99% of the time, the arrays only need to be small, around 36 elements or so. However, there are some edge cases that will need more like 90-100 or even 3-400, but if I always allocate the maximum "just in case," that somehow doesn't sit well with me 🤷‍♂️

Edited by yetanothertroll
Oops, forgot half my point
Link to comment
Share on other sites

Hi!

21 hours ago, yetanothertroll said:

A full mark and sweep GC? No thanks. If I wanted random two second freezes while the GC churns all the RAM, I'd just fire up Logo!

Well, at least the GC will only run when you actually allocate memory. By the way, this is how Microsoft Basic works in the C64 and also in the Atari. It is not as slow, because you can assume that the number of memory blocks is always small. In Microsoft Basic, calls to "STR$()", "MID$", etc allocate new temporary strings that are left as garbage, and need to be reclaimed later.

 

FastBasic, like Atari BASIC, uses fixed memory addresses for STR$ and CHR$, this is faster but means that you can't use STR$() twice in the same expression, for example this: "? STR$(1)<>STR$(0)" will print 0 both in FastBasic and Atari BASIC.

 

21 hours ago, yetanothertroll said:

I was thinking more along the lines of the old C malloc routines, which can reuse previously freed blocks without any GC sweep. Also, if an array is Dim'ed already, if it's Dim'ed again, the metadata for the previous size, address, etc., if any, are still available to the second call to Dim, yes?

Not currently - FastBasic does not stores the size of an array, and as you can see from the source, DIM receives the size of the array in bytes, allocates memory and then stores in the variable. In FastBasic, arrays and strings are pointers, the same size as integers, and stored in registers. The size of the allocated area would need to be stored inside the allocated area itself.

 

21 hours ago, yetanothertroll said:

EDIT: I forgot to mention why I might want to expand certain arrays, with or without preserving their contents. 99% of the time, the arrays only need to be small, around 36 elements or so. However, there are some edge cases that will need more like 90-100 or even 3-400, but if I always allocate the maximum "just in case," that somehow doesn't sit well with me 🤷‍♂️

 

Well, there is no other use for the memory really, as no other program will run concurrently :), so it is not "wasted".

 

Have Fun!

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

I think part of my issue was I misunderstood how and when drivers/handlers for peripherals like the CX85 keypad and 850 interface module are loaded, sometimes from boot disk, sometimes from the peripheral's own firmware, so I figured I'd need to play nice and worry about hogging too much memory when my program started up. But I also do have some ideas for a fairly fast and lightweight dynamic memory management system.

 

Also, this might be useful for timing things longer than 18-20 minutes or so,

 

' Result will be in LongTime%

Proc LongTime
 Repeat
  Long20A = Peek(20)
  Long19A = Peek(19)
  Long18A = Peek(18)
  Long18B = Peek(18)
  Long19B = Peek(19)
  Long20B = Peek(20)
 Until (Long18A = Long18B) And (Long19A = Long19B) and (Long20A <= Long20B)
 LongTime% = 256.0
 LongTime% = ((Long18A * LongTime%) + Long19A) * LongTime% + Long20A
EndProc

 

Next, figuring out P/M sprites and how they interact with GTIA modes, wish me luck lol

Link to comment
Share on other sites

Hi!

14 hours ago, yetanothertroll said:

I think part of my issue was I misunderstood how and when drivers/handlers for peripherals like the CX85 keypad and 850 interface module are loaded, sometimes from boot disk, sometimes from the peripheral's own firmware, so I figured I'd need to play nice and worry about hogging too much memory when my program started up. But I also do have some ideas for a fairly fast and lightweight dynamic memory management system.

Yes, when your program is running, it will use all available memory.

 

14 hours ago, yetanothertroll said:

Also, this might be useful for timing things longer than 18-20 minutes or so,

 

' Result will be in LongTime%

Proc LongTime
 Repeat
  Long20A = Peek(20)
  Long19A = Peek(19)
  Long18A = Peek(18)
  Long18B = Peek(18)
  Long19B = Peek(19)
  Long20B = Peek(20)
 Until (Long18A = Long18B) And (Long19A = Long19B) and (Long20A <= Long20B)
 LongTime% = 256.0
 LongTime% = ((Long18A * LongTime%) + Long19A) * LongTime% + Long20A
EndProc

The current development version of FastBasic has a 'TIME%' function that returns the same as your proc, you can download it from the bottom of this page: https://github.com/dmsc/fastbasic/actions/runs/8963403908

 

14 hours ago, yetanothertroll said:

Next, figuring out P/M sprites and how they interact with GTIA modes, wish me luck lol

🙂 

 

You can start with the simple P/M demo at https://github.com/dmsc/fastbasic/blob/master/samples/int/pmtest.bas and change the graphics mode at the first line.

 

Have Fun!

 

  • Thanks 1
Link to comment
Share on other sites

Here's something fun, simple arithmetic with bools is slower than an If/Else, with executing the Else being the fastest. All the array stuff is meant to more or less mirror what's going on in the actual code I'm trying to speed up a bit. In the actual code, the equivalent to X will normally be 15 or 16, TempWord(j) will be between 0 and X, and the code will be run up to 15 or 16 times per screen pixel,

 

 Get K

 Print "Bools fools K = "; K; " "; 0 < 1; " "; 1 < 1; " ";

 Dim TempByte(3) Byte, TempWord(3) Word
 X = 65
 TempWord(0) = K : TempWord(1) = K
 TempWord(2) = K : TempWord(3) = K
 Timer
 For i = 1 To 10000
  j = i & 3
  TempByte(j) = 3 - (TempWord(j) < X) ' True = 1 ' 720 jiffies
  ' If TempWord(j) < X
  '  TempByte(j) = 2 ' getting here 706
  ' Else
  '  TempByte(j) = 3 ' 688
  ' EndIf
 Next
 j = Time
 Print j

 

Yup, you guessed it, I'm trying to structure a Mandelbrot set / Burning Ship plotter as if it were a game, with up to four pixels being calculated "simultaneously," plus a Matrix-like "digital rain" effect that also needs to be animated at least somewhat smoothly, thus the "Decay" array stuff, just to get more used to how games are structured, and next I'll try adding P/M graphics so the "player" can select a portion to zoom in on or out with a joystick or paddles. It already uses the keyboard to do this kind of awkwardly, and when I remembered the 400/800 didn't have a built-in numeric keypad, I rationalized that maybe there's a CX85 attached, with its handler taking up some memory so maybe I shouldn't go grabbing it all for myself 😅

 

The Matrix has you.png

Link to comment
Share on other sites

Are comparisons with multiples of 256 optimized in certain cases? For example, (x < 512) doesn't even need to look at x's low order byte, same for (768 <= x).

Could it be worth doing if it isn't being done already?

 

Link to comment
Share on other sites

Wow, string variables are pure evil, always 256 bytes even when they might never actually hold more than 20 characters or so. So much for KISS when it comes to a kerning table for a proportional font to make every column count in graphics modes 11 and 9 😿

sat7.thumb.png.5f9471cc5e505af9b7aa7026f8e8949f.png

Link to comment
Share on other sites

1 hour ago, yetanothertroll said:

Wow, string variables are pure evil, always 256 bytes even when they might never actually hold more than 20 characters or so.

It's probably the worst thing about FB, strings are so limited if you need something bigger and so wasteful if they are small.

 

  • Like 1
Link to comment
Share on other sites

Hi!

6 hours ago, yetanothertroll said:

Wow, string variables are pure evil, always 256 bytes even when they might never actually hold more than 20 characters or so. So much for KISS when it comes to a kerning table for a proportional font to make every column count in graphics modes 11 and 9 😿

 

4 hours ago, TGB1718 said:

It's probably the worst thing about FB, strings are so limited if you need something bigger and so wasteful if they are small.

Yes.

 

For most uses - like the above, a string variable is not the proper way to store data, you need to use byte arrays (for data that can change) or byte data (for data that is predefined).

 

You can even store strings in a byte data, this example:

DATA str1() BYTE = "My short text"
DATA str2() BYTE = "My LONG text"

PROC Show x
  ? "The String: '"; $(x); "' is "; $(x)[4,5]
ENDPROC

@Show &str1
@Show &str2

Prints:

The String: 'My short text' is short
The String: 'My LONG text' is LONG 

 

 

For a table, just a byte data is better:

DATA table() BYTE = 1, 2, 3, 5, 7, 11, 13, 17, 19

sum = 0
FOR i=0 TO 8
  ? table(i),
  sum = sum + table(i)
NEXT i
? : ? "Sum: ", sum

 

IMHO, using those two, the limit of 256 character strings is not that relevant. Use string only for small texts and printing.

 

Have Fun!

 

Edited by dmsc
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Posted (edited)

Nice! However, one should be careful with variable names starting with Inc, Dec, and possibly other keywords:

 

000000r 1               ; Variables
000000r 1               	.segment "HEAP"
000000r 1               	.export fb_var_REDIBLE
000000r 1  xx xx xx xx  fb_var_REDIBLE:	.res 6	; Float variable
000004r 1  xx xx        
000006r 1               	.export fb_var_INCREDIBLE
000006r 1  xx xx xx xx  fb_var_INCREDIBLE:	.res 6	; Float variable
00000Ar 1  xx xx        
00000Cr 1               	.export fb_var_REDULOUS
00000Cr 1  xx xx        fb_var_REDULOUS:	.res 2	; Word variable
00000Er 1               	.export fb_var_INCREDULOUS
00000Er 1  xx xx        fb_var_INCREDULOUS:	.res 2	; Word variable
000010r 1               	.export fb_var_LAREIMUST
000010r 1  xx xx        fb_var_LAREIMUST:	.res 2	; Word variable
000012r 1               	.export fb_var_DECLAREIMUST
000012r 1  xx xx        fb_var_DECLAREIMUST:	.res 2	; Word variable
000014r 1               	.export fb_var_LAREIDO
000014r 1  xx xx xx xx  fb_var_LAREIDO:	.res 6	; Float variable
000018r 1  xx xx        
00001Ar 1               	.export fb_var_DECLAREIDO
00001Ar 1  xx xx xx xx  fb_var_DECLAREIDO:	.res 6	; Float variable
00001Er 1  xx xx        

 

Even array variable names might have shadows tagging along,

0000B4r 1               	.export fb_var_DECAYPIXELX
0000B4r 1  xx xx        fb_var_DECAYPIXELX:	.res 2	; Byte Array variable
0000B6r 1               	.export fb_var_DECAYPIXELY
0000B6r 1  xx xx        fb_var_DECAYPIXELY:	.res 2	; Byte Array variable
0000B8r 1               	.export fb_var_DECAYTARGET
0000B8r 1  xx xx        fb_var_DECAYTARGET:	.res 2	; Byte Array variable
0000BAr 1               	.export fb_var_DECAYCURRENT
0000BAr 1  xx xx        fb_var_DECAYCURRENT:	.res 2	; Byte Array variable

...

0000D8r 1               	.export fb_var_AYPIXELX
0000D8r 1  xx xx        fb_var_AYPIXELX:	.res 2	; Word variable
0000DAr 1               	.export fb_var_AYPIXELY
0000DAr 1  xx xx        fb_var_AYPIXELY:	.res 2	; Word variable
0000DCr 1               	.export fb_var_AYCURRENT
0000DCr 1  xx xx        fb_var_AYCURRENT:	.res 2	; Word variable
0000DEr 1               	.export fb_var_AYTARGET
0000DEr 1  xx xx        fb_var_AYTARGET:	.res 2	; Word variable

 

Edited by yetanothertroll
Interesting quarry of a typo'ed variable hunt
Link to comment
Share on other sites

Hi!

13 hours ago, yetanothertroll said:

Nice! However, one should be careful with variable names starting with Inc, Dec, and possibly other keywords:

Yes, this is an unfortunate inheritance from Atari BASIC (and most 8-bit BASICs): you can't begin a variable name with a statement.

 

See this in Atari BASIC:

image.thumb.png.2c0648ac1859b8513bd546147c2e6b7c.png

The "PRINTED=0" one shows "-1" in the C64 and gives a confusing error in the BBC; the "NOTA=2" gives an error in the C64 and the BBC - probably better than simply showing the odd result as in the Atari.

 

The parser in FastBasic is a little more forgiving, but there are a lot of possible keywords that can clash with variable names.

 

If you are using the PC compiler, you can use the '-l' to output the parsed program and see if any variable got mangled. I could add an option to require a space after any keyword, this would make all of those parsing gotchas go away.

 

Have Fun!

 

Link to comment
Share on other sites

After I had my filthy filthy way with your pmtest.bas, compiling with -l gives me no apparent variable name mangling,

' P/M test program, cribbed from dmsc's pmtest.bas

GRAPHICS 0 ' Setups graphics mode
PRINT FRE(),

PMGRAPHICS 2 ' And P/M mode
PRINT FRE()

' How to implement the fifth player thing?

FOR I = 0 TO 3
  MSET PMADR(I), 128, 0 ' Clears P/M 0-3 Memory
NEXT I

PAUSE
FOR I = -4 TO -1
  J = 16 + I * 4 + RAND(4)
  PRINT I, J
  SETCOLOR I, J, 10
NEXT I

' P/M data and blank (to clear P/M)
DATA PMDATA() PMDATA BYTE ROM = $38, $44, $54, $44, $38

' Initial Conditions

DIM XPOS(3), YPOS(3), YOLD(3), XSPD(3), YSPD(3)
DIM DECAY(3) BYTE, PITCH(3) BYTE

FOR I = 0 TO 3
  XPOS(I) = 6400
  YPOS(I) = 2560
  YOLD(I) = 0
  XSPD(I) = 64 - I * 16
  YSPD(I) = - I * 64
  DECAY(I) = 0
  PITCH(I) = 0
NEXT I

REPEAT
  I = (I + 1) & 3

  XPOS(I) = XPOS(I) + XSPD(I)
  YPOS(I) = YPOS(I) + YSPD(I)

  YSPD(I) = YSPD(I) + 2

  ' Can the collision logic be used to skip all these if/elifs
  ' if sprite i isn't touching the screen border?

  ' Can some comparisons against multiples of 256 be optimized?
  ' eg, (x < 512) doesn't even need to look at x's low order byte
  ' ditto for (768 <= x)

  ' Is it worth sweating?

  IF(YSPD(I) < 0) AND(YPOS(I) < $0800)
    YSPD(I) = RAND(20)
    PRINT I ; " top dy=" ; YSPD(I)
    @ BOING I
  ELIF(0 <= YSPD(I)) AND($3600 <= YPOS(I))
    YSPD(I) = - YSPD(I)
    XSPD(I) = XSPD(I) + (RAND(64) - 32)
    PRINT I ; " bottom dx=" ; XSPD(I) ; " dy=" ; YSPD(I)
    @ BOING I
  ENDIF

  IF(0 <= XSPD(I)) AND($6500 <= XPOS(I))
    XSPD(I) = - XSPD(I) - 1
    PRINT I ; " right dx=" ; XSPD(I)
    @ BOING I
  ELIF(XSPD(I) <= 0) AND(XPOS(I) < $1800)
    XSPD(I) = - XSPD(I) + 1
    PRINT I ; " left dx=" ; XSPD(I)
    @ BOING I
  ENDIF

  @ MOVEPM I ' Move P/M Graphics

  ' Freaking P/M magnets/collisions, how do they work?
  ' if incollision(i) then xSpd(i) = -xSpd(i)

  K = DECAY(I)
  IF 0 = K
    SOUND I
  ELSE
    SOUND I, PITCH(I), 8, K
    DECAY(I) = K - 1
  ENDIF

UNTIL KEY()

WHILE KEY()
  GET K
WEND

SOUND

PRINT FRE(),
PMGRAPHICS 0
PRINT FRE()

PRINT
PRINT "OK"

GET K

END

PROC MOVEPM MOVEPM MPI

  X = XPOS(MPI) / 128
  Y = PMADR(MPI) + YPOS(MPI) / 128
  YO = YOLD(MPI)
  A = ADR(PMDATA)

  PAUSE
  IF Y <> YO
    MSET YO, 5, 0 ' Clear old sprite
    MOVE A, Y, 5 ' Draw sprite at new vertical pos.
  ENDIF
  PMHPOS MPI, X ' Set new horizontal position

  YOLD(MPI) = Y

ENDPROC

PROC BOING BOING B
  DECAY(B) = 8
  PITCH(B) = 64 + 48 * B + (RAND(64) - 32)
  SETCOLOR B - 4, RAND(16), RAND(16)
ENDPROC
                                        

...although the repeated proc names are odd enough, but no problemo, but as for the vars,

00000Er 1               	.export fb_var_DECAY
00000Er 1  xx xx        fb_var_DECAY:	.res 2	; Byte Array variable
000010r 1               	.export fb_var_PITCH
000010r 1  xx xx        fb_var_PITCH:	.res 2	; Byte Array variable
000012r 1               	.export fb_var_AY
000012r 1  xx xx        fb_var_AY:	.res 2	; Word variable

What's this "AY" variable? Where did it come from? So far as I can tell, the compiled program works just fine, but it just has this "AY" variable that isn't used anywhere. Maybe I should see if it causes a conflict if there's also an actual AY or AY% variable in the source code

Link to comment
Share on other sites

19 hours ago, dmsc said:

...

 

IMHO, using those two, the limit of 256 character strings is not that relevant. Use string only for small texts and printing.

 

Have Fun!

 

256 bytes for a temp string that will have only two characters ever is not fun, but is mutable data kosher?

 

 Data LTKern() Byte ROM = "1-1+141719.4.7.9.+(-(+7.#7-)-1-7+)+1+7+."

 Data LTKtmp() Byte = 2, 0, 0 ' Caution, mutable!

 aLT = &LT$
 aLTKern = &LTKern
 aLTKtmp = &LTKtmp

...

  ' Search kerning table LTKern() for c-p digraph
  If i < k
   LTKtmp(1) = c
   LTKtmp(2) = p
   j = LTKern(0) - 1 ' Len($(aLTKern)) - 1
   Do
    If $(aLTKtmp) = $(aLTKern)[j,2]
     ' Found, keep them close together
     Exit
    ElIf j = 1
     ' Not found, separate them
     Plot X, LTY + 5
     Dec X
     Exit
    EndIf
    j = j - 2
   Loop
  EndIf

 

Link to comment
Share on other sites

Hi!

1 hour ago, yetanothertroll said:

...although the repeated proc names are odd enough,

That bug was fixed in current version..... I should prepare a new release sometime... not enough time :) 

 

1 hour ago, yetanothertroll said:

but no problemo, but as for the vars,

00000Er 1               	.export fb_var_DECAY
00000Er 1  xx xx        fb_var_DECAY:	.res 2	; Byte Array variable
000010r 1               	.export fb_var_PITCH
000010r 1  xx xx        fb_var_PITCH:	.res 2	; Byte Array variable
000012r 1               	.export fb_var_AY
000012r 1  xx xx        fb_var_AY:	.res 2	; Word variable

What's this "AY" variable? Where did it come from? So far as I can tell, the compiled program works just fine, but it just has this "AY" variable that isn't used anywhere. Maybe I should see if it causes a conflict if there's also an actual AY or AY% variable in the source code

 

It came from your "DECAY(I)=0", this is first parsed as "DEC AY", creating a variable "AY", then the parser sees that the line is not completely parsed and rewinds, doing the correct parse. The variable is not cleared, as the parser currently does not remove created variables. This could be fixed in the PC compiler, but it is not really fixable in the Atari IDE.

 

Have Fun!

  • Like 1
Link to comment
Share on other sites

Hi!

33 minutes ago, yetanothertroll said:

256 bytes for a temp string that will have only two characters ever is not fun, but is mutable data kosher?

 

 Data LTKern() Byte ROM = "1-1+141719.4.7.9.+(-(+7.#7-)-1-7+)+1+7+."

 Data LTKtmp() Byte = 2, 0, 0 ' Caution, mutable!

 aLT = &LT$
 aLTKern = &LTKern
 aLTKtmp = &LTKtmp

...

  ' Search kerning table LTKern() for c-p digraph
  If i < k
   LTKtmp(1) = c
   LTKtmp(2) = p
   j = LTKern(0) - 1 ' Len($(aLTKern)) - 1
   Do
    If $(aLTKtmp) = $(aLTKern)[j,2]
     ' Found, keep them close together
     Exit
    ElIf j = 1
     ' Not found, separate them
     Plot X, LTY + 5
     Dec X
     Exit
    EndIf
    j = j - 2
   Loop
  EndIf

 

 

Doing this with strings is bigger and slower than using the byte array directly:

 Data LTKern() Byte ROM = "1-1+141719.4.7.9.+(-(+7.#7-)-1-7+)+1+7+."

 aLT = &LT$

...

  ' Search kerning table LTKern() for c-p digraph
  If i < k
   For j = LTKern(0)-1 TO 1 Step -2
    If LTKern(j) = c AND LTKern(j+1) = p Then Exit ' Found, keep them close together
   Next
   If J=-1
    ' Not found, separate them
    Plot X, LTY + 5
    Dec X
   EndIf
  EndIf

 

In fact, with a double IF it would be faster, as the second array access won't happen if not needed:

 Data LTKern() Byte ROM = "1-1+141719.4.7.9.+(-(+7.#7-)-1-7+)+1+7+."

 aLT = &LT$

...

  ' Search kerning table LTKern() for c-p digraph
  If i < k
   For j = LTKern(0)-1 TO 1 Step -2
    If LTKern(j) = c
     If LTKern(j+1) = p Then Exit ' Found, keep them close together
    Endif
   Next
   If J=-1
    ' Not found, separate them
    Plot X, LTY + 5
    Dec X
   EndIf
  EndIf

 

Note that in FastBasic, taking the address of an array/data/string and storing it in a variable is slower than using it directly. "&STR$" (or "ADR(STR$)") is compiled to just loading the address from the var, just one token.

 

Have Fun!

 

  • Thanks 1
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...