Omegamatrix Posted June 9, 2011 Share Posted June 9, 2011 In my game I just needed to enable/disable a missile, and position an object with a cycle 73 or cycle 74 hmove all in 1 scanline. Playing around with how to do this I came up with some fairly economical code. I then made the demo posted below showing the yellow object positioned by a cycle 74 hmove, and the green object positioned by a traditional hmove. If you are wondering cycle 73 or 74 hmoves hide the black "comb" lines on in an Atari game. The consequence of using cycle 73 or 74 hmoves is that you can only fine position the object leftward. hmove74.zip Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 10, 2011 Author Share Posted June 10, 2011 (edited) I made a commenting error in my code saying X is bounded 1-10. When X is used its value is $FF, which causes the page boundary to be crossed hence why the 5 cycles delay works. I was playing around more and it seems easy enough to do any positioning by using Y for the delay and X for the index, like: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; prep to position top object ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; lda quickPos sta HMP0 and #$0F tay ; delay count ldx #0 ; 0 = GRP0, 1 = GRP1, etc.. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; position top object in 1 scanline ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; sta WSYNC ;--------------------------------------- nop ;2 @2 nop ;2 @4 nop ;2 @6 nop ;3 @8 .waitPos: dey ;2 @10 bpl .waitPos ;2³ @12/13 jmp.ind (indirectAddress) ;5 @17 jump into table below position_3: sta RESP0,X .byte $9D ; sta $1095,X (rom space), skip next two bytes position_15: sta RESP0,X .byte $9D position_30: sta RESP0,X .byte $9D position_45: sta RESP0,X .byte $9D position_60: sta RESP0,X .byte $9D position_75: sta RESP0,X .byte $9D position_90: sta RESP0,X .byte $9D position_105: sta RESP0,X .byte $9D position_120: sta RESP0,X .byte $9D position_135: sta RESP0,X .byte $9D position_150: sta RESP0,X sta HMOVE ;3 @74 nop ;2 @76 free time Opcode $9D always seem to take 5 cycles regardless of X's value. Edited June 10, 2011 by Omegamatrix Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 10, 2011 Author Share Posted June 10, 2011 Thinking about it some more, and it might be better to do illegal NOP's to avoid superchip areas. Maybe something like this: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; prep to position top object ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; -5 = RESP0 ; -4 = RESP1 ; -3 = RESM0 ; -2 = RESM1 ; -1 = RESBL ldx #-5 lda quickPos sta HMP0+5,X and #$0F tay ; delay count ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; position top object in 1 scanline ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; sta WSYNC ;--------------------------------------- nop ;2 @2 nop ;2 @4 nop ;2 @6 nop ;3 @8 .waitPos: dey ;2 @10 bpl .waitPos ;2³ @12/13 jmp.ind (indirectAddress) ;5 @17 jump into table below postion_3: sta RESP0+5,X .byte $1C ; nop $1595,X (rom space), skip next two bytes postion_15: sta RESP0+5,X .byte $1C postion_30: sta RESP0+5,X .byte $1C postion_45: sta RESP0+5,X .byte $1C postion_60: sta RESP0+5,X .byte $1C postion_75: sta RESP0+5,X .byte $1C postion_90: sta RESP0+5,X .byte $1C postion_105: sta RESP0+5,X .byte $1C postion_120: sta RESP0+5,X .byte $1C postion_135: sta RESP0+5,X .byte $1C postion_150: sta RESP0+5,X sta HMOVE ;3 @74 nop ;2 @76 free time ;--------------------------------------- Where using the negative values for X will wrap the page boundary and add a cycle. This could easily turned into a subroutine as well. Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 10, 2011 Author Share Posted June 10, 2011 (edited) The old routine used a register that held a horizontal position value (0-159) and a second register for the "quickPos" value. This was a waste of ram, so I made a little routine that just uses "quickPos" values for location, and updates them with joystick movement. This way you free up ram! Find all your new values in overscan and then quickly load the "quickPos" object you want mid kernel. L_EDGE_VAL = $71 R_EDGE_VAL = $C0 LEFT_ADJ = $9A RIGHT_ADJ = $50 lax quickPos and #$0F sta tempOne txa ; quickPos lsr lsr lsr lsr tay ; HMxx >> 4, use Y to find next HMxx value bit SWCHA ; test joystick bvc .moveLeft bmi .exitRoutine ; if no change ;else move right... cpx #R_EDGE_VAL beq .alignLeft cpx #LEFT_ADJ beq .fixRight lda RightTab,Y cpy #9 beq .addDelayCount bne .prepForAddition ; always branch .alignLeft: lda #L_EDGE_VAL .byte $0C .alignRight: lda #R_EDGE_VAL .byte $0C .fixLeft: lda #LEFT_ADJ .byte $0C .fixRight: lda #RIGHT_ADJ bne .storeQuickPos ; always branch LeftTab: .byte $10 .byte $20 .byte $30 .byte $40 .byte $50 .byte $60 .byte $70 .byte $90 nop .byte $A0 .byte $B0 .byte $C0 .byte $D0 .byte $E0 RightTab: .byte $F0 ; shared .byte $00 ; shared .byte $10 .byte $20 .byte $30 .byte $40 .byte $50 .byte $60 nop .byte $70 .byte $90 .byte $A0 .byte $B0 .byte $C0 .byte $D0 ; .byte $E0 shared with CPX #immediate opcode .moveLeft: cpx #L_EDGE_VAL beq .alignRight cpx #RIGHT_ADJ beq .fixLeft lda LeftTab,Y cpy #7 bne .prepForAddition dec tempOne .prepForAddition: clc .addDelayCount: adc tempOne .storeQuickPos: sta quickPos .exitRoutine: I almost forgot, you do have to initialize the horizontal value one time, i.e.: lda #$71 ; this is position "0", in a range of 0-159 sta quickPos Edited June 10, 2011 by Omegamatrix Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 10, 2011 Author Share Posted June 10, 2011 Then again it makes it a lot easier just to spend the bytes and do a big old table of values. It is by far the fastest, and makes the code easier to understand and manage, and it easier to calculate new positions for objects when they jump more than 1 pixel. ldy objHpos lda PositionTab,Y sta HMP0+5,X and #$0F tay ;...... PositionTab: .byte $71,$61,$51,$41,$31,$21,$11,$01,$F1,$E1,$D1,$C1,$B1,$A1,$91 .byte $72,$62,$52,$42,$32,$22,$12,$02,$F2,$E2,$D2,$C2,$B2,$A2,$92 .byte $73,$63,$53,$43,$33,$23,$13,$03,$F3,$E3,$D3,$C3,$B3,$A3,$93 .byte $74,$64,$54,$44,$34,$24,$14,$04,$F4,$E4,$D4,$C4,$B4,$A4,$94 .byte $75,$65,$55,$45,$35,$25,$15,$05,$F5,$E5,$D5,$C5,$B5,$A5,$95 .byte $76,$66,$56,$46,$36,$26,$16,$06,$F6,$E6,$D6,$C6,$B6,$A6,$96 .byte $77,$67,$57,$47,$37,$27,$17,$07,$F7,$E7,$D7,$C7,$B7,$A7,$97 .byte $78,$68,$58,$48,$38,$28,$18,$08,$F8,$E8,$D8,$C8,$B8,$A8,$98 .byte $79,$69,$59,$49,$39,$29,$19,$09,$F9,$E9,$D9,$C9,$B9,$A9,$99 .byte $7A,$6A,$5A,$4A,$3A,$2A,$1A,$0A,$FA,$EA,$DA,$CA,$BA,$AA,$9A .byte $50,$40,$30,$20,$10,$00,$F0,$E0,$D0,$C0 Quote Link to comment Share on other sites More sharing options...
+JAC! Posted June 18, 2011 Share Posted June 18, 2011 Newbie question (maybe ): Looking at the code it seems that the preparation code results in so many cycles that it cannot be applied in consequtive scanlines and due to the STA RESPx there the object will not be present in the line itself right? So the "only" advantage is not to have the black "comb" (which is sure worth the effort in some situations like blue background). Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 19, 2011 Author Share Posted June 19, 2011 Yep, the reason for doing early hmove's is to hide the black comb lines. The main pain with using early hmove's is you simply can't use a WSYNC followed by a HMOVE. Quote Link to comment Share on other sites More sharing options...
+JAC! Posted June 19, 2011 Share Posted June 19, 2011 >The consequence of using cycle 73 or 74 hmoves is that you can only fine position the object leftward And are the valid HM values also restricted is you only use HMOVE and no RESPx ? (Why are they restricted at all?) Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted June 19, 2011 Share Posted June 19, 2011 >The consequence of using cycle 73 or 74 hmoves is that you can only fine position the object leftward And are the valid HM values also restricted is you only use HMOVE and no RESPx ? (Why are they restricted at all?) The HM values are still the same, but their *effects* are different. With a normal (cycle 3) HMOVE, the HM values let you shift an object's position between 7 pixels to the left and 8 pixels to the right. When you do a cycle 73 or cycle 74 HMOVE, the same HM values shift an object's position between 15 pixels to the left and 0 pixels to the right. This is due to technical reasons related to the way HMOVE works, as well as the way the position counters for the players, missiles, and ball work. Basically, HMOVE by itself can move objects only to the left, by feeding extra pulses to the position counters so the objects end up being drawn sooner than they otherwise would have been. Since there's no way to *subtract* pulses from the position counters to make the objects get drawn later (i.e., moved to the right), HMOVE turns on a latch that causes the horizontal blank to be extended 8 pixels-- creating the "HMOVE bar"-- and that causes the objects to be shifted to the right by 8 pixels. Strobing HMOVE at cycle 73 or 74 sets the latch, but then the latch gets released by the leading edge of the horizontal blank, so there's no "HMOVE bar," consequently there's also no way to move the objects to the right. Michael Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted June 19, 2011 Share Posted June 19, 2011 MiniDig has a list of HMOVE Timing values. Normal cycle 3 HMOVE is listed as cycle 79 in the list: HMPx values 0 1 2 3 4 5 6 7 8 9 a b c d e f 73 -8 -9 -10 -11 -12 -13 -14 -15 0 -1 -2 -3 -4 -5 -6 -7 74 -8 -9 -10 -11 -12 -13 -14 -15 0 -1 -2 -3 -4 -5 -6 -7 ** 75 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK 76 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK 77 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK 78 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK 79 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK Quote Link to comment Share on other sites More sharing options...
+JAC! Posted June 20, 2011 Share Posted June 20, 2011 Thanks to the good explanations. Now I also see why the bar is always black, no matter what the COLUBK is. For my current effect I need right moves, but it's good to know about this anyway. Peter. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted June 20, 2011 Share Posted June 20, 2011 Thinking about it some more, and it might be better to do illegal NOP's to avoid superchip areas. I am not 100% sure, but IIRC even a NOP would trigger the hotspots. Quote Link to comment Share on other sites More sharing options...
+batari Posted June 20, 2011 Share Posted June 20, 2011 Thinking about it some more, and it might be better to do illegal NOP's to avoid superchip areas. I am not 100% sure, but IIRC even a NOP would trigger the hotspots. You are correct, NOP absolute instead of BIT will not save you from wayward accesses. However, this particular case will only access Superchip read areas and so it should be safe. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.