今さら参考にならないと思いますが、読み物としてお読みください
生まれてはじめて「まともに」書いた65816のコードはこれだけです
(抜粋)
LZEXT.ASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; LZExt LZExt Extracter for 65816/SNES
;
; Input : a.X LZ address
; Y Extract Address
; Output : a.X Extracted Data Start Address
; Broken : A,X,Y,dbr,Bank 7E Ram
; 備考 : バンクまたぎ対応
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RamBank = $7e
RamAdr = RamBank*$10000
LZStart = $7200
;sndadr = $f
;calctmp = $1f
LZTemp = sndadr+3
LZBit = sndadr+4
LZValue = sndadr+5
LZSave_X = sndadr+6
LZGBflag = sndadr+8
LZsave_Y = sndadr+9
LZMVN = sndadr+13
LZExtSave = sndadr+11
LZRomAdr = sndadr
LZRomBank = LZRomAdr+2
LZRamAdr = calctmp
LZRamBank = LZRamAdr+2
LZExtData = RamAdr+LZStart
LZExt
php
phb
sep #$20
phx ; src store
ldx.w #0 ; direct register clear
phx
pld
stx LZRomAdr ; address=0
sta LZRomBank ; bank=rombank
stx LZRamAdr ; address=0
lda.b #LZExtData/65536
sta LZMVN+1
sta LZMVN+2
sta LZRamBank ; bank=rambank
pha
plb
; ldx rom address
; ldy.w #LZExtData.mod.65536 ; ram address
sty LZExtSave
plx
; Self Program Code
lda #$54 ; mvn
sta LZMVN+0
; lda LZRamBank
; sta LZMVN+1
; lda LZRomBank
; sta LZMVN+2
lda #$6b ; rtl
sta LZMVN+3
; sep #$20 ; 8 bit mode for A register
jsr xyx
lda [LZRomAdr],y
cmp #$4c
bne nocomp
iny
lda [LZRomAdr],y
dey
cmp #$5a
bne nocomp
jsr xyx
inx
inx
inx
inx
inx
inx
jsr LZEmain
ldx.w LZExtSave
lda.b #LZExtData/65536
plb
plp ; Pop Status Flag
rts ; Return
nocomp
jsr xyx
lda LZRomBank
plb
plp
rts
xyx
phx
tyx
ply
rts
; rombank.X : LZData
; rambank.Y : ExtData
LZEMain
jsr xyx
sep #$20 ; 8 bit mode for A register
lda.b #1 ; exx
; ld hl,lzdata
sta LZBit ; ld b,1
; exx
jsr getLZbit ; call getLZbit
; kokomade ok
emain
jsr getLZbit ; call getLZbit
bcc comp ; jr nc,comp
; exx
lda [LZRomAdr],y ; ld a,(hl)
iny ; inc hl
bne emain_ ; バンクまたぎ
inc LZRomBank
ldy #$8000
emain_
jsr xyx
sta [LZRamAdr],y ; ld (de),a
iny ; inc de
jsr xyx
bra emain ; jr emain
comp
jsr getLZbit ; call getLZbit
bcs comp1 ; jr c,comp1
lda.b #0 ; xor a
jsr getLZbit ; call getLZbit
rol a ; rla
jsr getLZbit ; call getLZbit
rol a ; rla
sta LZTemp ; ld c,a
; exx
lda [LZRomAdr],y ; ld a,(hl)
iny ; inc hl
bne comp_ ; バンクまたぎ
inc LZRomBank
ldy #$8000
comp_
; exx
pha ; ld l,a
lda.b #255 ; ld h,-1
pha
lda LZTemp ; ld a,c
bra comp3
comp1
; exx
lda [LZRomAdr],y ; ld a,(hl)
iny ; inc hl
bne comp1_ ; バンクまたぎ
inc LZRomBank
ldy #$8000
comp1_
; exx
pha ; ld l,a
; exx
lda [LZRomAdr],y ; ld a,(hl)
iny ; inc hl
bne comp12_ ; バンクまたぎ
inc LZRomBank
ldy #$8000
comp12_
; exx
pha ; ld h,a
clc
cmp #$20 ; cp 20h
bcc comp2 ; jr c,comp2
lsr a ; rlca
lsr a ; rlca
lsr a ; rlca
lsr a
lsr a
and.b #7 ; and 7
bra comp3 ; jr comp3
comp2 ; exx
lda [LZRomAdr],y ; ld a,(hl)
php ; inc x
iny
bne comp2_ ; バンクまたぎ
inc LZRomBank
ldy #$8000
comp2_
; exx
plp ; or a
beq comp4 ; jr z,comp4
comp3
sty LZsave_X
txy
plx
pha ; ld c,a
lda.b #0 ; ld b,0
pha
rep #$20 ; 16bit mode
txa ; X -> A
xba
ora.w #$e000 ; or 0e0h
sty LZsave_Y
clc
adc LZsave_Y ; add hl,de
tax ; A -> X
pla
xba
inc a ; inc bc
; inc a ; inc bc
; phb
jsl LZMVN ; X=src,Y=LZExtData,A=Len
; plb
sep #$20 ; 8bit mode
tyx
ldy LZsave_X
jmp emain ; jr emain
comp4 pla ; trash
pla ; trash
rts
getLZbit
pha ; exx ?
rol LZGBflag ; rlc c
php
dec LZBit ; djnz getLZbit0
bne getLZb0
lda #$08 ; ld b,8
sta LZBit
lda [LZRomAdr],y ; ld c,(hl)
sta LZGBflag
iny
bne getLZb0 ; バンクまたぎ
inc LZRomBank
ldy #$8000
getLZb0
plp ; for Carry Flag
pla ; exx ?
rts
実はこれ
これのパクりです
Cのソースコードも公開されており、圧縮ルーチンはCでほぼそのまま(ただし実行自動解凍部分は除く)、アセンブラは、Cであるのを一度Z80アセンブラに移植し、その後 65816 に移植しました
でも、無知であるため、あんまり最適化されていないのです
でも、これだけで24Mbit分のデータを4MbitのROMに収めることができ
まるで誇大広告なように誇張していました
また、そんなにストレスを感じさせない解凍速度を実現していました
起動直後に圧縮済で約49kbytesのサウンドバンクを展開し、それをSPC700に転送するのですが、そんなにストレスを感じない、当たり前の速度を実現しています
このソースコードをChatGPT(GPT5-Thinking)に最適化させてみたら?
ちなみに動作確認は行っていません
LZEXT-OPT.ASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; LZExt – LZExt Extracter for 65816/SNES (Optimized)
; Input : A = ROM Bank, X = LZ data offset, Y = Extract dst offset
; Output : A = RAM Bank (LZExtData bank), X = Extracted start low
; Clobber: A,X,Y, DBR, P, some DP vars
; Note : Uses long-indirect [dp],index → bank crossing handled by HW
; MVN self-mod JSL for fast block copy (RAM→RAM)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RamBank = $7e
RamAdr = RamBank * $10000
LZStart = $7200
; ---- DP layout (置き場所は既存のsndadr/calctmp流用でOK) ----
; LZRomPtr: 24-bit (lo/hi/bk) ; 圧縮元
; LZRamPtr: 24-bit (lo/hi/bk) ; 展開先
; 以下ワーク
.set LZRomPtr, sndadr ; +0,+1,+2
.set LZRamPtr, calctmp ; +0,+1,+2
.set LZBit, sndadr+3
.set LZVal, sndadr+4
.set LZTmp, sndadr+5 ; 汎用
.set LZSrcLo, sndadr+6 ; MVN用 一時ソース
.set LZDstLo, sndadr+8 ; MVN用 一時デスト
.set LZLenA, sndadr+10 ; MVN用 A退避
; ---- 固定の展開先(従来互換)----
LZExtData = RamAdr + LZStart
; ---- MVN trampoline(自己書換)----
LZMVN:
.byte $54 ; MVN (srcBank,dstBank) ← 後で両方RamBankに書く
.byte RamBank
.byte RamBank
.byte $6B ; RTL
; ===========================================================================
LZExt:
php
phb
; DPを安全ゾーンに(既存コード準拠)
sep #$20 ; A 8bit
phx
ldx #$0000
phx
pld ; D=0
; 入力をDPポインタへ設定
; ROM ptr = (A:Bank, X:ofs), RAM ptr = (7E:LZExtData)
stx LZRomPtr
sta LZRomPtr+2
ldy #LZExtData & $FFFF
sty LZRamPtr
lda #LZExtData>>16
sta LZRamPtr+2
; DBRは触らない([dp],index は24bit解決するため不要)
; ヘッダ "LZ" 確認(長間接で一発)
ldy #0
lda [LZRomPtr],Y
cmp #$4C
bne .NotComp
iny
lda [LZRomPtr],Y
cmp #$5A
bne .NotComp
iny ; ヘッダ2B消費
; エンジンへ
jsr LZEmain
; 戻り値セット
ldx #LZExtData & $FFFF
lda #LZExtData>>16
plb
plp
rts
.NotComp:
; ヘッダ不一致 → 何もしない
lda LZRomPtr+2 ; ROM bank を戻すだけ
plb
plp
rts
; ===========================================================================
; getLZbit: Carry = 次ビット(1/0)
; LZBit: 残りビット数(1..8), LZVal: 現在のビットバッファ
getLZbit:
dec LZBit
bne .shift
lda #8
sta LZBit
lda [LZRomPtr],Y
sta LZVal
iny
.shift:
lda LZVal
asl a ; MSB→Carry
sta LZVal
rts
; ===========================================================================
; LZEmain – 本体(Z80版のフローを保持、XYスワップ排除)
LZEmain:
; 初期ビット供給
lda #1
sta LZBit
jsr getLZbit
.Main:
jsr getLZbit
bcc .Comp ; 0=マッチ, 1=リテラル
; ---- Literal: *dst++ = *src++ ----
lda [LZRomPtr],Y
; RAM書き込み: [LZRamPtr],X
sta [LZRamPtr],X
inx
iny
bra .Main
.Comp:
jsr getLZbit
bcs .Comp1
; a = (getbit<<1)+getbit
lda #0
jsr getLZbit
rol a
jsr getLZbit
rol a
sta LZTmp ; a→LZTmp (length種別)
; l = *src++
lda [LZRomPtr],Y
iny
pha ; push l
lda #$FF
pha ; push h=-1
lda LZTmp ; a(種別)
bra .Comp3_prep
.Comp1:
; l = *src++; h = *src++
lda [LZRomPtr],Y
iny
pha ; l
lda [LZRomPtr],Y
iny
pha ; h
cmp #$20
bcc .Comp2
; a = h>>5 (&7)
lsr a
lsr a
lsr a
and #7
bra .Comp3_prep
.Comp2:
; a = *src++; if (a==0) end
lda [LZRomPtr],Y
iny
beq .Done
.Comp3_prep:
; ---- ここで A=コード, スタック=[h][l] ----
; 長さ = A + 2 (元コード互換:inc 1回のみ)
clc
adc #2
dec a ; MVNのAは「(len-1)」なので-1
sta LZLenA
; 後方参照のソース= (dst + ((h|0xE0)<<8) + l)
; 1) 現在dstオフセットXを16bitで取り出し
phx
rep #$20 ; A=16
txa
; 2) l,h を取り出してオフセットに加算
sep #$20 ; 8bitで扱う
pla ; h
ora #$E0
xba ; A:00→AH=h|E0
pla ; l
rep #$20
xba ; AH:AL = h|E0 : l
clc
adc 0,S ; 0,S には元の X 値(16bit)が乗ってる
tax ; X = src(下位16bit)
sep #$20
plx ; X を戻す…のではなく、今のXはdst!なので退避不要
; 3) MVN 呼び出し準備
; X = srcLow16, Y = dstLow16, A = (len-1)
; src/dst Bank は 7E に固定(自己書換済み)
; Y = 現在の展開先
txy ; preserve dst → Y
rep #$20
tya
xba
xba ; no-op(確実化)
; 16bitへ戻しておく
sep #$20
lda LZLenA
jsl LZMVN ; RAM→RAM 高速コピー
; MVN 後は Y と X が len 分進む(仕様)
; 我々の「通常運転」では X=dst を使うので、Y→Xへ合わせる
tyx
bra .Main
.Done:
rts
参考 同じもののZ80版
LZEXT.MAC
; -----------------
;
; Subroutine 圧縮データの展開(実はLZ*OMのパ○リ(^^;;)
; Inputs HL=圧縮データアドレス DE=展開先アドレス
; Outputs HL=展開したバイト数
;
; unsigned int lzext(sd, dd)
; unsigned int sd, dd;
;
LZEXT:
PUSH DE
PUSH HL
EXX
POP HL
LD B,1
EXX
CALL GETBIT
EMAIN: CALL GETBIT
JR NC,COMP
EXX
LD A,(HL)
INC HL
EXX
LD (DE),A
INC DE
JR EMAIN
; -----------------
COMP: CALL GETBIT
JR C,COMP1
XOR A
CALL GETBIT
RLA
CALL GETBIT
RLA
LD C,A
EXX
LD A,(HL)
INC HL
EXX
LD L,A
LD H,-1
LD A,C
JR COMP3
; -----------------
COMP1: EXX
LD A,(HL)
INC HL
EXX
LD L,A
EXX
LD A,(HL)
INC HL
EXX
LD H,A
CP 20H
JR C,COMP2
RLCA
RLCA
RLCA
AND 7
JR COMP3
; -----------------
COMP2: EXX
LD A,(HL)
INC HL
EXX
OR A
JR Z,COMP4
COMP3: LD C,A
LD B,0
LD A,H
OR 0E0H
LD H,A
ADD HL,DE
INC BC
INC BC
LDIR
JR EMAIN
; -----------------
COMP4: POP HL
SBC HL,DE
RET
; -----------------
GETBIT: EXX
RLC C
DJNZ GETBI0
LD B,8
LD C,(HL)
INC HL
GETBI0: EXX
RET
さいごに
65816をガチで組むこと自体、珍しいことなのかもしれません
更に、任天堂非公認ソフトということもあるので、もっと珍しくなります