Jump to content

e1will

Members
  • Posts

    377
  • Joined

  • Last visited

Everything posted by e1will

  1. You're right, of course... To be honest I was just looking for an excuse to play with code that would detect the different environments, and the pause vs "emulator palette" thing seemed like a decent use of that (possibly nonexistent) feature/bug. Speaking of 7800 console detection, I was reading the thread you started about code to handle the 7800 pause vs 2600 TV type switch, and was wondering you could re-post the code for that (if you're willing to share it). (The link you'd posted no longer works.) --Will EDIT: Never mind, I see you just posted it!
  2. I seem to recall a similar feature with an Apple ][ emulator I was using a few years back... I know there are plenty of unused read-only locations, but that doesn't mean that existing cartridges aren't depending on a certain behavior when reading them (either by design or by accident) that using them for "what environment am I running on" info might break. It's an interesting question what sort of locations COULD theoretically be used for that purpose without breaking anything... any address returning an indeterminate value might already be used by existing programs as a quasi-random number, and any address that always returned the same value might be relied on as well (again, either by design or accidentally). (Then I guess there's the third category of hardware-dependent values... I was reading elsewhere that LDA $07 would return "$x7" on a 2600 but "$x0" on a 7800.) --Will
  3. Nah, I'm not interested in copy protection... I was mainly thinking of it in terms of the "pause" vs "color/BW" switch functionality. Since emulators generally include a pause function already, I thought I could use that switch as a "pause function" on a real 2600 and use it to display an alternate color palette in the emulator. --Will
  4. Is there a way for the code to detect whether it's being run under an emulator vs. on an actual 2600? I realize this may be either a desirable development feature or an undesirable emulator bug to be squashed depending on how you look at it, but I'm just curious if there is currently a way to tell. --Will
  5. OK. Yeah, that could be helpful inside of a kernel, although I suppose you'd need to either "refresh" it every 13 scanlines or do some addition at the end of the kernel to compensate. (As a side note, when you mentioned using T1024T, I thought, hmm, maybe I could use that instead of a RAM slot to cache the stack pointer in the game I'm working on now, since I use the stack pointer for other things inside my graphics kernel. But then I realized that since the graphics kernel is only called one way, I don't really need to store the stack pointer at all.. it's always going to be F9 once it gets to the kernel. So that's one byte of RAM saved.) --Will
  6. I'm intrigued... where are these bits? --Will
  7. Well done! I like the separate dot / maze colors. --Will
  8. There are two issues I've noticed with the "disable playfield" function in Stella (Alt-N). (They're not new to 2.8.4, the behavior appears to be the same under 2.8 and 2.7.7). First, Alt-N doesn't seem to disable all of the playfield. In the attached bin, the light blue area of the initial screen is the playfield, but only the top 2/3 is hidden when I toggle Alt-N. The lower playfield still displays. If it helps, the top and bottom playfield sections are drawn using different kernels in different banks (bank 5 for the top part, banks 2 (playfield) and 4 (P0/P1) for the bottom part.) I don't know why this would affect it but I thought I would mention it. Second, Alt-N (as well as the other sprite enable/disable toggles) does not work when the game is paused. Would it be possible to change this? It would be really helpful to be able to examine individual sprites in "freeze frame" when they're not moving around the screen. Thanks, --Will DUCK.BIN
  9. Yeah, I was curious if there was any way to do a conditional zero-page LDA in 5 cycles instead of 6. As I just learned from supercat, there indeed is, if the zero-page location you need to read can be relocated to an address that corresponds to the opcode for a 2-cycle NOP. --Will
  10. Ah, clever! I like that technique quite a bit. I'll have to remember that. --Will
  11. Here's the full kernel. I do have a workaround in the form of duplicating code to save the cycle required for the BNE-JMP, and the code does run fine. (I've attached a .bin of the project I'm working on... it's called Duck Attack and is in a VERY early state, but the duck sprite should give you an idea of why I have separate indexed pointers for HMPx/NUSIZx, GRPx and COLUPx. I'm sacrificing some vertical resolution to try to get big, detailed multicolor sprites.) first_row_past_obj_2 ; +3 = 59 LDA #LAST_ROW ; +2 = 61 STA OBJ0_TOP ; +3 = 64 first_row_clear LDX #$00 ; +2 = 66 LDY #$00 ; +2 = 68 LDA (OBJ0_COLOR_PTR),Y ; +5 = 73 BNE first_row_wall_color ;+2 = 75 STA HMOVE ; +3 = 78 (=2) LDA OBJ0_DEF_COLOR ; +3 = 5 LDY #V_WALL_SIZE ; +2 = 7 JMP second_row ; +3 = 10 ;------------end of branch-------------------- first_row_wall_color ; +3 = 76 (=0) STA HMOVE ; +3 = 3 NOP ; +2 = 5 LDY #V_WALL_SIZE ; +2 = 7 JMP second_row ; +3 = 10 ;------------end of branch-------------------- first_row_no_obj_2 ; +3 = 50 NOP3 ; +3 = 53 NOP ; +2 = 55 NOP ; +2 = 57 NOP ; +2 = 59 NOP ; +2 = 61 JMP first_row_clear ; +3 = 64 ;------------end of branch-------------------- second_row_past_obj_2 ; +3 = 60 LDA #LAST_ROW ; +2 = 62 STA OBJ1_TOP ; +3 = 65 second_row_clear NOP ; +2 = 67 NOP ; +2 = 69 NOP ; +2 = 71 NOP3 ; +3 = 74 LDA MISSILE_COLOR ; +3 = 77 (=1) STA HMOVE ; +3 = 4 STA COLUP1 ; +3 = 7 LDA #$00 ; +2 = 9 JMP third_row_set_gr ; +3 = 12 ;------------end of branch-------------------- second_row_no_obj_2 ; +3 = 51 NOP ; +2 = 53 NOP ; +2 = 55 NOP ; +2 = 57 NOP ; +2 = 59 NOP3 ; +3 = 62 JMP second_row_clear ; +3 = 65 ;------------end of branch-------------------- ;----------------- ORG $90CE ; push this as high as possible to RORG $F0CE ; ensure it follows without BRK to $9100 ;----------------- ;****this code must be in called banks******** MAC PF_CODE_F0CE SwitchToEnd ; +3 = 75 NOP ; +2 = 77 (=1) STA HMOVE ; +3 = 4 LDA SWITCH_TO_BANK_1 ; +4 = 8 JMP BlankOnAndExit ; +3 = 11 ;============end of branch==================== draw_bk_change ; +3 = 67 LDA ROW_COUNT ; +3 = 70 CMP #LAST_ROW ; +2 = 72 BCS SwitchToEnd ; +2 = 74 LDX BK_LOOKUP ; +3 = 77 (=1) STA HMOVE ; +3 = 4 LDA BkTable,X ; +4 = 8 STA COLUBK ; +3 = 11 SEC ; +2 = 13 JMP LoadBallAndMissile ; +3 = 16 ;------------end of branch-------------------- no_pf_change ; +4 = 56 (from next page) LDY BK_INDEX ; +3 = 59 CMP (BK_PTR),Y ; +5 = 64 BCC draw_bk_change ; +2 = 66 INY ; +2 = 68 LDA ADJUSTED_LEVEL ; +3 = 71 (no time for a CLC here) ADC (BK_PTR),Y ; +5 = 76 (= 0) STA HMOVE ; +3 = 3 STA BK_LOOKUP ; +3 = 6 INY ; +2 = 8 STY BK_INDEX ; +3 = 11 SEC ; +2 = 13 JMP LoadBallAndMissile ; +3 = 16 ;------------end of branch-------------------- ENDM PF_CODE_F0CE ;----------------- ORG $9100 RORG $F100 ;----------------- MAC PF_CODE_F100 PF_bank_switch ; = 29 LDY SWITCH_TO_BANK_0,X ; +4 = 33 STA HMCLR ; +3 = 36 INC ROW_COUNT ; +5 = 41 LDA ROW_COUNT ; +3 = 44 LDY PF_INDEX ; +3 = 47 CMP (PF_PTR),Y ; +5 = 52 BCC no_pf_change ; +2 = 54 INY ; +2 = 56 LAX_Y PF_PTR ; +5 = 61 (illegal opcode) INY ; +2 = 63 LDA (PF_PTR),Y ; +5 = 68 INY ; +2 = 70 STX PF1 ; +3 = 73 STA PF0 ; +3 = 76 (=0) STA HMOVE ; +3 = 3 LDA (PF_PTR),Y ; +5 = 8 STA PF2 ; +3 = 11 INY ; +2 = 13 STY PF_INDEX ; +3 = 16 LoadBallAndMissile ; 16 LDY ROW_COUNT ; +3 = 19 LDA (ENABL_PTR),Y ; +5 = 24 STA ENABL ; +3 = 27 (two cycles too late... LDA (MISSILE_GR_PTR),Y ; +5 = 32 nothing we can do, though) STA CACHE_MISSILE ; +3 = 35 LDA SWITCH_TO_BANK_1 ; +4 = 39 ;**********end shared-bank code*************** ENDM PF_CODE_F100 first_row ; 39 TYA ; +2 = 41 SBC OBJ0_TOP ; +3 = 44 BMI first_row_no_obj ; +2 = 46 TAY ; +2 = 48 (row count) LDA (OBJ0_MOVE_PTR),Y ; +5 = 53 BEQ first_row_past_obj ; +2 = 55 STA HMP0 ; +3 = 58 AND #$07 ; +2 = 60 ORA #V_WALL_SIZE ; +2 = 62 STA CACHE_NUSIZ ; +3 = 65 LAX_Y OBJ0_GR_PTR ; +5 = 70 (illegal opcode) LDA (OBJ0_COLOR_PTR),Y ; +5 = 75 STA HMOVE ; +3 = 78 (=2) BNE first_row_no_flash ; +2 = 4 LDA OBJ0_DEF_COLOR ; +3 = 7 LDY CACHE_NUSIZ ; +3 = 10 second_row MAC SECOND_ROW_LOGIC STA COLUP0 ; +3 = 13 STX GRP0 ; +3 = 16 STY NUSIZ0 ; +3 = 19 LAX CACHE_MISSILE ; +3 = 22 (illegal opcode) ASL ; +2 = 24 ASL ; +2 = 26 AND #$30 ; +2 = 28 STA CACHE_NUSIZ ; +3 = 31 STA HMCLR ; +3 = 34 LDA ROW_COUNT ; +3 = 37 STX HMM1 ; +3 = 40 SEC ; +2 = 42 SBC OBJ1_TOP ; +3 = 45 BMI second_row_no_obj ; +2 = 47 TAY ; +2 = 49 LDA (OBJ1_MOVE_PTR),Y ; +5 = 54 BEQ second_row_past_obj ; +2 = 56 STA HMP1 ; +3 = 59 AND #$07 ; +2 = 61 ORA CACHE_NUSIZ ; +3 = 64 STA CACHE_NUSIZ ; +3 = 67 LDA (OBJ1_COLOR_PTR),Y ; +5 = 72 BNE second_row_no_flash ; +2 = 74 LDA OBJ1_DEF_COLOR ; +3 = 77 (=1) STA HMOVE ; +3 = 4 STA COLUP1 ; +3 = 7 LDA (OBJ1_GR_PTR),Y ; +5 = 12 ENDM SECOND_ROW_LOGIC third_row_set_gr ; 12 STA GRP1 ; +3 = 15 LDA CACHE_NUSIZ ; +3 = 18 STA NUSIZ1 ; +3 = 21 STX ENAM1 ; +3 = 24 TSX ; +2 = 26 JMP PF_bank_switch ; +3 = 29 ;------------end of kernel loop--------------- first_row_no_obj JMP first_row_no_obj_2 first_row_past_obj JMP first_row_past_obj_2 second_row_no_obj JMP second_row_no_obj_2 second_row_past_obj JMP second_row_past_obj_2 ;------------end of jumps--------------------- second_row_no_flash ; 75 NOP ; +2 = 77 (=1) STA HMOVE ; +3 = 4 STA COLUP1 ; +3 = 7 LDA (OBJ1_GR_PTR),Y ; +5 = 12 STA GRP1 ; +3 = 15 LDA CACHE_NUSIZ ; +3 = 18 STA NUSIZ1 ; +3 = 21 STX ENAM1 ; +3 = 24 TSX ; +2 = 26 JMP PF_bank_switch ; +3 = 29 ;------------end of branch-------------------- first_row_no_flash ; +3 = 5 NOP ; +2 = 7 LDY CACHE_NUSIZ ; +3 = 10 SECOND_ROW_LOGIC ; 29 STA GRP1 ; +3 = 15 LDA CACHE_NUSIZ ; +3 = 18 STA NUSIZ1 ; +3 = 21 STX ENAM1 ; +3 = 24 TSX ; +2 = 26 JMP PF_bank_switch ; +3 = 29 ;------------end of branch-------------------- The starting point for the code is a JMP to first_row, which is somewhere in the middle of the code block. If the kernel has to draw P0, P1, M0, M1 and a new playfield line, there are exactly 0 cycles free as best I can tell. The playfield, missile sprites and ball graphics are stored in a separate bank, so I have to spend 8 cycles jumping to and back from that bank (since the player sprites are in bank 1, where the kernel is.) I use the stack pointer to index which bank is needed, since there will be about 3 full banks of PF & missile data once the game is through. (And just under 1 full bank's worth of player sprites, so I won't be able to put those in the same bank as the PF data.) I may have reached the limits of my cycle-squeezing abilities, although you guys will probably be able to find something I missed. I've already dropped the WSYNCs and am using all the registers (including the stack pointer, which I cache beforehand.) The good news is that I don't need any extra cycles at this point. I can live with the duplicated code, since I was able to arrange it in such a way that none of the branches are out of range. The main problem I have with it now is that STA ENABL is getting called 2 cycles too late, and I can't figure out a way to call it earlier without forcing STA PF0 to be written 2 cycles too early. The ball sprite is "hidden" behind the P0 sprite most of the time so it's not too noticeable visually, but it does create some glitches moving from room to room (he gets "caught" on certain edges.) Thanks, --Will DUCK.BIN
  12. You know, that's a very good suggestion. I hadn't thought of that, I may end up doing that if I wind up with any spare RAM. Thanks! --Will
  13. Unfortunately there aren't any immediate-mode LDAs in there to merge; they're all of the form LDA (PF_PTR),Y STA PF1 INY LDA (PF_PTR),Y STA PF0 etc. That's not to say there aren't other places in the code I might be able to save a cycle, I was just curious if the BNE/JMP logic was a candidate for trimming too. Thanks, --Will
  14. The LeaveAccumulatorAlone branch is actually very far away from the LDA, about 100 bytes below it. Unfortunately there's no way to move it closer. Here's what I have as a workaround, but of course this is not ideal: StartOfLine ; some code here BNE LeaveAccumulatorAlone ; 2 LDA $80 ; 5 ; about 100 bytes of STAs and LDAs JMP StartOfLine LeaveAccumulatorAlone ; 3 NOP ; 5 ; a second copy of the 100 bytes of STAs and LDAs above JMP StartOfLine --Will
  15. Hi all, I've been wracking my brain trying to figure out if I can shave a cycle off of this piece of code in a timing-critical section... it seems like it SHOULD be possible to squeeze an extra cycle out, but I can't figure out how. I know there are some neat tricks with the BIT opcode that can hop over a byte or two, but I haven't found a way to use BIT in a way that cuts the cycles. BNE LeaveAccumulatorAlone LDA.w $0080 DoNextThing ... LeaveAccumulatorAlone JMP DoNextThing Before the BNE I'm at cycle 0; at "DoNextThing" I'm at 6 cycles regardless of whether I've taken the branch. Is there any way to wind up at "DoNextThing" at 5 cycles by changing the addressing mode of the LDA to zero-page and presumably doing something other than JMPing back to it for the taken branch? I'm starting to think it's not possible and 6 is the best I can get, but I thought maybe there might be a way I don't know about. --Will
  16. Ah, that would be better. Thanks! And now that you mention it, there are probably a couple more places in my kernel where a BCC would be more appropriate than a BMI. --Will
  17. Hi all, I need to shave 2 cycles off my game kernel and I'm curious if I could drop the CLC before the ADC in this section of code: LDA ROW_COUNT CMP (BK_PTR),Y BMI SomeplaceElse INY LDA ADJUSTED_LEVEL CLC ADC (BK_PTR),Y Will the carry flag always be set if the code gets to the INY? In other words, are there any cases I should worry about the branch not being taken but the carry flag being cleared nonetheless? If it will always be set, then I can make sure ADJUSTED_LEVEL is one less than it would otherwise be before entering the kernel, and just let the carry get added in. Both ROW_COUNT and the first (BK_PTR),Y are guaranteed to be within the range $00-$7F, if that helps. --Will
  18. Thanks (on both counts). I agree, there is definitely a problem with my video drivers (HP laptop running Windows XP) because other apps that try to full-screen also generate a blue screen. However, I've also noticed this sporadic full-screening of Stella on my other PC (a Dell running Vista). It doesn't particularly bother me on the Dell since the Dell does support full screen mode and it's simple enough to turn the full-screen back off after it enables itself. --Will
  19. One other thing: occasionally (somewhere between every 10th and 20th time), Stella will spontaneously go into "Full-screen" mode when a game is selected. Unfortunately for me my video drivers do not support full-screen mode so I get a blue screen of death whenever it does this. It would be nice to either track down and fix the cause of the occasional switch to full-screen mode or provide some configuration option of disabling Full-screen mode entirely for those of us with crappy video drivers. --Will
  20. Sent you a PM with the new assembly. (For the benefit of anyone else reading: If I move the balloon location to zero page, the balloon does not disappear when I toggle the debugger; it only disappears when it's stored in extended RAM.) --Will
  21. Thanks. I'll PM you the source... I noticed another artifact that may shed some light on it. I have a "collapsing walls" room that works beautifully... unless you toggle the debugger: 1. Start on the blue screen. Go down three screens (past the duck, past the balloon) to a purple room with a shield in it. 2. Go left. The walls will collapse on you unless you escape to the left or right... 3. But if you toggle the debugger in the trap room, the playfield gets messed up until you leave and return. Both the trap room playfield and the balloon location are stored in expanded memory (I'm trying to use the CBS RAM Plus 256-byte method... maybe it's not supported 100%?) and that memory seems to be zeroed out when entering the debugger. The player location (room X/Y) is stored in normal memory and is not affected. --Will
  22. I don't know if this is a Stella bug or my bug (I'm trying to write a homebrew), but I notice that going into debug mode and then returning to gameplay mode "eats" the sprite that was being displayed. Since I'm new at this it's entirely possible (likely) I'm doing something wrong in the code, but it still seems odd that simply toggling debug mode on and off would change which objects are being displayed. It's almost as if entering debug mode is clearing my variables. Here's how to reproduce this: 1. Run duck.bin (attached). 2. Player starts on a blue screen. Go down two screens (past a flashing duck on a red screen) to a yellow "Y" shaped screen. 3. There's a blue and white balloon sprite on the yellow screen. If you do nothing, the balloon remains on the screen indefinitely. 4. If you press the ` key to toggle debug mode, then press it again, the balloon disappears. Any ideas what could be happening? --Will DUCK.BIN
  23. That works perfectly! Thank you very much. --Will
  24. Can any kind-hearted assembly expert tell me what I'm doing wrong here? I want to put the number five into the A register. This seems to work fine: LDA #$05 It generates the (immediate mode) opcodes A9 05. However, when I use a symbolic name for readability: MAX_LIVES = #$05 LDA MAX_LIVES ...DASM generates the wrong opcodes: A5 05, which result in it trying to read from memory location 05 rather than store the number $05 in the A register. Any ideas what I'm doing wrong here? Replacing the = with EQU or EQM does not seem to help. (I apologize if this is not the right place to post this.) --Will
×
×
  • Create New...