Jump to content
IGNORED

"Sierra Maestra", an Early WIP


vidak

Recommended Posts

PS. One weird thing I'm noticing is that the Stella debugger formats my graphics data differently every time I compile a new version of the binary. Sometimes it recognises the graphics data as graphics, and sometimes it doesn't...

 

The disassembler in Stella is greatly improved for the upcoming release, so hopefully that will fix your specific issue here.

Link to comment
Share on other sites

Moving the GRP1 graphics table labels to the top of the table, and deleting any adjustment numbers in setting up the pointers improves the situation.

 

The problem is, I don't know why. There are 17 bytes of zeroes, yet everything but the top 2 lines of graphics data is displayed.

 

Also the GRP1 colours remain unchanged from before.

 

Interesting...

coloured_guerrilla025.asm

post-61331-0-44645900-1517101887_thumb.png

Link to comment
Share on other sites

Okay I think I know why.

 

The graphics are being loaded from the bottom of the table. The padding of 15 lines pushes the graphics data far enough down to make it ready to be loaded on the first scanline it is called. say we have a band which is 40 lines long. 40-2 = 38 (see below). The height of the temporary GRP1 graphics is 23. So 38-23 =15. The graphics need to be padded to the height of the band, and the last line of the 38 byte long table is the first byte always loaded.

 

I am also not drawing GRP1 on the first two scanlines of every band up and down the screen.

 

The reason why the colours are wrong still evades me. Screen band 0 is more or less correct, but none of the others are.

 

That leads me to believe I've messed up the code which copies over the pointers from Band 0's pointers.

 

GRP0's shape changes on the RIGHT side of the screen. Perhaps this is a kernel timing issue... this is what I mean when I talk about artefacts.

  • Like 1
Link to comment
Share on other sites

Alright it's the weekend here in Sydney, so I have time to work on the game!

I will do a live-update debugging for anyone who is interested. I find this method of debugging is the best for me because I can track EXACTLY what I did after every compile.

 

I have fixed the colouring problem of the enemy characters by manually copying over the graphics pointers from EnvGfxPtr and EnvClrPtr:

    lda EnvGfxPtr
    sta EnvGfxPtr+2
    sta EnvGfxPtr+4
    sta EnvGfxPtr+6
    sta EnvGfxPtr+8
    
    lda EnvGfxPtr+1
    sta EnvGfxPtr+3
    sta EnvGfxPtr+5
    sta EnvGfxPtr+7
    sta EnvGfxPtr+9
    
    lda EnvClrPtr
    sta EnvClrPtr+2
    sta EnvClrPtr+4
    sta EnvClrPtr+6
    sta EnvClrPtr+8
    
    lda EnvClrPtr+1
    sta EnvClrPtr+3
    sta EnvClrPtr+5
    sta EnvClrPtr+7
    sta EnvClrPtr+9

I can identify two problems:

 

Number One

 

The colour of Che seems to shear on the right side of the screen. The graphics shape of GRP0 is correct, but the graphics seem to be applied one line BEFORE they're supposed to on the right side of the screen. I will use the Stella Debugger to have a look at this.

 

Number Two

 

This is very confusing. The last line of the GRP1 graphics loads properly, but seems to have the colour $00 applied to it, instead of $FE, like it is supposed to. I checked the Stella debugger, and the colour that is being loaded is definitely $FE, which is bright yellow, but it seems to show up as black. This is very confusing.

 

My best guess for why this is happening is that I am setting COLUP1 earlier on in the kernel before i WSYNC lower down in the kernel, and then I am drawing GRP1 right after the WSYNC at the top of the kernel. I thought these two TIA register writes were in sync, but maybe the colour for GRP1 is being written again after I have drawn it.

As always, if there are any people reading this before I update again, feel free to jump in and give your opinions.

coloured_guerrilla027.asm

guerilla27 bin.zip

post-61331-0-33181000-1517626411_thumb.png

Link to comment
Share on other sites

Okay changing the temporary enemy graphics colour table from this:

BatistaSoldierClr
    ds 15, $00

    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE     <- 23 bytes of $FE

To this:

BatistaSoldierClr
    ds 14, $00

    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE
    .byte $FE   <- 24 bytes of $FE

Fixes the issue. Which leads me to believe that I am right about COLUP1 being written to out of sync.

So instead of doing this in the kernel:

.KernelLoop

     sta WSYNC

     lda CheGfxTemp
     sta GRP0
     lda EnvGfxTemp
     sta GRP1

<kernel code>
     ...
     sta COLUP1

     ...

     dec SCANLINE_COUNTER
     bne .KernelLoop

I think I will have to do this:


.KernelLoop

     sta WSYNC

     lda CheGfxTemp
     sta GRP0
     lda EnvGfxTemp
     sta GRP1
     lda EnvClrTemp
     sta COLUP1


<kernel code>
     ...
     sta EnvClrTemp

     ...

     dec SCANLINE_COUNTER
     bne .KernelLoop

I think this will fix the sync issue, at the cost of a less efficient kernel.

coloured_guerrilla028.asm

post-61331-0-10695500-1517627162_thumb.png

Edited by vidak
Link to comment
Share on other sites

Okay I have rejigged the kernel. It is now a lot faster, even though it doesn't allow GRP1 to touch the left side of the screen. The whole kernel takes 41 cycles now, instead of 53 cycles.

 

I am loading the colour table for GRP1 synchronously with the graphics, and this fixes the indexing issues for the colour of GRP1. PROBLEM ONE IS SOLVED.

 

Here is another improvement: now the colour table for GRP0 is CONSISTENTLY off by one line. This means there must be a static indexing issue.

 

To the Stella Debugger to see what the problem is!

post-61331-0-90770900-1517630766_thumb.png

coloured_guerrilla029.asm

Link to comment
Share on other sites

OKAY FIXED.

 

What I am uploading here is the final version for this stage of the game's development!

 

What I still need to do is:

 

- Create a network of screens for Che to move through.

- Create the logic for the movement through this world: (i.e. when Che moves off the left side of the screen, he appears on the right side of the next screen to the left of that screen)

- Create movement routines for the peasants and the batista soldiers, maybe simple AI to this effect

- Create graphics for the peasants and the soldiers

- Create graphics for the jungle environment

- Perhaps add more tasks for the kernel, seeing as I am only using 43-44 cycles for the entire kernel. This is only 58% of the maximum cycles needed for the kernel. So I think I may add in playfield graphics.

 

THEN

 

- Create the kernel and logic for the "Lock and Load" portion of the game, after I have completed the above portion of the game, the Overwold portion.

Guerrilla32_bin_FINAL.zip

coloured_guerrilla032.asm

post-61331-0-69632200-1517635996_thumb.png

  • Like 2
Link to comment
Share on other sites

Okay! I am dedicating some time to the project today.

 

The next thing I'd like to achieve in the development of the game is to create an overworld map, and have the character Che be able to move around in it. I have been looking at the source code of Anguna (link) to have some idea about how I'd be able to achieve this. Anguna has a 13x13 unit square overworld, and you are able to move from one screen to another.

 

The rough idea behind implementing an overworld is that you have a table of data describing what the overworld is like, and as your main player character moves off one side of the screen, you manipulate a counter or a pointer so that it points to a different part of the overworld table. I think I can handle this.

 

I think I will not have enough space in 4KB in order to implement an overworld and the main game mechanic. I will expand the game to 16KB. I think F6 bankswitching will allow me to achieve this. This is how gauauu structures his 4 banks:

 

BANK 0 - Main Loop Logic, "Preparation" Macros - I assume this is for updating graphics pointers etc.

BANK 1 - main kernel, header graphics kernel, some code related to the third boss, and the graphics ROM

BANK 2 - Header pointer preparation logic and game logic crammed in between the spaces

BANK 3 - password entry, and room background graphics preparation

ram

 

I take it that the banks are structured in such a way as to best fit all the data in, not necessarily for easily interpreting the structure of the game program.

 

I have begun learning the programming language Ada for another project I want to work on, and the way gauauu has structured their code into smaller packages agrees with the way coding is meant to be done in Ada. So far I have been compiling a rather large single file. I think I will begin to break my program up into smaller files and make much more use of macros in order to reduce the amount of engineering I have to do. Making the tools do the work is the way Ada pictures the overall development process of software engineering. I will use this philosophy here on out while programming the Sierra Maestra project.

 

For the next few weeks I will begin a description/explanation of how Anguna implements a multi-screen world that you are able to move through. The licence that gauauu has provided the Anguna source under allows me to do this.

Link to comment
Share on other sites

Your project is coming along nicely.

 

Just a side note about having lots of files vs one large file. Besides the obvious benefit of better organizing your project it can also help with compile times. This isn't an issue for an Atari game, because they're so small. For larger commercial projects the compile time can be significant. Having many source files and a proper make file which only builds what's necessary can make a big difference.

 

Regarding organizing the banks. You'll want to keep kernels and corresponding data in the same bank for performance reasons. I.E. don't switch banks while racing the beam. My understanding is that you're going to have a few different render modes for this game. I'd put each one in its own bank and fill the remainder of each with corresponding graphics data. Everything else should go into a different bank to start. Then you can move stuff around as space gets tight.

Link to comment
Share on other sites

Oh dear. My source code for Anguna is such a mess, I'm terrified that you would use it for an example.

 

That said, most of what you said was correct about the game code. A couple notes: Bank 2 is mostly enemy definitions with some other bits and pieces as I had space. Bank 3 has all the room definitions.

 

Anyway, feel free to ask questions about how I did things. Often the answer, though, will be because I was learning and didn't know any better until it was too late :-)

Link to comment
Share on other sites

Thanks so much for your responses!

 

Another idea I'm having is to develop some reuseable routines for people to easily copy to allow them to make games in ASM more easily.

 

The multi-band kernel I have is very easily copied and used, and I think copying and manipulating gauauu's multi-screen overworld could also be presented in a simple way.

 

I will certainly ask questions when I feel stuck!

 

I agree that the graphics ROM and the kernel should be in the same bank - I think that's the fastest way. In fact I think that's the only way you can structure the bank switching - any other way would be incredibly and possibly prohibitively complex...

 

Do you think it is worth me explaining how the multi-screen overworld is implemented in Anguna? If so, would it be better do it in this thread, or on my AtariAge blog?

 

I wonder if I'm forgetting anything...

 

===

 

Anyway, I love how naughty ASM is. It has absolutely no enforced typing whatsoever, so you can, as in Yar's Revenge, make a pointer load /any/ part of the ROM for /any/ purpose. All the naughty tricks that you're able to pull off with the 2600 completely depend on the fact that you almost always program in ASM. It's such a wonderful platform - the hardware is so adaptable and really wants to help you. HMOVE rules can be broken with useful results, etc etc

I knew I really liked the 2600... coming to know it more intimately gives me a lot of satisfaction and fulfillment.

  • Like 1
Link to comment
Share on other sites

 

 

Do you think it is worth me explaining how the multi-screen overworld is implemented in Anguna? If so, would it be better do it in this thread, or on my AtariAge blog?

 

 

Couldn't hurt -- more explanations out there of things are never a bad thing, particularly if it helps you understand how you want to design your world layout.

Link to comment
Share on other sites

I've been giving it some thought, and I think I might not create a static world placed in ROM for this game, and instead randomly generate a world through which to move.

 

I was reading what was going on in the IntyBASIC forum, and I really liked this port of Grail of the Gods: http://atariage.com/forums/topic/275233-grail-of-the-gods-rogue-like-or-mini-rpg/

I think the main problem that a randomly generated world will create is that it will be heavy on the RAM - a real premium in this context.

 

I will do some brainstorming and thought experiments to draw up some ideas about how to randomly generate this world.

 

I think random generation will be a better idea for this game. It will allow you to replay the game over and over and get a different environment each time.

Link to comment
Share on other sites

hi vidak, i saw your source code,

 

do you remember that i'm looking for a simple 127 2LK or a 127 one line KERNEL, with masked graphics example?

 

i really cannot understand your game, i have tried, may be if i try to understand frosty but i don't need a frosty setup for pf with bands and all that.

 

is there a simple way to learn masked graphics ?

 

ty anyway Vidak foy you excellent work.

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------

UPDATE: VIDAK I`M STUDYING ALL YOUR BLOG, ALL YOUR CODE, IT'S A MUST.

 

CHEERS.

Edited by zilog_z80a
Link to comment
Share on other sites

masking requires several things:

 

- a pointer to the mask

- a mask which is the same size height as the sprite graphics

- the mask must also be padded with zeroes to make the entire block equal to the number of scanlines on the screen (NB - 2 line kernel)

- a pointer to the graphics

- the graphics ROM (usually stored upside down)

 

- a kernel, with:

 

a structure like this:

 

LDY SCANLINE_NUMBER

LDA (GFX_PTR),Y

AND (MASK_PTR),Y

STA GRPX

 

that's the basics. you can find all of this in my blog posts on stay frosty, and you can find all of this in my source code, which I think is adequately commented. If I have some time I may write up a quick tutorial.

 

I am not using a 2 line kernel in my game, but depending on what you want to achieve, masking speeds up the kernel so much you may not need a 2 line kernel. masking should enable you to draw: GRP0, GRP1, /and/ some playfield graphics.

 

Keep at it, I reckon. I think you'll get it eventually. Personally I think the hardest thing about kernels is the pointers. They're type-independent objects so they'll load whatever they're pointing at.

  • Like 1
Link to comment
Share on other sites

The big trick that masking achieves is transferring the work the CPU has to do in real-time calculation to pre-calculating it in ROM. It's a wonderful trick. In fact engineers in embedded programming do this. Instead of storing information in RAM, you store it all in ROM, and it saves space and speeds up performance. This is a good idea with the Atari 2600 seeing as we only have 128 BYTES of RAM. Because bankswitching is well understood now, you should make the ROM do as much of the work as possible.

 

For instance what I've done for the GRP1 graphics drawn in the separate bands is just pad their graphics with $00 for 15 to 17 lines. This way if I want to move them vertically, I just adjust the pointer - I either load it sooner or later up and down the screen. This saves you many, many cycles. SkipDraw, DoDraw, SwitchDraw and FlipDraw are many times slower than just padding the ROM and messing with the pointer.

 

Anyway, there is a good DoDraw 2 line kernel on 8bitworkshop.com under the heading Complex Scene 1 and Complex Scene 2. I learned a lot by studying that code.

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

OKAY. I AM RETURNING TO THIS PROJECT.

It has only been 39-40 days anyway. A lot of my friends and party members are always asking me about this game, and their positive encouragement recently has made me want to return to development. If I keep making games people are interested in, I think I'll never want to stop working on them!

 

I still have a lot of time left in the kernel for drawing the screen, so I think I can return at some time and include playfield graphics or missiles. But I will do that later.

 

I will call this next phase "Phase Three" - and I will dedicate it to creating a multi-screen world in which for you to move Che around in. "Phase One" was me learning how to draw the screen and a moving multi-colour character AT ALL, "Phase Two" was creating a multi-band kernel.

 

What I want to achieve in Phase Three is a randomly generated multi-screen world. I think I will achieve this with a polynomial counter. Anguna has a 13x13 screen world. I think I'll aim for that size.

 

I was thinking "how would it be possible to create a two-dimensional world using a polynomial counter?" I thought maybe I would have 13 individual different polynomial counters for each row/column of the square world, but after some daydreaming I thought perhaps a single polynomial counter that snaked down the world like this would probably work best:

|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-8-|-10|-11|-12|-13|
|-14|-15|-16|-17|...|---|---|---|---|---|---|---|---|

What would happen is that if you moved up one cell, the polynomial counter could decrement by 13 positions, and if you moved down a cell, it would increment 13 positions.

 

The benefits of randomly generating a world are:

  • I would not have to store a complete isomorphic representation of the world in ROM
  • Players could still have the option of playing the same world again by entering a seed number
  • Players would be able to share interesting worlds by sharing seed numbers
  • Players would have the choice of randomly generating a world through a title screen menu
  • Random generation would add replay value

I don't fully understand polynomial counters yet, but I anticipate that a big disadvantage of using them would be that they would use up more RAM. However as I am writing this, I don't see how this would really be so, as the polynomial counter would really just be counting up and down for a single screen. The counter just moves up and down its one-dimensional pathway, and it gives the illusion that there is a permanent world cast out into two-dimensional space. Really there is just one screen, being loaded with different elements depending on what position the counter is at along its pathway.

 

I will reduce the number of bands in the multi-screen kernel from 5 to 4. I want to give the moving enemies and friendly NPCs more room to randomly move around on the screen. As it stands, I am not happy with the amount of vertical room that NPCs have to move.

 

So the polynomial counter will have 13 screens x 4 screen bands through which to step.

 

I want there to be a sense of progression to the game, so I will add in a finite number of bosses. I am hoping that the polynomial counter will generate, say, the locations for 5 bosses randomly throughout the world without needing a separate subroutine, but what I may do is have the polynomial counter for the total world to distribute trees and NPCs, and, eventually, playfield graphics, and then a separate routine to nicely position the five boss garrisons throughout the world.

I want the boss locations to be kind of like "castles" -- they'll be a barracks or a big building into which you enter.

 

======

 

Work timeline:

I think I will be able to actually put some effort into coding on tuesday next week, because it's a holiday here in Australia, and my girlfriend has time off work and some family members are flying 4000km to come stay in my city for the holiday.

 

======

Social encouragement:

Finally, I want to say that if you're like me, and you've been finding this year really hard emotionally, I'm always here to talk. It has been very difficult finding steady work, and I am not sure I actually want to continue in the career of academic philosophy after my PhD, so I am naturally feeling worried about my scholarship running out in 3 months.

 

If you feel ashamed about yourself, and if you feel pathetic - I'm always here to listen. Depression is an artificial mental state. It is also temporary. The reason why you (and I) might feel depressed is not because of YOU (or in my case, ME). Everyone well-intentioned person in this Atari community is a wonderful person, and deserves to pursue a life and career that is fulfilling and satisfying.

 

I have JUST gotten over a spell of deep, deep depression - AND paranoid delusions after testing to see if I actually do have schizophrenia, so I am very very relieved. I hope I can be the person to believe in someone else while they can't believe in themselves. I want this because I have so many people in my life who have believed in me while I couldn't.

 

FIGHT IT. YOU CAN DO IT. FIGHT AND WIN. YOU ARE A GOOD AND WORTHWHILE PERSON.

Edited by vidak
  • Like 5
Link to comment
Share on other sites

OKAY.

 

First of all, in between finishing Phase Two of the game development and starting this new phase, I learned how to use git properly, so you can find the repository of the source code for this game here:

 

https://github.com/bootlicker/guerrilla-game

I was able to begin the work on the randomly generated overworld map today, instead of Tuesday. I think tomorrow will be busy, and I will be able to return to work on this on Tuesday.

16-bit Linear Feedback Shift Registers

This person's blog series contains a useful rundown of exactly how a polynomial counter (a Linear Feedback Shift Register) works to create a one-dimensional sequence of pseudo-random numbers.

 

I opted for a 16-bit LFSR because I thought I had the RAM left over to do so. I was initially very excited to try and build a large world based on the two bytes of RAM, which would afford 65000+ screens of randomly generated content. I imagined I could generate a 256 screen x 256 screen square world.

 

I figured out that masking off 6 bits of the 16 bit number output of the LFSR would allow me to create 64 Choose 4 Combinations of unique screens across 65k+ screens. This would guarantee that virtually every screen on a 256x256 world would be a unique screen. A combination is the measure of the number of possible sets of some finite number of different objects that can be made from a smaller selection of that superset. A combination is different from a permutation because it doesn't care about the order of the smaller set of unique items. The fact that one of the superset's items appear at all makes a unique combination. If some other ordering of the same set of objects appears, combinatorial counting does not count that set again.

 

The main problem is, 256 horizontal screens is too large a number of screens to calculate in order one at a time.

 

This standard/famous code from batari takes at most 20 cycles to execute:

        lda Rand8   ; 3  3
        lsr         ; 2  5
        rol Rand16  ; 5 10
        bcc noeor   ; 2 12
        eor #$D4    ; 2 14
.noeor              ;
        sta Rand8   ; 3 17
        eor Rand16  ; 3 20

There are 37 x 76 cycles in Vertical Blank, and there are 30 x 76 cycles in Overscan, which means if I dedicated ALL the cycles in Overscan, which is currently empty for me right now, I would only be able to calculate at most 114 loops of the above.

 

So there is clearly a computational limit to calculating new rows of content in this pseduo-two-dimesional method.

 

I have done some brief research on two-dimensional LFSRs. This is the structure of the usual 2D LFSR:

 

6-Figure4-1.png

 

The CN network on the left are registers of identical length with different XOR taps. These different registers are then selected to output into a small array of registers of identical length. The control unit then chooses which output register to mux with which input register. N is the bit length of the registers, and M is the number of parallel registers. One register at a time is looped back around into the CN network, and one register at a time is outputted from the CN network.

 

This confirms my suspicion - I think in order to reduce the amount of cycles required to compute some non-single iteration of the LFSR, a large amount of RAM would be needed.

 

As I am writing this, I'm wondering if it isn't possible somehow to "buffer" the surrounding screens of the current screen the player might be inhabiting. Calculating the left and right screen only requires one iteration of the feedback register. I wonder if there's a mathematical trick to reduce the amount of cycles required to go through in order to calculate a new Y-index of the overworld, instead of a new X-index. Perhaps a lookup table? A lookup table could perhaps be used to do multiple loops through the feedback register, just a row up and a row down. It might be possible to do some algebra and find out what XOR $D4 to XOR $D4 to XOR $D4 to an unknown number is...

 

Otherwise every movement up or down a Y-index is a full-row's worth of screens' calculations...

 

There has got to be a faster way... Feel free to jump in and give your two cents!

 

==

 

On the other hand, one could remove the STA and LDA functions and speed the routine up quite a bit... You know, just have EOR $D4

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