saving SSHAPE graphic strings to a disk file in BASIC 7.0

Started by ruthven, February 02, 2009, 08:01 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ruthven

Hi everyone,

I'm new to this forum and new to BASIC 7.0 programming.  I've just started getting into graphics and was disappointed by how slow the computer draws circles, lines, paints enclosed areas, etc.  One saving grace is that once graphics are drawn, the graphic data can be saved into a string with SSHAPE and then duplicated very quickly (much quicker than the initial drawing from scratch) with GSHAPE.  My question is can graphic strings created with SSHAPE be saved to a file on disk?  If so, my program wouldn't take so long to draw the opening screen--instead of taking the time to draw everything out from scratch, it could just load the predefined images from disk and start mass producing them across the screen with GSHAPE, presumably much quicker.

I've tried OPENing a sequential file for WRITEing and then sending the graphic string to it using PRINT#1,A$ but when I open the file for READing, as soon as I try to retrieve the string with INPUT#1,A$ the computer crashes--that is, it locks up, freezes and I have to reset.  I've tried this same process of OPENing WRITEing and READing sequential data with regular strings (i.e. A$="TEST") and this works just fine... so I'm thinking graphic strings have to be saved in a binary way.  If this is true, how would I go about BSAVEing it?--I guess I'm asking how do I figure out which bank# and starting/ending address points to a particular graphic string?  (if using BSAVE is indeed the way to go...)

Any info on this would be greatly appreciated--thanks!

airship

SSHAPE and GSHAPE were intended for storing and moving shapes on the screen within a program.

INPUT has problems with values that translate to ASCII input delimiters, such as CHR$(13), a carriage return. I don't know why it would crash, though - you should just get bad data, I would think.

Guess you'll have to wait for more informed opinions than mine.

But I do suggest that when you generate and save your shapes you use FAST mode. You won't be able to see the screen draw, but you really don't need to, and it'll go twice as fast.
Serving up content-free posts on the Interwebs since 1983.
History of INFO Magazine

saehn

I'm sure someone's going to come up with a better answer, but you could do something like this (pseudo code) and stay mostly in BASIC.

z = (beginning of appropriately free memory space)
save shape a$
for x = 0 to len(a$)
poke z, asc(left$(a$,x)) : rem i know that format is wrong but the idea is that you poke the ascii value of each char into your memory space
next x
bsave"your-string", z to z+len(a$)

ruthven

Thanks for the info guys!  I didn't even know about FAST mode!--I just assumed it was running at it's full 2MHz when in C128 mode.  Wow, that seems to take care of my problems--there's still some rendering time, but it's been cut down to a very acceptable level and I like the screen being turned off while the sprites are initially drawn (just seems more professional).

This takes care of all my needs for now and possibly the future, but I think there could still be a use for saving SSHAPE graphic strings to disk.  I think I understand the logic of your theoretical code Saehn--copy the ascii value of every character of graphic string to an address that I specify, hence I know where it can be retrieved.  I'm not sure if this will work though because of the nature of BLOAD--I think I can only BLOAD from (in this case) a disk and put that data at an address I specify.  With your code, I could BLOAD the contents of the graphic string back to any address, but I don't think the string itself would be associated with it any longer.  And I don't know how I would go about setting a new string (A$) to equal all those ascii characters strung together across an address range.

I looked up a C128 memory address map yesterday and found that BASIC variables, arrays and strings are contained within bank 1, address 1024-64511... this is if I'm reading the chart correctly--perhaps somebody could check me on that—here's the link:

http://www.zimmers.net/anonftp/pub/cbm/manuals/anthology/p125.jpg

This doesn't help me find a specific string's address but I figured I could just copy all variables including my graphic strings this way.  This would perhaps make for some unnecessary loading time but would be fine with me.  Unfortunately it didn't work--after I used SSHAPE to create all my graphic strings, I tried doing a BSAVE"graphics",D0,U8,B1,P1024 to P64511.  This took a LONG time to save and a LONG time to load after, so I got excited thinking I actually solved it.  But when it finished loading I wasn't able to retrieve any of my variables--not just my graphic strings, but even regular variables like WV=1, SP=100, etc. had not retained their values.  So it makes me wonder what I actually saved and loaded back into memory.

So all though I should be all set for rendering graphics for now (in FAST mode), I'd still appreciate any more info anybody has on this.  Thanks!

airship

You're saving the entire variable data space, but not the pointers that assign variable names to a specific block of data.

Back to people who know more than I do...
Serving up content-free posts on the Interwebs since 1983.
History of INFO Magazine

saehn

Quote from: ruthven on February 04, 2009, 12:20 AMI think I understand the logic of your theoretical code Saehn--copy the ascii value of every character of graphic string to an address that I specify, hence I know where it can be retrieved.  I'm not sure if this will work though because of the nature of BLOAD--I think I can only BLOAD from (in this case) a disk and put that data at an address I specify.  With your code, I could BLOAD the contents of the graphic string back to any address, but I don't think the string itself would be associated with it any longer.  And I don't know how I would go about setting a new string (A$) to equal all those ascii characters strung together across an address range.

That particular string would no longer be associated with it, but you could always load up a new one or reload it to the old string:

start-add = 12345 : rem insert yours here
end-add = 23456 : rem insert yours here
bload"data", start-add, end-add : rem can't remember if you have to specify end, just chop if not
for x=start-add to end-add
a$=a$+chr$(peek(x))
nextx

Seems like that should do it. If you don't know how long a file's going to be in advance, you could just get into the habit of adding a zero to signify the end of string.

But I don't know why you don't just save sprite data with a binary save like shown in the programmer's ref guide. :-)

ruthven

Thanks saehn--that code snippet pretty much answers my question--I'll have to try it tonight.

QuoteBut I don't know why you don't just save sprite data with a binary save like shown in the programmer's ref guide. :-)

Ah, yes that would be the easy way to do it--and the way I wish I could do it... but if I'm not mistaken, doesn't the C128 only allow for 9 or 10 sprites at a time?  My project consists of something like 38 sprites (currently)--and I think I'm using the term "sprite" loosely; these graphics will not move across the screen, they are stationary.  Occasionally (depending on user input) they will have to be updated by a new "sprite" being placed directly over the old one quickly (with GSHAPE).  This will be the extent of their animation.  Also, these "sprites" (or "graphics" is probably the more correct term) are fairly large in dimension--I think legitimate "sprites" can only be 24 x 21 pixels.


airship

Serving up content-free posts on the Interwebs since 1983.
History of INFO Magazine

saehn

Quote from: ruthven on February 04, 2009, 05:03 AMthese graphics will not move across the screen, they are stationary.

so why don't you just save them in data statements? :-P

ruthven

Quoteso why don't you just save them in data statements? :-P

Alright, you got me there....  because putting all the coordinates, etc. in data statements would just be way too clean and efficient for a sledgehammer programmer like myself!  Call me an idiot, but I never even thought of that--even though I've drawn graphics out of data statements in other old versions of BASIC.  It always takes me a little bit to get my bearings (even with commonsense issues) when I learn a new version of BASIC.  I was sleeping apparently--thanks for waking me up!  :)

saehn

Quote from: ruthven on February 04, 2009, 10:55 PMIt always takes me a little bit to get my bearings (even with commonsense issues) when I learn a new version of BASIC.  I was sleeping apparently--thanks for waking me up!  :)

Nah, you'll get there. :-) Show us what you do (results or source), I'm sure many of us will be interested!

ruthven

Thanks for the vote of confidence.  This is my first C128 project so don't expect much.  Actually, it's my first serious program for any Commodore platform--I had only written one or two programs for the C64 before this, and all they did was scroll some text across the screen for retro-looking credits to a super 8 movie I was filming.  My current project is nothing too original I'm sure--I'm trying to make a simulation of a music synthesizer for the SID.  I'm just trying to make it look cool with all these graphics...

SmallCleverDinosaur

I'm glad you abandoned the idea of saving/loading the entire area of variables and then trying to relink them. That is probably impossible to achieve unless you have some serious insight as to how variables are handled in the C128. For instance, there is a concept in Commodore litterature known as "garbage collection" when it comes to handling variables. Does anyone know what that means?

Anyway :)

I had a thought. Why don't you use a separate program to create all of your images on a blank hires screen with the BASIC graphic commands. Then BSAVE the enitre hires screen (or as much as you use) to disk. Once on disk your program can begin by BLOAD'ing that area back into memory. Then you can use SSHAPE to create all of your graphical variables which then can be GSHAPE'd as the program runs.

It's probably much faster to BLOAD a bitmap screen than to use graphic commands to create the bitmap before you SSHAPE it. And if you use the bitmap area effeciently you may not need to BLOAD that much data.

Don't forget though to also BSAVE the color memory at 7168-8191, or you will loose all color information. Very convinient that the color memory is placed just below the hires bitmap area at 8192 :)
Ignorance is a precious thing. Once lost, it can never be regained.

ruthven

Yeah, I have given up the idea of saving the entire variable data space--I don't know how to save the pointers that assign variable names to specific data, so none of my variables retain their values.  Besides, my goal was to speed things up--loading the entire variable data space takes a LONG time from disk I discovered.  Faster to draw it out manually (even in slow mode).

I still have yet to try Saehn's suggestion of POKEing every character of a graphic string to an address I specify, so I know what addresses to BSAVE.  Actually, just using FAST mode to manually draw all the graphics and save shapes seems to work pretty well, so I had stopped pursuing this idea of saving graphics strings to disk...

However, your idea is interesting and I'm surprised I hadn't already thought of it considering I read someone's thread on BSAVEing bitmap data.  I had assumed that BLOADing an entire hires screen might be time consuming, but I'll have to give it a shot sometime to see.  Thanks!

SmallCleverDinosaur

I will also tempt you with an alternate solution :)

As you may know it's possible to include binary data into a BASIC file. If you LIST any C64 game that is written in machine language (almost all of them are), you'll only see a single line like "10 SYS2061" and that's all there is. The rest of the program is hidden beyond the end of the BASIC program. So when the BASIC program is executed it calls a machine language program (the game) that was loaded into memory together with the BASIC program.

This could be done with your program too. If you place your hires data after the BASIC program in memory, you can make the C128 save the binary data together with your BASIC program. So when the program is loaded into the computers memory the binary data comes along as well.

This is a bit more trickier but a lot more neat :)

If you want more details on this, please let me know.
Ignorance is a precious thing. Once lost, it can never be regained.

ruthven

That sounds like the most professional way of going about it... I assume this alternate method is even faster than BLOADing the bitmap screen(?)  Would this require me to learn any assembly/machine language?  Cause assembly/machine language has always seemed way beyond me; I've always been (and at this point, probably always will be) a BASIC kind of guy, if you know what I mean (wow, that sounds lame).  :)

I'm still a newbie to Commodore's flavor of BASIC.  At this point I'm hammering away at other aspects of my current project, beyond the graphics...  Once I have everything all set, I will probably ask you for more details on this method...  But being such a newb, I feel like I'm rapidly getting in over my head, so I'll save the advanced stuff till after I get the rest of the program working.  :)

SmallCleverDinosaur

The loading time for using this method instead of using BLOAD is probably much the same since the amount of data remains the same. It's just a little more professional :) And once the program is loaded and started the user doesn't have to wait for another load to occur (the BLOAD).

This technic doesn't require you to know assembler/ML-programming but a small understanding of the hexadecimal system is good. As you'll se in the tutorial below :)

Achieving this is actually quite simple. I have made a small demo to demonstrate it. It works perfectly fine in VICE of course :)

First of all, make sure your comupter/VICE are newly reset. That is for making sure that all system pointers are set as they should be.

Type: 10 SYS7181 (make sure there are no spaces!)

Now type
POKE7181,238
POKE7182,32
POKE7183,208
POKE7184,76
POKE7185,13
POKE7186,28

What you just have done is to POKE a small ML-routine into the computers memory. Now type RUN. The border should start to shift colors very rapidly.

You'll have to break it with RUN/STOP-RESTORE.

If you type LIST you will still only see the line you entered before. That's because the ML-routine is placed in memory beyond the BASIC program and as such it will not be affected by the LIST command.

Now, we want to save the BASIC program AND the ML-routine together. If we would use the DSAVE command at this point the C128 would only save the BASIC portion because it knows where in memory the program ends.

The pointers that holds the address where the program ends are located at 4624-4265 ($1210-$1211). The values here will now be 13 and 28 respectively. That means hexadecimal values of $0D and $1C or a combined address of $1C0D which equals decimal 7181 which in turn is the first address we used in the series of POKE's. That means that the BASIC program now ends right where our ML-routine begins so the ML-routine won't be saved if we use DSAVE.

So, we have to move the pointers up in memory in order to include the ML-routine when DSAVE'ing the BASIC program.

Type POKE 4624,19. This will move the end of the BASIC program to address $1C13 (7187) or one byte beyond our ML-routine, effectively including our ML-routine into the BASIC program.

Now DSAVE the program. The DSAVE will include the ML-routine and once you DLOAD it back into memory the code will be there. Of course, it doesn't have to be ML-code, it can be any kind of data, for example hires data to use for SSHAPE'ing :)
Ignorance is a precious thing. Once lost, it can never be regained.

airship

The main problem with this method, of course, is that if you change the BASIC program at all, you wipe out your machine code. So you need to be careful with it.
Serving up content-free posts on the Interwebs since 1983.
History of INFO Magazine

ruthven

Yeah, unfortunately I never learned any Hex either so this all seems like Greek to me! :)

Interesting though, and I may give it a try when it comes time.  Can this method be used for an entire program rather than just a routine?  Would this in essence convert the whole program to machine code with increased execution speed?--I'm not talking about loading time, but in-program speed of constantly cycling through GET and joystick commands/subroutines for input?

If not, are there any compilers for BASIC 7?--software that essentially converts BASIC programs down to lowlevel machine code for faster execution?  This would be a dream for non-assembly programmers like myself.  My current synthesizer project isn't fast enough for accurate timing with hitting keys to play notes (though it's not too bad)...  It's the kind of thing that would be better programmed in assembly to begin with, but if I could convert the finished program all to machine code, it could be awesome...

saehn

Quote from: ruthven on February 23, 2009, 12:36 PMIf not, are there any compilers for BASIC 7?--software that essentially converts BASIC programs down to lowlevel machine code for faster execution?

Blitz! 128: http://cottonwood.servebbs.com/wiskow/

RobertB

     Also the Basic 128 compiler by Abacus.

              Truly,
              Robert Bernardo
              Fresno Commodore User Group
              http://videocam.net.au/fcug
              CommVEx v5 info - http://www.commodore.ca/forum and click on ComVEX

SmallCleverDinosaur

Quote from: ruthven on February 23, 2009, 12:36 PM
Can this method be used for an entire program rather than just a routine?
Yes it can, the C128 doesn't care about the nature of the data, it just loads it into memory following the BASIC program. As I mentioned before, this is how most C64 games are loaded into memory. Most of them are made up of 1% BASIC and the rest is ML.

Quote from: ruthven on February 23, 2009, 12:36 PM
Would this in essence convert the whole program to machine code with increased execution speed?
No, this is just a technic to make sure that data in any form (ML-routines, hires data etc) is being loaded into the computers memory at the same time as the BASIC program. You'll have to compile the BASIC program to ML yourself. It would have been a neat feature though, if the C128 would have turned it into ML :)

Quote from: RobertB on February 23, 2009, 04:54 PM
Also the Basic 128 compiler by Abacus.
Does anyone know if this on can be found on the internet?
Ignorance is a precious thing. Once lost, it can never be regained.

Hydrophilic

Well there were several ideas given, but none seem to directly save SHAPE data.  Here is my solution:

100 A=POINTER(A$):REM A$ IS THE SHAPE DATA
110 BANK1:A=PEEK(A+2)*256+PEEK(A+1)
120 BSAVE"SHAPE",B1,P(A)TOP(A+LEN(A$))

Note that A starts out as the address of the string descriptior.  Then we peek the descriptor to get the address of the actual string data.

Actually loading it is a little more complex because we generally won't know how large the data is until we load it.  Here's my solution:

100 FF$="A":FORX=1TO7:FF$=FF$+FF$:NEXT: FF$=FF$+LEFT$(FF$,127)
110 A$=FF$:A=POINTER(A$)
120 BANK1:A=PEEK(A+2)*256+PEEK(A+1)
130 BLOAD"SHAPE",B1,P(A)
140 L=PEEK(175)*256+PEEK(174)-A
150 POKE POINTER(A$),L

The first line creates a template string of max length (255 chars) and only needs to be executed once (assuming you never destroy FF$).

Lines 110-150 do the loading and could be used as subroutine to load multiple shapes (of course you'd need a variable for the filename).

Line 110 first creates a new variable for the shape to load from the template variable.  The rest of 110 and 120 setup up the address like in the save shape program.

Line 130 loads the shape into the string data.

Line 140 calculates the length of the loaded data by peeking the load-end-address+1 (set by KERNAL) and subtracting it from load start address.  NOTE: When using a fast-serial device (1571,1581,etc.) this will fail with the original, buggy, 1985 ROMs of the C128 because the KERNAL fails to set the load end address!

Line 150 updates the string descriptor with the correct length

Try this without updating the string length and the computer will likely go crazy (it did for me before I added lines 140 and 150).
I'm kupo for kupo nuts!