Jump to content
Starsickle

RETROTrek - Early Development Thread

Recommended Posts

Posted (edited)

This gets into the associative arrays discussion from 2019. It's pretty clean to have a RAM disk on a specific device number, but for best utility it should emulate a drive. Typically we can equate file# and secondary address#, as for reading and writing sequentially they have to be between 2 and 14 and have to be unique, so the access to the RAM disk should permit that. We already have the vectored hooks for the device# I/O, so it can be prototyped as a Golden RAM ($400-$7FF) utility and if it works out, we could look at packaging it for addition to the Kernel.

As a RAM disk, we have a lot of flexibility about set up information, because that can be in the commands that can be sent to the pseudo disk drive, so there is no need to strain to get information into the dev#, file#, sa# data.

There is a very strong case to have the routines that OPERATE the a HighRAM RAMdisk in FlashROM somewhere, because placing it in High RAM requires a lot of trampolining, which would slow it down substantially, while LowRAM is quite scarce relative to HighRAM OR FlashROM.

 

Edited by BruceMcF

Share this post


Link to post
Share on other sites
19 hours ago, BruceMcF said:

It's pretty clean to have a RAM disk on a specific device number, but for best utility it should emulate a drive.

That's pretty clever...

Share this post


Link to post
Share on other sites
Posted (edited)

Back to important things, i.e. RETROTREK (hey, I like all caps instead of partial caps... but that's just me).

For your edification, here is code I wrote that reads data loaded into RAM Bank 1.  I LIKE having data in the RAM banks because it can be packed in very efficiently, as opposed to BASIC floating point numbers.  I dislike it because of the PEEKing I have to do, but I can work with that.

Anyway, here's the BASIC code, and the binary file it loads and reads from.  MAYBE this could help you with a plan of attack.

 

ALLSHIPS.BIN shipbrowser.bas

Screen Shot 2020-10-03 at 9.11.38 PM.png

Edited by rje
  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)

I'm looking it over - the hardest part of all this really is that everything is in 2 letter variable names - but I can see you start at the starting address (AD) and allocate a specific amount of bytes to the name (24).

First poke unknown - assuming it's a sentinel value or some other control value.

Next is the quick ship profile, which is a Single character Archetype with 5 characters (uhh, a few have 6...). This is pretty slick, as it can be used for more procedural generation and representation.

After that we're jumping around a bit, but CHR$(48+X) I assume is just navigating the character Table. Otherwise everything is a record of N size, controlled by LN for the line, jumping to a new AD because you know the size of each record in terms of characters and integers.

If you don't mind, I'd like to try my hand of recombobulating this so I can store my own ship information, which currently looks like so:

41100 REM //SUBROUTINE - STARSHIP NAMES AND IFFS - ===========TODO - MOVVVEEEEEE
41101 SID$(0,0) = "ENDEAVOR" : SID$(0,1)="ALLI"
41102 SID$(1,0) = "NME-1" : SID$(1,1)="KLRG"
41103 SID$(2,0) = "NME-2" : SID$(2,1)="KLRG"
41104 SID$(3,0) = "NME-3" : SID$(3,1)="KLRG"
41105 SID$(4,0) = "NME-4" : SID$(4,1)="KLRG"
41106 SID$(5,0) = "NME-5" : SID$(5,1)="KLRG"
41107 SID$(6,0) = "NME-6" : SID$(6,1)="KLRG"
41108 SID$(7,0) = "ASTORIA" : SID$(7,1)="ALLI"
41109 SID$(8,0) = "RET-1" : SID$(8,1)="INDA"
41110 SID$(9,0) = "MERCH-1" : SID$(9,1)="DORA"
41111 RETURN
	41120 REM //SUBROUTINE - SHIP DATA DEFINITION - SEE README  ================
41121 DATA 3, 3, 90, 3000,   18, 0, 300,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41122 DATA 1, 1, 30, 1000,   6,  0, 100,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41123 DATA 2, 2, 60, 2000,   12, 0, 200,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41124 DATA 3, 3, 90, 3000,   18, 0, 300,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41125 DATA 1, 1, 30, 1000,   6,  0, 300,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41126 DATA 2, 2, 60, 2000,   12, 0, 200,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41127 DATA 3, 3, 90, 3000,   18, 0, 300,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41128 DATA 3, 3, 90, 3000,   18, 0, 300,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41129 DATA 2, 2, 60, 2000,   6,  0, 200,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41130 DATA 1, 1, 30, 1000,   6,  0, 100,   6, 6, 6, 6, 6,   0, 0, 0, 0, 0
41131 RETURN

If I learn how to do this (and make it work), I definitely could just throw all the object data in the game into segments of a 512k High Ram Bank. Reading sequentially is a pain and N data lines grows, but perhaps, in known and well-defined cases, an ID can be assigned that matches a memory address for direct access, sorting, etc?

Would love to have this sort of layout with the ability to highlight and select a line by inverting the color of the currently selected line. This is why I have colors set in variables along with their inversions.

 

Edited by Starsickle

Share this post


Link to post
Share on other sites
Posted (edited)

This is what I'm considering for the main file, although there's some concerns with how this will work.

15 MC%=0 : REM //THE MASTER COUNTER - USED FOR PROGRAM JUMPING.
16 LS%=0 : REM //LOADED FILE STATE - CONTROLLING THE LOADED FILE.

19 REM // DEPENDING ON GAME STATE: INIT DATA, MAIN LOOP, EXTRA, TERMINATE
20 ON LS% GOSUB 28,1000,1000
	

The Master Counter will increment every time a file is jumped. This will help me determine if the program is functioning properly. It's not needed, as I originally thought I could use MC to control or save a line number.

The Loaded File State is the important one. This determines the program state, and when any given program file is loaded for execution into Low RAM, it HAS to hit a check versus the state first.

So - what's wrong? The fact they are defined above. I think I could move those down into GOSUB 28 (which is implicitly LS%=0, right?), but now the problem exists of the following:

A lot of this program is broken up into subroutine pieces and helpers. If I overwrite the program every time I load a new file, I have to make sure I make several pieces redundant, or only include them within the file loaded for execution. This is very big design challenge, because one has to remember not to overwrite important pieces of data that remain in the appropriate Low RAM stack.

Outside of that challenge? Well, there is an alternative, but I don't think it's possible - I could load the program entirely into High RAM, but I don't think it's executable from that area, as it's simply not segmented like the Low RAM. This would be an interesting ability, though.

 

EDIT: I also am not sure about how to capitalize the title - feel free to choose between RETROTrek, RetroTrek, and RETROTREK.

Edited by Starsickle

Share this post


Link to post
Share on other sites

Optimization question:

Pic below needs some work, as it takes up many lines in the program and requires several helper methods and variables. I'm not sure if I should store some elements as dedicated string variables or not, because I'm unsure if it will save space, especially if I end up sending the screens like Game Over, Mission Complete, and similar into subprogram files along with their helper methods.

 

Question - Screen element optimizizing.png

Share this post


Link to post
Share on other sites
Posted (edited)
Quote

I'm not sure if I should store some elements as dedicated string variables or not, because I'm unsure if it will save space, 

Yes, you will save space, at the mental load of remembering those variables.  And might render quicker -- save the entire first three lines of the message box into one variable, and the lower three into another var.

Regardless, "print a message" could be a subroutine that expects the message to be in a string like ms$.  The subroutine nestles said content into a nice message box.  Use len(ms$)/2 to pad it on either side with spaces... etc.

 

Quote

If you don't mind, I'd like to try my hand of recombobulating this so I can store my own ship information

Of course!  I hope the example helps.  I've started encoding as much data as possible into binary files for storage in high RAM.  I've even got a couple of trade tables leveraged that way.  Anything to help organize things better.

(And I see you've found some errors in my data.  I will have to fix those!)

I've also shoved the star charts into binary files for loading into high RAM.  THAT took up on the order of 20K of low RAM for one measly subsector... totally unacceptable.

 

 

Now I've got a question for you.  Your screen presentation has several views on it.  I was wondering if you do what I've been doing -- that is, brute-forcing the data to specific places on the screen, all at once -- or if you've figured out a more elegant, flexible, maintainable way to present data?

Because I'm kind of stuck on the data view.  I've got this pretty set of "portal"-like areas, into which I very very carefully try to render data, and it's going to drive me mad having to keep track of cursor positions.  I need to separate the concerns, so that I have loose coupling but high cohesion.

 

Screen Shot 2020-10-07 at 9.51.09 AM.png

Edited by rje

Share this post


Link to post
Share on other sites
Posted (edited)
On 10/6/2020 at 9:19 AM, Starsickle said:

I'm looking it over - the hardest part of all this really is that everything is in 2 letter variable names - but I can see you start at the starting address (AD) and allocate a specific amount of bytes to the name (24).

First poke unknown - assuming it's a sentinel value or some other control value.

Next is the quick ship profile, which is a Single character Archetype with 5 characters (uhh, a few have 6...). This is pretty slick, as it can be used for more procedural generation and representation.

...

Reading sequentially is a pain and N data lines grows, but perhaps, in known and well-defined cases, an ID can be assigned that matches a memory address for direct access, sorting, etc?

Would love to have this sort of layout with the ability to highlight and select a line by inverting the color of the currently selected line. This is why I have colors set in variables along with their inversions.

Inverting the text is a GREAT way to show selection, old school and effective.  I'll keep that in mind, as well.

By the way, the first POKE sets the view to Bank 1.  It doesn't affect the LOAD... but it's needed for all the PEEKing.

And yeah, if you can map an ID directly to a memory address (or use known indexes... item 5 is always at offset 5 x 64 or etc), then that works.  If you're BORED you can pre-parse records with varying lengths, and store the addresses into a BASIC array.  I prefer not to do that if possible!

(P.S. "sorting" would probably be exactly that: building a BASIC array of addresses based on some sort criterion).

 

 

 

Edited by rje

Share this post


Link to post
Share on other sites
Posted (edited)

This is how I'm doing it. I have plans for a full color mode someday, but today is not that day. For now, I simply GOSUB to invert colors, but storing as 4 variables makes it more obvious what's happening.

10 FC%=5 : BC%=0 : FI%=0 : BI%=5 : COLOR FC%, BC%
70 REM //SUBROUTINE - SET COLORS TO STANDARD
71 COLOR FC%, BC%
72 RETURN
73 REM //SUBROUTINE - SET COLORS TO INVERTED
74 COLOR FI%, BI%
75 RETURN

 

1 hour ago, rje said:

Now I've got a question for you.  Your screen presentation has several views on it.  I was wondering if you do what I've been doing -- that is, brute-forcing the data to specific places on the screen, all at once -- or if you've figured out a more elegant, flexible, maintainable way to present data?

For me, important cursor positions for LOCATE are documented within each subroutine. What's more valuable - A REM line of documentation or N pairs of integer variables? You at least need the upperleft corner of each window you write characters in, right?

Your MFD window is very large for something that is presenting options for the player. You have about 24 characters of room before an even border with the top windows. Split that window, and make the lower right a text spam message queue or a some window to present description or data. Perhaps a mode tracking variable for the menu state you are currently in, and after that just use "1110 GET A$: IF A$ = "" THEN 1110" and more to control the input handling immediately instead of risking errors. In UX thought - every action (even in error) the player takes should invoke some kind of feedback from the game - so that has to go someplace.

 

Edited by Starsickle

Share this post


Link to post
Share on other sites

Problem with the switch statement from earlier:

21 ON CS% GOTO 25,1500,2000,9997,30000
22 GOTO 31110

Presumably, you cannot initialize CS to 0 because once the file is loaded, CS is overwritten. but what happens right now is that the program slips through to 22 (Error). If 22 was not there, it slips to 25, which sets CS to 1, and eventually leads to a REDIM error.

How do I use this correctly? Apparently the implicit 0 does not trigger 25...

 

Share this post


Link to post
Share on other sites
23 minutes ago, Starsickle said:

This is how I'm doing it. I have plans for a full color mode someday, but today is not that day. For now, I simply GOSUB to invert colors, but storing as 4 variables makes it more obvious what's happening.


10 FC%=5 : BC%=0 : FI%=0 : BI%=5 : COLOR FC%, BC%
70 REM //SUBROUTINE - SET COLORS TO STANDARD
71 COLOR FC%, BC%
72 RETURN
73 REM //SUBROUTINE - SET COLORS TO INVERTED
74 COLOR FI%, BI%
75 RETURN

 

Elegant and very clear!  Thank you.

 

Quote

For me, important cursor positions for LOCATE are documented within each subroutine. What's more valuable - A REM line of documentation or N pairs of integer variables? You at least need the upperleft corner of each window you write characters in, right?

Yes - I store the upperleft, the width, and height.

You've made me realize that some of my windows are very specific to the data being rendered: the status line at the top is ALWAYS going to be an alarm bar with the same component groups listed.  While the actual component groups may change ship by ship, the nature and form of the data will always be the same.  So there could be coupling of data with presentation.

 

Quote

Your MFD window is very large for something that is presenting options for the player. You have about 24 characters of room before an even border with the top windows. Split that window, and make the lower right a text spam message queue or a some window to present description or data. Perhaps a mode tracking variable for the menu state you are currently in, and after that just use "1110 GET A$: IF A$ = "" THEN 1110" and more to control the input handling immediately instead of risking errors. In UX thought - every action (even in error) the player takes should invoke some kind of feedback from the game - so that has to go someplace.

I'm agreeing with the tendency to move to use GET instead of INPUT.  

I was kind of thinking that I need to swap out the middle windows based on the game's current mode/state.  User input changes that state; windows reorganize accordingly.

 

Share this post


Link to post
Share on other sites
Posted (edited)
49 minutes ago, Starsickle said:

Problem with the switch statement from earlier:


21 ON CS% GOTO 25,1500,2000,9997,30000
22 GOTO 31110

Presumably, you cannot initialize CS to 0 because once the file is loaded, CS is overwritten. but what happens right now is that the program slips through to 22 (Error). If 22 was not there, it slips to 25, which sets CS to 1, and eventually leads to a REDIM error.

How do I use this correctly? Apparently the implicit 0 does not trigger 25...

I wrote a quick thing that shows that when CS%=0, it never takes a branch, assigned or no.  Is that what you're seeing?

You probably want something interesting.  Otherwise you'd force CS% into the range you expect with something like

 if CS%=0 then CS%=1 

Two ways to store game state are (1) a SEQ file, which requires an SD image mounted; (2) POKEing it into your favorite RAM bank (Ha!).  A third, uncool, way to store game state would be to VPOKE it into VERA RAM.  I've used video RAM before to store game data, like a poor man's steganography.  I don't recommend it unless you have a reason to use it.

Bank 1 might be the easiest way to preserve state.  One byte for CS%... and 8191 more bytes for more stateful data.  Then use Bank 2 and up for other data...
 

rem get ready to load a different BASIC program.
rem because this blows away low RAM, we gotta
rem save state in high RAM.
poke $9f61,1 :rem switch to bank 1 
poke $a000, cs% and 255 :rem store the low byte
...load another BASIC file...

...at the beginning of the just-loaded BASIC file...
poke $9f61,1 :rem switch to bank 1
cs% = peek($a000) :rem fetch state
on cs% goto ....

 

Edited by rje
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)

The good news: It works again. The game is playable again.

The Bad news: I think only the DATA segment of memory is preserved between LOADs.

RetroTrek-WIP20.thumb.png.b84e27439c196d8e8e507258cd118ccd.png

So in this pic, you can see that some of the string literals did not survive, neither did any of the dimension-ed arrays before they were filled in the Data Initialization file.


But...curiously:

3106 PRINT " RETROTREK - "VN$"          STATUS: "SU$"          RAM: "RF$

Of course, the debug output for ship data DID survive. Well, mostly. The IFFs did not survive, the ship names did not survive. Yikes. I happy the program can execute correctly, but now I have this new problem that defied expectations.

Edited by Starsickle

Share this post


Link to post
Share on other sites
On 10/8/2020 at 7:10 AM, Starsickle said:

The good news: It works again. The game is playable again.

The Bad news: I think only the DATA segment of memory is preserved between LOADs.

...;

So in this pic, you can see that some of the string literals did not survive, neither did any of the dimension-ed arrays before they were filled in the Data Initialization file.


But...curiously:


3106 PRINT " RETROTREK - "VN$"          STATUS: "SU$"          RAM: "RF$

Of course, the debug output for ship data DID survive. Well, mostly. The IFFs did not survive, the ship names did not survive. Yikes. I happy the program can execute correctly, but now I have this new problem that defied expectations.

When chaining programs, you have to make sure that the first program is the largest, in terms of code size. This is because BASIC locks in the beginning and end of variable memory when you RUN a program, and it does not move variables when you chain to another program. 

So if you start on a program that's 12K, then load a program that's 15K, the 15K program will overwrite some of your variables. 

So make sure your startup program is the largest. 

Share this post


Link to post
Share on other sites

 

1 hour ago, TomXP411 said:

When chaining programs, you have to make sure that the first program is the largest, in terms of code size. This is because BASIC locks in the beginning and end of variable memory when you RUN a program, and it does not move variables when you chain to another program. 

So if you start on a program that's 12K, then load a program that's 15K, the 15K program will overwrite some of your variables. 

So make sure your startup program is the largest. 

RETROTREK.BAS = 40kb.

DATAINIT.BAS = 4kb.

Uhhhhhh.....

Share this post


Link to post
Share on other sites

Yeah. I'm mystified. The main file clearly allocates more to memory in every regard than the data initialization file, so the explanation doesn't seem to fit.

No matter what the case - I need to fix this, and I don't know how. Design wise, everything important is allocated, and if the program can't even correctly execute a random string literal after going back into main fail execution? I am clueless as to what to change or how to make this work.

Share this post


Link to post
Share on other sites

Hmm... does your main program actually initialize any variables before LOADing the variable init program?

 

Share this post


Link to post
Share on other sites

This is the majority of the block before the program handles its title screen input, after which it loads DATAINIT.PRG:
 

18 REM // DEPENDING ON GAME STATE: INIT DATA, MAIN LOOP, EXTRA, TERMINATE
29 REM //SETUP ENGINE, START GAME, MAIN LOOP, (TBD), END PROGRAM
20 IF CS%=0 THEN : CS%=1
21 ON CS% GOTO 25,1020,2000,9997,30000
22 GOTO 31110
25 REM //ENGINE SETUP AND INITIALIZATION=====================================
26 REM //NOTE: NEVER EVER OVERWRITE THESE!
27 CS%=0 : PL%=27 : REM //CONTROL STATE AND PREVIOUS LINE
28 VN$="0.0.1" : VD$="SEPTEMBER 14TH, 2020"
29 FC%=5 : BC%=0 : FI%=0 : BI%=5 : COLOR FC%, BC%
30 SX%=640 : SY%=480 : REM //SCREEN SIZE
31 DIM MSGQ$(20)    : REM //OUTPUT SPAM QUEUE
32 QH=1 : QS=0        : REM //QUEUE HEAD, TAIL, AND STATUS
33 REM //===PROGRAM START=========================================
34 CLS : GOSUB 3120
35 LY%=43 : LX%=1 : GOSUB 1100
36 INPUT "PLEASE MAKE YOUR SELECTION"; MNU$
37 GT$ = MNU$
38 DEF FN FR(X)=FRE(0)-65536*(FRE(0)<0) : REM //COLLECT GARBAGE AND FREE BYTES
39 GOSUB 73 : POKE $30D,0 : POKE $30E,50 : SYS $FFF0
40 RF$ = STR$(FN FR(0))
41 PRINT FN FR(0);"BYTES FREE" : GOSUB 70
42 REM //===PROGRAM DATA ALLOCATION=================================
43 DIM GD%(10)       : REM //GAME DATA - SEE README.
44 DIM RGN$(9,19,2)     : REM //SPACIAL REGION - SEE README.
45 REM DIM SCTR$(9,9,2)     : REM //SPACIAL SECTOR - SEE README.
46 DIM SPCS%(7,5) : REM //SPECIES LIST - SEE README.
47 REM //DIM PLANETS$(13,3)     : REM //CONTAINS PLANET NAME, TYPE, IFF
48 DIM SHPS%(10,16) : DIM SID$(10,2)    : REM //SHIPS LIST AND SHIP NAME AND IFF
49 NS%=0 : GD%(1)=10 : GD%(0)=0
50 SU$="GRN" : REM //SHIP STATUS
51 GA$ = "*********************************************************"
52 GB$ = "*                                                       *"
58 CS%=2
59 GOTO 1000

DATAINIT.PRG is less than 100 lines with whitespace, but both creates DATA and shoves values into the various arrays.

As reminder: check above image some posts up to see what is happening.

A good but laborious experiment is if I were to change the line numbers of the DATAINIT.PRG program to never touch any of the numbers in the main file? What would happen? Would the string literals upon execution be restored?

Edited by Starsickle

Share this post


Link to post
Share on other sites

Disaster has Struck - A PSU incident has caused my Data HDD to stop responding completely, meaning it's effectively dead. A lot of recent projects since June have been busted, which means basically all of them. RetroTrek will have to be started over from Scratch, as I have lost even the Code, Design work, and Documentation.

I am currently in Crisis Response Mode, but I have no doubt I will need at least a good hug in a few days.

Share this post


Link to post
Share on other sites

That's bad... I remeber one of my HDD died, and data was so valuable to me that I handed it over to data recovery service. My be you should do the same?

Share this post


Link to post
Share on other sites

Shit man that sucks.

I had a (tiny) share of bad luck happen to me in the past, and since then a rsync job runs every 4 hours to backup all my stuff to a different machine at least...

Share this post


Link to post
Share on other sites

There is some hope: I ordered a replacement PCB. An affordable waste if it doesn't work, but I believe I can do it myself.

Needless to say, despite the small preparations I've made, I'm going to be more aggressive about backing up data.

  • Like 2

Share this post


Link to post
Share on other sites

Remember, GitHub is free, and you can maintain private repos. Never depend on a single device to store your code and assets!

Share this post


Link to post
Share on other sites

q@Starsickle I'm sorry to hear what happened, sucks indeed. Let us know if the HDD can ve revived.

I'd also like to backup SlithyMatt. Git (and GitHub) is a great way to manage and backup your coding work. I had to use it for my work - and I bet you, I wouldn't have done it on my own. Even the simplest things required a lot of googling - sometimes even now. But it's worth it. The whole project history gets saved and you can visit any point on the timeline. It's great. Now I use it for my hobby stuff, too.

  • Like 1

Share this post


Link to post
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.

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.


×
×
  • Create New...

Important Information

Please review our Terms of Use