Упражнение в самоизменяющейся процедуре копирования памяти, 6502 ASM

Ниже приведена моя процедура самоизменения для копирования памяти на Commodore 64.

Я записал char codes и number of repeats в таблицу и заполнил screen_ram этой процедурой.

Жду предложений по оптимизации. В данном случае у меня в приоритете память.

memCopy:    
  sourceAddress=*+1 ; mark self modifying addrres
fetchNewData:
  lda data_table    ; read char value into A
  ldx data_table+1  ; read repeat value into x
  inc sourceAddress     
  inc sourceAddress 

  cpx #00           ; if X=0 
  beq end           ; finish copying

  destination=*+1
- sta SCREEN_RAM
  inc destination
  dex
  bne -

  jmp fetchNewData

end:
  rts   

; data format:  <char>,<number of repeats>,[<char>,<number of repeats>,...],00,00

data_table: 
!by 01,03,02,02,......,00,00

person wizofwor    schedule 10.02.2016    source источник
comment
Проголосуйте за вопрос об оптимизации сборки 6502 на основе C64 в 2016 году. Сделал мой день, чтобы прочитать об этом.   -  person clemej    schedule 11.02.2016
comment
Я вижу здесь некоторый изъян во всей идее увеличения младшего байта адреса инструкции, не заботясь о старшем. Таким образом, вы можете заполнить только 256 байт SCREEN_RAM и только в том случае, если он был выровнен по 256 байтам в начале.   -  person lvd    schedule 11.02.2016
comment
Привет, wizofwor, вам может быть интересно узнать, что в Зоне 51 у нас есть предложение, касающееся ретро-вычислений: area51.stackexchange.com/proposals/94441/   -  person Matt Lacey    schedule 17.03.2016


Ответы (3)


Правильное приращение адреса инструкции должно производиться так:

address=*+1
    lda self_modifying_address
    inc address+0
    bne *+5
    inc address+1

таким образом, вероятно, пренебрегая всей экономией памяти для самомодифицируемого кода.

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

.loop
fetch_ptr=*+1
    ldx #0
    lda filler_bytes,x ;have two tables, first contains only filler bytes,
    ldy repeat_bytes,x ;second only repeat counts
    beq .exit
    inc fetch_ptr      ;this way you save 1 increment

fill_ptr=*+1
    ldx #0
.fill
    sta SCREEN_RAM,x
    inx
    bne +
    inc .fill+2 ;only self-modify high byte of address in the instruction
+   dey
    bne .fill

    stx fill_ptr

    jmp .loop
.exit
    rts


filler_bytes !byte 1,2,3,4,5,4,3,2,1
repeat_bytes !byte 4,4,5,5,6,6,5,5,4,0
person lvd    schedule 11.02.2016
comment
Я не совсем понял inc address+0, inc address+1 вещь. Это для обработки переполнения? - person wizofwor; 11.02.2016
comment
В яблочко. Адрес здесь представляет собой 16-битное (или 2-байтовое) значение, хранящееся в виде последовательных байтов с прямым порядком байтов: сначала младшая часть адреса (адрес+0), затем старшая часть (адрес+1). Поэтому вы увеличиваете 16-битное значение следующим образом: inc low_part : bne *+5 : inc high_part. - person lvd; 11.02.2016

JMP fetchNewData -> BEQ fetchNewData. Переместить INC sourceAddress после BEQ end и не надо из CPX #0 (после LDX). на 3 байта меньше.

person i486    schedule 10.02.2016

В дополнение к предложениям i486, если data_table ограничен 128 значениями (включая завершающие 0,0), вы можете сэкономить пару байтов (и много циклов), избегая самоизменяющихся LDA и используя вместо этого регистр Y.

Все это я показал ниже. Вы также можете сохранить еще один байт (удалить один INY), поместив значения data_table в две отдельные таблицы.

В качестве альтернативы вы можете использовать Y для индексации SCREEN_RAM, но я не парень C64...

  ldy #0
fetchNewData:
  lda data_table,y  ; read char value into A
  iny               ; [could remove if two tables]
  ldx data_table,y  ; read repeat value into x
  beq end           ; finish copying [x=0]
  iny

  destination=*+1
- sta SCREEN_RAM
  inc destination
  dex
  bne -
  beq fetchNewData

end:
  rts   

; data format:  <char>,<number of repeats>,[<char>,<number of repeats>,...],00,00

data_table: 
!by 01,03,02,02,......,00,00
person Nick Westgate    schedule 11.02.2016