Могу ли я превратить это в цикл с помощью какой-нибудь 16-битной магии?

Я начинаю с сборки 6502 прямо сейчас, и у меня проблема с циклами, которые должны иметь дело с числами больше 8 бит.

В частности, я хочу перебрать некоторые ячейки памяти. В псевдо-C-коде я хочу сделать это:

    // Address is a pointer to memory
    int* address = 0x44AD;
    for(x = 0; x < 21; x++){
        // Move pointer forward 40 bytes
        address += 0x28;
        // Set memory location to 0x01
        &address = 0x01;
    }

Итак, начиная с адреса $44AD, я хочу записать $01 в оперативную память, затем перейти вперед $28, записать в нее $01, затем снова перейти вперед $28, пока не сделаю это 20 раз (последний адрес для записи - $47A5).

Мой текущий подход — развертывание цикла, которое утомительно писать (хотя я думаю, что ассемблер может сделать это проще):

ldy #$01
// Start from $44AD for the first row, 
    // then increase by $28 (40 dec) for the next 20
sty $44AD
sty $44D5
sty $44FD
    [...snipped..]
sty $477D
sty $47A5

Я знаю об абсолютной адресации (используя Аккумулятор вместо регистра Y - sta $44AD, x), но это дает мне только число от 0 до 255. Я действительно думаю, что хочу что-то вроде этого:

       lda #$01
       ldx #$14 // 20 Dec
loop:  sta $44AD, x * $28
       dex
       bne loop

По сути, начните с самого высокого адреса, затем зациклитесь. Проблема в том, что $14 * $28 = $320 или 800 dec, что больше, чем я могу хранить в 8-битном регистре X.

Есть ли элегантный способ сделать это?


person Michael Stum    schedule 05.02.2014    source источник


Ответы (2)


6502 — это 8-битный процессор, поэтому вы не сможете полностью вычислить 16-битные адреса в регистрах. Вам нужно будет перейти через нулевую страницу.

      // set $00,$01 to $44AD + 20 * $28 = $47CD
      LDA #$CD
      STA $00
      LDA #$47
      STA $01

      LDX #20  // Loop 20 times
      LDY #0
loop: LDA #$01 // the value to store
      STA ($00),Y // store A to the address held in $00,$01
      // subtract $28 from $00,$01 (16-bit subtraction)
      SEC
      LDA $00
      SBC #$28
      STA $00
      LDA $01
      SBC #0
      STA $01
      // do it 19 more times
      DEX
      BNE loop

В качестве альтернативы вы можете использовать самомодифицирующийся код. В целом это сомнительный метод, но он распространен во встроенных процессорах, таких как 6502, потому что они очень ограничены.

      // set the instruction at "patch" to "STA $47CD"
      LDA #$CD
      STA patch+1
      LDA #$47
      STA patch+2

      LDX #20  // Loop 20 times
loop: LDA #$01 // the value to store
patch:STA $FFFF
      // subtract $28 from the address in "patch"
      SEC
      LDA patch+1
      SBC #$28
      STA patch+1
      LDA patch+2
      SBC #0
      STA patch+2
      // do it 19 more times
      DEX
      BNE loop
person Raymond Chen    schedule 05.02.2014
comment
Большое спасибо! Читая о косвенном режиме, это ускользнуло от меня. Самомодифицирующийся код тоже интересен, мне нужно обдумать всю идею кода = памяти, которая была у старой машины. - person Michael Stum; 05.02.2014
comment
СТК?? СЭС, однозначно. Между прочим, для удобства чтения в самомодифицирующемся коде я использую $C0DE вместо $FFFF, а моя подсветка синтаксиса подсвечивает его желтым цветом — это позволяет очень легко определить места, где вы делаете что-то корявое. - person Eight-Bit Guru; 05.02.2014
comment
@EightBitGuru Извините, мои наборы инструкций перепутаны. - person Raymond Chen; 05.02.2014
comment
Nitpick: вторая STA в первом фрагменте кода должна иметь значение $01. - person Anonym Mus; 15.02.2014
comment
Поскольку этот вопрос помечен c64, вам следует избегать использования адресов нулевой страницы $00 и $01, поскольку они используются для портов ввода-вывода и конфигурации ПЗУ/ОЗУ ЦП 6510. Если вы хотите, чтобы ваш код безопасно запускался из BASIC с помощью команды SYS, лучше всего использовать диапазон от $FB до $FE. - person Lars Haugseth; 24.04.2014
comment
Раньше я использовал самомодифицирующийся код на C64. Конечно, это уродливо, но когда вам нужно, чтобы оно работало ровно на одном компьютере, оно работает. - person David Crowell; 16.09.2014

Более эффективный способ скопировать 1k данных:

    ldy #0
nextvalue:
    lda address, y
    sta address, y

    lda address+$100, y
    sta address+$100, y

    lda address+$200, y
    sta address+$200, y

    lda address+$300, y
    sta address+$300, y
    iny
    bne nextvalue 

Несколько заметок:

  • Быстрее, так как сокращаются накладные расходы на цикл. Занимает больше места из-за большего количества команд.

  • Если используемый вами ассемблер поддерживает макросы, вы можете легко настроить его, сколько блоков обрабатывает код.

Возможно, это не на 100% относится к этому, но вот еще один способ иметь циклы длиннее 255:

nextblock:
    ldy #0
nextvalue:
    lda address, y
    iny
    bne nextvalue

;Insert code to be executed between each block here:

    dec numblocks
    bpl nextblock

numblocks:
    .byte 3

Несколько заметок:

  • На данный момент код не делает ничего значимого, а выполняет цикл "numblocks" раз. «Добавьте свой собственный код» :-) (Часто я использую это вместе с некоторым самомодифицирующимся кодом, который увеличивает, например, адрес sta, y)

  • bpl может быть опасен (если вы не знаете, как он работает), но в этом случае работает достаточно хорошо (но не будет, если адрес numblocks содержит достаточно большое значение)

  • Если вам нужно снова выполнить тот же код, необходимо переустановить numblocks.

  • Код можно сделать немного быстрее, поместив numblocks на нулевую страницу.

  • Если это не нужно для чего-то другого (как это часто бывает), вы можете использовать регистр X вместо ячейки памяти.

person Jupp3    schedule 07.05.2014