如何使用 65c816 程序集为 SNES 修改精灵位置?

How to modify sprite position using 65c816 Assembly for the SNES?

我正在尝试修改精灵的位置,但我不知道该怎么做

我花了几个小时寻找答案,但 none 其中与我正在使用的汇编程序一起工作:WLA-DX. I am extremely new to 6502 assembly, so forgive me if my logic is really skewed. I was following the tutorial on georgjz's tutorial on GitHub. My sprite is just a single colored square and the pallet is the default pallet that comes with yy-chr

Header.inc

;==LoRom==      ; We'll get to HiRom some other time.

.MEMORYMAP                      ; Begin describing the system architecture.
  SLOTSIZE 00                ; The slot is 00 bytes in size. More details on slots later.
  DEFAULTSLOT 0
  SLOT 0 00                  ; Defines Slot 0's starting address.
.ENDME          ; End MemoryMap definition

.ROMBANKSIZE 00              ; Every ROM bank is 32 KBytes in size
.ROMBANKS 8                     ; 2 Mbits - Tell WLA we want to use 8 ROM Banks

.SNESHEADER
  ID "SNES"                     ; 1-4 letter string, just leave it as "SNES"

  NAME "SNES Testing         "  ; Program Title - can't be over 21 bytes,
  ;    "123456789012345678901"  ; use spaces for unused bytes of the name.

  SLOWROM
  LOROM

  CARTRIDGETYPE [=10=]             ; [=10=] = ROM only, see WLA documentation for others
  ROMSIZE                    ;  = 2 Mbits,  see WLA doc for more..
  SRAMSIZE [=10=]                  ; No SRAM         see WLA doc for more..
  COUNTRY                    ;  = U.S.  [=10=] = Japan   = Australia, Europe, Oceania and Asia   = Sweden   = Finland   = Denmark   = France   = Holland   = Spain   = Germany, Austria and Switzerland  [=10=]A = Italy  [=10=]B = Hong Kong and China  [=10=]C = Indonesia  [=10=]D = Korea
  LICENSEECODE [=10=]              ; Just use [=10=]
  VERSION [=10=]                   ; [=10=] = 1.00,  = 1.01, etc.
.ENDSNES

.SNESNATIVEVECTOR               ; Define Native Mode interrupt vector table
  COP EmptyHandler
  BRK EmptyHandler
  ABORT EmptyHandler
  NMI VBlank
  IRQ EmptyHandler
.ENDNATIVEVECTOR

.SNESEMUVECTOR                  ; Define Emulation Mode interrupt vector table
  COP EmptyHandler
  ABORT EmptyHandler
  NMI EmptyHandler
  RESET Start                   ; where execution starts
  IRQBRK EmptyHandler
.ENDEMUVECTOR

.BANK 0 SLOT 0                  ; Defines the ROM bank and the slot it is inserted in memory.
.ORG 0                          ; .ORG 0 is really 00, because the slot starts at 00
.SECTION "EmptyVectors" SEMIFREE

EmptyHandler:
       rti

.ENDS

.EMPTYFILL [=10=]                  ; fill unused areas with [=10=], opcode for BRK.  
                                ; BRK will crash the snes if executed.

Snes_Init.asm

.MACRO Snes_Init
    sei         ; Disabled interrupts
    clc         ; clear carry to switch to native mode
    xce         ; Xchange carry & emulation bit. native mode
    rep     #    ; Binary mode (decimal mode off), X/Y 16 bit
         ldx    #FFF  ; set stack to FFF
         txs

         jsr Init
 .ENDM

 .bank 0
 .section "Snes_Init" SEMIFREE
 Init:
    sep     #    ; X,Y,A are 8 bit numbers
    lda     #F    ; screen off, full brightness
    sta     00   ; brightness + screen enable register 
    stz     01   ; Sprite register (size + address in VRAM) 
    stz     02   ; Sprite registers (address of sprite memory [OAM])
    stz     03   ;    ""                       ""
    stz     05   ; Mode 0, = Graphic mode register
    stz     06   ; noplanes, no mosaic, = Mosaic register
    stz     07   ; Plane 0 map VRAM location
    stz     08   ; Plane 1 map VRAM location
    stz     09   ; Plane 2 map VRAM location
    stz     0A   ; Plane 3 map VRAM location
    stz     0B   ; Plane 0+1 Tile data location
    stz     0C   ; Plane 2+3 Tile data location
    stz     0D   ; Plane 0 scroll x (first 8 bits)
    stz     0D   ; Plane 0 scroll x (last 3 bits) #[=11=] - #ff
    lda     #$FF    ; The top pixel drawn on the screen isn't the top one in the tilemap, it's the one above that.
    sta     0E   ; Plane 0 scroll y (first 8 bits)
    sta     10   ; Plane 1 scroll y (first 8 bits)
    sta     12   ; Plane 2 scroll y (first 8 bits)
    sta     14   ; Plane 3 scroll y (first 8 bits)
    lda     #    ; Since this could get quite annoying, it's better to edit the scrolling registers to fix this.
    sta     0E   ; Plane 0 scroll y (last 3 bits) #[=11=] - #ff
    sta     10   ; Plane 1 scroll y (last 3 bits) #[=11=] - #ff
    sta     12   ; Plane 2 scroll y (last 3 bits) #[=11=] - #ff
    sta     14   ; Plane 3 scroll y (last 3 bits) #[=11=] - #ff
    stz     0F   ; Plane 1 scroll x (first 8 bits)
    stz     0F   ; Plane 1 scroll x (last 3 bits) #[=11=] - #ff
    stz     11   ; Plane 2 scroll x (first 8 bits)
    stz     11   ; Plane 2 scroll x (last 3 bits) #[=11=] - #ff
    stz     13   ; Plane 3 scroll x (first 8 bits)
    stz     13   ; Plane 3 scroll x (last 3 bits) #[=11=] - #ff
    lda     #    ; increase VRAM address after writing to 19
    sta     15   ; VRAM address increment register
    stz     16   ; VRAM address low
    stz     17   ; VRAM address high
    stz     1A   ; Initial Mode 7 setting register
    stz     1B   ; Mode 7 matrix parameter A register (low)
    lda     #
    sta     1B   ; Mode 7 matrix parameter A register (high)
    stz     1C   ; Mode 7 matrix parameter B register (low)
    stz     1C   ; Mode 7 matrix parameter B register (high)
    stz     1D   ; Mode 7 matrix parameter C register (low)
    stz     1D   ; Mode 7 matrix parameter C register (high)
    stz     1E   ; Mode 7 matrix parameter D register (low)
    sta     1E   ; Mode 7 matrix parameter D register (high)
    stz     1F   ; Mode 7 center position X register (low)
    stz     1F   ; Mode 7 center position X register (high)
    stz     20   ; Mode 7 center position Y register (low)
    stz     20   ; Mode 7 center position Y register (high)
    stz     21   ; Color number register ([=11=]-ff)
    stz     23   ; BG1 & BG2 Window mask setting register
    stz     24   ; BG3 & BG4 Window mask setting register
    stz     25   ; OBJ & Color Window mask setting register
    stz     26   ; Window 1 left position register
    stz     27   ; Window 2 left position register
    stz     28   ; Window 3 left position register
    stz     29   ; Window 4 left position register
    stz     2A   ; BG1, BG2, BG3, BG4 Window Logic register
    stz     2B   ; OBJ, Color Window Logic Register (or,and,xor,xnor)
    sta     2C   ; Main Screen designation (planes, sprites enable)
    stz     2D   ; Sub Screen designation
    stz     2E   ; Window mask for Main Screen
    stz     2F   ; Window mask for Sub Screen
    lda     #
    sta     30   ; Color addition & screen addition init setting
    stz     31   ; Add/Sub sub designation for screen, sprite, color
    lda     #$E0
    sta     32   ; color data for addition/subtraction
    stz     33   ; Screen setting (interlace x,y/enable SFX data)
    stz     00   ; Enable V-blank, interrupt, Joypad register
    lda     #$FF
    sta     01   ; Programmable I/O port
    stz     02   ; Multiplicand A
    stz     03   ; Multiplier B
    stz     04   ; Multiplier C
    stz     05   ; Multiplicand C
    stz     06   ; Divisor B
    stz     07   ; Horizontal Count Timer
    stz     08   ; Horizontal Count Timer MSB (most significant bit)
    stz     09   ; Vertical Count Timer
    stz     0A   ; Vertical Count Timer MSB
    stz     0B   ; General DMA enable (bits 0-7)
    stz     0C   ; Horizontal DMA (HDMA) enable (bits 0-7)
    stz     0D   ; Access cycle designation (slow/fast rom)
    cli         ; Enable interrupts
    rts
 .ends

Testing.asm

.include "header.inc"
.include "Snes_Init.asm"

SpriteData: .incbin "sprite.sprite"
ColorData: .incbin "sprite.pal"

VBlank:    ; Needed to satisfy interrupt definition in "Header.inc"
    RTI

Start:
    Snes_Init
    sei                     ; disable interrupts
    clc                     ; clear the carry flag
    xce                     ; switch the 65816 to native (16-bit mode)
    lda #f                ; force v-blanking
    sta 00
    stz 00            ; disable NMI

    ; transfer VRAM data
    stz 16              ; set the VRAM address to [=12=]00
    stz 17
    lda #
    sta 15              ; increment VRAM address by 1 when writing to 19
    ldx #[=12=]                ; set register X to zero, we will use X as a loop counter and offset

VRAMLoop:
    .16BIT
    lda SpriteData, X       ; get bitplane 0/2 byte from the sprite data
    sta 18             ; write the byte in A to VRAM
    sta [=12=]00             ; write the byte in A to VRAM
    inx                     ; increment counter/offset
    lda SpriteData, X       ; get bitplane 1/3 byte from the sprite data
    sta 19             ; write the byte in A to VRAM
    sta [=12=]00             ; write the byte in A to VRAM
    inx                     ; increment counter/offset
    cpx #                ; check whether we have written  *  :  bytes to VRAM (four sprites)
    bcc VRAMLoop            ; if X is smaller than , continue the loop

    ; transfer CGRAM data
    lda #
    sta 21               ; set CGRAM address to 
    ldx #[=12=]                ; set X to zero, use it as loop counter and offset

CGRAMLoop:
    lda ColorData, X        ; get the color low byte
    sta 22              ; store it in CGRAM
    inx                     ; increase counter/offset
    lda ColorData, X        ; get the color high byte
    sta 22              ; store it in CGRAM
    inx                     ; increase counter/offset
    cpx #                ; check whether 32/ bytes were transfered
    bcc CGRAMLoop           ; if not, continue loop

    stz 02             ; set the OAM address to ...
    stz 03             ; ...at [=12=]00

    ; OAM data for first sprite
    lda #      ; horizontal position of first sprite
    sta 04
    lda #       ; vertical position of first sprite
    sta 04
    lda #[=12=]                ; name of first sprite
    sta 04
    lda #[=12=]                ; no flip, prio 0, palette 0
    sta 04

    ; make Objects visible
    lda #
    sta 2C
    ; release forced blanking, set screen to full brightness
    lda #[=12=]f
    sta 00
    jmp GameLoop            ; all initialization is done

GameLoop:
    wai                     ; wait for NMI / V-Blank
    jmp GameLoop

我试图至少让精灵每帧向右移动 1 个像素,但精灵根本没有移动

============================================= ==============================

编辑:我添加了@Michael 评论中的代码,没有任何改变。这是我更新的代码:

Snes_Init.asm

 .define SpriteX [=13=]A0
 Init:
    sep     #    ; X,Y,A are 8 bit numbers
    lda     #
    sta     SpriteX
    ;Initialization code
    rts
.ends

Testing.asm

VBlank:    ; Needed to satisfy interrupt definition in "Header.inc"
    jsr MoveSprite
    RTI

; Code

CGRAMLoop:
    ;More code

    ; OAM data for first sprite
    lda SpriteX      ; horizontal position of first sprite
    sta 04
    lda #       ; vertical position of first sprite
    sta 04
    lda #[=14=]                ; name of first sprite
    sta 04
    lda #[=14=]                ; no flip, prio 0, palette 0
    sta 04

    ;Even more code

并且我在 CGRAMLoop 之后添加了 MoveSprite 子程序

我没有看到任何实际会改变精灵位置的代码。

但是假设您有一个用于存储当前 X 位置的 zeropage 变量:

.define spriteX  [=10=]A0  ; I used address $A0 as an example. Just pick some address
                        ; that you aren't already using for something else

在初始化期间的某处你给它一些初始值:

sep #
lda #
sta spriteX

然后你可以编写一个子程序来递增该值并将其写入 OAM:

MoveSprite:
    php
    sep #
    stz 02
    stz 03 
    inc spriteX
    lda spriteX
    sta 04
    lda #                            
    sta 04  ; This write is necessary even if you're not changing the Y position,
               ; because there's some internal latching going on in the PPU.
    plp
    rts

然后您可以从 VBlank 中断处理程序中调用该子例程。
请注意,您可能需要一个额外的计数器来仅每隔一帧(或您喜欢的任何间隔)增加位置;否则精灵会很快滚动。


您还需要启用 VBlank NMI 才能真正触发中断:

lda #
sta 00        ; Enable VBlank NMI

jmp GameLoop     ; all initialization is done