Jump to content

JeremiahK

Members
  • Posts

    360
  • Joined

  • Last visited

Everything posted by JeremiahK

  1. I don't think it will be possible to do any fancy gravity effects of blocks falling through the black bars between rows. I can't think of a way to do this without splitting the entire kernel up into 4-line sections, which would greatly complicate everything.
  2. I thought of an algorithm for checking the matches: Matches are only checked when the game enters "match mode", which is triggered when a move is made. Loop through all the rows. Step through each block in the current row, starting at the 2nd one. Compare each block to the previous one, and if they are the same, increment a counter to keep track of the run. If they are not the same, or if the end of the row is reached, check the run counter. If it is a run of 3 or more, step back through the row, setting the match bits on the matching blocks. Reset the run counter, and finish the row. Then do the same thing for the columns. I doubt there will be enough time to do this all in one frame, so the work can be split up across multiple frames. Use a RAM counter to keep track of which logic frame we are on. Checking 1-2 rows and columns per frame should suffice. After the matches are found, the next step is taken. Maybe play a color animation to show the matches flash quickly, then remove the blocks, add to the score, and switch to "gravity mode". If no matches are found (an illegal move was made), move the blocks back, and go back to normal/move-making mode. In gravity mode, the highest black block, or hole, in each column is found. All blocks above the hole are moved down, and a random block is spawned at the top. After all holes are filled, switch back to move-making mode.
  3. Did you find a bug in the board layout in RAM? I think it's causing the issue with the cursor not going to the top row. Maybe I am misunderstanding how it works, but to center the board, I had to rearrange the order of player/missile/playfield objects. I wanted to make sure it was still displaying everything in the right order, so I used the debugger to slowly overwrite the board RAM with 0's to see which byte of RAM corresponds to which block in the grid. Everything was fine horizontally, but the vertical order seemed wrong. If you write 00 to $80, the top left block turns black. If you do $81, the bottom left block turns black. $81 is the next block up, and so on up the grid. Somehow the layout is not being set up correctly. I will have a look at it and see if I can fix it. I think leaving the RAM is fine, we shouldn't need the 32 bytes (probably?). I wanted to mention it now, because it will be more difficult to add this in later if we need to. I don't know how your match code is looking, add a branch to master for it and push the code to it, if you have already started on it. We can probably work on it together.
  4. One thing we need to do is restructure how the board data is stored in RAM. As it is, we use a whole byte for every block, which holds the color of the block. This makes it easy to draw the kernel, but it hogs half the available RAM, plus the temp buffer for inside the display. Since we only need to display 8 colors (7+1 to include black/blank slots), we can store each block color in exactly 3 bits. Add another bit for marking matches, and we can fit exactly 2 blocks into 1 byte of RAM, freeing 32 bytes. In order to display the board, in the black bars between rows, we can use the 3-bit values to index into the color table, and load them into the color buffer. Then do a cursor check to modify the value for cursor display. I think the best way to organize the bits would be like: %mMcccCCC where m=1st block's match bit, M=2nd block's match bit, c=1st block's color index, C=2nd block's color index. This way, the match bits can be easily accessed after a BIT instruction.
  5. Sounds good, I will adjust the obj's Xpos in RAM. We can keep that in mind for later if we need a little extra space. I think I can use copy mode, and if I do it right, I won't need to write to the playfield every line, just set COLUPF to black after the last PF block is drawn to hide the copies.
  6. I can work on centering the game board, I have been thinking about ways to do that. For program control, I am thinking it might be best to store game state info in RAM, such as attract mode, gameplay mode, gameover/screensaver mode. These bits are checked at the start of vertical blanking, and the code jumps to the correct logic routine. It might be possible to use one universal kernel, like a classic 2K game. Also, I see the object positions are being set the complicated way. Since they don't move, I can hard-code it to save a lot of logic time and some ROM as well. In fact, if we never need to move the objects, we can move the positioning routine to the init code, set and forget, and not waste any logic time at all. This would go along with centering the display, so I can tackle all of that today. A thought I had about cursors, with potential for a 2-player mode later, would be a direct on-off flash for player 1, and a pulsing light-to-dark effect for player 2. Could also be selectable with the difficulty switches (or game modes), if for some reason you may want the cursors to look exactly the same, kind of like "invisible tanks".
  7. Another option is to implement the move in such a way that a selection isn't neccessary. Use the joystick to move the cursor over the block you want to move, hold fire, and move the joystick in the direction you want to switch. You could process all this in the joystick code. When a directional input is detected, the fire button's state determines whether to move the cursor, or switch the blocks.
  8. I like what's going on here. I noticed the game is 2K. Are you planning on limiting the game to 2K? I love well-done 2K games, they have their own charming simplicity. I am actually currently working on a 2K yahtzee/farkle game, and by design I am limiting it to 2K. Coding 2K games can be fun, because you can focus more on polish. I have actually been thinking of doing a bejeweled game as well for a month or so. My plans mainly concerned the kernel. I have been trying to work out how to use the players/missiles to draw higher resolution jewel sprites, similar to Space Invaders. I don't think this is really possible for anything over 6 sprites wide, so your approach with simple squares seems to be the only solution, outside of DCP+. I saw a potential issue in the InitSystem portion of code. You are reading the first byte of RAM to initialize your RNG, but first you do a EOR #1 to prevent a value of zero. However, if $80's starting value is #1, the eor flips it to #0. If you pull up the debugger, set $80 to 01, and set the program counter to F800, every jewel is set to red. A simple fix could be to add a BNE before the EOR, skipping it unless $80 actually was zero. (I see MLdB mentioned this as well.) Also, I don't think it is reliable to use RAM for RNG initialization. Some consoles/setups work half-okay, others don't work at all. Here is a thread about RAM startup values. I have heard INTIM is the best option for this. About what to work on next, I would suggest deciding how to implement a cursor. One thing you could do is simply flash the selected block. You could either flash it on and off, or cycle the luminosity for the classic pulse effect.
  9. Are you using paddle controllers? The game only works with paddles.
  10. Yall crazies have fun a stay safe tonight!

  11. Late to the party, but this is some elegant coding right here!
  12. Nice demo! I always love your work! 512 bytes is always fun to do. I have yet to do a larger demo, myself, but the smaller ones are easy to manage, while still being big enough to do an effect well.
  13. Excellent! Thank you stephana. I had thought it was some sort of bug in my code, because I think it had worked in an earlier version awhile back.
  14. Yes, INTIM is what you are looking for. It's not located in the zeropage, and none of the registers for setting the timer are either, so even after clearing the zeropage, it is still unknown.
  15. It's been a long time... This week is my vacation from work, my first since getting my job almost 18 months ago. I have been slowly working on this project when I have the chance, but I am going to be spending a lot of time on it this week. I didn't feel the need to post an update until now. I have finished the music engine for the game, as well as the intro music. I took a lot of time to make sure the tempo was correct, and it works in both 50Hz and 60Hz. Holding Game Reset on startup will force 50Hz mode, and the COLOR/BW switch selects the color palette. This will allow for both PAL50 and PAL60. (I cut the size to 4K for this demo, so if you press Game Select, it restarts. Stella doesn't seem to be auto-detecting PAL properly, but the timing works on a real system.) I only need minor tweaks from here, such as syncing the animation to the music. And allowing to mute the sound with the difficulty switches. The plan is to have the animated stars scrolling past until you press fire, which is when the game detects if you are using a joystick or paddle. Initially, the game title will be displayed above the cat, and after a game is played, score information will be displayed instead. Below the cat will be a scrolling menu, with 3 lines visible at a time. I have already finished the code for displaying the text, I just need to make it do what I want... ? nyancat.bin
  16. Posting this here in a new thread. I saw the topic of RAM management mentioned in another thread, but I didn't want to hijack it. Obviously there are many ways to skin a cat, to each their own. I wanted to share how I like to do it. I would love to hear how the rest of you do this, as well. For my projects, RAM typically falls into 1 of 3 categories, global, local, or temporary: Global RAM: Exactly what it sounds like. This is everything that needs to be accessible throughout the project. Your global frame counter should be here, current score, high score, etc. Local RAM: These are variables that are specific to a particular game mode, display kernel, menu, etc. They only need to be used in one area of code, and they can be recycled elsewhere. Temporary RAM: Technically, this shouldn't normally be needed. Each set of local variables should probably define their own temporary variables. But if you want to create subroutines that can be accessed from multiple areas, for example, a text display kernel, it may be best to define a space in RAM that is known to be available, regardless of the current local variables. The way I implement this is by defining each section of RAM in a separate file, for organization. First, I define all the global variables: filename: ram_global.asm RamGlobal ; start of global RAM Frame ds 1 Variation ds 1 Score ds 6 Rand16 ds 2 RamLocal ; start of local RAM Next, I define local variables in separate files: filename: ram_game.asm RORG RamLocal ; reset RAM position to local RAM SomePointer ds 2 PlayerColor ds 1 BallPosX ds 1 BallPosY ds 1 Temp ds 1 And I tie it all together in another file: filename: variables.asm SEG.U VARS ; create a segment of labels that isn't output into the binary RORG $80 include ram_global.asm include ram_menu.asm include ram_game.asm include ram_credits.asm include ram_temp.asm
  17. My thoughts exactly, would love to see what others are using
  18. Here is a slightly modified version of the routine, I was using an extra temp variable before. This version uses 32 bytes of RAM, the earlier one used 33. 8_char_routine.asm
  19. While planning my Nyan Cat game, I decided I wanted the main menu to allow up to 8 characters per line. This could easily be done with each word hard coded into the ROM, but I wanted to allow each character to be set on-the-fly. This takes less ROM when a lot of messages are needed, but it also allows this kernel to be used for 8-digit scores, if desired. I am considering remaking the score display for my game to utilize this. I have been working on this display for quite a while now, and I am glad to say it is finished! It uses 3 pre-shifted graphics tables for the font. I am using a font that is 7 pixels high, allowing 36 unique characters to fit in each page. This works perfectly for me, as I am using 10 numbers, plus 25 letters (letter-O is shared with zero), plus a space, but this could be adjusted as necessary. If this was only used as a score display, all 3 graphics tables would easily fit within a single page. The kernel and pre-kernel subroutines take up a total of 235 bytes. I split them into 2 separate routines so that I could more easily utilize them in different areas of my game, but there is no reason why they couldn't be combined. 32 bytes of RAM are used in this routine, 8 for pointers, 21 for graphics buffers, and 3 for temp variables. The graphics tables are all upside-down. Any bits not used are cleared. XXXXX___ Table #0 has the data shifted to the far left. XXX___XX Table #1 has the first 2 bits shifted to the far right, and the last 3 bits shifted to the far left (so it "wraps" around the end of the byte to the beginning). __XXXXX_ Table #2 has the data shifted almost all the way to the right. Edit: As-is, the code looks for a label FontGfx0, which would be Table #0. It assumes that Table #1 and #2 are in the following 2 pages. The way text is selected for the routine is by loading Y with the offset of the first of the 8 characters to display. You would set up a table as such: Table HEX ## ## ## ## ## ## ## ## HEX ## ## ## ## ## ## ## ## HEX ## ## ## ## ## ## ## ## The reason for this is that you can save some ROM by overlapping messages with each other. Edit: As-is, the code assumes this table is called FontText. Actually using these routines would look like this: sta WSYNC SLEEP 3 ldy #0 ; start with character #0 jsr CharLoad ; prepare for kernel jsr CharKernel ; draw kernel SLEEP 9 ; or change color, for example ldy #8 ; start with character #8 jsr CharLoad ; prepare for kernel jsr CharKernel ; draw kernel I have included both routines, as well as an example binary. 8_char_display.bin 8_char_routine.asm
  20. I believe that post was the original inspiration for me, then one day in the shower I figured out how to put it all together, haha. That would be interesting, although it would require a bit of ROM.
  21. Not that I know of, but I had wondered that as well. I know there was a color-test cart made that I thought simply rendered a typical test screen. After looking up the video, though, it looks a bit more complex than that. WARNING: Mute the sound!
  22. Thanks guys! I was hoping the game would be under 512 bytes, but it would have to be drastically stripped down. Doing simple things without RAM takes a lot more ROM... I would like to do a 128-byte RAM game someday. Probably as a 256-byte cart that does initialization, pushes the code to RAM, and jumps into it. The inspiration for the gameplay was taken from this video:
  23. You need to be some sort of masochist to code for the VCS, because the system is so limiting. One of its most well-known limitations is the mere 128 bytes of RAM on board. Programmers have spent ages seeing what is possible using these 1024 bits of information. So, I thought, why not limit myself to the extreme? I challenged myself to make a complete game using absolutely NONE of the on-board RAM. And I did it! It took a lot longer to actually finish than I had anticipated, but I have a fully-functional 2-player pong clone using none of the RAM, namely without addressing anything within $80-$FF or any of its mirrors, neither reading nor writing. This should work perfectly on a system with the RAM disabled or otherwise re-routed. Since I am not using RAM, I am only using the CPU registers for storing information. I can only use X and the stack pointer for storage, though, while A and Y take on more temporary roles. I used T1024T and INTIM to temporarily store data while swapping X with the SP, which is neccessary twice per frame. The SP normally holds the scores (both values from 0-10) encrypted into 7 bits, with the high bit controlling the vertical speed of the ball. The X register normally holds the vertical position of the ball (0-95), with the high bit controlling the vertical direction of the ball. X is also used to trigger "score mode", where a sound is played for about half a second before switching to "serve mode". These are simply special X values that cannot occur during a rally. The Interrupt flag is used to control the horizontal direction of the ball. Since there is no way to test the value of this flag, I am using the stack to push the CPU state to NUSIZ0. GRP0 is used for the score graphics, and since it never moves, I can use a simple collision detection against the playfield to determine what the Interrupt flag is. The ball's horizontal position is not stored, as it is moved with HMOVE. All other game mechanics are done with either checking the bounds of the data, or checking collisions between objects. If you disable different graphics objects, strange things will happen! In attract mode, the screen color cycles, and if you have the BW/COLOR switch set to COLOR, it keeps the color on game reset, otherwise it resets to black and white. This also just happens to fit within 1K of space. I could probably squeeze a few more bytes out to maybe add changeable paddle sizes using the difficulty switches, but I like it as it is. ramless_pong.bin ramless_pong.asm
×
×
  • Create New...