You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

关键修正说明

  1. 目标地址计算:从UDGPOINTER取出UDG基地址到DE,然后将TARGETUDGOFFSET乘以8(每个UDG占8字节),加到DE上,得到目标槽位的起始地址。
  2. LDIR指令使用:LDIR会将HL指向的BC个字节复制到DE指向的地址,同时自动递增HL、DE并递减BC,直到BC为0,正好适合复制8字节的UDG数据。

这样修改后,TEST函数就能根据TARGETUDGOFFSET的值,将从DYNAMIC_UDGS取出的UDG写入对应的内存槽位,实现动态替换的需求。

内容的提问来源于stack exchange,提问作者Davy C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 17:37:31