El pasado martes fue mi cumpleaños y quería obsequiaros con un pequeño trabajo que he estado haciendo durante los últimos días. Se trata de un ejemplo real de una implementación en 6502 de una rutina de descompresión de aPLib optimizada para velocidad. Está basada en un código anterior para el micro 65C02 para Apple IIc, que como algunos sabréis, posee más instrucciones y modos de direccionamiento que permiten ahorrar bytes, así que la conversión ha sido complicada: como tiene muchos bucles desenrollados y el código de 6502 ya de por sí ocupa mucho más que el de Z80, al final la rutina ha quedado en 327 bytes, pero es un 60% más rápida que Exomizer y los datos comprimidos apenas suelen ser 2% más largos.
Sé que por aquí paran un par de gurús del 6502 (Silicebit, Chema) del mundo Oric y quería que echasen un vistazo por si podían optimizarla... como dije más arriba, está basada en un código anterior de 65C02 que es más corto. Ese es un procesador que posee direccionamiento indirecto puro, no como el 6502 que únicamente trae indirecto indexado (que además gasta un ciclo de reloj más) y eso te obliga a inicializar el registro Y a cero y mantenerlo así todo el rato, con el coste en bytes que supone. Otras características del 65C02 que no puedo usar, son el mover a pila el registro X y el registro Y directamente sin pasar por el acumulador.
Esta rutina es la versión 15 y por más vueltas que le doy no consigo optimizarla más. Cualquier sugerencia será bienvenida. Es complicado, ya que el procesador no tiene ningún registro de 16 bits ni tampoco instrucciones para cálculos aritméticos de 16 bits, con lo que las operaciones a 8 bits se llevan gran parte del código, y por consiguiente del tiempo de proceso. El que el reloj del 6510 en el Commodore 64 PAL sea de 985 kHz (ni siquiera 1 MHz), ya termina de matar al micro.
Código: Seleccionar todo
; aPLib 6502 depacker for C64 by Francisco Crespo (Black Hole)
; Based on an earlier 65C02 depacker for Apple IIc by Peter Ferrie
; Depacker variables
.DEFINE TMPY = $A7
.DEFINE tmp = $A8 ; A8/A9
.DEFINE newsrc = $AA ; AA/AB
.DEFINE ecx = $F7 ; F7/F8
.DEFINE last = $F9 ; F9/FA
.DEFINE src = $FB ; FB/FC
.DEFINE dst = $FD ; FD/FE
.MACRO GETBIT_M
txa
asl
bne ++
lda (src),Y
inc src
bne +
inc src+1
+ rol a
++ tax
.ENDM
.MACRO GETSRC_M
LDA (src),Y
INC src
BNE +
INC src+1
+
.ENDM
.MACRO PUTDST_M
STA (dst),Y
INC dst
BNE +
INC dst+1
+
.ENDM
depack: LDX #$80
LDY #$00
STY ecx+1 ; Y=0
literal:
GETSRC_M
PUTDST_M
;ldy #2
LDA #$02
STA TMPY
nexttag:
GETBIT_M
bcc literal
GETBIT_M
bcc codepair
GETBIT_M
bcs onebyte
GETSRC_M
lsr
beq donedepacking
STY ecx ; Y=0
rol ecx
sta last
STY last+1 ; Y=0
JMP domatch_with_2inc
donedepacking:
RTS
onebyte
;ldy #1
;sty ecx
;iny
LDA #1
STA ecx
LDA #2
STA TMPY
lda #$10
getmorebits
pha
GETBIT_M
pla
rol
bcc getmorebits
STY tmp+1 ; Y=0
bne domatch
PUTDST_M
JMP nexttag
codepair
jsr getgamma
LDA ecx
SEC
SBC TMPY
STA TMPY
ora ecx+1
bne normalcodepair
jsr getgamma
JMP domatch_lastpos
normalcodepair
GETSRC_M
sta last
jsr getgamma
;dey
;sty last+1
dec TMPY
LDA TMPY
STA last+1
;cpy #$7d
CMP #$7D
bcs domatch_with_2inc
;cpy #5
CMP #$05
bcs domatch_with_inc
lda last
bmi domatch_lastpos
;tya
LDA TMPY
bne domatch_lastpos
domatch_with_2inc
inc ecx
bne domatch_with_inc
inc ecx+1
domatch_with_inc
inc ecx
bne domatch_lastpos
inc ecx+1
domatch_lastpos
;ldy #1
LDA #1
STA TMPY
lda last+1
sta tmp+1
lda last
domatch:
sta tmp
lda dst
sec
sbc tmp
sta newsrc
lda dst+1
sbc tmp+1
sta newsrc+1
lda ecx
beq dloop
- LDA (newsrc),Y
INC newsrc
BNE +
INC newsrc+1
+ PUTDST_M
dec ecx
bne -
lda ecx+1
beq +++
dloop: LDA (newsrc),Y
INC newsrc
BNE ++
INC newsrc+1
++ PUTDST_M
dec ecx
bne dloop
dec ecx+1
bne dloop
+++ JMP nexttag
getgamma:
LDA #$01
STA ecx
STY ecx+1 ; Y=0
txa ; GETBIT_1
gloop: asl a
bne ++
lda (src),Y
inc src
bne +
inc src+1
+ rol a
++ rol ecx
rol ecx+1
asl a ; GETBIT_2
bne ++
lda (src),Y
inc src
bne +
inc src+1
+ rol a
++ bcs gloop
tax ; stillbitsleft
rts
end_depack:
Me gustaría dedicar este hilo a mis futuros "apaños" con el Commodore 64. No prentendo que mis trabajos sean equivalentes a los de grupos como Remember o Nostalgia, puesto que ni tengo intención de complicarlos con trainers ni cargadores de puntuaciones, ni pegar una intro con un logotipo delante de cada juego.
Solo voy a convertir algunos juegos en cinta que me gustaron de joven, ya estoy muy mayor para buscar otro reconocimiento.