25 November 2018, 00:46 | #1 |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
Finding start and end of my program's memory?
Hi folks, I have an Amiga C newb question I would appreciate some help with.
I'm porting a UNIX game from forever-ago to the Amiga. I was trying to port it to the CBM 700 machines, but turns out it needed 128k, and I haven't figured out how to get past the 8 bit banking stuff. Anyway, the game runs and everything, but in looking at what save game/restore game did, I think they just saved off a chunk of the computer's memory as raw data, and then read it back in. That's how the top 10 scores work, for example (simpler, as it puts it into a struct, so easy to get start/end addresses). For saving the game, it uses sbrk(0) to get the end of the program's memory. sbrk() is part of <unistd.h>, which doesn't have an Amiga counterpart. https://linux.die.net/man/2/sbrk It uses the address of a certain variable as the start point. I'm not entirely sure how it determined, in the original UNIX build, that that variable was the first one to be stored in memory. I don't have the make scripts but I guess it just called that file first. I'm not having good luck googling this. Is there a clever Amiga way to find out the end of the program's memory? I do have some malloc going on, so I don't think I can try to cleverly locate one variable as the last thing allocated, and use that. |
25 November 2018, 01:19 | #2 |
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,539
|
No. You can't. Amiga ist not Unix. All processes share the same memory and memory allocations may happen at many different locations.
|
25 November 2018, 02:02 | #3 |
Registered User
Join Date: Jan 2008
Location: Warsaw/Poland
Age: 56
Posts: 2,039
|
If I understand correctly what you need, you can deabsolute all (or only one?) address when program is saving, and reallocate these addresses when save game is loading. Some Amiga games used this method. Of course you must known which pointers must be de/reallocated.
|
25 November 2018, 02:20 | #4 |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
Thanks for quick answers. It helps, if only to stop me from wasting more time in going down this rabbit-hole.
I believe "bugger!" sums up my current feelings. Well, looks like I'm going to get intimately acquainted with the 500 structs and variables that need to be saved off and reloaded in order to 'save game'. Warty |
25 November 2018, 11:09 | #5 |
Total Chaos forever!
Join Date: Aug 2007
Location: Waterville, MN, USA
Age: 49
Posts: 2,200
|
If the structs and variables are allocated in the same order each time, I'd write a custom allocator that pads the beginning of the structure with SIZEOF *BYTE[] (which should be 4 bytes on current Amiga models) so that you can add a singly linked list node to the structs at allocation time.
All you have to do is either replace all calls to the malloc family of functions with macro calls or write a link library that diverts malloc and friends that links in before the C runtime libraries do. |
28 November 2018, 03:41 | #6 |
Registered User
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
|
I think I have divined the relevant chunks of data that would need storing and restoring. Let me list out the types, and ask for advice.
3 screen buffers worth of 80x23 chars. no problem. Code:
unsigned char bufPlayer[23][80] = {0}; /* maintain a 1k block of memory as a memory map to the player's version of the map */ unsigned char bufMonsters[23][80] = {0}; /* maintain a 1k block of memory as a memory map to the position of all monsters */ unsigned char bufMaster[23][80] = {0}; /* maintain a 1k block of memory as a memory map to the master game map */ unsigned char *pBufPlayer; /* pointer to a 1k block of memory as a memory map to the player's map of the floor */ unsigned char *pBufMonsters; /* pointer to a 1k block of memory as a memory map to the position of all monsters */ unsigned char *pBufMaster; /* pointer to a 1k block of memory as a memory map to the master map of the floor */ Code:
char whoami[80]; /* Name of player */ char fruit[80]; /* Favorite fruit */ Code:
int quiet; /* Number of quiet turns */ int purse; /* How much gold the rogue has */ int ntraps; /* Number of traps on this level */ int no_move; /* Number of turns held in place */ int no_food; /* Number of levels without food */ int no_command; /* Number of turns asleep */ int max_level; /* Deepest player has gone */ int max_hp; /* Player's max hit points */ int level; /* What level rogue is on */ int lastscore; /* Score before this turn */ int inpack; /* Number of things in pack */ int hungry_state; /* How hungry is he */ int group; /* Current group number */ int fung_hit; /* Number of time fungi has hit */ int food_left; /* Amount of food in hero's stomach */ bool notify; /* True if player wants to know */ bool amulet; /* He found the amulet */ bool after; /* True if we want after daemons */ Code:
bool ws_know[MAXSTICKS]; /* Does he know what a stick does */ bool s_know[MAXSCROLLS]; /* Does he know what a scroll does */ bool r_know[MAXRINGS]; /* Does he know what a ring does */ bool p_know[MAXPOTIONS]; /* Does he know what a potion does */ But it gets fuzzy for me once I get past the simpler stuff: Some structs with 2 int members (worst case, I unpack these and then repack the structs). Code:
coord oldpos; /* Position before last look() call */ coord delta; /* Change indicated to get_dir() */ coord ch_ret; /* Where chasing takes you */ Code:
char *ws_type[MAXSTICKS]; /* Is it a wand or a staff */ char *ws_made[MAXSTICKS]; /* What sticks are made of */ char *ws_guess[MAXSTICKS]; /* Players guess at what wand is */ char *w_names[MAXWEAPONS]; /* Names of the various weapons */ char *s_names[MAXSCROLLS]; /* Names of the scrolls */ char *s_guess[MAXSCROLLS]; /* Players guess at what scroll is */ char *r_stones[MAXRINGS]; /* Stone settings of the rings */ char *r_guess[MAXRINGS]; /* Players guess at what ring is */ char *p_guess[MAXPOTIONS]; /* Players guess at what potion is */ char *p_colors[MAXPOTIONS]; /* Colors of the potions */ char *a_names[MAXARMORS]; /* Names of armor types */ This is where I start to think ooh, this is going to be hard for me to do the allocations myself. Many of these have pointers that get allocated, etc. Code:
struct trap traps[MAXTRAPS]; struct thing player; /* The rogue */ struct stats max_stats; /* The maximum for the player */ struct room rooms[MAXROOMS]; /* One for each room -- A level */ struct room *oldrp; /* Roomin(&oldpos) */ struct object *cur_ring[]; /* Which rings are being worn */ struct object *cur_armor; /* What a well dresssed rogue wears */ struct magic_item p_magic[MAXPOTIONS]; /* Names and chances for potions */ struct linked_list *mlist; /* List of monsters on the level */ struct linked_list *lvl_obj; /* List of objects on this level */ struct delayed_action d_list[MAXDAEMONS]; I can do this with what I might call the "brute force" method: preceding through each of these variables, pushing them into a kind of stack in temp memory, saving out, freeing the temp memory, and doing the reverse on restore. But would a more experienced C programmer have a smart way to do it? This whole thing is basically a learning experience for me, so a brute force attack is something I'll do if I have to do to finish the project, but I'd rather spend the time (even if same amount of time or more) learning a better technique. Ideas so far: 1. alloc a big block of memory equal to the total I'll need (this is a known quantity, and doesn't change from game to game). Dole out the contents of this bit by bit as each variable is initialized. Basically get rid of global variables and replace them with pointers to slots internal to the superblock I allocated. Saving and restoring is then as simple as saving out that block, and reading it back in (if same session), or reading it back in to wherever it is in a new session. Either way, since it's one allocation as far as Amiga is concerned, it will be contiguous. 2. brute force. Use the variables exactly as I do now, but for save, allocate a single big chunk of memory, iterate through all the variables, pushing them into it, saving that chunk off, freeing the chunk. Do opposite on restore. 3. Get really smart idea here and do that instead. I haven't measured yet how much memory is needed, but I think it's 10k or less, so even on an unexpanded A1000, temporarily allocating another 10k of memory shouldn't be a problem. |
28 November 2018, 07:50 | #7 |
Registered User
Join Date: Jan 2008
Location: Warsaw/Poland
Age: 56
Posts: 2,039
|
For me exist 4 options, for relloc absolute save pointers.
1. Alloc extra memory for one full save, copy save game to this memory, deabsolute all necessary pointers, save this extra memory buffer as normal save. When saved game is loaded relloc all necessary pointers. 2. Deabsolute all necessary pointers on original save data, save this memory as normal save, later rellocate all deabsoluted pointers, continue playing game. When saved game is loaded relloc all necessary pointers 3. Add one extra longword to saved file. Store/place in this extra longword origin of saved game address. Save as saved game original save plus 4 bytes. When game is loading use this extra longword to deabsolute/rellocate all necessary pointers (included origin pointers too). 4. Modify using by game all game pointers from absolute memory to normal offsets. Nothing more is necessary for save/load game. Which is the best/easiest i dont know, i dont wrote programs in C. |
28 November 2018, 09:22 | #8 |
son of 68k
Join Date: Nov 2007
Location: Lyon / France
Age: 51
Posts: 5,355
|
Several times i've seen saved games containing irrelevant data, things that actually never changed.
So first thing i would do is checking, for each variable, if it really needs to be saved. Object names, for example, typically don't. So find out whatever is constant from game to game, and remove that out of the save list. If you have lots of strings that are constant, create an array of them and replace pointers to them by indexes into that array. Then you only have to save these indexes, not the strings themselves and it makes your life easier. Anyway, the amount of work to do depends on how much "clean" you want your code to be... |
03 January 2019, 11:24 | #9 |
bye
Join Date: Jun 2016
Location: Some / Where
Posts: 681
|
here's another more or less useful approach:
- write your own malloc - allocate one memory block for your malloc with a fixed address, large enough to be sufficient for the programs life time. - now your malloc knows the start plus the used size. simply save and load that area. cons: - your program won't start if that memory area isn't available... on a system with mmu you can fix this, by allocating an address outside of RAM/ROM and map real memory to that location. |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Tracking a game/program's memory usage | clebin | Coders. General | 5 | 01 December 2017 10:46 |
Disassembling - finding a game start address | SparkyNZ | Coders. Asm / Hardware | 12 | 05 March 2015 12:36 |
Best program to test memory? | Mustangjeff | support.Hardware | 5 | 05 October 2012 07:43 |
Strange problem with cyclone not honouring start/end track? | BarryB | support.Apps | 2 | 20 November 2011 01:15 |
Finding a bad memory chip | ehbowen | support.Hardware | 0 | 17 November 2007 23:55 |
|
|