Jump to content
Sknarp

(WIP) ATAX-ATAX (Development Thread)

Recommended Posts

Posted (edited)

WIP_01_ATAX.thumb.png.3350945553c36626ceb6b04e28dfd793.png

 

ATAX-ATAX is my newest project. I've been working on it for about 8 days so far, so I figured I should try to keep track of my development. I still have quite a lot to do. This is going to be a 4k game, but I'm confident that that won't be a limiting factor in this project. If you're curious why I think 4k will be plenty of room:


 

Spoiler

 

---BITS!---
There's several places where I only need 1 bit: The player0 has four directions to

move in, we can assign each one to a bit for four bits total. The player0's reflected

property can be stored in one bit. Player1 needs a 1 bit flag for his missile's

direction of movement and one bit to control it turning around when it reaches the

edge of the sceen, we can use one bit per flag for a total of two bits. This adds up

to seven bits, I put all seven bits in one variable, and made sure that single unused

bit is being masked in all operations, so that unused bit can still be utilized for

something else later.

---Shared sprite data---
Four of the five enemies have the same sprite, so I just have one copy of the sprite

data and use an indexed loop to set the other three pointers to that one, saving a lot

of bytes that would have been used by the three redundant sprites.

---Understanding NUSIZ and CTRLPF---
So I wasted time reverse-engineering the NUSIZ and CTRLPF options when all this

information already exists somewhere online. Anyway, once I understood what each bit

was being used for I found that using binary made it much easier to work with. Doesn't

save much, just makes the work go by faster.

---ASM init's---
I've been rewriting all the variable inits to optimized ASM. Overall a lot of LDA's

can be removed or changed to shifts and while each change is small it can add up.

---A world without BCD---
I've stripped every single BCD number out, in effect everywhere that used a BCD

previously now uses a binary number which overall means a pretty big savings in bytes.

---Low cost logic---
I use things that have reliable values to drive multiple events, rather than waste by

using a seperate variable for each action. On the title screen a single 1-byte counter

is being used to fade in the title screen, play/stop playing a sound effect, and

restrain the fire button at the same time. This same counter is being used in the main

loop for an enemy respawn timer, to adjust the probability of player1 attacking, and

to slow down the speed at which player0 takes damage at the same time.

---simple math only plz---
I take the time to make sure the math is simple... most of the numbers are divisable

by two. Actual divison is used sparingly and always in powers of two. I use bit

shifts. The same logic is applied to the vast majority of my comparisons, I focus on

checking for zero or nonzero instead of comparing to numbers whenever possible.

---simplified AI---
I control one of the five enemies with one bit for it's AI. The other four enemies are

controlled with one loop, with just a few lines of code outside of the loop to allow

one of them to shoot missiles.

 

 

 

So anyway, I've got the sprites displaying correctly, I have a good chunk of game logic in place, and at this point I'm up to ten screens and have 558b left. I am sure I can reclaim more bytes, and that i can fit plenty more in. I'll try to keep updating as things happen instead of as a retrospective.

Edited by Sknarp
  • Like 2

Share this post


Link to post
Share on other sites

Reclaiming bytes via kernel

 

558b free

 

Spoiler

So in the previous post I mentioned that I had 558b free and that I was sure I could reclaim more. First off, that 558b was with the debugscore on, which uses 190 bytes more than no options, or 170 bytes more than the noscore option. So if I was to set it to const noscore=1 then I would have 728 (558+170) bytes free. I don't plan on using the score at all so that's the best I can do right? Well, not if we actually dig into the multisprite kernel and see that a lot of that space is being used for the score routines, which we don't need. After a bit of trial and error and  a few extra STA WSYNCs, I was able to make a scoreless MSK. In total this saved 86 bytes! Free space: 814 (728+86)

 

814b free

 

So that saved some bytes, but here's another idea, which DOES NOT save any bytes, but this shows how I went about testing it.

Reclaiming bytes via RAM sprites
 

Spoiler

 

 rem EXAMPLE ONE
 rem drawing an arrow to player0 sprite in ROM
   player0:
   %00000000
   %00010000
   %00111111
   %11111111
   %00111111
   %00010000
   %00000000
end
 
 rem EXAMPLE TWO
 rem using RAM s-y
 s=0 : y=0 :  player0pointerhi=0
 t=16 : x=16
 u=63 : w=63
 v=255
 player0pointerlo = $E9
 player0height=7

 rem EXAMPLE THREE
 rem using ram s-y and in ASM
 asm
 lda #0
 sta s
 sta y
 sta player0pointerhi
 lda #7
 sta player0height
 lda #16
 sta t
 sta x
 lda #63
 sta u
 sta w
 lda #233
 sta player0pointerlo
 lda #255
 sta v
end

 

So the second and third example both work just as well as example one, and I was willing to remove "LDA #255" since it would have sped up the routine and would only have caused there to have been two dots in the middle of the arrow, I could live with that. The problem was simply that doing this doesn't save any ROM at all... I believe this is because of the images being stored as bytes that need to be page aligned, so this doesn't circumvent that requirement. I scrapped this idea because it's not useful in this application.

Share this post


Link to post
Share on other sites

Saved another 124 bytes by replacing 10 if-then statements with 1 on-goto and shortening labels in the

part of the code that changes the playfields. This was something I had been meaning to do for a while,

but got sidetracked with trickier optimizations.

I still have to program the second game mode, win conditions, and reset switch support. Right now there

are 10 screens (not counting the title), I'd like that number to be 20 or 30 so I have a lot more pixel

drawing to do. Currently at 960b free.

Title screen, shows how CTRLPF manipulation can be used to get around MSK limitations.

WIP_02_TITLE.thumb.png.07f99faa6c7749a8dcc9d80b05f0a3b6.png

Share this post


Link to post
Share on other sites

Today's improvements: gave the sprites more character, freed up some RAM for a change.

I managed to remove the final bit of the score routine, which was swapping around temps and

scorepointers. This means lots of extra RAM:
 

Spoiler

$bf
$c0
$c1
$c2
(C3 is playfieldpos)
$c4
$c5    
$c6    
$c7    
$c8    
$c9
(CA to D0 are temps and vars used for player repositioning)
$d1
$d2
$d3

13 variables that are unused (4 from not needing a minikernal, 6 from the scorepointers, and 3 from the

score). I'm not sure if temp1,temp2 and temp6, temp7 are usable, but if they happen to be then the second

chunk would be 8 spaces long instead of 6 and the third would be 5 long instead of 3. Contiguous space

is always nice to have. Right now I'm only using variable a-i, that leaves j-z(17) and the freshly freed variables(13) for 30

free variables. I also took some time to make the sprites look a bit better.
WIP_03_SPRITES.thumb.png.67c99fdf33b9fac377eb73c6ac93e97c.png 

  • Like 1

Share this post


Link to post
Share on other sites

Daily Progress Report: Kernel improvements, adding more playfields.

Did some more editing of the MSK, moved some subroutines into the empty space previously occupied by the scorepointer routines, this saved an additional 22 bytes. Then I went into the scoregraphics, deleted all of the bytes used for drawing the numbers, and adjusted the ORG offset to save another 80 bytes that was being wasted defining the score digits 0-9 This puts the free space over 1K (960+22+80)

1062 bytes of ROM free. 30 bytes of RAM free.

After adding a couple more playfields, I noticed that the last row the playfield was being printed twice. In the kernel there was a few STA PFx commands at the bottom that I had removed. When I put them back in, it fixed the double row issue- but it brought back a pretty common bug- there was a few pixels of extra playfield, 1 scanline high by about 1 pfpixel wide. I went back to the bottom of kernel and moved the PFx opcodes to the top of the list of STAs so they happened a few cycles earlier, and finally the bottom of the screen is totally clean. Adding two more playfields brought me down to 916b free - even though the playfield only uses 19 bytes of data there's an unfortunate amount of padding. I don't think I should devote any more time to trying to shave off bytes though, instead I'm just going to see how many playfields I can realistically fit in while still allowing myself a little bit of room for the game modes I have planned.

Share this post


Link to post
Share on other sites

It occurred to me that the "extra playfield pixels" I referenced in the last post could be re-purposed to do something useful. I had 40 cycles of free time at the bottom of the display kernel, I wrote a simple hack that uses 28 of those cycles to draw the contents of a variable to PF1, I chose the freed up "score" variable for this, added a little bit of code to the main loop and.... now I have an HP bar. The player's color was the only indicator of health before, going from bright yellow to dark yellow to signify shield, and bright white to dark grey to signify ship damage. I admit it was a little convoluted but it was the most efficient way of doing it I could think of at the time. Now there is an HP bar just below the bottom of the playfield so there is an additional indicator of remaining HP. The only issue with the way I implemented this is that there is a mirror/repeat of the HP bar on the right side. Please try the binary and tell me if this works, or if I should just remove it. Note: This is still a WIP so gameplay is not 100% implemented yet- but there's enough for you to play around with for the purposes of this test.

WIP_AA_HPTEST.bin

  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)

Small fixes:
I forgot to mention in the last update that I put a cap on the counter which controlled when the player could shoot- this fixed a glitch where sometimes you couldn't fire right away (because the counter had wrapped back to zero) After posting the binary, I also took another look at the title screen and decided that some of the letters looked too blocky, so minor edits to the title screen.

The HP bar hack:
One byte of RAM is used to be able to draw to 8 pfpixels right below the bottom of the normal playfield area. This was based on a suggestion I received to add a health bar. (This is the binary that I posted) After posting the binary,  I then received another suggestion that the health bar should change colors, so I used one more byte of RAM to be able to change the color. Now I have an HP bar that goes from green (full HP)  to yellow (when HP is below 45%) to flashing red (when HP is below 11%) This trick takes about 100 bytes to pull off, mostly because of the conditionals used to decrease the size and change colors of the bar, making it the least optimized part of the whole program. 916b of free ROM (with 12 screens) becomes 806b free. The 30 bytes of free RAM becomes 28 bytes. I'll have to really evaluate if this feature is worth the cost, but for now I'll leave it in until space gets crunched more- at which point I may either need to remove it or (hopefully) find a way to optimize the conditionals.  

Adding playfields
Further changes since the last post. I added a couple more playfields, this brings it up to 14 playfields with 660b free. After looking at what I have so far, I shuffled the default order of the playfields around so that there's better continuity from one screen to the next. I still need to add a few more, then I can start programming in the different game modes.

EDIT:

I figured out how to draw the HP bar on ONLY the left side of the screen, but to do so I have to draw it as 2 scanlines tall instead of the 3 scanlines tall it is in the binary.

WIP_04_HPBAR_2SCANLINES.thumb.png.150e7f7f498a06606e7a17c9a067feb4.png

Edited by Sknarp

Share this post


Link to post
Share on other sites

Player0 sprite:
The player0 sprite wasn't completely centered, This was easy enough to fix

Spoiler

 rem old sprite
   player0:
   %00000000
   %00011111
   %00111110
   %01111100
   %00111110
   %00011111
   %00000000
end
 rem new sprite
   player0:
   %00000000
   %00011110
   %00111100
   %01111000
   %00111100
   %00011110
   %00000000
end


The sprite looks almost exactly the same during gameplay, but now won't be offset if you turn around in a

tight area, this helps to ensure you won't get stuck in a wall.

 

Kernel Timing
I used something like 15 cycles where I had 12 to work with, there was always a bit of room to go a little

over-cycle at this point in the kernel (I belive at least 10-20 over was the wiggle room), but I found

that one of the opcodes was unneeded (STA PF0), so I removed it and should be cycle exact now.

 

Two Week Progress Report
Currently at 16 screens (not counting the title), and 528b free. I also still have 28 variables free. I am

now 14 days into development of this game. I still have some time this evening so I might still get more

done tonight.

 

Share this post


Link to post
Share on other sites

Switch Support & Game Modes

When I was programming in the switch support I saved 4 bytes on every switch check by using bit checks instead of bB keywords
 

Spoiler

 

Instead of:
 if switchreset then goto title
I use:
 if !SWCHB{0} then goto title


 


Title screen now supports Select Switch as well as Fire.

Main game now supports Reset Switch.

Win Screen supports both the Select Switch and Reset Switch.

Two game modes (LEFT "Difficulty" Switch) and Two difficulty settings (RIGHT "Difficulty" Switch):

 

Quote

LEFT Switch in B position: Default screen order, enemies respawn on a timer. (Auto Mode)
LEFT Switch in A position: Random screen order, enemies all spawn at once and don't respawn until you
change screen. (Random Mode)
Both Modes: Kill a specific number of enemies to win
RIGHT Switch in B position: Kill 100 Enemies To Win, HP restored every 25 (Easy Game)
RIGHT Switch in A position: Kill 250 Enemies To Win, No HP restoration (Hard Game)


Less than 150b left. Let me know if there are any game-breaking bugs I need to fix.

New binary! Enjoy :)

WIP_ATAX-ATAX_by_SKNARP.bin

  • Like 3

Share this post


Link to post
Share on other sites
3 hours ago, Random Terrain said:

Did you mean to have some of the enemies move in a jerky way? Some people use fixed point variables to have enemies move at different speeds while still moving smoothly.

Is it their movement speed, or is the jerkiness happening when you are on the same Y as the enemy? If it's the later, I think it's a kernel-related issue that's over my head.

Share this post


Link to post
Share on other sites
2 hours ago, Sknarp said:

Is it their movement speed, or is the jerkiness happening when you are on the same Y as the enemy? If it's the later, I think it's a kernel-related issue that's over my head.

 

Move your ship to the bottom and stare at the bottom 3 enemies. The top two seem to be smooth, but those bottom 3 move a little, then stop a little, move some more, stop a little, and so on. Very jerky movement, at least on Stella.

Share this post


Link to post
Share on other sites
Posted (edited)

I tried to use FP, hung the compiler BAD - eventually got it to work, but it didn't actually have the desired effect because of decimal points being truncated when applied to an integer like x positions.
Hanging the compiler (I assume) caused me to eventually be unable to compile any code-  and also caused some sort of leak that was making my entire computer slow down.
I was able to fix ability to compile by moving the most vital files to a new directory (The old directory will be deleted just to avoid that happening again) and the effects on my computer was mitigated by excessive swearing and a forced reboot. (I probably needed to do a fresh restart anyway, I'm lazy about those)
At the moment I am not intending to use Fixed Point anything for anything :P I will still try tweaking the for loop to see if I can make it look more fluid

Edited by Sknarp
asdfasdfdsf

Share this post


Link to post
Share on other sites

You shouldn't be using any for-next loops for enemy movement, so that's probably where the problem with jerkiness is coming from.

 

Does this fixed point variable example program compile for you:

 

https://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#ex_fixed_point_sprite

 

If it does, you were probably doing something wrong. It uses code like this:

   ;```````````````````````````````````````````````````````````
   ;  Player0 fixed point variables for more flexibility in
   ;  gameplay mechanics.
   ;
   dim _P0_L_R = player0x.a
   dim _P0_U_D = player0y.b

 

And this:

  _P0_L_R = _P0_L_R - 0.52

Share this post


Link to post
Share on other sites

It's done to save on space, indexing the memory space of one player and incrementing the index/offset..It's probably better I just show the code rather than fail to explain it. If you want to compile this you'll need the modified includes in the same directory, otherwise there's not enough free space.

WIPsourceAA.bas multisprite_kernel.asm score_graphics.asm

Share this post


Link to post
Share on other sites

While looking over the code, I reformatted the comments so it would help me keep track of where I was. I also changed some of the code here and there to save a few bytes or maybe even a few cycles. For example, I got rid of some redundant collision checks.

 

I eventually saw that the "diceroll/32" skip in the for-next loop you used to slow down the enemies was the cause of the jerkiness, so I got rid of that and changed your enemies to fixed point so they now move smoother. If you keep it in, you can change the numbers to whatever speed you'd like.

 

That for-next jerkiness is gone, but an enemy will still jerk whenever the player moves to the same horizontal position. That's because of the change you made to multisprite_kernel.asm. If you add more banks, you might want to get rid of your version of multisprite_kernel.asm and use the original. That will eliminate the weird jerkiness of the enemies whenever the player moves up and down.

 

sknarp_2019y_07m_30d_0607t.bin

 

sknarp_2019y_07m_30d_0607t.bas

  • Like 3

Share this post


Link to post
Share on other sites

Nice, added FP only using 74 bytes- most of which are if statements unfortunately but it might be well worth the cycles for sure.
Could I ask a couple of questions about the opts you did? I noticed in the for loop for movement you used <> instead of typical equivalency (= or !=)- I couldn't find much information on if this has advantages- is it faster? Does it take less bytes?
Also in the main loop you combined the collision checks, but on the win screen you separated out the switch checks, Which is better: using a bunch of &&'d ifs in one line or doing them as single checks over seperate lines?
I really appreciate your help and input- your website is one of the main things I refer to when I am curious how to use a function of bB.

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, Sknarp said:

I noticed in the for loop for movement you used <> instead of typical equivalency (= or !=)- I couldn't find much information on if this has advantages- is it faster? Does it take less bytes?

 

If you're talking about where I jump over the added if-thens with the fixed point movement, I had to reverse the condition:

 

https://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#endif

 

 

Quote

Also in the main loop you combined the collision checks, but on the win screen you separated out the switch checks, Which is better: using a bunch of &&'d ifs in one line or doing them as single checks over separate lines?

 

You had "if !SWCHB{0} || !SWCHB{1}" but the AND/OR/NOT Chart says that we can't use NOT with OR. I think you did that in a couple of places, so I separated them.

Share this post


Link to post
Share on other sites

Alright, so I went ahead and tested if <> has any advantage over !=, because I would love to know of any way to optimize- unfortunately it makes zero difference. In my tests of four conditonal checks using != vs the same four checks rewritten with <> instead, both sets of four checks take up exactly 32 bytes of ROM (8 bytes per conditional check) and take 64 cycles (16 cycles per) so it's just a matter of programmer's choice of style.
I really don't understand why you're saying I can't write a conditional that uses a ! token and an || token... There's no reason as to why you can't in the linked page, and the "more information link" just brings you to another copy of the same warnings with again no explanation and a link back to the first verbatim warning. Also it does seem to work just fine, and even if the chart is accurate it says you can use OR with = and the condition if !varthis || !varthat is the same thing as saying if varthis=0 || varthat=0 so I'm really confused on WHY it would only work when written one way, Is there a source post somewhere that explains where the chart came from?

Share this post


Link to post
Share on other sites

It came from the creator of batari Basic. Some things that are not allowed will compile, but you can end up with weird problems later on and not know why.

 

I sent a PM to someone to see if any of those things in the chart had been changed over time without telling the rest of us. It may take a few days to get a reply.

 

I don't know if something changed, but we could only use <>. != wasn't an option.

 

About OR and NOT, why can we only use one OR? Why can't we use AND and OR? It is (or was) the limitations of the language. After I get a reply, we might find out that some of those limitations no longer exist. If so, it would have been nice to have been told.

  • Thanks 1

Share this post


Link to post
Share on other sites

Thank you, that would be an awesome bit of information to have for sure. I've been using the syntax if !var1=var2 then a lot so if I need to change it to if var1<>var2 then it's not going to cost me any efficiency and I'll just adjust my style, but if it's safe to do it either way it's good to have the flexibility. As for using if !myvar then instead of if myvar=0 then I can't find the exact post right now but I'm positive I got the idea from some previous post on AA about space saving techniques, so it's important from an efficiency standpoint to know if you can't use ORs with the trick. I understand that it'll be a while before we get the definitive answer, I might just do a preemptive rewrite to be safe either way.

Share this post


Link to post
Share on other sites

I got a reply:

 

Quote

It looks to me like "if !a || !b then" returns reasonable results. So NOT seems to work with OR.

The rest of the stuff seems true.

 

I'll update the bB page.

 

He also said this:

 

Quote

!= isn't valid bB syntax.

 

About using NOT, bit operations do not use an equal sign in if…then statements:

 

https://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#bit

 

I think someone once posted that "if !_Fried_Pickle then" is faster than ""if _Fried_Pickle = 0 then". I know for sure that the "= 0" version uses two extra bytes.

  • Like 1

Share this post


Link to post
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.

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