Jump to content
IGNORED

Question: How do you move lines of code/subroutines around?


CDS Games

Recommended Posts

Sorta stymied here.

 

I've been hacking in Stella because it's quick and easy (no need to compile, etc.). It's worked really well for changing things that are already in place.

 

But now I'm finding myself needing to free up space and move code around by deleting/moving lines.

 

I can't do that in Stella, can I?

 

And just generally, how does one go about moving subroutines around? Does deleting, say, a couple of lines at F100 and moving everything up break all the subsequent JMPs, JSRs and immediate addresses, so then you have to go back and fix everything? Or does the compiler have a way of fixing that automatically?

 

Trying to be as low-impact with code as possible as I don't really know what I'm doing. :)

Link to comment
Share on other sites

I've used the debugging in recent Stella versions but not in that way.

 

You can't just move stuff around as you say - the JMP/JSR stuff is a problem but little in comparison to data that can be referenced indirectly. Such references aren't easy to pick up as generally you'll have 2 load/store operations to zero-page and the reference will usually be by LDA (zp),Y instructions.

 

Using a disassembler utility to generate source then doing more complex hacks and reassembling would be the easy way to do what you want.

But even then, the indirect references aren't taken care of, you have to go through and find them yourself.

 

An alternative might be to patch in JMP instructions and put your hacks before the start of the ROM. Although that's only going to be easy for smaller ROMs that don't occupy the entire 4K contiguous space available.

 

Additional to that problem, you'd also be changing the # of cycles used but that would mainly be a problem during the active display.

Since most game logic stuff takes place offscreen/VBlank you could probably get away with it.

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

Jumping around gets very messy (especially if you only need a couple of bytes' difference for the change)...and makes the hack increasingly difficult to maintain - bugfixing an initially unseen problem can be a major pain.

 

What game are you altering? Chances are, it's been disassembled with some or complete reverse-engineering already if it's a popular one.

Link to comment
Share on other sites

Hmm, I see. Yeah, a few bytes I can sometimes scrounge here or there, and I've also been NOP'ing out routines I don't need, but when I need the existing routines then I get stuck. :)

 

I'm shoehorning an old game idea into Atlantis as a long-term project, and luckily I found a PAL hack/disassembly which helps enormously. With that I can sorta piece together what I need to do, it's just a matter of finding the room.

Link to comment
Share on other sites

This one is very easy to bank if you want to double (or quadruple) the rom size. Basically, everything from $124C to $1812 (including subroutines $1EB1 & $1FC2 and all display data) can go in one bank and everything else goes in another. Nothing shared in the original game IIRC, so it's easy to separate.

 

Or, you could try your hand at making the existing routines more efficient for a 4k hack (altering JMPs to use unconditional branching instead whenever possible, reusing identical code that is branched over, etc). There's plenty of examples in any game I've run across.

  • Like 1
Link to comment
Share on other sites

I remember doing the 480i + animation hack for Stellar Shuttle on the 8-bit computer.

I did it as a series of patches. It became somewhat cumbersome and hard to debug.

That will be the same for anything once it gets beyond a couple of hundred additions/changes or even less.

 

It's much easier if you can produce some reverse-engineered source. I've done it various ways on other things and it can be a rapid process with the right tools.

The problem with 6502 code is the indirect stuff - even the best AI won't be able to work out what z-page stores are just that and what ones are setting up indirect pointers which might move around when you change stuff.

 

But Stella can even help out there. Thanks to the way it deduces code and graphic sections, it can help out - through deduction you can often work out what is setting up a pointer and what isn't.

 

Once you're done there, start modifying and the job will be much easier than hacking.

  • Like 1
Link to comment
Share on other sites

Atlantis is one of the easier games to figure out in that respect...almost everything is handled independently (that's why it has such a large kernel for it's sparse display).

The program only deals with LSB's for indirect addressing, so bitmaps of a given type (ship, city, and digits) must exist on the same page as their relatives. Digit bitmaps are the only ones which must begin on a page break (using the existing routines), because the "multiply by 10" subroutine does not adjust for an offset there.

 

In the original (unhacked) game, there are 2 bytes of free ROM (the two zeros before the $1C00 page break), and 2 bytes of free RAM ($9D and $A8 are never touched by anything after the cold start routine clears them)...but much more can be reclaimed:

 

Like many games that don't require many variables, the indirect address ram table used for printing scores are right at the top of user ram ($80-$8B). Since blank scanlines are dumped to the screen before printing the score anyway, you could save much of this ram by moving these variables to the END of user ram...and calculate their addresses during the kernel instead.

 

The same applies to indirect pointers for city gfx and variables holding the seabed gfx...those could be setup during the kernel using temp ram instead of dedicated ram. Even better would be to scrap the use of variables completely...so the PF seabed can be less-blocky.

 

Interested?

  • Like 1
Link to comment
Share on other sites

Thank you both, that's very helpful!

 

Nukey, the seabed being less blocky sounds like a fantastic deal. I've made one of the cities a large dome--but then that forces me to have the playfield be the same on that entire level, which is pretty limiting.

 

Maybe this would be a good opportunity for me to learn bankswitching rather than trying to squeeze more out of 4k, as I'd like to have separate color and other data for the different kinds of enemy ships, and I have to add some extra features to the dome and guns.

 

Is there a good bankswitching tutorial somewhere?

Link to comment
Share on other sites

Bankswitching just means a different part of ROM becomes visible at a given range of addresses, there's various schemes for the 2600.

 

Switching banks is usually a case of just accessing a particular memory location and the hardware in the cart does the rest.

 

From a programming perspective it can get tricky with references to locations, many assemblers provide directives like .BANK to inform the assembler that another bank is in force, allowing assembly to the same addresses again.

 

Additionally you lose a certain amount of cycles when doing the bank switch so it's worth planning what bits of the program are to go in the new bank.

A good idea might be to move large portions of the VBlank stuff - but the other consideration of course is that you might have data that needs to be accessed and it might need to reside in the same bank.

But there's various schemes around - some switch the entire 4K where others allow e.g. 2K static and 2K switchable. You might want to select a scheme that suits your needs. Of course the other consideration is how easy implementing the given scheme is on a real cartridge.

 

I tried a Google Search with "atari 2600" "banking schemes" and it came up with some good links.

  • Like 1
Link to comment
Share on other sites

I dunno. But it's pretty easy once you get the hang of it. In practice, it's just a lot of searches followed by cut/paste operations (look for any referenced labels in the code you are pasting to the alternate bank, and make sure they exist in that same bank. If something is called by both, make a duplicate).

 

As for when to bankswitch, all you need to find is a common spot that is used every frame regardless of whatever else is happening. Searching around INTIM/TIMINT loops is a good guess. These are often used to take up remaining cycle time before (and sometimes after) a display is drawn. Then just add JMP's to get to 'em. I prefer to paste the bankswitches right at the start of rom in each bank...and use addresses $1FF9-$1FFB to hold the JMP to a cold start routine in the second bank ;-)

 

Examples below:

The first is the original unaltered game after disassembly and editing in label names for stuff that the machine misses. I do the latter for ram addresses, too...so I can move variables around or delete unnecessary ones.

 

The second assembly has the score printing setup moved to the display kernel. Not only did this save ram (as described above), but streamlined the program in a couple other areas too. The removed lines from the original code is left in place and commented-out (so you can easily see a comparison from the original).

 

The third assembly has F8 banking implemented...all the disabled stuff in the previous assembly removed (so you can compare the second and third assemblies more clearly).

Atlantis - original.asm

Atlantis - Step1.asm

Atlantis - Step2.asm

  • Like 1
Link to comment
Share on other sites

Bugfix...a branch was too low. Should be between A1+1 and A1+2:

 

 

L1CDB:
       sed                            ;2
       bit    rD3                     ;3
       bmi    L1CFE                   ;2
       clc                            ;2
       adc    rA1+1                   ;3
       sta    rA1+1                   ;3
       bcc    L1CFE                   ;2
;skip the rest if no carry into 10k (thus no credit is earned)
       lda    rA1+2                   ;3
       adc    #$00                    ;2
       sta    rA1+2                   ;3
       lda    rA1                     ;3 original game also kept track of score rollovers
       adc    #$00                    ;2 ...shown as the lower 2 digits of the score.
       sta    rA1                     ;3
       inc    rF8                     ;5 increase # of credits (up to 255 in reserve!)
L1CFE:
       cld                            ;2
       rts                            ;6

 

No idea if the game was intended to keep track of up to 99 score rollovers...as this is not mentioned in the manual. However, this IS recorded in the original unaltered game.

Link to comment
Share on other sites

  • 3 weeks later...

Thanks Nukey! Gonna take some time and study this...it'll take a while to sink in. :)

 

Meanwhile, I've been able to patch in snippets of code to your 8k Superman and it's been going remarkably well. Just find some free space, add a JMP and then JMP right back again. I can definitely see how it would make troubleshooting a real pain though...luckily I don't anticipating having to do a lot of it.

Link to comment
Share on other sites

'Course, that's just my opinion of how to hack bankswitching into a completed program - split the display from the rest and treat the whole thing as a contiguous 8k (or 16k, etc). If a program is constantly checking for a specific criteria, it could work out better to deal with that in a separate bank.

 

If you are working with the disassembly of Superman, there's no real need to use JMPs (unless a branch gets out of range or something). The assembler will shift everything around for you to make room for your additions.

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