-
Posts
152 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Gallery
Events
Store
Community Map
Everything posted by Sohl
-
Code and Data redundancy when bank-switching?
Sohl replied to Sohl's topic in Atari 2600 Programming
@Thomas Jentzsch: Yes, that seems to help! I had to pad out the SWITCH_BANK macro with a NOP: BANKSWITCH_SIZE = 8 MAC SWITCH_BANK ORG BANK_ORG + $ff6 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;ml adjusted offset for F6 ROM ;j go in each bank RORG BANK_RORG + $ff6 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;ml adjusted offset for F6 ROM ;j go in the moveable code? TJ: You have to update ORG and ROGR in pairs when using bankswitching, because they are different. SwitchBank = (. & $fff) | $1000 ;j take this location and replace the left nibble with a 1? ;TJ: Just like START, this puts all bank switch code at the same spot in address space for each bank pha ;j MAC JMP_LBL jumps to here. The accumulator was loaded with one txa ;j nibble and x was loaded with the other. Move it to the accumulator pha ;j so it can be pushed on the stack. Setting up a return address. lda $fff6,y ;j Load the accumulator with one of the hot spots, causing ;j a bankswitch. rts ;j The bankswitch occurred, so this executes in the new bank ;j The rts is to an address that was calculated in JMP_LBL nop ;ml added for padding ;BANKSWITCH_SIZE = ((. & $fff) | $1000) - SwitchBank ;j Calculates the size of the ;j SwitchBank code. Then take this address and limit it to a range of ;j %00001111 11111111 to ;j %00011111 11111111 .... Why the limit? TJ: The limit comes from the 4K address space we are using. ENDM Any idea why the BANKSWITCH_SIZE calculation is not working in DASM? I get a "Expression in mnemonic not resolved" error. Thanks a lot for a usable workaround, Thomas, as well as the original bank-switching support macros! With this more function-based banking code structure, I avoid a lot of graphics data redundancy and streamline things quite a lot. Now I have much more code space for game logic! And the macros give me an easy way to jump back and forth more if I need to move some code around later. I really appreciate the examples and support. Now I have a bit more debugging to do.... ? Not everything is back to 100% working yet in the new bank layout. -- Mike_Sohl -
Code and Data redundancy when bank-switching?
Sohl replied to Sohl's topic in Atari 2600 Programming
Additional observation for the bank switching issue above: The routine I'm trying to switch to, which is intended to be in Bank 2, shows up in my Stella disassembly window in Bank 1 if I select Bank1 with the CartridgeF6 pull-down option list. See screenshot. But the disassembly address is $5034, which should be Bank 2 right? Bank 1 contents should be shown with addresses in the $3xxx range, shouldn't they? -
Code and Data redundancy when bank-switching?
Sohl replied to Sohl's topic in Atari 2600 Programming
I've implemented @Thomas Jentzsch's bank-switching macros, but I've run into a minor issue and a major issue. Here is my adjusted version of the macros to work explicitly with a 16 K 'F6' style ROM: ;=============================================================================== ; Bank-Switching and cross-bank common code (need to keep at same alignments) ; ; These are Thomas Jentzsch's Atari standard bankswitching macros, ; tailored for F6 by M. Losh ; ; Comment tags: ; 'j' = Just Jeff on AA; ; 'TJ' = Thomas Jentzsch ; 'ml' = M. Losh ;=============================================================================== BANK_SIZE = $1000 ;j Define a constant,(4K bank size here) ; Use at the start of each bank, required by JMP_LBL macro MAC START_BANK ;TJ {1} = bank number. ;j example: start each bank with with argument ;j START_BANK 0, START_BANK 1, etc TJ: for F4 you should start with 0, else the following variables will overrun BANK_NUM SET {1} ;j Transfer the START_BANK argument to BANK_NUM BANK_ORG SET $C000 + BANK_NUM*BANK_SIZE ;j skips over the first potential banks; ml revised to $C000 for F6 16K type ROM ;j (4K) banks then sets the start address (ORiGin) of the ;j bank that was set in the argument. BANK_RORG SET $1000 + BANK_NUM*BANK_SIZE*2 ;j If bank 2, for example, BANK_RORG ;j would be $5000. Why? TJ: Because we need an odd first nibble here SEG code ;j Start the code segment ORG BANK_ORG, $55 ;j Put $55 at the start of a bank? ;TJ: This is the address of the bank in the ROM. These can be anything, but they must be consecutive overall. ;TJ: $55 is just redefining the default fill value for unused areas RORG BANK_RORG ;j Put the relocatable code in the calculated location? TJ: The origin used by the code in this bank. Different origins per bank make debugging easier. ECHO "Start of bank", [BANK_NUM]d, ", ORG", BANK_ORG, ", RORG", BANK_RORG ;j print stuff ; verify start addresses: START{1} = (START & $fff) | BANK_RORG ;j Define START of the bank as this location ;j with the left nibble stripped off and replaced with the ;j left nibble of BANK_RORG? TJ: Startup address of the bank, this code just verifies that all banks use the place in their logical address space ;TJ optional Space for RAM+ ; DS.B $100, "#" ;ml not using RAM+ ENDM ; Insert code for executing the bankswitch, ; should be put at the very end of each bank, right before the hotspots BANKSWITCH_SIZE = 7 MAC SWITCH_BANK ORG BANK_ORG + $ff6 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;ml adjusted offset for F6 ROM ;j go in each bank RORG BANK_RORG + $ff6 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;ml adjusted offset for F6 ROM ;j go in the moveable code? TJ: You have to update ORG and ROGR in pairs when using bankswitching, because they are different. SwitchBank = (. & $fff) | $1000 ;j take this location and replace the left nibble with a 1? ;TJ: Just like START, this puts all bank switch code at the same spot in address space for each bank pha ;j MAC JMP_LBL jumps to here. The accumulator was loaded with one txa ;j nibble and x was loaded with the other. Move it to the accumulator pha ;j so it can be pushed on the stack. Setting up a return address. lda $fff6,y ;j Load the accumulator with one of the hot spots, causing ;j a bankswitch. rts ;j The bankswitch occurred, so this executes in the new bank ;j The rts is to an address that was calculated in JMP_LBL ;BANKSWITCH_SIZE = ((. & $fff) | $1000) - SwitchBank ;j Calculates the size of the ;j SwitchBank code. Then take this address and limit it to a range of ;j %00001111 11111111 to ;j %00011111 11111111 .... Why the limit? TJ: The limit comes from the 4K address space we are using. ENDM ; a convenience macro, put at the end of every bank MAC END_BANK RORG $ff6 ;ml adjsuted position for F6 ROM ds 6, 0 ;j Put zeros in the last (ml) 6 bytes for the hot spots before the ; reset and break vectors .word (START & $fff) | BANK_RORG ; RESET .word 0 ; BRK (unused) ENDM ; Define a label which can be used by JMP_LBL macro ;Example: ; DEF_LBL Foo ;j Insert this in the place where you want to be able to jump to. MAC DEF_LBL ; address ;j Figure out where we are. {1} ;j Argument is Foo. Labels the code? TJ: Yes, required to have an address {1}_BANK = BANK_NUM ;j Foo_BANK is the bank number we are currently in. ENDM ; Jump to a label in other or same bank. The assembler will take care if the ; code has to bankswitch or not. ; Example: ; JMP_LBL Foo MAC JMP_LBL ; address IF {1}_BANK != BANK_NUM ;tj convenience IF ;j If Foo_BANK is not current bank ldy #{1}_BANK ;j Load y with the hot spot offset lda #>({1}-1) ;j Load MSB of Foo's address ldx #<({1}-1) ;j Load LSB of Foo's address jmp SwitchBank ;TJ always at the same address in each bank ELSE ;j If we're already in the same bank jmp {1} ;j then just jump Foo ENDIF ENDM The minor issue I have is that DASM does not like that BANKSWITCH_SIZE calculation, so I commented it out and replaced it with an hardcoded size of 7. I have placed the bank start and bank end macros into my code to insert the needed bank-switching code instructions and to define the needed symbols and assemble-time equates (for example): ;=============================================================================== ; BANK 0 -- Non-Playing Mode(s) ;=============================================================================== START_BANK 0 START: ColdStart_B0: lda $1ff6 ; switch to Bank 0 jmp InitSystem ; (SKIPPING AHEAD ... ) ;=============================================================================== ; MainFrameStart - Chooses correct main routine to begin new frame, based on mode ;=============================================================================== DEF_LBL MainFrameStart SUBROUTINE lda Mode and #NonplayFlag beq .playing jmp MainNonPlay .playing: lda Mode and #InsideCellFlag beq .exo jmp MainIntra .exo: jmp MainExo ;=============================================================================== ; Main Routine for Non-Play Title/GameOver screen ;=============================================================================== MainNonPlay: jsr VerticalSync jsr VBlankNonPlay jmp KernelNonPlay ; NOTE: KernelNonPlay will bank-switch & jump to OverScanNonPlay ; OverScanNonPlay will bankswitch and jump back to MainFrameStart ; which will choose which main routine to run, if game has not ; started yet, it will jump back to MainNonPlay to complete this ; main loop (distributed as it may be!) ; (SKIPPING AHEAD ... TO END OF DRAWING KERNEL ) NonPlayScreenDone: JMP_LBL OverScanNonPlay ;=============================================================================== ; Define end of Bank ;=============================================================================== SWITCH_BANK END_BANK ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ;=============================================================================== ; BANK 1 -- Drawing Kernels, graphics data ;=============================================================================== START_BANK 1 ColdStart_B1: lda $1ff6 ; jump to Bank 0 nop nop nop ; (SKIPPING AHEAD ... TO BANK 2 ) ;=============================================================================== ; OverscanNonPlay - Non-playing screens overscan function; check for new game start ; -------------- ;=============================================================================== DEF_LBL OverScanNonPlay SUBROUTINE lda #2 ; LoaD Accumulator with 2 so D1=1 sta VBLANK ; STore Accumulator to VBLANK, D1=1 turns image output off ; (SKIPPING AHEAD ...) FinishOverscanNonPlay: .OSwait: lda INTIM ; Check the timer bne .OSwait ; Branch if its Not Equal to 0 JMP_LBL MainFrameStart ; JuMP to Main ;=============================================================================== ; Define End of Bank ;=============================================================================== SWITCH_BANK END_BANK ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ;=============================================================================== ; BANK 3 -- Music, support stuff? ;=============================================================================== START_BANK 3 (ect.) The major issue I have is that when running, the bankswitch only lasts one instruction in Stella. The first screenshot is Stella in Bank 0 just before I load $FF6+2 (y=2 to actually load $FF8) to switch to Bank 2. The second screenshot shows that after loading $FF8, Stella recognizes it should be in Bank 2, but the disassembly window is not showing $5xxx address range for Bank 2. The RTS call should set the PC $5034 (address of my OverScanNonPlay routine), however after I single step, as you can see in the third screenshot, Stella says I am back to Bank 0, and the offset $034 (which would be OK in Bank 2) is not a valid routine in Bank 0. Here are excerpts of the listing file that shows the call to the OverScanNonPlay in Bank 0, end of Bank 0, and the entry point of OverScanNonPlay in Bank2: *********** IN BANK 0 ******************** ... 2155 c869 2156 c869 NonPlayScreenDone 0 c869 JMP_LBL OverScanNonPlay 1 c869 IF OverScanNonPlay_BANK != BANK_NUM 2 c869 a0 02 ldy #OverScanNonPlay_BANK 3 c86b a9 50 lda #>(OverScanNonPlay-1) 4 c86d a2 33 ldx #<(OverScanNonPlay-1) 5 c86f 4c ef 1f jmp SwitchBank 6 c872 - ELSE 7 c872 - jmp OverScanNonPlay 8 c872 ENDIF 2158 c872 ... 2505 ce12 ;=============================================================================== 2506 ce12 ; Define end of Bank 2507 ce12 ;=============================================================================== 2508 ce12 2509 ce12 ; ORG $CFFA ; set address to 6507 Interrupt Vectors 2510 ce12 ; RORG $FFFA ; set address to 6507 Interrupt Vectors 2511 ce12 ; .WORD ColdStart_B0 ; NMI 2512 ce12 ; .WORD ColdStart_B0 ; RESET 2513 ce12 ; .WORD ColdStart_B0 ; IRQ 2514 ce12 0 ce12 SWITCH_BANK 1 cfef ORG BANK_ORG + $ff6 - BANKSWITCH_SIZE 2 cfef 3 cfef RORG BANK_RORG + $ff6 - BANKSWITCH_SIZE 4 cfef 5 cfef 6 cfef 1f ef SwitchBank = (. & $fff) | $1000 7 cfef 8 cfef 9 cfef 48 pha 10 cff0 8a txa 11 cff1 48 pha 12 cff2 b9 f6 ff lda $fff6,y 13 cff5 14 cff5 60 rts 15 cff6 16 cff6 17 cff6 18 cff6 19 cff6 20 cff6 0 cff6 END_BANK 1 cff6 RORG $ff6 2 cff6 00 00 00 00* ds 6, 0 3 cffc 4 cffc 00 10 .word.w (START & $fff) | BANK_RORG 5 cffe 00 00 .word.w 0 2517 d000 2518 d000 2519 d000 ;=============================================================================== *********** IN BANK 2 ******************** ... 4874 e034 ;=============================================================================== 4875 e034 ; OverscanNonPlay - Non-playing screens overscan function; check for new game start 4876 e034 ; -------------- 4877 e034 ;=============================================================================== 4878 e034 0 e034 DEF_LBL OverScanNonPlay 1 e034 OverScanNonPlay 2 e034 00 02 OverScanNonPlay_BANK = BANK_NUM 4880 e034 SUBROUTINE 4881 e034 a9 02 lda #2 ; LoaD Accumulator with 2 so D1=1 4882 e036 85 01 sta VBLANK ; STore Accumulator to VBLANK, D1=1 turns image output off ... Weirdly, in the third screen shot, the Stella PC field shows '5034 OverScanNonPlay' correctly, but the disassembly window is labeled 'Bank 0' and shows code from the $1xxx range, which is Bank 0 code. And if I keep stepping, I am definitely executing in Bank 0, which is not intended and soon leads to an illegal instruction crash. Does anyone see anything wrong about how I'm using Thomas Jentzsch's macros? I am running on the latest Stella 6.5.3 for Windows, and it seems to recognize by .bin as an F6 16K type correctly. Any help or hints here would be appreciated! -- Mike_Sohl -
Code and Data redundancy when bank-switching?
Sohl replied to Sohl's topic in Atari 2600 Programming
Thanks @SvOlli. I'll study this and try to grok it! -
Modest progress this weekend: Got a very basic kernel running for the "intracellular" (new) gameplay mode. It has a "free" ribosome that can be moved around the cytoplasm with the joystick. Also, there are three latched viruses at the cell membrane near the top of the screen in this mode. There will be a bunch of other stuff that can show up in the cytoplasm, so I'm a long, long way from having this playing as intended. Also, based on the discussion in the "Code and Data redundancy when bank-switching" thread, I see the advantages of reorganizing by memory bank usage and moving some code and data around a bit to make things a lot less redundant. Hopefully this won't take too long or cause too much regression breakage!
-
Code and Data redundancy when bank-switching?
Sohl replied to Sohl's topic in Atari 2600 Programming
Thanks @SpiceWare for the reference. I will study it! @Ben_Larson, yes this does seem more logical and straightforward the more I think about it. It will just take some significant shifting of code among the banks! Really seems I should go this way. -
Code and Data redundancy when bank-switching?
Sohl replied to Sohl's topic in Atari 2600 Programming
@SpiceWare, @JetSetIlly, @Thomas Jentzsch Thanks for your comments. I'm not sure I'd want to go that way, but good to be aware of. Are those bank switching schemes well-supported in emulation and hardware such as Harmony carts or AA hardware carts? So far, I'm trying to use my suggestion 2) to define the code once (keep it DRY!), but set up subroutine instance in each bank where I want to use it: MAC PosObjectCode ... ; (code here) ENDM ;;;; BANK 1 PosObject_B1: PosObjectCode ret ;;; ... (in some later code) jsr PosObject_B1 ;;;; BANK 2 PosObject_B2: PosObjectCode ret ;;; ... (in some later code) jsr PosObject_B2 Another thing I'm thinking about is re-organizing my code along a more functional axis rather than "gameplay" mode. For example, if I can fit all my drawing kernels, at least the ones for active gameplay into a single bank, then I don't need to worry about duplicating data for sprites, PFA graphics, color tables, etc. that are common across multiple gameplay modes, so maybe this looks like in my case: Bank 0 - Sound and some big support routines (score graphics prep)? Bank 1 - Graphics kernels for all screens? Bank 2 - VBlank & Overscan routines for all gameplay screens, lots of game logic for all gameplay modes Bank 3 - Initialization, maybe some specialized graphics kernel code for non-gameplay screens (title/game over/game version selection) that does not need sprites from Bank 1 I think if I do it this way, the bank-switching can be slightly less smart... the Bank 2 would always switch to Bank 1 for drawing the screen (albeit to different specific routines), then always switch back to Bank 2 for overscan processing. This seems better than in my current setup, where I might have two different banks to switch back to. But it would take some work to get there! Do any of you tend to set things up in functional sections like this, or have different banks handle different phases of play (more time-sequential)? -
Today I transitioned to using a 16K ROM for my work-in-progress game Immunity. As of now, I plan to have bank 0 handle the title/game over screens and it also has some common code for preparing playfield graphics for score digit drawing. Bank 1 is for the one play mode with "outside" the cell membrane action, while the new bank 2 will be for "inside" the cell membrane action, and bank 3 is currently unused. Maybe I will move my sound-effects stuff there, or other special routines that can be fairly self-contained. Anyway, I've noticed that I want to have a lot code and data in common between bank 1 and bank 2. Several of the sprite graphics and some other lookup tables plus short routines like the player/object positioning routine and many more are likely needed in both 1 and 2, or at least accessible from both banks. Maybe this is tolerable, but seems wasteful, and breaks the DRY ("don't repeat yourself") principle. I have a lot of ugly things like bank-annotated data and subroutine labels, such as VIRUS_SPRITE_Gfx vs VIRUS_SPRITE_B2_Gfx, and PositionObject vs PositionObject_B2, etc. I seem to really need two banks for the two main modes of play, since the display kernel for the first mode is already quite large and I imagine the other mode is at least as bad, if not worse, plus the amount of non-common game logic will not fit if both play modes are squeezed into a single back, as far as I can tell. Some strategies that I have considered, but have not worked too hard to implement yet are: 1) Use ORG to align as much of the common data as possible to a common offset within each segment, so the same $Fxxx label is correct in both banks. The data still needs to be duplicated in the source code for each bank, but the labels for one bank would be interchangeable with the other, so code does not need to refer to bank-specific names. 2) Define any routine that will be repeated in two or more banks as a MACRO, so at least the source code is not just copy-and-paste repeated, even if the binary code is actually repeated 3) Use the "trampoline" construct, or whatever it should be called, to be able to bank-switch and call a single instance of the code and then bank-switch back to the caller's correct bank when done (not sure how to automatically return to the correct bank without checking other state data in RAM). An example of 2) I am using... in Bank 1, I have: PrepScoreForDisplay: SUBROUTINE lda $1ff6 ; (Happens in Bank 1) switch to Bank 0 jsr DoPrepScoreForDisplay ; do the work in Bank 0 lda Mode and #InsideCellFlag beq .extracellular lda $1ff8 ; Switch back to Bank 2 bvc .done .extracellular: lda $1ff7 ; Switch back to Bank 1 .done: rts ; (Happens in Bank 1 or 2) return In Bank 2, I have (positioned at the same exact offset from the start of the bank): PrepScoreForDisplay_B2: SUBROUTINE lda $1ff6 ; (Happens in Bank 2) switch to Bank 0 jsr DoPrepScoreForDisplay ; do the work in Bank 0 lda Mode and #InsideCellFlag beq .extracellular lda $1ff8 ; Switch back to Bank 2 bvc .done .extracellular: lda $1ff7 ; Switch back to Bank 1 .done: rts ; (Happens in Bank 1 or 2) return The actual work is done in the "DoPrepScoreForDisplay" routine that is only in Bank 0 (based on @SpiceWare's example code!). For this long routine, it is worth it, but for a ten to twenty statement routines, it does not seem efficient to write and use this bank-switching code for each function that would be shared between multiple banks. Is there a more efficient, streamlined way to do this, or does it just come down to recognizing the point were a common routine is long enough to make a bank-switching call and return worth it? Other ideas? What have you code-masters done to keep the data and code redundancy to a tolerable level? If you can point out good examples of code to study or articles or postings, please share and point me in a good direction. I've tried to absorb some previous good examples posted here in AA, but I'd like to learn more, so any additional comments or suggestions would be appreciated! -- Mike
-
Hi gang! Just a short update in case you're curious what is going on. I just fired up my editor and assembler to resume development after a short break. (I've been side-tracked on some DOS VGA stuff.) Nothing new to show yet, but I've bitten the bullet to go to a 16K ROM layout (F6 bank-switching). I still had lots of room in the 8K ROM, but with the new "Intracellular" gameplay mode I will be working on and the other variations I'm thinking about (as I posted about just above), I don't think there is any reasonable way to get it all into 8K. Not impossible for someone brilliant and super-skilled, but for little ol' me, I need a bit of breathing room! Also during my coding break, I worked on some potential artwork for cartridge label/instructions/box and mock-up of the packaging box. I'm keeping it under wraps for now, but I think it will be awesome, and that's before anyone with more talent lends a hand!
-
Thanks @alex_79! If I'm still having issues, I will try your suggestions.
-
Additional game variation questions For the full game, I imagine most people including potential customers will want more game variations, but if you have any specific feedback on these options, let me know! 1) Two-player game version: One controls the human side (ribosome & macrophage), while the other controls the infecting viruses. Since the antibodies only shoot on a line, the virus movement may be only changed at certain times, or cannot be changed when an antibody is "in flight". Perhaps at the tissue change, the human and virus players can swap (player swapping could be an independent game variant too?). Scoring may be different than in the one-player version, such as viruses destroyed versus viruses created after latching? Or can think of other ways to score the two-player version? 1b) Two-player cooperative mode: * Two ribosomes fighting viruses side by side, see who gets the most? Might not want to have the macrophage part then. * 2nd player controls macrophage? Maybe the macrophage can nudge a virus into a better place to help the ribosome's aim? 2) Single-player as virus: If I implement the two-player mode, it would not be difficult to make a one-player as virus version. Would people enjoy this mode? 3) Intracellular vs. Extracellular vs. Both: Since the beginning when I conceived the game, I wanted to have an "inside" the cell gameplay mode where a "free" ribosome could move around doing multiple things: battle the virus RNA coming into the cell, collect energy and vitamin molecules, and 'nibble' the latched viruses to determine how to make antibodies. I envisioned that this mode would alternate with the "outside" the cell perspective that is now implemented in the playable demo, and what you do in could affect the other. E.g. if you don't make any antibodies during the intracellular mode, you will not have as many to fling in the extracellular mode. Would people enjoy having an option to lock into just one of the modes? That way you could pick extracellular-only and get the current demo version basically, but you could pick intracellular only if you prefer it, or the alternating "campaign" mode that I had in mind from way back. 4) Minor variants: I've thought of variants with different strengths and weaknesses, such as: * Speed of ribosome - faster or slower movement (but will it be too hard to control? Might want a bit of accel/decel in motion?) * Health robustness - loose health more slowly and/or regain it more quickly * Antibody production efficiency - build antibodies more/less quickly In the intracellular or both variants, the collection of energy or vitamin molecules could also affect the factors in part 4). Can you think of other variants? Oh, buy the way, I think I'm flipping some colors in the main title & win screens. This will line up better with the potential box art I have in mind if this game ever gets into the AtariAge store!
-
@ZeroPage Homebrew I've made an implementation of the fire-to-reset feature. Seems to work OK, but I want to look for unrelated potential remaining bugs before I make another public demo release. But here is the reset progress screen.
-
@ZeroPage Homebrew Looks like I better implement a fire-button reset! I think it will be an extended press version so you don’t accidentally immediately restart and miss your chance to see your final results of the just completed game. I can make a little progress bar advance while you hold fire to reset, maybe over 64 TV frames or something (1 second). Thanks again for showing my WIP and the many kind words of encouragement!
-
@Prizrak @KevKelley Thanks to both of you for the words of encouragemet!
-
Wow, thanks! Will you try Difficulty A? ? I'll need to get a Twitch login to join the chat.
-
Implementation notes: As mentioned before, this is an 8K ROM, using the simple F8 bank-switching scheme. I had hoped to keep it to 4K, but ran out of space before I even had title/end screens and sound. Gameplay is in the high bank and title and ending screens are implemented in the lower bank. Moving the score digit prep routine to the low bank helped open up a lot of space to finish up the game logic and sound effects. Viruses are drawn with both Player sprites in order to have the grayish virus body and red/yellow viral spikes. The two moving viruses are kept in separate vertical strata. Interlace “flicker” is used when a macrophage is active or when a moving virus is low enough to vertically overlap latched viruses. The latched viruses are drawn with player replication to get two or three to appear. If the macrophage is also active for a low virus when there are also latched viruses, a special version of the neutralized virus is drawn that is made from just one player sprite but having alternating colors to still appear to have a body and colored spikes. During the action, most players should not notice this drawing “cheat”. Many on-screen elements are done with PFA graphics, including Health and Breath scores, bar graphs of the viral load and antibody reservoir, tissue texture, cell membrane, and the endoplasmic reticulum base for the antibody ribosome. The cell membrane animation is table driven based on an index derived from the frame count. The membrane will take on a different configuration when at least one virus has latched on, where the middle opening will close completely and the the left and right openings will twitch at a reduced size, representing a “shocked” condition of the cell. Antibodies are drawn with a player missile object. The size and position relative to the ‘line of fire’ are adjusted through an animation cycle to create a rotating Y appearance (to better appear similar to common illustrations of antibodies). There are times in the animation process where the missile is visually stretched due to kernel code limitations and/or the color changes when passing a virus due to the requirement that the player sprite and player missile graphic need to share the same color. Sound effects are driven by the overall frame count. One voice is used for the heartbeat lub & dub sound and the breath sound, while the second voice is used for antibody and virus event sounds. In the non-gameplay screens, the main title theme music and short win and loose music flourishes are implemented with two voices to have a “bass” and “lead” part or simple two-note intervals. I think the screen scanline count is fairly stable, but there may be a few glitches yet to work out if possible. There are some obvious cases when the viruses move vertically, it can distort the PFA drawing of the tissue in the background, making it appear slightly wobbly. While not intended, it sort of looks interesting, so I may leave that as-is. I was considering some additional animation of the background tissue but I probably won’t in this 8K demo ROM. We will see for later! Special thanks to Darrell Spice Jr. for code excerpts such as the score prep routine and object positioning routine, and Duane Alan Hahn for publishing Andrew Davie’s tutorials, and a lot of other great material including sound and music info for the 2600.
-
I’m back with a new homebrew game, where you fend off a viral infection to hopefully build ‘Immunity!’ You play the role of parts of the human immune system, in which you can fling antibodies from a ribosome, and if you neutralize an infecting virus, you can direct a macrophage to gobble it up. But watch out, if any virus finds its way to your cell membrane, it can find a place to latch on, which will increase the viral load and decrease your health! Clear enough viruses from one tissue type and your point of view will switch to another, in the sequence: lung, mucus membrane, bone, muscle, brain, and artery. Within each tissue type, you will find the virus mobility differs, adding challenge to your task. In this demo version of the game, you gain some antibodies each time you clear a virus and additional antibodies each time you clear a tissue type. Completely clear all infecting viruses before you run out of antibodies to win immunity. But if you run out of antibodies with the infection still present, or your health drops to zero, there is a deathly Game Over. Take care lining up antibody shots, as the cell membrane pores continually cycle between being wide open and more closed. This ‘demo’ work-in-progress version of the game is an 8K ROM that I hope you enjoy and help me improve by reporting bugs, gameplay issues, or ideas for the future. I do have a few ideas already to add flourishes to the title screen and main gameplay screens in the demo version. I also have ideas for a ‘full’ version of the game with a second gameplay mode with different kinds of action and strategy. Eventually, if this comes together as I envision, I hope it will be worth making available as a boxed cartridge with printed manual here through Atari Age! Controls: Immunity uses the Left joystick. Press fire (as prompted in the title screen) to begin gameplay. Move the cell’s ribosome Left or Right with the joystick. Press Fire to fling an antibody up, hopefully striking a virus and neutralizing a portion of its outer spikes (which will change color from dark red to yellow to indicate neutralization). Viruses have six zones of spikes. Hitting a virus when it is farther from the cell membrane will cover more of the zones with one antibody ‘shot’, three zones for the farthest, two zones for the mid-distance, and only one zone per antibody when the virus is low and close-in. Note: a flung antibody will be slowed dramatically if it hits the cell membrane or more solid parts of the surrounding tissue, and you cannot fling another antibody until the current either hits a virus or reaches the top of the screen. When a virus is completely neutralized and all spikes are yellow, push Up on the joystick to switch control to a macrophage, which will aligned vertically with the virus. Then use the joystick to direct the macrophage left or right toward the virus,. The macrophage will ‘chomp’ as it moves. If you you engulf and trap the virus by positioning the macrophage directly over it, press the Fire button to completely dissolve the virus and automatically switch control back to the ribosome. Or if you choose, you can switch back to the ribosome without clearing the virus yet by pulling the joystick Down. The game supports (B)eginner and (A)dvanced game options via the Left Difficulty selection switch. In Beginner mode, the viruses move significantly slower, and there are fewer viruses per tissue and overall to clear. The Reset switch will directly take you back to the title screen. Scoring: Whether you’ve won immunity or met an untimely death, when the game is over, the screen will alternate between the title/game over screen and the final gameplay screen, which will allow you to see what your final health level was and how many “breaths” you took before winning or loosing. The breath count is based on time, but when you run out of antibodies, your health percentage is added to your breath tally. Health starts at 96% and rises each time a tissue is cleared, up to a maximum of 99%. Each time a virus latches into a cell membrane, health is lost. Up to three viruses will latch within a tissue type. Each latched virus will replicate into four additional viruses when the tissue type switches. Other gameplay notes: You can fling antibodies “through” (around) latched viruses. Completely neutralized viruses cannot latch into the cell membrane. Only one virus can sweep across the cell membrane at a time looking for a latching site, so if a second virus is present, it will not fully descend until the first virus is either eliminated or latches on. Preview video: A slightly buggier version is demonstrated on my Odysee channel. Let me know what you think! I hope you enjoy my game theme in our current times! imm_proto_0_6.bin
-
Thanks @Omegamatrix. I don't have all that much game logic in the overscan period at this point (around 300 cycles at this point), but maybe I will later. When is TIMINT flag cleared? Very soon after writing to a TIMxT interval register?
-
I'm working on a new game. I used @SpiceWare's "Collect" sample code as a starting point for my OverScan subroutine. However, during running of my game, sometimes the RIOT timer count-down is not working with the divide-by-64 as expected, but rather seems like divide-by-1, so naturally the timer is wrapping around before I can catch it and I get a way-to-long overscan period, messing up vsync. So I get a partial 87-line frame between each 262-ish frame (still working to get that exactly correct). During this behavior, in Stella the screen flashes and the RIOT reg subwindow shows TIMINT as $C0 and INTIM Clks always as 1. This weird behavior can last a few seconds to several seconds, then can go back to normal for awhile. Real hardware seems to be affected the same way... I get occasional frame break up. On my LCD TV, it goes grayscale or weird color and vertical stripy during these periods. So RIOT is acting like I am not storing to TIM64T but TIM1T, but my code is still definitely storing into TIM64T, which I can see occur in Stella IO window. Earlier in my kernel, for the main play area, I'm using the timer two other times with TIM8T when I'm updating player sprite positions and other sprite drawing preparations. I'm not sure if using those could leave my timer in a weird state. Now here is the funny part... I have come up with two hacky "fixes" for this weird behavior that don't make much sense to me. See code excerpt below. 1) FIRST OPTION - Delete Darrell's initial STA WSYNC instruction, changing the timing of the later STA TIM64T to ending on cycle 21 rather than 11. 2) SECOND OPTION - Immediately read INTIM after storing to TIM64T Either of these works individually or together, both in Stella sim and on real hardware. If these work consistently, I suppose I can leave it as-is, but they seem hacky, especially the 2nd option. Note in the code below I commented-out the later OSwait store to WSYNC to make sure I was not barely missing the countdown to 0. OverScan: ; sta WSYNC ; FIRST OPTION HACKY(?) FIX <<<< COMMENTED OUT! ; Wait for SYNC (halts CPU until end of scanline) lda #2 ; LoaD Accumulator with 2 so D1=1 sta VBLANK ; STore Accumulator to VBLANK, D1=1 turns image output off lda #32 ; set timer for 27 scanlines, 32 = ((27 * 76) / 64) sta TIM64T ; set timer to go off in 27 scanlines lda INTIM ; SECOND OPTION HACKY FIX <<<<< EXTRA READ HERE!!! SEEMS TO HELP PROPER DIV-BY-64 WORK. ; my game logic... OSwait: ; sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda INTIM ; Check the timer bne OSwait ; Branch if its Not Equal to 0 rts ; ReTurn from Subroutine If there is something subtle about using the timer I'm not taking into account, or good practices to cleanly reset the timer function, I would appreciate hearing about them and learning. Maybe others would learn too! Thanks. -- Mike
-
Subject: Inconsistent timer behavior... what a RIOT! Hi @SpiceWare! (Others jump in if you can help me!) I've used your timer code as a starting point for my OverScan subroutine for a new game I'm working on. However, during running of my game, sometimes the RIOT timer count-down is not working with the divide-by-64 as expected, but rather seems like divide-by-1, so naturally the timer is wrapping around before I can catch it and I get a way-to-long overscan period so vsync does not happen. So I get a partial 87-line frame between each 262-ish frame (still working to get that exactly correct). During this behavior, in Stella the frame flashed and the RIOT registers subwindow shows TIMINT as $C0 and INTIM Clks always as 1. This weird behavior can last a few seconds to several seconds. Then can go back to normal for awhile. Real hardware seems to be affected the same way... I get occasional frame break up (on my LCD TV, it goes grayscale and vertical stripy). So it is acting like I am not storing to TIM64T but TIM1T, but my code is still definitely storing into TIM64T, which I can see occur in Stella IO window. Earlier in my kernel, for the main play area, I'm using the timer two other times with TIM8T when I'm updating player sprite positions and other sprite drawing preparations. I'm not sure if using those could leave my timer in a weird state. Now here is the funny part... if I immediately read INTIM after storing to TIM64T, it acts normally and I get a chance to catch the timer countdown and all is well. If this works consistently, I suppose I can leave it in like this, but it seems hacky. Note in the code below I commented-out the store to WSYNC to make sure I was not barely missing the countdown to 0. lda #32 ; set timer for 27 scanlines, 32 = ((27 * 76) / 64) sta TIM64T ; set timer to go off in 27 scanlines lda INTIM ; EXTRA READ HERE!!! SEEMS TO HELP PROPER DIV-BY-64 WORK. ; my game logic... OSwait: ; sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda INTIM ; Check the timer bne OSwait ; Branch if its Not Equal to 0 rts ; ReTurn from Subroutine If there is something subtle about using the timer I'm not taking into account, or good practices to cleanly reset the timer function, I would appreciate hearing about them and learning! -- Mike
-
Harmony Encore SD slot still spring-loaded push-push?
Sohl replied to Sohl's topic in Harmony Cartridge
Batari: No, I should be just fine with the one I got! I just did not want to mess it up right away. Thanks for clarifying. -- Mike P.S. Maybe update the manual to make it clear that there are two types of SD slots and how to identify and use each. I am fairly sure I downloaded the latest manual .pdf file when my cart arrived in the mail last week. -
Hi! I received my Harmony Encore and so far it is working great! I can run any of the ROMs I put on the SD card. Thanks for this cool product. However, when I first put in the full-size SD card, I didn't really feel the normal spring pushback and click. And when I try to press the SD card in further, I don't get feel it move and get ready to pop out. Is it possible the current carts don't use the sping-load click mechanism any more? I know some users had trouble before and damaged their Harmony carts by yanking out the SD card, and want some advice before I try to remove the SD card from my Harmony Encore to reload so I don't damage it. Thanks for any info!
-
@stephena, @Thomas Jentzsch New information: I cannot reproduce the issue now. ??? As far as I can tell, I only did two things in a different way compared to before: (1) Just a few minutes ago, I ran the 'saveconfig' command to post the .cfg file as suggested. I also tried launching Stella in a different way. When I was seeing the issue, I was launching Stella in Windows 10 using a little bat file on the command line. The file is 'debug.bat' and contains just this line: Stella -debug [my-program-name].bin (but with my actual program name spelled out in the file) (2) As an experiment, and just after I ran saveconfig and closed Stella, I instead launched Stella from a Windows desktop icon (so no .bin defined). In the file dialog bog, I navigated over to my .bin and loaded it, opened debugger mode, and now see the ROM disassembled as expected (no issue). And now if I re-launch using my debug.bat file, it opens in the debugger mode immediately as I want and it has good disassembly. I even tried to delete the saveconfig's .cfg file before launching with the .bat and it still disassembles OK. So I can no longer reproduce the issue I reported. I'm not sure how what I did fixed it, but it seems to. I ran Stella many dozens of times yesterday with the .bat file and the disassembly was always short. I can still DM some files if you want to take a look, but I know it will be much harder to solve if it cannot be reproduced reliably. ? Speculation: Stella somehow had some unhelpful config state stored somewhere that keep causing me issues yesterday, but by doing (1) or (2) it got cleared out and things are working as expected now. Is this feasible somehow? Anyway, thanks for offering help. Stella is such a great tool for developers and players alike!
- 96 replies
-
Thanks Stephena. I'll DM since I don't want to publicly release this game just yet! ? EDIT... new info in next post!!!
- 96 replies
-
Hi Thomas. Thanks for jumping in! I did try the Disassemble @ command in the raw block, at several different point, to no effect. .cfg file attached. It does not seem to show much, at least to me! imm.cfg
- 96 replies
