Sega Genesis DMA将我的精灵移动到屏幕上

发布于 2025-02-06 17:33:44 字数 3312 浏览 2 评论 0原文

编辑:毕竟不是DMA这样做的事实是,日常工作使我的其他寄存器陷入困境。傻我。

因此,我试图了解DMA(直接内存访问)在SEGA Genesis上的工作原理,并且我获得了VRAM填充模式,主要按照我期望的方式工作,但是有一个小问题。这样做似乎将我的游戏精灵转移到屏幕上,我不明白为什么。作为参考,这是我在融合中运行的游戏的图片:

​红色正方形作为前景瓷砖,云和天空是背景瓷砖。窗口层是屏幕外的。我正在使用VRAM配置,其中前景层处于VDP地址$ C000(SEGA Code $ 40000003),Sprite Attribute Table售价$ D800($ 58000003)的背景$ e000($ 60000003),以及$ $ f000($ 7000003)的窗口。我已经设置了可以在控制器上按C启动DMA的位置。这是执行DMA命令的例程。

dma_fill:
    ;explanation of macros and defined constants:
    ;pushRegs = MOVEM.L ___,-(SP)
    ;popRegs  = MOVEM.L (SP)+,____
    ;DI = MOVE #$2300,SR
    ;pushf = MOVE SR,-(SP)
    ;popf  = MOVE (SP)+,SR
    ;CD5 = %10000000
    ;VDP_CTRL = $00C00004
    ;VDP_DATA = $00C00000

    ;input:
    ;D2.L = what address to write to.
    ;D1.W = DMA LENGTH
    ;D0 = WHAT DATA TO USE TO FILL VRAM
    pushRegs D3-D7
    pushf
        DI      ;we don't want interrupts during this time.

        MOVEQ.L #-109,D3            ;quickly move #$FFFFFF93 into D3
        LSL.W #8,D3
        OR.B D1,D3                  
        ;d3 contains $93xx where xx is the low byte of dma length
        ;this is the correct command to give the vdp
        
        
        LSR.W #8,D1 ;shift high byte of dma length down to low byte
        
        MOVEQ.L #-108,D4            ;quickly move #$FFFFFF94 into d4
        LSL.W #8,D4                 ;D4 = #$FFFF9400
        OR.B D1,D4
        ;d3 contains $94xx where xx is the high byte of dma length
        ;this is the correct command to give the vdp
        OR.L #CD5,D2        ;tells the vdp the next write is a DMA write
        
.wait:
        move.w VDP_ctrl,d7
        and.w #%0000000000001000,d7     ;See if vblank is running
        bne .wait                       ;wait until it is
        
        MOVE.W #($8100|%01110100),(VDP_CTRL)    ;ENABLE DMA
        move.w #$8F01,(vdp_ctrl)                ;set auto-inc to 1
        MOVE.W #$9780,(vdp_ctrl)                ;enable dma vram fill
        ; HALT
        MOVE.W D3,(vdp_ctrl)                    ;set dma length low byte
        MOVE.W D4,(vdp_ctrl)                    ;set dma length high byte
        MOVE.L D2,(vdp_ctrl)                    ;set destination address
        
        MOVE.W D0,(vdp_data)                    ;write the data, dma begins here.
        ;do I need to wait for DMA to finish before continuing?
; .waitDma:
        ; MOVE.W (vdp_ctrl),d6
        ; btst #1,d6
        ; bne .waitDma
        
    
        move.w #($8100|%01100100),(VDP_CTRL)    ;DISABLE DMA
        move.w #$8F02,(vdp_ctrl)                ;set auto-inc back to 2
    popf        ;restore flags and interrupt level
    popRegs D3-D7
    RTS

填充值的参数为0(d0空的8x8瓷砖的瓷砖编号),D1的填充长度为$ 0400,目的地为$ 40000003(前景tilemap),我希望VRAM区域为$ C000 - $ c400填充空白瓷砖。但这是实际发生的事情。

大约要清除前4行,请记住,在我的游戏中,每个“ MetaTile”是四个8x8瓷砖。),就长度而言,这似乎就很正确。但是狗雪橇已经改变了位置。鉴于我在VDP地址$ d800上有Sprite属性表,这确实没有意义。出于好奇,我尝试了相同的功能,其中长度参数更改为$ 0200,当我这样做时,没有一个瓷砖被清除,但是精灵仍在屏幕上移动到同一位置。多么奇怪。我能找到的现有文档要么不是很好地翻译成英语,要么并没有真正解释所有在内存中实际发生的事情的细节(它更多地关注如何使DMA发生,这并不难以弄清楚,但我不明白为什么我的精灵会因此移动。)

EDIT: It wasn't the DMA doing that after all, it was the fact that the routine clobbered my other registers. Silly me.

So I'm trying to understand how DMA (Direct Memory Access) works on the Sega Genesis, and I've gotten the VRAM Fill mode to work mostly the way I would expect, however, there's one little problem. Doing it seems to shift my game sprite around the screen and I don't understand why. For reference, here is a picture of my game running in Fusion:

enter image description here

For the necessary background info, the puppy dog is a sprite and all other graphics are tiles, with the boxes, the lime, and the red squares as foreground tiles, and the clouds and sky are background tiles. The window layer is off-screen. I'm using the VRAM configuration where the foreground layer is at VDP address $C000 (sega code $40000003), sprite attribute table at $D800 ($58000003) background at $E000($60000003), and window at $F000 ($7000003). I've set things up to where I can press C on the controller to start DMA. Here's the routine that executes the DMA command.

dma_fill:
    ;explanation of macros and defined constants:
    ;pushRegs = MOVEM.L ___,-(SP)
    ;popRegs  = MOVEM.L (SP)+,____
    ;DI = MOVE #$2300,SR
    ;pushf = MOVE SR,-(SP)
    ;popf  = MOVE (SP)+,SR
    ;CD5 = %10000000
    ;VDP_CTRL = $00C00004
    ;VDP_DATA = $00C00000

    ;input:
    ;D2.L = what address to write to.
    ;D1.W = DMA LENGTH
    ;D0 = WHAT DATA TO USE TO FILL VRAM
    pushRegs D3-D7
    pushf
        DI      ;we don't want interrupts during this time.

        MOVEQ.L #-109,D3            ;quickly move #$FFFFFF93 into D3
        LSL.W #8,D3
        OR.B D1,D3                  
        ;d3 contains $93xx where xx is the low byte of dma length
        ;this is the correct command to give the vdp
        
        
        LSR.W #8,D1 ;shift high byte of dma length down to low byte
        
        MOVEQ.L #-108,D4            ;quickly move #$FFFFFF94 into d4
        LSL.W #8,D4                 ;D4 = #$FFFF9400
        OR.B D1,D4
        ;d3 contains $94xx where xx is the high byte of dma length
        ;this is the correct command to give the vdp
        OR.L #CD5,D2        ;tells the vdp the next write is a DMA write
        
.wait:
        move.w VDP_ctrl,d7
        and.w #%0000000000001000,d7     ;See if vblank is running
        bne .wait                       ;wait until it is
        
        MOVE.W #($8100|%01110100),(VDP_CTRL)    ;ENABLE DMA
        move.w #$8F01,(vdp_ctrl)                ;set auto-inc to 1
        MOVE.W #$9780,(vdp_ctrl)                ;enable dma vram fill
        ; HALT
        MOVE.W D3,(vdp_ctrl)                    ;set dma length low byte
        MOVE.W D4,(vdp_ctrl)                    ;set dma length high byte
        MOVE.L D2,(vdp_ctrl)                    ;set destination address
        
        MOVE.W D0,(vdp_data)                    ;write the data, dma begins here.
        ;do I need to wait for DMA to finish before continuing?
; .waitDma:
        ; MOVE.W (vdp_ctrl),d6
        ; btst #1,d6
        ; bne .waitDma
        
    
        move.w #($8100|%01100100),(VDP_CTRL)    ;DISABLE DMA
        move.w #$8F02,(vdp_ctrl)                ;set auto-inc back to 2
    popf        ;restore flags and interrupt level
    popRegs D3-D7
    RTS

With the parameters of a fill value of 0 (the tile number of an empty 8x8 tile) in D0, a fill length of $0400 in D1, and a destination of $40000003 (the foreground tilemap), I would expect the VRAM region of $C000-$C400 to be filled with blank tiles. But here's what actually happens.

enter image description here

About the first 4 rows of metatiles get cleared (keep in mind that in my game each "metatile" is four 8x8 tiles.), which seems about right as far as the length is concerned. But the dog sprite has changed position. Given that I have the sprite attribute table at VDP address $D800, this really doesn't make sense. Out of curiosity, I've tried the same function with the length parameter changed to $0200, and when I do that, none of the tiles get cleared but the sprite is still moved to the same location on screen. How bizarre. The existing documentation I could find was either not very well translated into English, or doesn't really explain all the details on what actually happens in memory (it focuses more on HOW to make DMA occur, which wasn't too hard to figure out, but I don't understand why my sprites move as a result.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

以为你会在 2025-02-13 17:33:44

真正的问题是我没有显示的代码,我的寄存器被凝结了,这导致精灵移动。问题不是DMA。

The real problem was code that I hadn't shown, and my registers were getting clobbered which caused the sprites to move. It wasn't the DMA that was the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文