Jump to content

Recommended Posts

Hi everyone interested,
while we are at the middle of creating our entry for the 30 years Lynx compo organized by atarigamer.com, I thought it might be nice to share some details on tools or concepts we use.
I will happily answer questions but some replies may take a while during the compo :)

We work on a rendition of Assembloids, a fast-paced action puzzler. The concept was originally developed by Photonstorm for the flash game 'Quartet'
and we ported it to C64 and Atari2600. The original artist contributed to the C64 variant back then and we have the official ok to spread the love across
multiple systems. The Atari Lynx surely was missing so far!

We try our best to give it some different style and touch for each system and to make the best use of the given hardware.
Not too long ago we formed some hobbyist homebrew game-dev group 'PriorArt' and you can find us here:
http://priorartgames.eu

For Assembloids Lynx, I (Martin) work on the code, Oliver 'v3to' Lindau does most of the main graphics and Kamil 'jammer' Wolnikowski is the man behind the sound effects and music.
I get most fun out of developing for any platform when I have everything understood from scratch and am aware of every single byte written.
This also means that all bugs are my own :)
So everything is written in assembly language and I usually write my own tools. For this game the only external tool I use is 'sprpck' that
basically does exactly what I need and had not time/motivation to write my own converter :) Thank you for it!
But no other libraries, loaders, converters or routines by others were used.
This may sound tedious but it is half-the-fun. Of course this approach heavily relies on information available somewhere and I thank in particular:
Bjoern Spruck for detailed information and examples,
Bastian Schick for much of the legacy documentation and pioneering work,
Karri for sharing thoughts and enthusiasm,
And LXdev for his 'Diary of an Atari Lynx developer' which was a great start!
And also Bernd Thomas for his flashcard that I needed alot to test things on real hardware which still differs alot from emulation,
Sascha 'Luchs' for hints and contacts and a couple of hardware-tests, i.e. for the true RGB image routine :)

the Retrokompott podcast (with Oliver Lindau) who triggered my urge to look into the Lynx,

Igor from atariage.com for constructing a 'center of knowledge and information' for the Atari Lynx
+ everyone involved in emulation-development which is the key for anything getting done today :)

[sorry if I forgot something while writing this up, feel free to ping me or simply comment here]

And, naturally, my own group mates for their patience during endless (still ongoing) test-iterations :)

Preferring coding in assembler from scratch, I soon needed/wanted some own loading routine. The one I now use loads pretty fast and the whole loader fits into the smallest possible decode-unit of 50 bytes. There is an Atariage thread about the start of my routine. I also prefer to stay optimal and not 'waste' space (this is purely emotional these days as 128KB or 1MB hardly make no cost difference anymore). Anyway, Assembloids Lynx suits into a 128KB cartridge.

This is already way too much text so I will take a break here and add details here and there :)

Cheers,

Martin 'enthusi' Wendt

  • Like 5
Link to comment
Share on other sites

This is the current memory layout.

There even is something like a freebuffer \o/

But we're not yet done and I have plans for it (and beyond, hehe) already.

The largest part are the audio player+data and 'facedata' which are the actual game graphics.

All main images are compressed in memory but not as compressed sprites (since the internal RLE compression is not very powerful) but rather as raw bitmap data with a bit-level Lempel-Ziv cruncher.

The smaller parts are stored as RLE sprites, however.

memfree.png

Link to comment
Share on other sites

Have you created your own sound routines as well?

 

Finding a good sound library was one of my difficult choices for "On Duty" and "Find a way to my heart". This is the first time I am trying to use SASS.

 

I found out that Handy Music is extremely compact. I was surprised in how much memory the SASS sound language saves. But of course writing your own code has no extra dead-weight. So it may be a good approach.

HM_CODE               000605
HM_RODATA             000028
HM_DATA               000008
HM_BSS                0000E5

SFX_RODATA            0000F9

MUSIC_RODATA          00051B

 

Link to comment
Share on other sites

For the Atari Lynx I wrote my first own soundroutine from scratch ever :)

I started with the obligatory MOD player back then, but that of course is somewhat RAM and CPU hungry.

Assembloids will use a 'brand new' audio engine that is pretty 'slim' and 'fast' (if you ask me :).

3 channels audio, 1 channel sound effects as samples. It runs at 60 Hz / vblank most of the time. I sometimes split between VBLANK IRQ and manual call once per frame depending on the routine running in the game.

 

 

  • Like 1
Link to comment
Share on other sites

Attached is a still image of the title screen.

I am currently changing the color ordering for one of the converters while Jammer is working on instrument design for our music player and v3to prepares graphics for the credits.

 

assembloids.png

Edited by enthusi
added an 'a' :)
  • Like 3
Link to comment
Share on other sites

We went for an arcade style game since we find that one of the strengths of the Lynx are quick, mobile games without much overhead and something
that you could pass around to friends&family to compete :)
This is also why we added a rather relaxed difficulty mode (1) and a much faster mode (3) for shorter games.

And what good would this be without a highscore chart?

After GameOver a CODE is generated that you can send to us along with your score and difficulty level 1-3 to verify your claims :)

http://enthusi.de/scores.html

Below is a crop of a code display (so don't hand that one in  :)

code.png

  • Like 4
Link to comment
Share on other sites

Deadline closing in.

Quite a number of last 'minute' (or day) changes.

Memory is well used up now :)

What is referred to as 'free buffer' is pretty well occupied by temporary data.

The whole main engine is now compressed and de-packed on boot to make room for more sound effects and additional gfx in the credits screen.

There was also space for a small hidden feature now :)

memlayout.png

  • Like 2
Link to comment
Share on other sites

1 hour ago, enthusi said:

Last minute I discovered a major difference between Lynx I and II when it comes to cart ROM block selection routines. Phew. Thanks to BS42 and other helpful comments.

Interresting. I'm not aware of anything like that as I haven't found any hint about such difference on the forum. Could you be more verbose?

 

  • Like 1
Link to comment
Share on other sites

I keep general information about the game as such here:

 

Technical details etc. continue in this thread.

The issue we had with Lynx I was the IODAT register.

The ROM routines (at least from Lynx II) end a cart ROM block select with a stz $fd8b.

That's fine with the Lynx II but NOT THE LYNX I. At least not, when IODIR is set like on the II.

I will run some tests to explore the differences including ROM in full detail (when I find time :).

 

 

Link to comment
Share on other sites

The hidden image uses 256 colors without flickering or anything.

The Lynx at the start uses the same routine but it is less obvious there.

The 256 color image (just 256 by accident, could be more but in practice you wont get that).

My routine changes up to 16 colors per rasterline but it uses very fast code and pre-selects the colors for the next line in advance. Also not using HBL interrupts as they would  be too slow. And assembler of course.

The attached image shows the colors used per line. You realize how I sort the palette (and thus bitmap pattern) for each line such, that the first used colors gets changed in green and red+blue first.

The bottom area does then use the same palette since the CPU is needed to play the music and even more CPU-costy for the fade out from top+bottom when you blend it out.

altallcolors.png

256col.png

  • Like 3
  • Thanks 1
Link to comment
Share on other sites

On ‎9‎/‎3‎/‎2019 at 4:31 AM, enthusi said:

The hidden image uses 256 colors without flickering or anything.

The Lynx at the start uses the same routine but it is less obvious there.

The 256 color image (just 256 by accident, could be more but in practice you wont get that).

My routine changes up to 16 colors per rasterline but it uses very fast code and pre-selects the colors for the next line in advance. Also not using HBL interrupts as they would  be too slow. And assembler of course.

The attached image shows the colors used per line. You realize how I sort the palette (and thus bitmap pattern) for each line such, that the first used colors gets changed in green and red+blue first.

The bottom area does then use the same palette since the CPU is needed to play the music and even more CPU-costy for the fade out from top+bottom when you blend it out.

altallcolors.png

 

Looks great. I always wondered how the DLI approach from Atari 800 would translate into Lynx, but you said you didn't use interrupt, so that made me curious, how you do it.


I then see 2 other options:

1. Timer

2. Kernel

 

In theory, if you sync your timer with the first scanline, then the exact cycle time for next scanline should be stable and predictable (unlike Atari's ANTIC, which has great many special cases).

 

A Kernel should therefore be even more simple, as the code to change 16 colors has always identical execution time, so the down time / NOP code will be same for each of the 102 scanlines. Meaning, once you time one scanline properly, remaining 101 should become aligned too.

But, if you play music, then Kernel goes out the window, as the CPU audio cost is then quite unpredictable.

 

 

 

What do you think is the best way to change palette 4-6 times across whole image ? I'd love to introduce that into my 3D engine and thus have multiple colored layers:

- clouds + sun

- mountains

- 2-3 shades for fog (e.g. 3D track disappearing into distance, gradually blending with the skybox)

- HUD

 

I was originally thinking of an interrupt, but it seems extremely wasteful to have that code executed for every single scanline, which would then destroy the framerate.

 What do you think ?

Link to comment
Share on other sites

If you manage to have less than 16 colors in the zones of palette change I see no problem. You can exchange the unused colors even one by one then. With speed code you get almost all done in one raster line at 60Hz plus you can use the 'double poll' method if IRQ is too slow:

Trigger IRQ via timer then poll in software for next line (uninterrupted). That is more or less how I do it.

Link to comment
Share on other sites

Another detail is the 'fade to grey' at game over!

I do not swoop color pre-calculated to greyscale but instead I read out the current palette and compute the average of red, green, blue.

This one worked well (but cheats a bit):

    lda green
    clc
    adc red
    lsr
    clc
    adc blue
    lsr    ;=((g+r)/2+b)/2
    and #$0f
    sta grey,x

as you see this is fast to compute but weights blue stronger.
Later I used:

    lda green
    clc
    adc red
    adc blue
    sta tmp
    asl
    clc
    adc tmp ; 3 * sum
    sec
    lsr
    lsr
    lsr ;/8

    sta grey,x

So instead of (green + red + blue) /3 I use (g+r+b)*(3/8) which is a good enough approximation ;-)

Then per frame I check for each palette entry if the red, green, blue content are brighter or dimmer than its corresponding grey-value and decrease or increase its value accordingly.

Including all the handling of upper and lower nibble for red+blue.

The fade_to_grey routine will work generically with any palette now. A good thing that the color registers are read-write ;-)

Some other SUZY regs unfortunately are not.

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

On ‎9‎/‎9‎/‎2019 at 12:45 PM, enthusi said:

If you manage to have less than 16 colors in the zones of palette change I see no problem. You can exchange the unused colors even one by one then. With speed code you get almost all done in one raster line at 60Hz plus you can use the 'double poll' method if IRQ is too slow:

Trigger IRQ via timer then poll in software for next line (uninterrupted). That is more or less how I do it.

I was thinking of that earlier, but even if you chain two timers, can you get enough timer's time to skip over half screen ? Meaning, you would only have 2 sets of color (skybox and ground) - e.g. 32 on-screen colors.

 

I'd hazard a guess that the two chained timers only give you -maybe - ~10-15 scanlines worth of CPU time, no ? Haven't run the exact numbers, but clearly recall when I was using just one timer, that at full length it expired veeeeery quickly.

 

On the other hand that would be great if you need a lot of colors. That's probably the way to go anyway, I just don't want to burn CPU time needlessly...

 

 

EDIT: I just realized, that regardless of what is the maximum amount of scanlines that the two timers can wait through, if I need more scanlines to skip, I would run the two timers again (just without the color update logic) and again. While wasteful, it's certainly significantly less wasteful than halting CPU waiting for the target scanline :)

 

I don't have my 10 tabs with Lynx docs open now (I'm in Jaguar mode), so how many scanlines can 1 timer at full length skip ?

Edited by VladR
BrainFart
Link to comment
Share on other sites

On ‎9‎/‎9‎/‎2019 at 12:45 PM, enthusi said:

 

Trigger IRQ via timer then poll in software for next line (uninterrupted). That is more or less how I do it.

You see the problem is, that in 3D, when you have a framerate of 12-15, you basically spend updating all those colors for 4-5 static frames, taking away valuable time from the 3D engine.

 

Well, I guess there's no reasonable way around this without redesigning the scene in a way that needs minimum amount of color changes.

 

But, with 3D, and elevation changes (e.g. camera height), you can't really do that, as each frame has slightly different camera YPOS (which would mean burning yet more cycles to adjust the IRQ targets, taking yet even more CPU time away from 3D engine, thus killing the framerate further), which changes the scanline where horizon changes to the ground.

 

I guess the only advantage here is that you adjust those IRQ targets only once per 5-6 frames...

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