Jump to content



2

Cycle 74 Hmoves


12 replies to this topic

#1 Omegamatrix OFFLINE  

Omegamatrix

    River Patroller

  • 4,796 posts
  • Location:Oh, Canada

Posted Thu Jun 9, 2011 2:54 PM

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.


Attached File  hmove74.zip   3.02K   34 downloads

#2 Omegamatrix OFFLINE  

Omegamatrix

    River Patroller

  • 4,796 posts
  • Location:Oh, Canada

Posted Thu Jun 9, 2011 10:16 PM

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 by Omegamatrix, Thu Jun 9, 2011 10:25 PM.


#3 Omegamatrix OFFLINE  

Omegamatrix

    River Patroller

  • 4,796 posts
  • Location:Oh, Canada

Posted Fri Jun 10, 2011 10:03 AM

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. :)

#4 Omegamatrix OFFLINE  

Omegamatrix

    River Patroller

  • 4,796 posts
  • Location:Oh, Canada

Posted Fri Jun 10, 2011 12:41 PM

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 by Omegamatrix, Fri Jun 10, 2011 12:43 PM.


#5 Omegamatrix OFFLINE  

Omegamatrix

    River Patroller

  • 4,796 posts
  • Location:Oh, Canada

Posted Fri Jun 10, 2011 3:19 PM

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


#6 JAC! OFFLINE  

JAC!

    Moonsweeper

  • 472 posts
  • Always looking for GFX and MSX for my demos
  • Location:Lebach, Germany

Posted Sat Jun 18, 2011 2:49 PM

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).

#7 Omegamatrix OFFLINE  

Omegamatrix

    River Patroller

  • 4,796 posts
  • Location:Oh, Canada

Posted Sat Jun 18, 2011 8:43 PM

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.

#8 JAC! OFFLINE  

JAC!

    Moonsweeper

  • 472 posts
  • Always looking for GFX and MSX for my demos
  • Location:Lebach, Germany

Posted Sun Jun 19, 2011 4:30 AM

>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?)

#9 SeaGtGruff OFFLINE  

SeaGtGruff

    River Patroller

  • 4,545 posts
  • Location:Georgia, USA

Posted Sun Jun 19, 2011 5:12 AM

View PostJAC!, on Sun Jun 19, 2011 4:30 AM, said:

>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

#10 SpiceWare OFFLINE  

SpiceWare

    Quadrunner

  • 5,990 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Sun Jun 19, 2011 5:42 PM

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


#11 JAC! OFFLINE  

JAC!

    Moonsweeper

  • 472 posts
  • Always looking for GFX and MSX for my demos
  • Location:Lebach, Germany

Posted Mon Jun 20, 2011 1:06 AM

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.

#12 Thomas Jentzsch OFFLINE  

Thomas Jentzsch

    Thrust, Jammed, SWOOPS!

  • 16,745 posts
  • Always left from right here!
  • Location:Düsseldorf, Germany

Posted Mon Jun 20, 2011 5:17 AM

View PostOmegamatrix, on Fri Jun 10, 2011 10:03 AM, said:

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.

#13 batari OFFLINE  

batari

    )66]U('=I;B$*

  • 6,236 posts
  • begin 644 contest

Posted Mon Jun 20, 2011 11:08 AM

View PostThomas Jentzsch, on Mon Jun 20, 2011 5:17 AM, said:

View PostOmegamatrix, on Fri Jun 10, 2011 10:03 AM, said:

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.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users