Jump to content
IGNORED

Opinion: Order of routines in game?


Recommended Posts

Hey guys,

 

Am working on a new title, won't announce yet, but I'm just curious...how do you guys like to structure your bits of source?

 

As I am writing a 2K game, in the style of the original Atari launch titles, I am trying to keep everything in one file, with a main loop consisting of JSRs to each piece. This is followed by each of those routines, down the page. I am trying to keep things relative to their position on the TV frame, so I can keep my head straight.

 

How do you guys do it?

 

-Thom

Link to comment
Share on other sites

Back in the day we were working full time on a game so I kept the entire game basically in my head. I always kept a single sheet of graphic paper with every RAM location, its variable name and maybe a comment. Most development systems should easily be able to search for the routine name so it doesn't/shouldn't matter where the routine was located.

 

I may be wrong, but I don't think we used JSR very often. It's expensive time wise and uses some RAM. I think I preferred branches and there are several tricks with branches.

  • Like 1
Link to comment
Share on other sites

Yup, I am using Peter Dell's excellent WUDSN environment, which is built atop Eclipse (I usually use either vi or emacs, so this is...how shall I put it?...FANCY for me...) ;), which can keep track of all the routines across multiple files, etc, if wanted.

 

I am also using MADS as the assembler (which has all sorts of nice macro features, which...I don't think i'll use, this time around), but it has some very nice syntax...

 

the JSR pattern I'm using is literally the main loop from Combat...

 

So far, so good, i'm able to keep a stable 262 line frame, and i'm slowly adding and testing bits in (right now, am debugging the score output, next...the black/white switch support)...

 

My 6502 assembler experience has mostly been on the 400/800, and Apple ][, and this is the first full game that I'm writing for the system, so I am taking it slow and deliberate, poking at all the registers, as I flesh things out... It really is proving to be a very zen-like experience, sharpening the noodle. :)

 

-Thom

  • Like 1
Link to comment
Share on other sites

I'm still a 6502/Stella novice and I'm using JSRs extensively because (1) I get lost in my own code after a while and this helps me organize, and (2) I generally have an idea of what the subroutine will be, but have little idea of how to write it, so they make good placeholders. I figure I can re-arrange the code later if I start running out of space or time.

Link to comment
Share on other sites

At a higher level I use this loop:

  • Process User Inputs
  • Movement logic for things not controlled by a player
  • Prep for display (position all TIA objects, initialize values needed by Kernel, etc)
  • Kernel (your code that updates TIA scanline-by-scanline in real-time to draw the display)
  • Collision processing
and I start out with 1-3 in Vertical blank and 5 in Overscan as Vertical Blank has a lot more processing time available than Overscan. As the project progresses I may have to shift some of the routines around, such as putting user input processing at the end of Overscan after Collision processing has finished.

 

For my earlier projects I kept everything in a single file. Now I have to use multiple files as some of the code is in assembly(6507) and some of the code is in C (ARM).

 

For the reasons Dan mentioned, I usually limit the use of JSR to just those routines that are used from multiple places. Everything else is coded inline. That said, for my Collect tutorial I made extensive use of JSR because it makes it easier for a new programmer to follow the code.

 

Main:
        jsr VerticalSync    ; Jump to SubRoutine VerticalSync
        jsr VerticalBlank   ; Jump to SubRoutine VerticalBlank
        jsr Kernel          ; Jump to SubRoutine Kernel
        jsr OverScan        ; Jump to SubRoutine OverScan
        jmp Main            ; JuMP to Main

...

VerticalBlank:
        jsr Random
        jsr ProcessSwitches
        bit GameState
        bpl NotActive       ; skip timer and joystick if game is not active
        jsr UpdateTimer
        jsr ProcessJoystick
NotActive:        
        jsr PositionObjects
        jsr SetObjectColors
        jsr PrepScoreForDisplay
        rts             ; ReTurn from Subroutine
Link to comment
Share on other sites

I usually start with a template, which has subroutines for VBlank, Kernel, Overscan etc. And I add new subroutines to it.

 

But at some stage, when space becomes rarer, I start inlineing. Especially routines which are called only once, are getting inlined then. Sometimes I convert them into macros, so that the general structure of the code remains the same. The further the development processes, the less the order of code and data follows any other logic than fitting best to the problem.

  • Like 1
Link to comment
Share on other sites

At a higher level I use this loop:

  • Process User Inputs
  • Movement logic for things not controlled by a player
  • Prep for display (position all TIA objects, initialize values needed by Kernel, etc)
  • Kernel (your code that updates TIA scanline-by-scanline in real-time to draw the display)
  • Collision processing

This is the same order of things that I ended up using. (I originally tried keeping my collision processing elsewhere, but it ended up fitting better during overscan)

 

The further the development processes, the less the order of code and data follows any other logic than fitting best to the problem.

I don't have a lot of experience, but I'm using a lot of macros (to avoid the JSR overhead, unless it's code that's actually being called from multiple locations), and separating routines into different files based on concerns (enemies, collisions, player, background, etc). But just like Thomas said, by the end, everything has turned into a giant mess, as I'm moving things all over the place trying to squeeze a routine in to the 15 unused bytes here and 60 unused bytes there.

Link to comment
Share on other sites

[...] everything has turned into a giant mess, as I'm moving things all over the place trying to squeeze a routine in to the 15 unused bytes here and 60 unused bytes there.

That's what a linker is for, which can do it better and faster than a human in most cases. ;-) dasm doesn't have one, but AFAIK there are several 650x compilers which have.

 

Personally, I use k65. It has an optimizing linker; you can simply use the "inline" keyword for a function to avoid JSRs; it has bankswitching built in; and much more.

 

I realize that optimizing by hand can be fun, but at some point it always starts to feel like a chore, at least to me. Having a compiler who takes care of that for me can help productivity a lot.

Link to comment
Share on other sites

Usually those unused bytes result from avoiding page crossing penalties. And when you have to align code to a new page, you have to manually look e.g. for a small table which fits into the space.

 

I doubt a compiler can help me here.

 

Of course it can, that's exactly what a linker does. With K65 for example, you can specify explicit alignments ("align 256" for example) or, if really needed, absolute addresses for each section of code or data. Or you can mark a block of code, a loop for example, as "nocross", meaning that this block must not cross a page boundary but otherwise doesn't need to be aligned exactly.

 

What the linker then does is try to find a configuration of memory locations for all functions and sections that satisfies these constraints. For example, it can place a section of code including a "nocross" loop in such a way that the space between that section and an adjacent section is exactly the size of a table of data or another section of code to squeeze in, wasting not a single byte.

 

Especially when it comes to "nocross" sections which do not need to be aligned exactly, only not crossing a page boundary, this is extremely helpful. A computer can optimize such a complex scenario much better than a human. And not having to hunt for code caves or shift around code after each change saves A LOT of time.

 

My first 32k demo TIM1T was written with dasm and manual optimization. My second demo, Ascend, used K65, and having a linker doing all the optimization work was like the difference between night and day.

Edited by Kylearan
  • Like 2
Link to comment
Share on other sites

One caveat: The problem in the case of k65 is that it uses a completely new syntax for everything, which comes with its own learning curve and might not be everyone's cup of tea. There are some "traditional" 6502 compilers (like the cc65 toolchain or, I think, 64tass) that also come with a linker, but I don't know how good they are or how well they play with VCS specific stuff.

Link to comment
Share on other sites

K65 looks really cool and definitely closer to a compiler than a traditional macro assembler.

 

I consider BASIC compilers like bB and vwB to be macro assemblers of a sort because they have the same function of turning high level code into assembly blocks, just more efficiently thus the asm games created with a lot of macro language code typically tend to require more memory than games written in pure asm and BASIC.

 

How is K65 in regard to memory optimization?

 

Link to comment
Share on other sites

Dang, I'm wishing I started with k65. That sounds really nice. One of my big beefs with dasm is that macros have to be defined BEFORE you use them, but most code gets defined WHERE you use it. Which means that if I change a routine from a macro to a proper subroutine, I have to physically move it. That seems like it should be the computer's job, not mine.


it has bankswitching built in;


How does the bankswitching work? Trying to make sure that my routines are in the same bank as the associated data (and avoiding the overhead of cross-bank jumps) ends up taking some work. If it does all that for me, then I'm really going to regret using dasm instead of k65.


There are some "traditional" 6502 compilers (like the cc65 toolchain or, I think, 64tass) that also come with a linker, but I don't know how good they are or how well they play with VCS specific stuff.


Folks on the NES development forums tend to recommend cc65. I wonder how it compares to k65.

Edited by gauauu
Link to comment
Share on other sites

...by the end, everything has turned into a giant mess...

Just kind of an observation that took me 35 years to come to learn...

 

The "mess" is a superior programming paradigm.

 

We've been dumbing down programming, like pretty much everything else, as the world has needed more programmers. Should a programmer's code be judged by the worst programmer's abilities? I do agree with the concept of maybe the next language or programming paradigm might bring nirvana. But after a 1000 languages and thousands of paradigms I started to think maybe that's not really working very well. And I do agree there's a lot of useful code that can be written in very high level languages by inexperienced people. But isn't there still a place for great code? Consider Boost. Pretty much everything inside Boost would be considered really bad programming. Yet at the same time it's considered great programming to use Boost. How can the creating of Boost be "bad" if the result is "good"? It's a strange paradox. The difference is of course the programmer's skill. Virtually everything you read or hear about programming will be geared toward less the skilled.

 

 

I embrace the "mess".

 

Just my two cents.

  • Like 2
Link to comment
Share on other sites

In some situations (like Boost), I see your point. But in this case, the mess isn't fixing some interesting problem in a creative but messy way. It's just disorganization that could be remedied by better tools.

 

So I don't think your advice applies to my mess :)

Link to comment
Share on other sites

I like the JSR thing to structure the game BUT it comes with a performance penalty, so when my game gets "complicated" enough, I resort to inlining everything and get my own nice mess :)

 

If you're writing for 4K or 8K ROM basically there is no enough bytes to make your code look "nice", you'll start reusing bytes and moving subroutines.

  • Like 1
Link to comment
Share on other sites

One of my big beefs with dasm is that macros have to be defined BEFORE you use them

If I remember correctly, there is one minor feature where you cannot use forward declarations in K65 as well, but right now I forgot what that was exactly. Most of the time (function declarations, data sections etc.) it works.

 

How does the bankswitching work? Trying to make sure that my routines are in the same bank as the associated data (and avoiding the overhead of cross-bank jumps) ends up taking some work.

You still have to declare manually which functions/data sections you want to put into which banks. However, calling a function in a different bank is as simple as prepending the keyword "far" in front of the function call, like this:

 

bank misc

func doMagic {
    <insert code here>
}

bank main

func myKernel {
    far doMagic    // Calls doMagic in the other bank, then return here again
}

This will cause K65 to automagically generate the necessary code stubs in both banks that take care of switching to bank "misc", call function "doMagic", switch back to bank "main" and continue exection after the far call.

 

If you later decide you want to put both functions into the same bank, simply change the "bank" statements accordingly, remove the "far" keyword and it will become a traditional JSR/RTS subroutine (or use the "inline" keyword in the function definition to inline it).

 

You can still do bank switching manually if you want, like you'd do it with dasm.

 

I've released the sources for "Ascend" and "Derivative 2600" if you want to have a look at a real-world example. Ascend uses the K65 built-in bank switching, Derivative 2600 manual bank switching.

 

Folks on the NES development forums tend to recommend cc65. I wonder how it compares to k65.

I have no idea since I don't know much about cc65. :) cc65 will have the advantage of using traditional 6502 syntax and thus will be easier to get into. I suspect k65 has a superior linker, it was specifically made for the VCS and it allows for much more compact code (it allows multiple instructions per line), but has a steeper learning curve as it uses its own 6502 syntax.

Link to comment
Share on other sites

Do you mean RAM? You still have to declare RAM variables manually and have full control over everything, it's like in dasm but with a different syntax. K65 won't try to optimize anything in that regard.

No I mean the ROM, Ascend and TIM1T are awesome demo's (please link the source code) but is K65 also suitable for building a 4K demo or does it impose overhead that eats up ROM?

 

There are a lot of interesting development methods and discussion forums like AtariAge are great resources for motivation and sparking ideas about development but I tend to follow Dan's old-school approach and keep the game or application in my head and write down notes on a scrap of paper about the general game design features and/or a list of the routines that will need to be changed for large enhancements.

 

For smaller changes, I keep a log at the top of BASIC and Assembly programs in LIFO order.

 

post-30777-0-29106900-1464378559_thumb.jpg

Link to comment
Share on other sites

[...] is K65 also suitable for building a 4K demo or does it impose overhead that eats up ROM?

Sorry for misunderstanding you at first. Sure, you can use K65 for 4K projects just fine, as it does not impose any overhead at all. Every 6502 instruction has an exact representation in k65, and optional convenience stuff like conditional blocks, loops or functions are compiled straight to assembly exactly the way you would expect it to with no overhead.

 

Only the in-built bankswitching adds overhead you cannot directly control, but you can always opt to not use it and do it manually (like you would do it with dasm), and for 4K it's not needed anyway.

 

[...] I tend to follow Dan's old-school approach and keep the game or application in my head and write down notes on a scrap of paper [...]

In the end it's all about having fun, and if you're more comfortable with the old-school approach and enjoy that more, then that's great!

 

For me it's a bit different. I have way too many ideas for demo effects and not enough spare time, and using a more modern framework like k65 allows me to be more productive and write more code more efficient in less time. Even if it automates away some of the manual old-school fun, for me it's even more fun to see if my demo effects can be realized on the VCS or not. :)

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

Sorry for misunderstanding you at first. Sure, you can use K65 for 4K projects just fine, as it does not impose any overhead at all. Every 6502 instruction has an exact representation in k65, and optional convenience stuff like conditional blocks, loops or functions are compiled straight to assembly exactly the way you would expect it to with no overhead.

 

Only the in-built bankswitching adds overhead you cannot directly control, but you can always opt to not use it and do it manually (like you would do it with dasm), and for 4K it's not needed anyway.

 

 

In the end it's all about having fun, and if you're more comfortable with the old-school approach and enjoy that more, then that's great!

 

For me it's a bit different. I have way too many ideas for demo effects and not enough spare time, and using a more modern framework like k65 allows me to be more productive and write more code more efficient in less time. Even if it automates away some of the manual old-school fun, for me it's even more fun to see if my demo effects can be realized on the VCS or not. :)

 

Very cool! Agree 100% about using a framework and a compiler to build fun games and demos for the VCS :)

 

Is the framework/runtime library for the routines in Ascend in K65?

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