Jump to content

mzxrules

Members
  • Posts

    24
  • Joined

  • Last visited

Everything posted by mzxrules

  1. I personally use the Python 3 programming language. This way I can store data in a sane format and write a script that converts it into whatever format is most optimal for my needs. For example, all of my NPC dialogs are stored right here in a very simple, straightforward python structure, very easy to modify unless you want to use the letter J. In my final output, the data for every message is split across 4 rom banks. Each character is converted into offset location of that character's left or right sprite sheets, where each sheet crams in 43 different 5 byte character sprites into 128 bytes using a "superstring" algorithm to pack them in.
  2. The first reason your program crashes because you haven't set up the vector table that tells the 6502 where to start execution. The vector table starts at memory address $FFFA. $FFFA/$FFFB stores the NMI pointer $FFFC/$FFFD stores the RESET pointer $FFFE/$FFFF stores the IRQ/BRK pointer Easiest way to fix this is to just set all three pointers like so, with "ENTRY" being changed to the label you want to jump to as your program start. ORG $FFFA .WORD ENTRY ; NMI .WORD ENTRY ; RESET .WORD ENTRY ; IRQ/BRK If you want to make a smaller rom, I believe ORG $F3FA works for 1K roms and ORG $F7FA works for 2k roms bc of address mirroring. When that's resolved, the program will stop screeching at you, but will not draw anything meaningful for a number of reasons, the biggest of which being your misunderstanding of what is necessary for draw things to the screen. I highly recommend reading and re-reading spiceware's Atari 2600 Let's Make A Game! to understand why your code won't work.
  3. Been thinking about implementing this one for far too long, so I decided to just do it today.
  4. Still plugging away at it slowly. Flute actually sounds like the flute now. Next big milestone I'm working towards is getting enemy missiles and the shield functional.
  5. I'm not sure how feasible this would be but I personally feel like a lot of my problems on this front would be solved if the assembler allowed mapping symbols to a 32 bit address space, instead of being limited to a 16-bit addresses and having everything overlapping one another.
  6. I've beaten SMB numerous times. With the A+Start trick you can eventually brute force your way to the final level, though personally I find it much easier to complete if you go into 8-1 with a fire flower. The bigger issue I have playing it now is that the movement mechanics are simply less refined when compared to Mario Maker 2 or Super Mario Bros. 35
  7. Each room is 8 bytes of data, so the overworld, dungeons 1-6 and dungeons 7-9 are 3 banks of 0x400 bytes. This does not include sprite data or the logic needed to unpack the level data. Using extra ram to store playfield data is essential to the approach I use... it's not just for making the environment destructible, it's also to allow the walls using the same base sprite have different patterns, leading to more complex and interesting rooms on the overworld.
  8. The problem was two things; the game is designed for a genesis controller so the hud kept constantly opening, which was easy to fix. The other thing is that I think this bit of code is the problem: https://github.com/DirtyHairy/UnoCart-2600/blob/master/source/STM32firmware/Atari2600Cart/src/cartridge_3ep.c#L21 The rom built by everyone else isn't an even 32 KB, there's a couple of bytes extra to it, so the number of banks is undercounted, and you end up swapping in bank 0 instead of the last bank. I've hastily made an updated build for the ZeroPage stream that addresses this and hastily adds some new things. There's a chance it might run out of cycles in some cases, I didn't do extensive testing but I think I avoided it for now. zelda.bin zelda_PAL60.bin
  9. the game isn't really in any state to be released as a playable demo at the moment, which is why I haven't posted a public binary of it on this thread. I am interested thought to see if/how it works on real hardware. The bow cannot be obtained because I hadn't gotten around to implementing appropriate behavior for the room where you'd normally obtain it. The boomerang has not been implemented either, but that's more because I'm not sure that it makes sense to implement one due to limit of being only able to use one item at a time.
  10. world kernel is the routine responsible for drawing the space that the player character can move around in and interact with things. It supports an enemy missile, but I haven't coded anything to use it just yet.
  11. Enemy combat is a space that is still very much a work in progress and I've probably been avoiding doing real work on it for too long. There is a bit of work that needs to be done in a few systems to get rid of some cheap hits, and perhaps it might be a little too hard to position yourself to safely attack enemies since knockback isn't implemented. I'd argue I'm still a noob as well, as I started with 2600 homebrew only last year. Development has moved to an 3E+ cart, so the only extra gizmo is ram; everything else should be stock hardware aside from the controller since atm you need a second button to pause the game. Sword/Bombs/Arrows/Flute all use the player missile sprite, while the ball is reserved for push blocks and other planned elements due to the fact that the ball shares the same color as the playfield. The world kernel is stored in the cart's ram so that sprite heights can be rewritten, which saves a few cycles over loading the data from a zeropage variable. Walls that are bombable just look like regular walls. Atm I don't intend to add cracked wall sprites to match the original game, partially because the rom bank that handles that is near max capacity.
  12. I'm a very long way from being able to invest money into physical carts, but I am interested in it for my own 2600 game (mainly for the ability to save game data). Also been wondering what goes into designing a custom memory mapper/what the limitations are design wise. Currently my game is coded to use an 3E+ cart. Memory wise it is mapped out sorta like this: Have reserved ~29k bytes of rom for the moment, though I could see that changing quite a bit. For ram, I need a minimum $100 contiguous bytes per bank, and at the moment I only really need $100 bytes of directly accessible read/write ram at a time. I have 3 $100 byte banks of ram for storing level state, and would only need a few bytes more to save all the state I'd ever need, unless I wanted to implement multiple save files. I also have another 3 $100 byte banks of ram that among other stuff contains a modifiable kernel.
  13. Not sure if this is the reason for these games, but in mine there is reason to limit my bounds to the range 0-134. My game was built off of the fundamentals shown in SpiceWare's Let's Make a Game! tutorial, and in it there's the routine PosObject that sets the x position of TIA objects ;=============================================================================== ; PosObject ;---------- ; subroutine for setting the X position of any TIA object ; when called, set the following registers: ; A - holds the X position of the object ; X - holds which object to position ; 0 = player0 ; 1 = player1 ; 2 = missile0 ; 3 = missile1 ; 4 = ball ; the routine will set the coarse X position of the object, as well as the ; fine-tune register that will be used when HMOVE is used. ;=============================================================================== PosObject: sec sta WSYNC DivideLoop sbc #15 ; 2 2 - each time thru this loop takes 5 cycles, which is bcs DivideLoop ; 2 4 - the same amount of time it takes to draw 15 pixels eor #7 ; 2 6 - The EOR & ASL statements convert the remainder asl ; 2 8 - of position/15 to the value needed to fine tune asl ; 2 10 - the X position asl ; 2 12 asl ; 2 14 sta.wx HMP0,X ; 5 19 - store fine tuning of X sta RESP0,X ; 4 23 - set coarse X position of object rts ; 6 29 For arbitrary x positioning across the entire screen, it's hard to do much better than this code. But it does come with a downside... it takes a variable 1-2 scanlines per call to position an object, which made it impractical when I want to reset my positions after drawing the HUD and Text kernel elements. As such, I came up with this instead: ;============================================================================== ; PosWorldObjects ;---------- ; Sets X position for all TIA objects within the world view ; X position must be between 0-134 ($00 to $86) ; Higher values will waste an extra scanline ; Does not touch Y register ;============================================================================== PosWorldObjects: SUBROUTINE ldx #4 PosWorldObjects_X: SUBROUTINE sec ; 2 .Loop sta WSYNC ; 3 lda plX,x ; 4 4 - Offsets by 12 pixels DivideLoop sbc #15 ; 2 6 - each time thru this loop takes 5 cycles, which is bcs DivideLoop ; 2 8 - the same amount of time it takes to draw 15 pixels eor #7 ; 2 10 - The EOR & ASL statements convert the remainder asl ; 2 12 - of position/15 to the value needed to fine tune asl ; 2 14 - the X position asl ; 2 16 asl ; 2 18 sta.wx HMP0,X ; 5 23 - store fine tuning of X sta RESP0,X ; 4 27 - set coarse X position of object ; ; 67, which is max supported scan cycle dex ; 2 69 bpl .Loop ; 3 72 sta WSYNC sta HMOVE rts By sacrificing some xpos locations, I'm able to position N different TIA objects in a constant N+1 scanlines. In practice, I use it to update both player sprites in 3 scanlines with PosWorldObjects_X and all 5 objects in 6 scanlines with PosWorldObjects.
  14. I've personally never been super interested in the 2600 until I started looking into how the games are programmed and trying a hand at it myself.
  15. Still plugging away at this. It took me way too long to figure out how to add in random item drops, but once I had a plan, coding it wasn't too bad. One thing I haven't figured out is why the audio seems to cut out randomly.
  16. It's quite a bit outside the scope of what you really need to know about Atari 2600 programming, but hardware interrupts are a mechanism in pretty much all programmable CPUs that makes it possible for a CPU to respond to events external to itself in a timely fashion. It gives a CPU the means to handle more than one task at once, and is part of how threading is implemented at the low level. To respond to events quickly, hardware is designed to allow code execution to be halted immediately at an arbitrary point, and jump to a different subroutine in such that the CPU is able to return where it left off later and continue execution. There are two core interrupt types, Maskable and Non-Maskable. Maskable interrupts are interrupts that can be temporarily ignored by the CPU. This allows the CPU to finish up a system critical task like modifying a variable that different threads need to access. Non-Maskable interrupts are interrupts that can't be ignored by the CPU, like the system being turned on, a reset button being pressed, or a critical hardware fault. Lastly, the Interrupt Vector table is a hardwired table that lets you assign a different handler for each type of interrupt supported by the CPU.
  17. Progress has been a bit slow. Been just doing minor refactoring and tweaked my world kernel so that ball sprites can be drawn on grid: I've also been trying to implement different enemies. I've got two more enemy types mostly implemented: Wallmasters and Like Likes (both rather annoying to deal with). I've also got Octoroks walking around, but haven't implemented their attack yet. I've also implemented dying: One thing that's kind of got me struggling is figuring out how best code enemies to maximize code reusability, while also being able to tweak minor stuff like health/enemy color.
  18. I'm definitely not doing it manually, that's for sure. But for my project, I'm using Python 3 instead of DASM to generate the final data that will be included in the rom. It offers me so much more flexibility with regards to how I manage my game's data than DASM does. The first one is rather clever. I can't use it in my own game though because I use the extra bits not used by the index to store other data. For the second one, I already have it written as tax/txa in my own code, but you do bring up a good point; I hadn't realized that pha/pla take more cycles than tax/txa, presumably because pha/pla require memory access.
  19. not sure if it counts as a killer hack, and maybe this has been thought of before but I'll post it anyway. My game is structured such that I have a world composed of 256 rooms, and then the playfield of each room is composed of up to 64 page-aligned 8x16 sprites, which comes out to 4 pages of sprite data. Instead of storing an index from 0-63 in the world data, and then multiply it later by 16 to build the 2 byte room sprite pointer, I swap the first 4 least significant bits with the 4 most significant bits and store that in my world data. For example, let's say my sprite data is at F000, and say I want to store an index to sprite 0x2D, which is located at F2D0. If I store the index as 0xD2 instead, then I can generate the room sprite pointer without needing 2 byte multiplication: ldx roomId lda roomSpriteIdx, x pha and #$F0 ; a is now lower half of the pointer sta roomSprPtrL pla and #$0F clc ; These two lines can also just be adc #$F0 ; ora #$F0 sta roomSprPtrH
  20. i split up my main asm file (it was 2.7k lines) and it's helped me get out of my funk:
  21. A short story: A few years back I was browsing r/speedrun when I stumbled on the Dragster drama. Someone looked at the assembly of Dragster and created a sim in an attempt to figure out if a certain someone's world record time was actually achievable. Having done quite a bit reverse engineering of Ocarina of Time, and dabbled for a day or so reversing The Legend of Zelda for the NES to figure out the heart container glitch, I figured I'd might try my own hand at it despite never having played an Atari game in my life before. Though I never got too far into understanding the code, I was amazed at how you can print the entire game on only a few pages of paper, and finally understood how it was possible to do animated sprites with so little ram. Fast forward to this year. I'd just replayed The Legend of Zelda on the switch (both quests of course), and I got it in my head that I wanted to make an Atari 2600 port. So here's how far I've gotten: The game is written in 6502 assembly. To create the sprites I use a GCS program called MegaZeux, which has a pretty nice built-in 1 bit sprite editor. I also coded my world editor in MegaZeux, using it's "robotic" language. "Robotic" can be fairly esoteric at times, but it's the first programming language I ever learned and it was the easiest thing I could think of to use without rolling my own rendering code. I also use Python 3 for data manipulation purposes. For example, in order to save rom space I use a superstring algorithm to pack the sprite data for the text kernel tighter. Additionally, the music sequence data is completely generated by Python, so that I could write music using proper notes instead of numbers. I can't say for sure that I will continue working on it, as I hit a bit of a wall and decided to switch gears to a different thing I'm working on. But I don't think I'll stop working on it for good just yet.
  22. By splitting the string into two strings, and alternating which string i initialize: ; Print A to X text__example1 .byte __A, __B, __E, __F, __I, __J, __M, __N, __Q, __R, __U, __V text__example2 .byte __C, __D, __G, __H, __K, __L, __O, __P, __S, __T, __W, __X text24_modified.asm
  23. I think I figured out a way to eliminate StartFrame1Text and reduce the ram requirements of text24.asm The only difference that I see between ____frame0_text and StartFrame1Text aside from the alternating Text indices is that ____frame0_text has a sleep 4, while StartFrame1Text has a sleep 6 To fix this, I use the overflow flag along with this macro to alternate between a 4 and 6 cycle delay: MACRO VSLEEP bvs .j0 .j0 bvs .j1 .j1 ENDM
×
×
  • Create New...