Jump to content
IGNORED

Commodore Pet Limitations


Recommended Posts

Hello All!

 

Once again I post a message for help in this great knowledgeable forum!

 

I recently started coding a game for my Commodore Pet. I'm fairly new to it and was wondering if anyone could provide some info regarding it's limitations.

I'm working on a Petscii based dungeon crawler and plan to include numerous types of enemies, magic, etc.

However, I have no clue what I'm up against and for me it's a welcomed challenge!

 

I would appreciate any info about the 31743 bytes free (4032 model).

For example does that include my code and all the variables I store?

Is there any way to plan ahead to possibly omit some features I would like to incorporate?

Any tips or tricks to save memory?

 

As usual, thanks much!

 

Edited by evildead9000
Link to comment
Share on other sites

Yup, that is right. That space has to contain both your code and all your variables. Tricks on saving memory depends on what you are coding. Concatenating as many statements as possible using colons on a single row is a given. Writing down which variables you are using and where you can reuse them, e.g. loop variables and other temp counters, will save you from using lots of garbage variables. Depending on your data structures, you might want to store maps as raw data into memory instead of varibles - think POKE/PEEK an area of 40x25 bytes instead of string variables. But one would have to see a chunk of your code to give more exact suggestions.

  • Like 2
Link to comment
Share on other sites

Great info! I was on the right track.

I was concatenating a lot and reusing counter variables. Now I'll truncate some words as well, so instead of "MAGIC", I'll use "MAG", or even just "M".

I'm also omitting many spaces, so instead of PRINT T$, I have PRINTT$. Definitely in my loops too and every other place possible! ?

 

However, I don't quite follow: " store maps as raw data into memory instead of variables - think POKE/PEEK an area of 40x25 bytes instead of string variables. "

 

Can you please clarify?

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

I don't know how you were planning to do it, but some developers might think a map looks like:

 

DIM MA$(25)

MA$(1)="####oooo#o#o#o#o##################xx##xx"

MA$(2)="#oooooooooooooooooooooooooooooooooooooo#"

etc

 

In my experience, it would be more efficient to lower the RAMTOP (can't recall the POKE on various PETs but would be POKE 56 on VIC-20 and C64) and then use an area of 40x25 bytes for your hidden map (which of course could have different dimensions than the screen, not sure why I picked those numbers) where you would PEEK and POKE instead of doing string handling operations with MID$ and stuff. Perhaps not the best example, but you can also store other data types where the values are integers 0-255 directly into memory cells instead of going through variable arrays, which sometimes could save you space.

  • Like 1
Link to comment
Share on other sites

DIM A%(20):FOR I=1 TO 20:A%(I)=0:NEXT would take 56 bytes and allows you to store integers -32768 to +32767 in each cell.

 

DIM A(20):FOR I=1 TO 20:A(I)=0:NEXT would take 119 bytes and allows you to store floating point numbers in each cell.

 

Lowering RAMTOP and use 20 bytes for direct access obviously only takes 20 bytes + 7 for the loop counter but those can only hold integers 0 to 255 in each cell.

 

Ok, it seems that POKE 53,PEEK(53)-1:CLR would lower RAMTOP by 256 bytes on all models except for PET 2001 that uses BASIC 1.

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

21 hours ago, evildead9000 said:

Great info! I was on the right track.

I was concatenating a lot and reusing counter variables. Now I'll truncate some words as well, so instead of "MAGIC", I'll use "MAG", or even just "M".

I'm also omitting many spaces, so instead of PRINT T$, I have PRINTT$. Definitely in my loops too and every other place possible! ?

 

However, I don't quite follow: " store maps as raw data into memory instead of variables - think POKE/PEEK an area of 40x25 bytes instead of string variables. "

 

Can you please clarify?

 

8 hours ago, carlsson said:

DIM A%(20):FOR I=1 TO 20:A%(I)=0:NEXT would take 56 bytes and allows you to store integers -32768 to +32767 in each cell.

 

DIM A(20):FOR I=1 TO 20:A(I)=0:NEXT would take 119 bytes and allows you to store floating point numbers in each cell.

 

Lowering RAMTOP and use 20 bytes for direct access obviously only takes 20 bytes + 7 for the loop counter but those can only hold integers 0 to 255 in each cell.

 

Ok, it seems that POKE 53,PEEK(53)-1:CLR would lower RAMTOP by 256 bytes on all models except for PET 2001 that uses BASIC 1.

Wow. This thread has taken me back to Commodore 8-bit BASIC optimization techniques that I haven't thought about in a very long time.

 

Commodore BASIC limits you to 80 "typeable" characters per line.  (2 x 40 character lines on a 40 column machine.)  One way to increase this is to use shortforms for the BASIC keywords.  PRINT can be abbreviated as ?.  Other commands can be entered by typing their first character followed by a shifted version of their second character, which will appear as a graphics symbol.  As an example, FOR can be entered as F shifted-ONEXT would be N shifted-E.  This will expand to a line larger than 80 characters that will still execute correctly.  The only downside is that you will need to reenter the entire line if you want to change it because the expanded line will be too long to edit.

 

For variables, Commodore BASIC only cares about the first two characters.  While you can use a variable such as SCORE%, BASIC only cares about the first two characters.  Use SC% to save space.  (If you want to mess with people reading your code you can use things like SCORE%, SCAB% and SCROLL%.  BASIC will consider them all to be the same and the names can be used interchangeably.)

 

When dimming arrays remember that they start at element 0, not 1.  If memory is tight, creating one less element can help.  So in carlsson's example above you can use DIM A%(19):FOR I=0 TO 19:A%(I)=0:NEXT  I would also remove as many spaces from the code as possible to save bytes.  Unfortunately this can make your code very difficult to read.

  • Like 3
Link to comment
Share on other sites

Actually, the better way to do maps is to store them on disk. Either use a file per map, or use a random access file and store your maps with fixed sizes....

 

So say your map fits on exactly one screen. You can store map as a PETSCII text file, then just loop INPUT# and PRINT to display it. Or you can go the Rogue route and randomly generate each level, placing, say, 5 rooms on the map, then connecting them with hallways. If you put the newly generated map directly in screen RAM and never store it in main memory, that's 1000 bytes you don't have to worry about. 



 

  • Like 2
Link to comment
Share on other sites

Great advice!

 

I did think about storing the levels on disk, but I'm going to try and avoid doing so.

At this time I have a few ideas to randomize the levels a bit, but have yet to implement. I also may not randomize; I'm not quite sure yet. We'll see how it goes.

 

I'm at 8k now, but already know a bunch unnecessary stuff to remove.

However, I may need to utilize your suggestions! Time will tell.

 

As always, thanks much!

Link to comment
Share on other sites

On 10/3/2019 at 5:20 PM, TomXP411 said:

Actually, the better way to do maps is to store them on disk. Either use a file per map, or use a random access file and store your maps with fixed sizes....

 

So say your map fits on exactly one screen. You can store map as a PETSCII text file, then just loop INPUT# and PRINT to display it. Or you can go the Rogue route and randomly generate each level, placing, say, 5 rooms on the map, then connecting them with hallways. If you put the newly generated map directly in screen RAM and never store it in main memory, that's 1000 bytes you don't have to worry about. 



 

I've decided it's best to access the map data from a disk. You're totally correct. Space wise of course it's better, but I mainly want to design the maps/levels instead of something random. Instead of similar levels randomly generated, I would like many levels that differ with total control of their design. I would never get that without accessing from a disk.

 

This is my test that I attempted:

10 open 5,8,5"level1,d0,r"

20 get#5,t$:print t$    <------I tried to use Input, but couldn't get it to work.

30 if st<>64 goto 20

 

I'm not familiar with storing the data as a PETSCII txt file. level1 is a prg file and has only 2 lines of data:

10 "----------------------------------------------------------------------

20 "(                                                                                                )

 

I substituted characters, but basically it's the top and side walls of a dungeon. The syntax is incorrect, but I am able to store the data as is trying to be economical. I do plan on storing other data on the disk, not just maps. Instead of cutting stuff out to make it work within the memory constraints, I'll keep as much as I can in and go for it!

 

When I run the code above, it does work, but I'm getting extra characters and I'm not sure why.

Initially I had them as data statements and the string in quotes. I also had numbers, which I used as poke values: poke32768+c,val(t$). It worked fine and I incremented c, but I also got extra characters.

 

Is there any big difference between printing it to the screen or poking it?

I'm off the mark and any further guidance would be much appreciated.

Thanks!

Edited by evildead9000
Link to comment
Share on other sites

I may be wrong about this and others will chime in, but I believe PRG files store part of the load address in the first few bytes of the file and that may cause INPUT some sort of heartburn.  The limitation with INPUT# is that what you read in has to fit within 80 characters, ad if it isn't quoted, you can't have commas or other punctuation errors of you'll get ?EXTRA IGNORED or ?REDO FROM START

 

GET# causes a different set of issues.  The default record separator in a file is CHR$(13) (carriage return).  GET# won't care about that and it'll give you back a CHR$(13) into your string.  You can test for this and skip it.  

 

So, what I would do is take your DATA statements, and write them to a SEQ file first (it won't have the load address issue) with something like:

 

10 OPEN 15,8,15:OPEN 2,8,2,"0:LEVEL1,S,W"

20 FOR I=1 TO (however many DATA statements you have):READ A$:PRINT#2,A$;CHR$(13);:GOSUB 100:NEXT

30 CLOSE 2:CLOSE 15

40 END

100 INPUT#15,EN,ER$,ET,ES,ED

110 IF EN>1 THEN PRINT ER$:STOP

120 RETURN

 

Now, your program can read this file with:

10 OPEN15,8,15:OPEN 3,8,3,"0:LEVEL1,S,R"

20 INPUT#3,A$:GOSUB 100:PRINT A$

30 IF ST<>64 THEN 20

40 CLOSE 3:CLOSE 15

50 END

100 INPUT#15,EN,ER$,ET,ES,ED

110 IF EN>1 THEN PRINT ER$:STOP

120 RETURN

 

Lines 100-120 check for a disk error and stop if it gets one - it's good to check for disk errors since Commodore BASIC can't detect them.

 

You don't say which version of PET BASIC you are using.  If you are using BASIC 4.0, it has built in disk commands that are a little easier than the BASIC 2.0 commands we've been using.  If you aren't familiar with those, let us know and we can help.  They are pretty easy to master.

  • Like 2
Link to comment
Share on other sites

1 hour ago, Casey said:

I may be wrong about this and others will chime in, but I believe PRG files store part of the load address in the first few bytes of the file and that may cause INPUT some sort of heartburn.  The limitation with INPUT# is that what you read in has to fit within 80 characters, ad if it isn't quoted, you can't have commas or other punctuation errors of you'll get ?EXTRA IGNORED or ?REDO FROM START

 

GET# causes a different set of issues.  The default record separator in a file is CHR$(13) (carriage return).  GET# won't care about that and it'll give you back a CHR$(13) into your string.  You can test for this and skip it.  

 

So, what I would do is take your DATA statements, and write them to a SEQ file first (it won't have the load address issue) with something like:

 

10 OPEN 15,8,15:OPEN 2,8,2,"0:LEVEL1,S,W"

20 FOR I=1 TO (however many DATA statements you have):READ A$:PRINT#2,A$;CHR$(13);:GOSUB 100:NEXT

30 CLOSE 2:CLOSE 15

40 END

100 INPUT#15,EN,ER$,ET,ES,ED

110 IF EN>1 THEN PRINT ER$:STOP

120 RETURN

 

Now, your program can read this file with:

10 OPEN15,8,15:OPEN 3,8,3,"0:LEVEL1,S,R"

20 INPUT#3,A$:GOSUB 100:PRINT A$

30 IF ST<>64 THEN 20

40 CLOSE 3:CLOSE 15

50 END

100 INPUT#15,EN,ER$,ET,ES,ED

110 IF EN>1 THEN PRINT ER$:STOP

120 RETURN 

 

Lines 100-120 check for a disk error and stop if it gets one - it's good to check for disk errors since Commodore BASIC can't detect them.

 

You don't say which version of PET BASIC you are using.'

I have a Commodore Pet 4032 with Basic 4.0.

 

I believe I successfully wrote the SEQ file. I loaded it in DirMaster and it is showing as a list of 102, which is the character I'm using for a wall.

However, when I try to read it, it only prints once.

Print a$ is printing the number 102. Poke32768, val(a$) prints the character I need.

I checked my code for typos a few times and I'm not sure why it's not printing/reading all of them.

 

Thanks for the quick reply!

Edited by evildead9000
Link to comment
Share on other sites

How do your DATA statements look like? I tested Casey's programs above and they work fine if your input is like this:

 

50 DATA"@@@@@@@@@@@@@@@@"

51 DATA"@AAAAAAAAAAAAAA@"

52 DATA"@BABABABABABABA@"

etc

 

Since you mention screen code 102, the checkered pattern, I assume your PET has the graphics keyboard with four columns of numeric keyboard to the right? On the business keyboard which has numbers on the top row and only three columns to the right, I don't think you can type in that symbol.

  • Like 1
Link to comment
Share on other sites

BASIC 4.0 makes working the disk easier.  

 

The same program snippet I provided above, in BASIC 4.0:

 

10 DOPEN#1,”LEVEL1”,D0,U8

20 INPUT#1,A$:GOSUB 100:PRINT A$

30 IF ST<>64 THEN 20

40 DCLOSE#1

50 END

100 IF DS>1 THEN PRINT DS$:STOP

110 RETURN

 

In BASIC 4.0, DS and DS$ are reserved variables that return error status from the disk drive, so it simplifies the error checking.  DS returns the error number alone, DS$ returns the entire error string (00, OK,00,00)

  • Like 1
Link to comment
Share on other sites

Okay, before I jumped the gun. It was reading the data correctly with no added characters, but only one line. I checked the SEQ file in DirMaster and the entire map shows.

ST is not 64(end of data status), so it should loop.

 

I do appreciate the simplified code, which is much easier to understand.

My game code gosubs to the code snippet above that I added.

The only line I modified was 50, which I changed to return, so it returns to the game code after the level loads. And of course I changed the gosub number in line 20 to match the changed line number.

 

At this point the first data line of my map appears and the game continues where it left off.

 

Any ideas what I may have done wrong?

Link to comment
Share on other sites

Strange.  I just tried it in VICE and it worked as expected.   If you just run the small version of the program that just reads the file and prints the contents to the screen, does it still just do one line?  

 

Can you show us the code in your program (at least the disk portions) and we can take a look?

 

Some of the PETs had issues with sending line feeds to the disk drive when you use PRINT# but I tried it with a 4032 with BASIC 4.0, both with an 8250 and a 4040 drive and they behaved the same way.

 

Link to comment
Share on other sites

Since I know how many lines of data there are I added a loop. I made three data lines for testing purposes. I omitted the error and status checks to save space. This is what I have now:

10 dopen#1,"level1",d0,u8:print"{home}" <-reverse heart to clear the screen

20 for c=1 to 3:input#1,a$:print"{up}"a$:next <-moves the next print up, so there's no extra line in between data

 

It prints the map from the very top of the screen perfectly. The SEQ file is fine (thanks again!). It appears the line with the ST check is ending the code after 1 pass. I re-checked my typing and it looked good; unless I need a new monitor and or glasses! :-) I've been using CBM PRG Studio to create my program. There I ran my code and it automatically launches xpet(from Vice). One major thing (which took me a while to figure out) was that for some reason it launched the emulator as a PET 8032 model. Very odd since it's been launching as 4032 ever since I've been using it.

 

It's been a busy day and I wanted to further test and reply sooner, but couldn't. I'm happy that it's working exactly as I need it. Perhaps, it doesn't work on an 8032? Also, when my game opens in that mode it doesn't fully work either. I've tested my code on my real PET with a PetSD and it's all good so far. In the future I will be purchasing a real drive. Thanks much for all your help and everyone else too; and for following through with great advice. I really learned a lot.

 

Link to comment
Share on other sites

Oh!  One other thing I forgot.  Any I/O command can cause ST to change, even if you don’t expect it would.  That can cause you problems you don’t expect to occur.

 

A common workaround for this is:

 

100 OPEN 8,8,8,”0:LEVEL1,S,R”

110 INPUT#1,A$:SS=ST

120 PRINT A$

130 IF SS<>64 THEN 110

140 CLOSE 8

 

This assures that you know the value of ST right after you do your INPUT and don’t lose it somehow by something else.  What I suspect, though I haven’t tried it yet, but the check of DS/DS$ or the INPUT#15 method to read the disk error channel may change the value of ST to 64 after you read the error status (it’s 1 record, and the last one the disk will give you - that might set ST to 64 at that point).  I may play with that this afternoon, but that would explain why your program only reads 1 line from the file.  The error check may set ST to 64 right after that.

  • Like 1
Link to comment
Share on other sites

Yes indeed. From emulation it shows that reading the error channel changes ST to 64. Also in your INPUT#15 statement above, doesn't it only take four arguments: error number, error string, track and sector? I'm not what ED should report though I suppose it returns 0 from the command channel.

 

I rarely used the ST variable but shouldn't be enough to GOSUB 100 if ST>0?

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

Yep, I just tried it.  Reading DS in BASIC 4.0 affects ST, so the updated version saving it off would work.

 

Some of the dual drives (I think the 8250) also return the drive number where the error occurred.  It may not be necessary to retrieve the drive number, but it is there if you need/want it, so I just included it in my example.

 

And you are right.  The disk I think only uses 0 and 64 for ST.  Cassette has several other values for ST also (64 being end of file as well)

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