如何在Z80汇编中动态将UDG写入ZX Spectrum指定字符槽
ZX Spectrum Z80汇编:动态写入指定UDG内存槽位问题
本人正在学习Z80汇编语言,小时候使用过ZX Spectrum但仅掌握BASIC。目前已编写一段可在指定位置打印三个UDG字符的代码,运行正常,但需要实现动态替换第二个UDG字符的功能:
- 通过
UDGOFFSET从DYNAMIC_UDGS中取出目标8x8 UDG数据 - 将该数据写入由
TARGETUDGOFFSET指定的UDG内存槽(ZX Spectrum的UDG存储起始地址由UDGPOINTER指向)
当前TEST函数可正确取出目标UDG,但无法实现动态写入指定槽位——目前代码总是将数据写入第一个UDG槽(对应字符chr$144),而需要根据TARGETUDGOFFSET定位到对应槽位(例如当TARGETUDGOFFSET为1时,应写入chr$145对应的槽)。
现有完整代码如下:
ORG 33000 ENTRY_POINT: LD A,7 LD (UDGOFFSET), A ; 对应第8个动态UDG字符 LD A, 1 ; 指向"ub" LD (TARGETUDGOFFSET), A ; 要替换的字符:144 + X = "ua"/"ub"/"uc"等 CALL CLS CALL MAIN MAIN: LD HL, MAIN_UDG LD (UDGPOINTER), HL LD A, 2 CALL OUTCHAN PUSH HL ; 需要实现函数:用UDGOFFSET从DYNAMIC_UDGS取UDG,写入TARGETUDGOFFSET指定的UDG槽(如144、145、146) CALL TEST LD A, (TARGETUDGOFFSET) ADD A, 144 ; 加载更新后的字符编码(144=ua,145=ub等)- 已正常工作 LD (MAIN_UDG_STR+12), A ; 替换字符串中对应位置的字符(+12对应"ub")- 已正常工作 POP HL LD A, 0 LD (MAIN_UDG_STR+1), A ; 垂直屏幕位置 LD A, 14 ; 水平屏幕位置 LD (MAIN_UDG_STR+2), A LD DE, MAIN_UDG_STR LD BC, MAIN_UDG_STR_END-MAIN_UDG_STR CALL PRINTSTRING RET TEST: ; 已实现:通过UDGOFFSET从DYNAMIC_UDGS取出正确的UDG LD A, (UDGOFFSET) SLA A SLA A SLA A LD E, A LD D, 0 LD HL, DYNAMIC_UDGS ADD HL, DE ; 问题:当前总是写入第一个UDG槽(chr$144),需要改用TARGETUDGOFFSET定位目标槽位 ; 原错误代码: ; LD DE, (UDGPOINTER) ; ChatGPT生成的无效代码片段: ; LD A, (UDGPOINTER) ; LD L, A ; LD A, (UDGPOINTER+1) ; LD H, A ; LD A, (TARGETUDGOFFSET) ; 加载偏移索引(0,1,2...) ; SLA A ; SLA A ; SLA A ; 乘以8(每个UDG占8字节) ; LD E, A ; LD D, 0 ; ADD HL, DE ; HL = UDG基地址 + (TARGETUDGOFFSET * 8) ; EX DE, HL ; DE = 目标指针 LD BC, 8 LDIR RET _VARS UDGOFFSET DEFB 0 ; 0-7 - 用于DYNAMIC_UDGS的偏移 TARGETUDGOFFSET DEFB 0 _CONSTANTS: UDGPOINTER EQU 23675 ; 23675和23676存储16位地址65368,即UDG存储起始地址 OUTCHAN EQU 5633 PRINTSTRING EQU 8252 CLS EQU 3503 ink EQU 16 paper EQU 17 flash EQU 18 bright EQU 19 _at EQU 22 off EQU 0 on EQU 1 blk EQU 0 blu EQU 1 red EQU 2 mag EQU 3 grn EQU 4 cyn EQU 5 yel EQU 6 wht EQU 7 ua EQU 144 ; UDG "A" ub EQU 145 ; UDG "B" uc EQU 146 ; UDG "C" DYNAMIC_UDGS DEFB 0,16,16,16,16,16,16,16 DEFB 0,24,36,4,8,16,32,28 DEFB 0,56,4,4,24,4,4,56 DEFB 0,68,68,68,56,4,4,4 DEFB 0,28,32,32,24,4,4,56 DEFB 0,32,32,32,60,34,34,28 DEFB 0,124,2,4,8,16,32,32 DEFB 0,24,36,36,24,36,36,24 MAIN_UDG DEFB 0,4,2,255,2,4,0,0 DEFB 0,68,40,16,40,68,0,0 DEFB 0,32,64,255,64,32,0,0 MAIN_UDG_STR DEFB _AT, 0, 0, ink, blk, paper, wht, flash, off, bright, off, ua,ub,uc ; 主精灵字符串 MAIN_UDG_STR_END EQU $ ; 字符串数据结束标记
解决方案
修改TEST函数,正确计算目标UDG槽位的内存地址,替换后的完整TEST代码如下:
TEST: ; 1. 计算源地址:从DYNAMIC_UDGS取出指定UDG LD A, (UDGOFFSET) SLA A SLA A SLA A ; 偏移量乘以8(每个UDG占8字节) LD E, A LD D, 0 LD HL, DYNAMIC_UDGS ADD HL, DE ; HL = 源UDG的起始地址 ; 2. 计算目标地址:定位到TARGETUDGOFFSET指定的UDG槽 LD DE, (UDGPOINTER) ; DE = UDG存储基地址 LD A, (TARGETUDGOFFSET) SLA A SLA A SLA A ; 目标槽偏移量乘以8 LD C, A LD B, 0 ADD DE, BC ; DE = UDG基地址 + (目标槽索引 * 8) ; 3. 复制8字节UDG数据 LD BC, 8 LDIR ; 从HL复制8字节到DE,自动递增地址 RET
关键修正说明
- 目标地址计算:从
UDGPOINTER取出UDG基地址到DE,然后将TARGETUDGOFFSET乘以8(每个UDG占8字节),加到DE上,得到目标槽位的起始地址。 - LDIR指令使用:
LDIR会将HL指向的BC个字节复制到DE指向的地址,同时自动递增HL、DE并递减BC,直到BC为0,正好适合复制8字节的UDG数据。
这样修改后,TEST函数就能根据TARGETUDGOFFSET的值,将从DYNAMIC_UDGS取出的UDG写入对应的内存槽位,实现动态替换的需求。
内容的提问来源于stack exchange,提问作者Davy C
相关产品推荐
相关产品推荐

