Jump to content

The Southsider

  • entries
    81
  • comments
    767
  • views
    138,550

Limits of Scale


Guest

891 views

As someone who has been taught Software Engineering, there is something both refreshing and frightening about assembly programming on the Atari. On the one hand, all of the standard rules about abstraction, modularity, generic data structures and exception handling go out of the window as there is simply not enough memory or cycles. On the other hand, the resulting code ends up becoming increasingly complex and tangled in the quest for space and efficiency!My PoP project is now the largest assembly program that I have ever written, much larger already than Hunchy II. However, I am also finding it increasingly difficult to make changes to the game. The problem is that the code is strewn with magic numbers and bits, and changing one part of the code can have unforseen consequences in another part. Sometimes, even changing the length of a particular bit of code can cause breakage due to memory alignment problems. The result is that updates to the code are becoming slower and slower. I am also rapidly running out of space for the code - the 6K provided by the Supercharger doesn't go all that far in a game like this.Anyway, I don't want to be too negative as things are not desperate yet! It is just that I can see a big code optimisation and reshuffling phase will be required soon if the game is going to progress much further. The latest update to PoP is attached to this blog entry. The new features this time are that the falling tiles now disappear when you step on them (after a short delay), and the switch in the final room now opens the door (see image below). I haven't yet fixed the collision detection with the doors, but I have a possible solution worked out.post-4520-1098389961_thumb.jpgUp to now I have been modelling the game around the SNES version (probably the best conversion). However, this is not the ideal version to use as it contains extra levels and graphics not found in the other conversions. I have now aquired the Gameboy Color version of the game (thanks to eBay) and I will be using this instead. The Gameboy version is probably the least advanced, and is a much closer fit to the capabilities of the Atari. The switch to the Gameboy version is already yielding some useful clues. It contains a possible solution to the falling tile problem that I described last time. Instead of showing the tiles falling the whole way down the screen, the falling tile is shown briefly at a fixed position under the original, and then removed entirely. This effect shows that the tile is falling without requiring any complex animation, and it also appears reasonably convincing. I am planning to implement this in my next release to see how it looks - I just need to decide if I want to use the sprites, missiles, ball, or PF. As discussed previously, all of these have advantages and disadvantages, and I'm not yet clear which will be the best compromise.ChrisP.S. Looking back at my earlier "Multi-Tasking" entry it appears that some good progress has been made, although it doesn't always feel like it! I have basically accomplished tasks 1,2,4,5,6,7, and 10. The big issues remaining now are really just the animations and guard movements, but I know these are going to be tough!

15 Comments


Recommended Comments

The stairs look good. It's amazing how much can be accomplished with so little graphics capability as long as you exercise a little creativity. The mind really fills in the rest.

Link to comment
The stairs look good.  It's amazing how much can be accomplished with so little graphics capability as long as you exercise a little creativity.  The mind really fills in the rest.

 

Thanks - I also think the stairs turned out well. I'm not sure about the closed final door, but it can always be tweaked later.

 

Chris

Link to comment

Hi there!

 

Great update!

 

So once this game is done, you can recycle larger parts of the engine for Castlevania 2600. :x

 

I completely see your problems described above. I ran into all sorts of computing brickwalls with Star Fire back then, which constantly forced me to balance things out. Move tasks from the overscan to the VBLANK and back, optimize here, optimize there, trade ROM for cycles here, trade RAM for ROM there and vice-versa... all until the final balance/compromise is found :sad:

 

Greetings,

Manuel

Link to comment
Great update!

So once this game is done, you can recycle larger parts of the engine for Castlevania 2600. :x

I completely see your problems described above. I ran into all sorts of computing brickwalls with Star Fire back then, which constantly forced me to balance things out. Move tasks from the overscan to the VBLANK and back, optimize here, optimize there, trade ROM for cycles here, trade RAM for ROM there and vice-versa... all until the final balance/compromise is found :sad:

 

Hi Manuel,

 

Thanks - by the time this game is finished I think I will need a long break from 2600 programming :) I have now convinced myself that everything about the game is feasible on the 2600, but I can see that it is going to be a real struggle to fit it all in!

 

Chris

Link to comment

One thing that might help is to create macros for "sbeq", "sbne", "sbcs", "sbcc", etc. which generate the indicated branch but provide a warning if the instruction following the branch is not in the same page as the target. In many spots in your code, this won't matter (so just use ordinary branches) but where it does matter, you can use the macros and be warned if things shift in a bad way.

Link to comment

Some tips for avoiding some of the problems you've run into:

 

1. Magic numbers - use equates, document them & only use them for the one purpose. Same thing goes for zero page variables.

 

2. Shifting code or data - figure out what code & data needs to be at one spot (i.e. on a single page) and lock it down with an ORG. For both Skeleton/Skeleton+ and Leprechaun, the main kernel starts at $1000, just so as I added to the rest of the game it wouldn't move.

Link to comment
One thing that might help is to create macros for "sbeq", "sbne", "sbcs", "sbcc", etc. which generate the indicated branch but provide a warning if the instruction following the branch is not in the same page as the target.  In many spots in your code, this won't matter (so just use ordinary branches) but where it does matter, you can use the macros and be warned if things shift in a bad way.

 

Sounds like an interesting idea - you don't happen to already have these macros defined as I find the DASM macro language rather cumbersome to use. Could the same technique be used for loads, as this is where I seem to have the most problems (when the data wraps over page boundaries)?

 

Some tips for avoiding some of the problems you've run into:

1. Magic numbers - use equates, document them & only use them for the one purpose.  Same thing goes for zero page variables.

2. Shifting code or data - figure out what code & data needs to be at one spot (i.e. on a single page) and lock it down with an ORG.  For both Skeleton/Skeleton+ and Leprechaun, the main kernel starts at $1000, just so as I added to the rest of the game it wouldn't move.

 

Thanks for the tips. I find ORGs to be a bit of a pain when changing the code as I often have to go through and renumber a load of them by hand.

 

Chris

Link to comment
Thanks for the tips.  I find ORGs to be a bit of a pain when changing the code as I often have to go through and renumber a load of them by hand.

I'll assume you already know about the "align" assembler command.

 

What I generally do when writing 2600 code is to start out by putting *everything* in subroutines. Everything. It makes debugging, editing, and moving stuff around so much easier.

 

Of course, eventually you run out of time/room and you have to move things out of subroutines, but I have found that starting out that way makes things much much easier.

 

Just my two cents.

Link to comment
Sounds like an interesting idea - you don't happen to already have these macros defined as I find the DASM macro language rather cumbersome to use.

 

It's pretty straightforward.

               mac     sbpl
               bpl     {1}
               if (* ^ {1}) & $FF00
               echo "PAGE CROSSING","WARNING ",{1}," at ",*
               endif
               endm

For checking data, use

               mac     pcheck
               if (* ^ {1}) & $FF00
               echo "PAGE CROSSING","WARNING ",{1}," at ",*
               endif
               endm

and, after each data item, do a pcheck with a label to the start. The warnings are a little less than helpful as simply output, but you can search for them easily in the .lst file.

Link to comment
I'll assume you already know about the "align" assembler command.

 

Instead of using 'align', I like to use a macro. The macro can take an argument for how much space is needed (so it doesn't advance a page if it doesn't have to), and can output to the screen/list file how much space it's skipping.

Link to comment
What I generally do when writing 2600 code is to start out by putting *everything* in subroutines.  Everything.  It makes debugging, editing, and moving stuff around so much easier.  Of course, eventually you run out of time/room and you have to move things out of subroutines, but I have found that starting out that way makes things much much easier.

Just my two cents.

 

I tend not to use subroutines very much as I always think of them as an expensive operation, particulay when you are using the stack pointer for other things and you have to save/restore. However, I probably should make more use of them in my code to eliminate duplication ... it is the old cycles vs memory tradeoff :sad:

 

It's pretty straightforward <snip>. For checking data, use <snip> and, after each data item, do a pcheck with a label to the start.  The warnings are a little less than helpful as simply output, but you can search for them easily in the .lst file.

 

Thanks - these look like some really useful macros! I do use ALIGNs a lot, but they get in the way when you want to shift things around. The macro approach should make code reorganization much less tedious.

 

Chris

Link to comment
Really enjoying the Blog so far.  Best of luck!  :sad:

 

Thanks - I'm always surprised that people are actually reading this stuff! I'm getting a bit frustrated by the project recently as you can probably tell, but hopefully this will pass.

 

Chris

Link to comment
Thanks - these look like some really useful macros!  I do use ALIGNs a lot, but they get in the way when you want to shift things around.  The macro approach should make code reorganization much less tedious.

 

Another thing I sometimes do with macros: include all the .DB statements within macros (rather than having them operate directly), so that then I can rearrange the order in which items appear in the code without having to rearrange the .DBs themselves. This is especially handy if, as in many of my projects, some of the DBs are in separate .include files. I can .include all of the DB-containing macros at the start of my source file, and then arrange the actual data as I see fit.

 

Another way I use this technique is to produce an "initialized data" segment. After the minigame contest, I'll post my code to SDI to show how that works. It's very easy in many 2600 projects to waste a lot of code with "lda #this/sta here/lda #that/sta there" etc. Four bytes for each location that needs to be initialized. Using an "initialized data" segment reduces this to one byte per byte plus a small "loader":

 ldx #IDATA_END-IDATA_START-1
lp:
 lda IDATA_ROM,x
 sta IDATA_START,x
 dex
 bpl lp

Nine bytes. So if there are four or more bytes that need initialization to different values, it's a win (13 bytes vs. 16). Even if two of the bytes need the same value, it's still a win. And of course, if more data need to be initialized, it's an even bigger win.

 

Indeed, if ZP RAM is abundant, it can make sense to put small tables of constants into ZP RAM since doing so will save a byte of code space for every access to one of them. And if an address is used three or more times with abs,y addressing mode, it can be changed to (zp),y saving a byte per instance at a total cost of two bytes.

 

Of course, in most cases ZP RAM won't be more plentiful than code space. But knowing how to take advantage of initiailized data is still very useful.

Link to comment
Another thing I sometimes do with macros: include all the .DB statements within macros (rather than having them operate directly), so that then I can rearrange the order in which items appear in the code without having to rearrange the .DBs themselves.  This is especially handy if, as in many of my projects, some of the DBs are in separate .include files.  I can .include all of the DB-containing macros at the start of my source file, and then arrange the actual data as I see fit.

 

Another way I use this technique is to produce an "initialized data" segment.  After the minigame contest, I'll post my code to SDI to show how that works.  It's very easy in many 2600 projects to waste a lot of code with "lda #this/sta here/lda #that/sta there" etc.  Four bytes for each location that needs to be initialized.  Using an "initialized data" segment reduces this to one byte per byte plus a small "loader":

 

Thanks for the suggestions. I did something similar to the initialized data segment in Hunchy 2 (on your suggestion). I haven't done this in PoP yet, but I will be doing a big reorganisation as soon as I have a free evening.

 

Chris

Link to comment
Guest
Add a comment...

×   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...