Jump to content
IGNORED

Super Space Acer


Tursi

Recommended Posts

  • 2 weeks later...

I have not tested on real hardware yet, so I doubt the starfield issues you saw are fixed, Yurkie, but in the meantime, here's a new build.

 

Posted a YouTube Video of the WIP here:

 

-Implemented new final weapons systems - three versions of wide gun and three versions of pulse cannon (still need to make shield a separate powerup and fix the wide gun icon)

-Fixed boss cockpit sprite to be hidden as intended

-added post-boss bonus tally

-Made boss flash white when engines hit using second color table

-made fat character set out of BIOS character set (idea by Sometimes99er)

-made enemy shots flash to call them out more

-changed screen color reset for bombs

-tweaked scoring

 

http://www.harmlesslion.com/cgi-bin/onesoft.cgi?64

Link to comment
Share on other sites

  • 4 years later...

Updated again! Just four years later. :) I seem to have updated both threads in the past, so I do so again. As before, this one I'll talk tech. But first:

 

 

New graphics for player, saucers, jets and copters (I'm slightly better than I was 26 years ago).

Enemy motion patterns in place - saucers weave and copters stop to shoot at you.

Nuke bombs are now armored.

Too many tweaks to list.

 

http://www.harmlesslion.com/cgi-bin/onesoft.cgi?64

 

(ignore the screenshots on the page - they are still the TI version)

 

The biggest change I made here besides the effort to update the graphics was to replace the underlying libraries with my own - a port of my "libti99" that I called "libti99coleco". https://github.com/tursilion/libti99coleco

 

On the whole, I doubt anyone but me will find it useful. But what may be interesting is that I spent a fair bit of time on dealing with the NMI, and as my approach might differ from most here, maybe a discussion is interesting. Maybe not. I dunno, I like to talk tech. ;)

 

My background, of course, is the TI-99/4A. Standard practice on that machine, recommended by TI, is to leave VDP interrupts disabled, and then enable them periodically in your main loop. Of course, we /have/ that option there. On the Coleco, VDP interrupt is an NMI.

 

My previous attempt to port this and make it look like the TI code relied on turning the VDP interrupt on and off inside the VDP itself, a solution I was not completely thrilled with, but did work (as long as timing was careful enough). I did this because some of the other approaches I saw when I started in the Coleco realm bothered me. ;)

 

For this pass, I decided to revisit that mechanism again. I now use a single flag byte to indicate the state of VDP interrupts. 0x01 indicates if interrupts are "enabled", and 0x80 indicates if an interrupt is "active". The NMI code now examines this byte when it fires.

 

If interrupts are "enabled", then the NMI code calls the code's interrupt function (which is responsible for updating the state of this variable and reading VDPST to clear the IRQ). If interrupts are not "enabled", then it simply sets the MSb to indicate that an interrupt was missed. Because the VDP is not accessed by the NMI, the interrupt line will stay high in this case and not retrigger, so the flag is necessary to tell the code that action is needed.

 

	push af					; save flags (none affected, but play safe!)
	push hl
	
	ld hl,#_vdpLimi
	bit 0,(hl)				; check LSb (enable)
	jp z,notokay
	
; okay, full on call, save off the (other) regs
	(omitted - just a bunch of pushes and a call, followed by pops and a jmp to clrup)

notokay:
	set 7,(hl)				; set MSb (flag)

clrup:
	pop hl					
	pop af
	retn
In the user code, a function (really a macro) is used to simulate the TI's enable interrupt function. This macro first sets the LSb to indicate that interrupts are enabled. Next, it checks whether the MSb is set, indicating a missed interrupt. If so, it calls the user interrupt handler. If not, it doesn't need to do anything else, if the interrupt fires then the NMI will call the function.

 

#define VDP_INT_ENABLE			{ __asm__("\tpush hl\n\tld hl,#_vdpLimi\n\tset 0,(hl)\n\tpop hl"); if (vdpLimi&0x80) my_nmi(); }
(I wasn't sure if I needed to save hl in the inline asm... since it could be used anywhere, I decided to be safe)

 

It was that transition point that was tricky - there's high possibility of a race condition, and I ran into them a few times. There are two effect of a race - in one, the user interrupt function is called twice, and in the other, the interrupt is missed and its never called. The former is just an annoyance in a program like this, but the latter causes a complete lockup.

 

I'm pretty sure I have it close now. Using the bit set and reset functions should give me atomic update of the byte (although I still need to verify that). Sequence is important too - the NMI is allowed to interrupt at any point. By setting the enable bit before checking for missed interrupt, the enable code catches the case of the NMI firing after it reads the status byte for comparison. The NMI /not/ setting the missed interrupt bit in that case removes the double function-call problem caused by the same case. The main thing I had to be careful about was ensuring that the C code used the bit functions to update the flags, and not a read-modify-write cycle. A read-modify-write could cause a flag set by the NMI to be overwritten if it fired between the read and write... and this happened numerous times before I finally looked at the code the compiler emitted. ;)

 

There's still a double-call race, and I just need to look closer at the Z80 instruction set to solve it. On the entry to the my_nmi function I have this:

 

	if ((vdpLimi & 1) == 0) return;	// not enabled - we probably raced, so don't run twice.
	vdpLimi = 0;			// clear the interrupt flags
The first line tries to detect the double-race by seeing whether the enable flag was already cleared (if this function is being called, then it must be set. But if we read it and see it's reset, then the NMI must have triggered and called it while we were starting up.) However, there is time between reading vdpLimi to test the bit and setting vdpLimi to 0 (hopefully only two instructions) during which the NMI could fire and call this function itself. To fix that I need an atomic reset and test, which I think I read something about somewhere... ;)

 

(Edit: in re-reading this, I think I remember why I didn't think this was a concern. The only case in which it can happen is that it is called by the user code, and then the NMI interrupts it. But the user code will only call it if the flag byte indicates a missed interrupt - in which case the VDP IRQ should still be high and so can't fire again. As long as the user code never directly reads VDPST, which could clear a pending interrupt, it's safe.)

 

Besides that, I spent some time on the VDP padding. Using documentation (mostly from MSX forums) and the VDP timing breakpoint in BlueMSX, I tweaked up my access functions. My biggest change was just a single line, but in my user interrupt code where I copied the sprite table up to the VDP. This code I wanted to be fast, but certain points in the code deliberately run long, causing me to miss the blank. (Level setup, graphics copies, things like that, nothing mid-gameplay). Since I wanted this copy to be fast as possible, I simply added a test - if I missed the blank, I skipped the sprite table copy. Since I only miss during setup times, that was fine.

 

I also started to break the code out into modules so I could start banking next version, and thus have room for the extra graphics and music. I'm trying something new for linking the banks a little more nicely than in the past, but I'll save that discussion for when it works. ;)

Edited by Tursi
  • Like 7
Link to comment
Share on other sites

  • 2 weeks later...

New version, mostly just updating the music, but also getting a slightly improved bank switching build-chain going, which this post will be about.

 

http://www.harmlesslion.com/cgi-bin/onesoft.cgi?64

 

http://youtu.be/3XItUA_a0f8

 

(Video may need an hour to finish uploading.) Music isn't finalized - some songs are too long, some are too short. I need to adjust lengths and set up correct loop points. But I wanted to get the data in there and the build process proven.

 

Now, I don't know how anyone else is doing bank switching (or IF anyone else is), but for Mario Bros I had to come up with something. SDCC supports named sections, but as far as I could work out, it didn't support getting those named sections into the linked output file.

 

I came up with a two part process - the first was a small hack to the SDCC linker so that the IHEX output would insert tags with the section name, and the second was a program I called 'makemegacart' that could read the IHEX file and drop the data into the correct location of a bank-switched image. (This replaces objcopy.) This worked fine, but had a couple of gotchas.

 

First was that all the segments were linked directly into the bank I wanted, and the banks had to be named "bankX" (where X was a number). Moving things around was a bit of a pain. So for this one I went back and improved that system a little bit.

 

I now assemble each file into its own section (some compilers have an option for this, but SDCC doesn't seem to), but I added a 'map' concept that I could pass into makemegacart, which tells it which banks to put each section in. This made it easier to shuffle data around and see how it affected the output. The map looks something like this (and since there was already something like it there, I put it in crt0.s):

 

.area _bank1
  .ascii "LinkTag:Bank1\0"
  .area _enemy
  .area _songpack1
 
.area _bank2
  .ascii "LinkTag:Bank2\0"
  .area _human
  .area _songpack2
 
.area _bank3
  .ascii "LinkTag:Bank3\0"
  .area _wineasy
  .area _winmed
  .area _winhard
 
.area _bank4
  .ascii "LinkTag:Bank4\0"
  .area _titlec
  .area _titlep
  .area _songpack3
The 'LinkTag' string just ensures there is something there, and helps identify areas in the output binary. Makemegacart also outputs a handy summary at the end, which uses those tags as reference:

 

1> 8 banks detected

1> Writing 128k megacart

1>

1> # SWITCH ROM_AD COL_AD FREE NAME

1> = ====== ======= ====== ===== ===============

1> 7 0xFFF8 0x00000 0xC000 9892 Bank7

1> 6 0xFFF9 0x04000 0xC000 2244 Bank6

1> 5 0xFFFA 0x08000 0xC000 3657 Bank5

1> 4 0xFFFB 0x0C000 0xC000 4291 Bank4

1> 3 0xFFFC 0x10000 0xC000 15271 Bank3

1> 2 0xFFFD 0x14000 0xC000 5717 Bank2

1> 1 0xFFFE 0x18000 0xC000 5360 Bank1

1> 0 0xFFFF 0x1C000 0x8000 7717 Fixed

1>

 

So that gets the code built, but what about the actual switching? I've got no easy solution there. It'd be nice if the compiler fully supported banking in the Z80 port, but I was not able to figure it out, so I did it manually with little inline functions to select a bank.

 

In Mario Bros I had functions that switched the bank and recorded which bank was last active, so they could return to the 'previous' bank, all manually of course.

 

I kept some of that system, but I found it much easier for functions that switched banks to cache the old bank themselves -- this way I was not restricted to a single level of bank switching. Unfortunately, I still had to manually trace the code paths to get the first round implemented, but it's no longer so difficult to add new functions.

 

Now, of course, any code that wants to be running WHILE the bank changes needs to live in the fixed memory. For this reason, certain code paths (ie: code running in bank 3 wants to call a function in bank 4) require a "trampoline" function in the fixed bank. All this function does is change the bank switch, call the desired function, change it back and return. (I have a couple of similar functions whose job it is to get data out of a data bank for code in a different bank). In Mario Bros this was tricky, but with the local 'previous bank' cache concept it's working much better.

 

void wrapcolchk(uint8 x) {
	unsigned int old = nBank;
	SWITCH_IN_BANK2;
	colchk(x);
	SWITCH_IN_PREV_BANK(old);
}
Another big mistake I did on Mario that I've not done here - was keeping the size of the fixed bank to under 16k. In Mario Bros, the first bank was nearly the full 32k, PLUS it had the runtime libraries at the end. This was painful, because I needed to make sure that any code that needed to call the runtime would have that bank mapped in. This included such things as division! Definitely easier not to have to worry about that. ;)

 

I'd be curious to hear what other people have done to link bank-switched code on the Coleco?

Edited by Tursi
  • Like 4
Link to comment
Share on other sites

  • 2 weeks later...

No discussion at all?

It's summer, dude. Everybody's surfing real waves of water instead of the internet. ;)

 

Thanks to the invaluable help of alekmaul, I was able to compile my C code of Jewel Panic into a bankswitched 64K ROM file. That happened last winter so details are fuzzy in my mind right now, but if I remember correctly, it involved using an altered crtcv.rel file... alekmaul was the guy who provided me with what I needed to make the compilation work, so he probably remembers this stuff better than I do right now. :)

Link to comment
Share on other sites

No discussion at all?

 

No discussion at all?

 

 

It's summer, dude. Everybody's surfing real waves of water instead of the internet. ;)

 

I agree, I know a lot of people are busy enjoying the outside rather than hanging out on forums, especially us Canadians that only see summer heat for 3 weeks of the year ;)

So here is some hopefully constructive feedback.

 

The game looks fast and the music is great, even though you will be adjusting it. I agree that the player ship seems too big (and the power ups too small)

I like how the bosses show damage as you wear them down. The bosses explosion is too fixed and probably should be more randomized.

It appears on some of the bosses there is a safe zone in the upper left or right where you can't be hit. I'm not a fan of bullet hell shooters (I sometimes feel like some games are cheating by making it too hard to evade bullets) so maybe that is a good thing in this case. I look forward to this one being completed, keep up the great wok.

Edited by Tarzilla
Link to comment
Share on other sites

Alek Maul have figured out how to do bank switching in C. http://www.gamopat-forum.com/t75551-colecovision-et-megacart#1945318

When I feel better, I may try out his tutorial.

I have thought of ways to put data into banks. The main game loop probably reside in the unchangable bank while enemy behavior per levels, graphics, title/cutscene sequence, additional stuff can be placed into the changable bank. I do wish C language would have the org command like asm have. However, the linker would be the one to compile the project and place data to where it needs to be via command line or something. I'm not professional programmer. Once I understand Alek Maul's tutorial, then I'll have a better picture what I am going to do. For now, compression and recursive programming is my tool.

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

I agree, I know a lot of people are busy enjoying the outside rather than hanging out on forums, especially us Canadians that only see summer heat for 3 weeks of the year ;)

 

I'm Canadian too. In Ottawa, you appreciate that it's only three weeks! But in Vancouver it spreads out more. ;)

 

The "ship is too big" won't even be looked at until the game is in it's final form -- I'm biased in that I've been looking at it for almost 25 years, but I assure you it doesn't affect playability. I'm thinking a ship select will put that complaint to rest, though.

 

 

 

The bosses explosion is too fixed and probably should be more randomized

 

it's 100% random. :) Maybe too frequent... I dunno, I liked it. I'll turn down the numbers and we'll see.

 

 

 

It appears on some of the bosses there is a safe zone in the upper left or right where you can't be hit

 

Yeah, that doesn't bother me. You used to be able to sit right on top of the bosses, but people found that too quickly. ;)

 

 

 

I first though people don't give a fu%# about it

 

Yeah... the Coleco forums are pretty tight knit, I was just hoping there were more technical sorts here - why else would I write the articles? :) I guess not many people understand what's going on under the hood, or don't care to talk about it if they do.

 

Or they're off surfing. Lucky bastards. ;)

 

 

 

Alex Maul have figured out how to do bank switching in C.

 

I saw that when I was looking around to see what other people had done... I didn't realize so few people had actually attempted it. It's really very simple, just have to stay organized because the compiler doesn't do all the magic for you. (Well, I tried to describe in detail how I'm doing it... maybe too much?)

 

I tried to find his "cvmkcart.exe" to see what it did differently from my makemegacart.exe, but it seems like it's not online anymore?

 

 

 

I have thought of ways to put data into banks. The main game loop probably reside in the unchangable bank while enemy behavior per levels, graphics, title/cutscene sequence, additional stuff can be placed into the changable bank. I do wish C language would have the org command like asm have. However, the linker would be the one to compile the project and place data to where it needs to be via command line or something. I'm not professional programmer.

 

If your code will all fit in 16k, though, that's absolutely the easiest way to do it. You never have to worry about code swapping out or trampoline functions, just switch in the data you want and start reading.

 

Some C compilers do let you "org" the code and data with pragmas, but, you are generally better off doing it on the linker side anyway, just for flexibility's sake. Learn to read the map file, too, it's invaluable for debugging as well as making sure things landed where you really want them. I didn't cover the link side above, but it's similar to what Alex posted on that linked post.

 

For mine, I've used a few variables just to break up the long lines a bit:

buildcol: $(objs)
 $(CC) -mz80 --vc --no-std-crt0 $(BANKS) --code-loc 0x8100 --data-loc 0x7000 -l"../../../libti99coleco/libti99.a" $(objs)
 
# banks - all defined the same way, we just need to declare them
BANKS = "-Wl -b_bank1=0xc000" "-Wl -b_bank2=0xc000" "-Wl -b_bank3=0xc000" "-Wl -b_bank4=0xc000" "-Wl -b_bank5=0xc000" "-Wl -b_bank6=0xc000" "-Wl -b_bank7=0xc000"

The variable "BANKS" just contains the parameters which instruct the linker to link each named section ("_bank1", "_bank2", etc - these are just names) at a specific address -- in this case each one is explicitly linked to 0xC000, which is the address of the switchable bank. You see it inserted in the actual build line as $(BANKS) - that's how variables are referenced in a makefile.

 

It's the "-Wl -b_bank1=0xc000" that links the bank1 segment to 0xC000 - it's easy enough. Coming from assembly it may feel like a loss of control at first, but you're still fully in control when you want to be - cases like this. The nice thing I that everything else is automatic - the addresses of all the code and data will line up like you expect them to. Your only job in the code is making sure they are actually banked in when you try to access them. (Forgetting this leads to interesting bugs ;) ).

 

The other two variables in that line:

$(CC) is just the compiler program, it's just "sdcc", but making it a variable makes it easy to add paths and the like if needed.

$(objs) contains a list of all the object files to link together:

# crt0 must be first!
objs = crt0.rel ssd_inc.rel superspaceacer.rel trampolines.rel bg.rel boss.rel enemy.rel music.rel human.rel wineasy.rel winhard.rel

(there's more than that, but no need to waste space on the forum).

 

Of course, before it can be an object file, it needs to be compiled. I have a similar line for each object file which both compiles the code, and puts it into the appropriate named segment:

bg.rel: bg.c game.h
 $(CC) $(CFLAGS) -o bg.rel bg.c --codeseg bg --constseg bg
boss.rel: boss.c game.h
 $(CC) $(CFLAGS) -o boss.rel boss.c --codeseg boss --constseg boss
enemy.rel: enemy.c game.h
 $(CC) $(CFLAGS) -o enemy.rel enemy.c --codeseg enemy --constseg enemy

The mapping from these meaningful named segments to the "bankX" segments is discussed above where I talk about the map concept - it means to move something around I just have to move the line in the map (and update the code), which is easier than my old method where I explicitly assigned everything per object file.

 

$(CFLAGS) just contains the options to the C compiler, for mine I use this:

CFLAGS = -mz80 --vc -c "-I../../../libti99coleco" "-I../../../include" -DENABLEFX --std-sdcc99 --opt-code-speed
Edited by Tursi
  • Like 4
Link to comment
Share on other sites

 

 

I'm Canadian too. In Ottawa, you appreciate that it's only three weeks! But in Vancouver it spreads out more. ;)

 

The "ship is too big" won't even be looked at until the game is in it's final form -- I'm biased in that I've been looking at it for almost 25 years, but I assure you it doesn't affect playability. I'm thinking a ship select will put that complaint to rest, though.

 

 

 

 

it's 100% random. :) Maybe too frequent... I dunno, I liked it. I'll turn down the numbers and we'll see.

 

 

 

 

Player selectable ship is a great idea.

 

post-38229-0-57346200-1438450887_thumb.png

What I meant was the little mini explosions currently go thru their loops in the exact same spot once they start. I think it would look better if after a loop or two they change location so it looks like they are randomly exploding all across the boss

  • Like 1
Link to comment
Share on other sites

What I meant was the little mini explosions currently go thru their loops in the exact same spot once they start. I think it would look better if after a loop or two they change location so it looks like they are randomly exploding all across the boss

 

it's random. :) Explosions are added and removed at random for the entire duration of the sequence. :)

 

You're also running a pretty old version there. ;)

Link to comment
Share on other sites

 

it's random. :) Explosions are added and removed at random for the entire duration of the sequence. :)

 

You're also running a pretty old version there. ;)

Sorry, I was just going by the youtube video, I haven't had a chance to get near my desktop computer to try the ROM, I'll do that today so I have my facts straight ;)

Link to comment
Share on other sites

Sorry, I was just going by the youtube video, I haven't had a chance to get near my desktop computer to try the ROM, I'll do that today so I have my facts straight ;)

 

That's fair! But it's a pretty old video too. Try this one... I posted it in the last tech post, but for some reason the thumbnail didn't show up.

 

 

Explosions haven't changed for years though, so that part will be the same. ;)

  • Like 2
Link to comment
Share on other sites

-performance optimizations
-bugfix - invisible/popup powerups no longer occur
-bugfix - bosses no longer make powerups go away
-bugfix - dying on blimp doesn't corrupt the boss image
-bugfix - even a score of 0 will show on the title page after a game now
-bugfix - fixed sprite corruption on title page
-re-centered collision code
-improved sprite flicker
-added player ship select with three unique ships
-added three secret gameplay modes
-reduced number of explosion cells on the boss
-helicopters now turn around and fly off screen after shooting

 

The biggest change is the ship select adds not only graphical changes, but gameplay modes. Use left and right on the skill select screen to choose.

 

http://youtu.be/aV7qhifeMy8

 

The 'Cruiser' is the big red ship you love to hate. It's a little slower than the others but has a persistent shield that only goes away as it takes hits. When shields are gone, and the ship destroyed, it's game over.

 

The 'Snowball' is the original white ship that the game started with, so it's the traditional mode with a timed shield and multiples lives.

 

And for those who wanted a smaller ship onscreen, the 'Ladybird' offers a smaller, quicker vehicle. It has a timed offensive shield which can be used to damage enemies and bosses, although it counts down more quickly. (For what it's worth, I had no idea Ladybugs were called Ladybirds in most of the rest of the english-speaking world. Or possibly Wikipedia lied to me. ;) )

 

There are also three secret gameplay modes so far. ;)

 

http://www.harmlesslion.com/cgi-bin/onesoft.cgi?64

 

So what tech to talk in this one? There's a little effect when it draws the 'schematic' views of the ships. Shifting through the patterns and dynamically defining the characters was easy, but I wanted a sort of a 'glow' effect as the pixels came in. Ultimately I did this with sprites. I made the graphics grey so they'd appear darker than the glow, and dynamically defined and moved a pair of white sprites. For each pixel being drawn, there is a little 'bloom' created by also defining the adjacent pixels. As the sprite moves, it appears to 'fade' down into the single grey pixel of the ship. It was a bit of a pain to get right (finding the right pattern and doing all the bit shifting, especially for the vertical sprites), but I like the effect. ;) You can see it in the video.

 

I've also been doing a lot of optimizations, since the original code was fairly crude and slow. One of the biggest ones has been replacing inline switch..case statements with function pointers.

 

It was fairly common in my code to have a handler for enemies (for example) in the main loop like so:

switch (enemyType) {
case SAUCER: do this;
case JET: do this;
case COPTER: do this;
}

and so forth, plus generic animation code, tests for specific cases... and so on.

 

In the best case, SDCC will reduce such things to a jump table. This means that it will calculate a list of addresses, one per entry, then calculate the index and just jump over to it. This is pretty good and if that's all it was, it would certainly be enough.

 

But in some cases, SDCC is not happy with the range provided, and needs to include additional tests to make sure the jump table is safe. Or worse, it decides to replace the jump table with a series of comparisons. In addition, I had additional unnecessary code running (for instance, the 'jet' enemy does not animate, and simply moves straight down the screen. But it still ran the animation code and checked for moving off the left and right edges).

 

I was able to replace this, and several state machines, with function pointers, which made a visible difference in performance. Separate functions almost certainly cost more in terms of code space, but not as much as you'd expect once all the special-casing code was removed. And the main enemy handler simply becomes:

enemy_func(idx);

When the enemy is created, in addition to defining its shape, color, and animation, now I also define the function that handles its movement.

 

So what does that look like? The declaration of a function pointer may look a little strange if you haven't used them very much:

void (*en_func[12])(uint8);    // pointer to enemy handler function

Note this is further complicated by being an array -- but the name goes inside the parenthesis, and this defines a function that returns void and takes in a single uint8.

 

Initializing it is as simple as passing the name of a matching function:

en_func[k]=enemysaucer;

and you call it much like any other function call. (If not for the array index, it would look identical to a normal function call).

 for (x=0; x<12; x++) {
  if (ent[x] != ENEMY_NONE) {
   en_func[x](x);
  }
 }

(I could probably have defined a function for ENEMY_NONE as well and saved that comparison, but testing for zero is usually faster than a function call's overhead.)

 

SDCC's handling of function pointer calls is not completely straightforward:

;enemy.c:276: en_func[x](x);
 ld l,e
 ld h,#0x00
 add hl, hl
 ld bc,#_en_func
 add hl,bc
 ld a, (hl)
 inc hl
 ld h,(hl)
 ld l,a
 push de
 ld a,e
 push af
 inc sp
 call ___sdcc_call_hl
 inc sp
 pop de

This code looks up the array index (which is probably half of it right there, no array would be simpler) to get the correct function pointer, then calls "sdcc_call_hl" to do the actual jump. This is a tight little indirection function, but it's still additional overhead, especially contrasted to a direct function call, which can be as simple as a direct call.

 

Here's a function pointer without the index, to show how expensive the array is. shieldsOn is defined to point to the appropriate shield activation function depending on the ship the player chose.

;superspaceacer.c:995: shieldsOn();
 ld hl,(_shieldsOn)
 call ___sdcc_call_hl

Even though arrays appear to be pretty pricey, SDCC can do some surprisingly clever things. I attempted to unroll a number of loops, but measuring the resulting performance suggested the original, seemingly more complex code, was actually faster. As with all things - if you optimize for performance, you can't say you're done until you measure it. ;)

Edited by Tursi
  • Like 4
Link to comment
Share on other sites

I'll try that, I've /never/ used any of the buttons. I'm not a big fan of WYSIWYG editors on the web. ;) That's why it's weird to me that sometimes it shows up, sometimes not, sometimes editting my message makes it work, sometimes not. :)

 

Thanks!

 

(worked, thanks! and the tag is "media", which is easy for cavemen like me to remember! ;) )

Edited by Tursi
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...