6502代码中有效的多个间接方向

发布于 2025-02-05 02:19:23 字数 2501 浏览 2 评论 0原文

我正在查看一个6502程序,该程序具有多个数组的字节(声音效果数据与特定语音相对应),该程序的长度不同 当前,这涉及在第一个(如果排队)中明确迭代,然后是第二个等,每个语音都有一组用于音量,延迟等的变量集,因此设置了代码以使用这些硬编码的标签。

我想将其汇入一个循环,将这些其他变量和声音效果数据索引。使用索引的地址索引,将变量索引非常简单,但是对声音效果数据进行索引涉及更多的工作,我想知道我是否缺少索引间接和间接索引的解决方案。

以下是我目前在做什么的独立示例。如果可能的话,我想收紧的部分是loadFromtable中的代码,理想情况下,同时使用xy地址:

  .equ  Ptr0,  0x80
  .equ  Ptr1,  0x81

  .org  0xFE00

  .org  0x0000

Init:
  LDX #0xFF
  TXS

Main:
  LDX #0x00
  LDY #0x00
  JSR LoadFromTable
  ; A should be 'H',  0x48

  LDX #0x01
  LDY #0x00
  JSR LoadFromTable
  ; A should be 'B',  0x42

  LDX #0x02
  LDY #0x02
  JSR LoadFromTable
  ; A should be 'A',  0x41

  JMP Main

LoadFromTable:
  TXA           ; Double outer index to account for 16 bit pointers
  ASL           ;   "
  TAX           ;   "
  LDA Table,X   ; Load the low byte of the array into a pointer
  STA Ptr0      ;   "
  INX           ; Load the high byte of the array into the pointer
  LDA Table,X   ;   "
  STA Ptr1      ;   "
  LDA (Ptr0),Y  ; Load the character at the inner index into the array
  RTS

  .org  0x0040

Table:
  .word Item0
  .word Item1
  .word Item2

  .org  0x0080

Item0:
  .byte 'H', 'E', 'L', 'L', 'O', 0x00

Item1:
  .byte 'B', 'O', 'N', 'J', 'O', 'U', 'R', 0x00

Item2:
  .byte 'C', 'I', 'A', 'O', 0x00

  .org  0x00FA

  .word Init
  .word Init
  .word Init

实现

从@nickwestgate乘坐拆分表的想法,并悬挂了@michael指出的初始指针计算,我已经从类似的东西中移动了:

PROCESS_MUSIC:
  ; ...
  BNE   MusDoB

MusChanA:
  ; ...
  LDA   MUSICA,X
  BNE   MusCmdToneA
  ; ...
  JMP   MusChanA

MusCmdToneA:
  ; ...
  BNE   MusNoteA
  ; ...

MusNoteA:
  ; ...
  LDA   MUSICA,X
  ; ...

MusDoB:
  ; ...
  BNE   MusDoDone

MusChanB:
  ; ...
  LDA   MUSICB,X
  BNE   MusCmdToneB
  ; ...
  JMP   MusChanB

MusCmdToneB:
  ; ...
  BNE   MusNoteB
  ; ...

MusNoteB:
  ; ...

MusDoDone:
  RTS

对这个更广泛的子例程:

PROCESS_MUSIC:
  LDX #0x01

PerChannel:
  ; ...
  BNE EndPerChannel
  LDA MusicTableL,X
  STA tmp0
  LDA MusicTableH,X
  STA tmp1

MusChan:
  ; ...
  LDA (tmp0),Y
  BNE MusCmdTone
  ; ...
  BEQ MusChan

MusCmdTone:
  ; ...
  BNE MusNote
  ; ...

MusNote:
  ; ...
  LDA (tmp0),Y
  ; ...

EndPerChannel:
  DEX 
  BPL PerChannel
  RTS

添加了以下表:

MusicTableL:
    .byte <MUSICA
    .byte <MUSICB

MusicTableH:
    .byte >MUSICA
    .byte >MUSICB

这将删除需要我最初使用的loadFromtable功能,总体上似乎要干净得多。

Issue

I'm looking at a 6502 program that has multiple arrays of bytes (sound effect data corresponding to a particular voice), which are of varying lengths. Currently this involves explicitly iterating through the first (if queued), then the second etc, and each voice has a separate set of variables for volume, delay etc, so the code is set up to use these hard-coded labels.

I'd like to roll this into a loop, indexing into these additional variables and the sound effect data. Indexing into the variables is fairly straightforward, using indexed addressing, but indexing into the sound effect data involves a lot more work, and I'm wondering if I'm missing something in the application of indexed indirect and indirect indexed addressing.

Below is a self-contained example of what I'm doing at the moment. The part I'd like to tighten up, if possible, is the code in LoadFromTable, ideally with some use of both X and Y addressing:

  .equ  Ptr0,  0x80
  .equ  Ptr1,  0x81

  .org  0xFE00

  .org  0x0000

Init:
  LDX #0xFF
  TXS

Main:
  LDX #0x00
  LDY #0x00
  JSR LoadFromTable
  ; A should be 'H',  0x48

  LDX #0x01
  LDY #0x00
  JSR LoadFromTable
  ; A should be 'B',  0x42

  LDX #0x02
  LDY #0x02
  JSR LoadFromTable
  ; A should be 'A',  0x41

  JMP Main

LoadFromTable:
  TXA           ; Double outer index to account for 16 bit pointers
  ASL           ;   "
  TAX           ;   "
  LDA Table,X   ; Load the low byte of the array into a pointer
  STA Ptr0      ;   "
  INX           ; Load the high byte of the array into the pointer
  LDA Table,X   ;   "
  STA Ptr1      ;   "
  LDA (Ptr0),Y  ; Load the character at the inner index into the array
  RTS

  .org  0x0040

Table:
  .word Item0
  .word Item1
  .word Item2

  .org  0x0080

Item0:
  .byte 'H', 'E', 'L', 'L', 'O', 0x00

Item1:
  .byte 'B', 'O', 'N', 'J', 'O', 'U', 'R', 0x00

Item2:
  .byte 'C', 'I', 'A', 'O', 0x00

  .org  0x00FA

  .word Init
  .word Init
  .word Init

Implementation

Taking onboard the split table idea from @NickWestgate and hoisting out the initial pointer calculation as noted by @Michael, I've moved from something like this:

PROCESS_MUSIC:
  ; ...
  BNE   MusDoB

MusChanA:
  ; ...
  LDA   MUSICA,X
  BNE   MusCmdToneA
  ; ...
  JMP   MusChanA

MusCmdToneA:
  ; ...
  BNE   MusNoteA
  ; ...

MusNoteA:
  ; ...
  LDA   MUSICA,X
  ; ...

MusDoB:
  ; ...
  BNE   MusDoDone

MusChanB:
  ; ...
  LDA   MUSICB,X
  BNE   MusCmdToneB
  ; ...
  JMP   MusChanB

MusCmdToneB:
  ; ...
  BNE   MusNoteB
  ; ...

MusNoteB:
  ; ...

MusDoDone:
  RTS

to this more generalised subroutine:

PROCESS_MUSIC:
  LDX #0x01

PerChannel:
  ; ...
  BNE EndPerChannel
  LDA MusicTableL,X
  STA tmp0
  LDA MusicTableH,X
  STA tmp1

MusChan:
  ; ...
  LDA (tmp0),Y
  BNE MusCmdTone
  ; ...
  BEQ MusChan

MusCmdTone:
  ; ...
  BNE MusNote
  ; ...

MusNote:
  ; ...
  LDA (tmp0),Y
  ; ...

EndPerChannel:
  DEX 
  BPL PerChannel
  RTS

with the addition of the following tables:

MusicTableL:
    .byte <MUSICA
    .byte <MUSICB

MusicTableH:
    .byte >MUSICA
    .byte >MUSICB

This removes the need for the LoadFromTable function I'd originally been using, and seems much cleaner overall.

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

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

发布评论

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

评论(2

我还不会笑 2025-02-12 02:19:23

这是一些想法。一个人正在通过已经翻了一番的索引(即,如果您可以安排它,否则它可能已经在较早的阶段已经在蓄能器中)。

另一个正在拆分地址表:

LoadFromTable:
  LDA TableL,X ; Load the low byte of the array into a pointer
  STA Ptr0      ;   "
  LDA TableH,X ; Load the high byte of the array into the pointer
  STA Ptr1      ;   "
  LDA (Ptr0),Y  ; Load the character at the inner index into the array
  RTS

TableL:
  .byte #<Item0
  .byte #<Item1
  .byte #<Item2

TableH:
  .byte #>Item0
  .byte #>Item1
  .byte #>Item2

如果您无法将表分开,则可能仍然可以通过这样做来摆脱INX:

  LDA Table,X   ; Load the low byte of the array into a pointer
  STA Ptr0      ;   "
  LDA Table+1,X ; Load the high byte of the array into the pointer
  STA Ptr1      ;   "

自我修改代码可能很有用。在零页上生活将是一个因素:

  LDA Table,X   ; Load the low byte of the array into a pointer
  STA Load+1    ;   "
  LDA Table+1,X ; Load the high byte of the array into the pointer
  STA Load+2    ;   "
Load:
  LDA $FFFF,Y   ; Load the character at the inner index into the array

您还可以查看在存储时是否将Y添加到指针中可以节省任何周期。它可能取决于最常见的路径(即通常不注入ptr2/load+2)。

Here are a few ideas. One is passing in an index that's already doubled (i.e. if you can arrange that, or it might already be in the accumulator at some earlier stage).

Another is splitting up the address tables:

LoadFromTable:
  LDA TableL,X ; Load the low byte of the array into a pointer
  STA Ptr0      ;   "
  LDA TableH,X ; Load the high byte of the array into the pointer
  STA Ptr1      ;   "
  LDA (Ptr0),Y  ; Load the character at the inner index into the array
  RTS

TableL:
  .byte #<Item0
  .byte #<Item1
  .byte #<Item2

TableH:
  .byte #>Item0
  .byte #>Item1
  .byte #>Item2

If you can't split up the tables, you can probably still get rid of an INX by doing:

  LDA Table,X   ; Load the low byte of the array into a pointer
  STA Ptr0      ;   "
  LDA Table+1,X ; Load the high byte of the array into the pointer
  STA Ptr1      ;   "

Self modifying code might be useful. Living on page zero will be a factor:

  LDA Table,X   ; Load the low byte of the array into a pointer
  STA Load+1    ;   "
  LDA Table+1,X ; Load the high byte of the array into the pointer
  STA Load+2    ;   "
Load:
  LDA $FFFF,Y   ; Load the character at the inner index into the array

You could also see whether adding Y to the pointer as you store it saves any cycles. It might depend on the most common path used (i.e. if it usually doesn't INC Ptr2/Load+2).

对你而言 2025-02-12 02:19:23

如果您试图在1 MHz 6502上生成实时音频,则我使用每个样本40个字节加上四个声音音乐,加上时间来实际喂食DAC,在我的情况下是两个零页面存储到AUDV0和AUDV1寄存器。

关键是使用该表单的四个代码段的“滚动”序列:

; Carry must be clear on entry; will be clear on exit
; Acc, Y, and other flags ignored on entry; trashed on exit
; X register ignored and left alone
ldy phase1
lda (wave1a),y
ldy phase0
adc (wave0d),y
sta AUDV0
lda (newPhase0),y
sta phase0
ldy phase2
lda (wave2c),y
ldy phase3
adc (wave3d),y
sta AUDV1

这种方法使在五圈范围内生产四个声音音乐范围是可能的,使用了半率和四分之一播放,底部有两个。八度,但将价值40个字节的零页指针连接起来(几乎是2600的RAM的三分之一!)。如果不需要选择播放速率,并且可以负担得起每个样本的额外256个字节,那么一个人可能会有主要的样本循环,例如:

 ldy outCounter
 clc
 bmi useSetB
useSetA:
 lda (wave0a),y
 adc (wave1a),y
 adc (wave2a),y
 adc (wave3a),y
 inc outCounter
 bne storeAndDone ; Will be 1-128
useSetB:
 lda (wave0b),y
 adc (wave1b),y
 adc (wave2b),y
 adc (wave3b),y
 inc outCounter
 nop ; Equalize time
storeAndDone:
 sta DACoutput

另外两个代码需要在上层之间进行某个时间运行of OutCounter的位置,何时再次清楚,从那时起,它很清楚,何时重新设置。

; Run when outCounter reaches 128
 ldx #6 ; Counts by 2 for each voice
fixLp1:
 lda wave0b,x
 sta wave0a,x
 dex
 dex
 bpl fixLp1

; Run when outCounter reaches 128
 ldx #6
fixLp2:
 sec
 lda wave0a,x
 sbc top0,x
 lda wave0a+1,x
 sbc top0+1,x
 bcc notTopYet
 lda wave0a,x
 sbc length0,x
 sta wave0a,x
 lda wave0a+1,x
 sbc length0+1,x
 sta wave0a+1,x
 dex
 dex
 bpl fixLp2
 bmi done2
notTopYet:
 lda wave0a,x
 sta wave0a,x
 lda wave0a+1,x
 sta wave0a+1,x
 dex
 dex
 bpl fixLp2
done:

代码的后半部分将相当长,但是只需要每256个样本一次运行一次,并且可以容纳任意循环部分。

If you're trying to generate real-time audio on 1 MHz 6502, I've done four-voice music using 40 bytes per sample plus the time to actually feed the DAC(s), which in my case was two zero-page stores to the AUDV0 and AUDV1 registers.

The key was to use a "rolling" sequence of four code snippets of the form:

; Carry must be clear on entry; will be clear on exit
; Acc, Y, and other flags ignored on entry; trashed on exit
; X register ignored and left alone
ldy phase1
lda (wave1a),y
ldy phase0
adc (wave0d),y
sta AUDV0
lda (newPhase0),y
sta phase0
ldy phase2
lda (wave2c),y
ldy phase3
adc (wave3d),y
sta AUDV1

This approach made it possible to produce four-voice music range over a five-octave range, using half-rate and quarter-rate playback for the bottom two octaves, but ties up 40 bytes worth of zero-page pointers (almost a third of the total RAM on the 2600!). If a choice of playback rates was not required, and one could afford to pad each sample out by an extra 256 bytes, one could have the main sample loop be something like:

 ldy outCounter
 clc
 bmi useSetB
useSetA:
 lda (wave0a),y
 adc (wave1a),y
 adc (wave2a),y
 adc (wave3a),y
 inc outCounter
 bne storeAndDone ; Will be 1-128
useSetB:
 lda (wave0b),y
 adc (wave1b),y
 adc (wave2b),y
 adc (wave3b),y
 inc outCounter
 nop ; Equalize time
storeAndDone:
 sta DACoutput

Two other pieces of code would need to run sometime between when the upper bit of outCounter is set and when it's clear again, and again between then it's clear and when it's re-set.

; Run when outCounter reaches 128
 ldx #6 ; Counts by 2 for each voice
fixLp1:
 lda wave0b,x
 sta wave0a,x
 dex
 dex
 bpl fixLp1

; Run when outCounter reaches 128
 ldx #6
fixLp2:
 sec
 lda wave0a,x
 sbc top0,x
 lda wave0a+1,x
 sbc top0+1,x
 bcc notTopYet
 lda wave0a,x
 sbc length0,x
 sta wave0a,x
 lda wave0a+1,x
 sbc length0+1,x
 sta wave0a+1,x
 dex
 dex
 bpl fixLp2
 bmi done2
notTopYet:
 lda wave0a,x
 sta wave0a,x
 lda wave0a+1,x
 sta wave0a+1,x
 dex
 dex
 bpl fixLp2
done:

The latter portion of the code would be fairly long, but it would only need to be run once every 256 samples, and could accommodate arbitrary looping sections.

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