跳转至

8086 汇编语言

内存

段地址和偏移地址

逻辑地址为 段地址:偏移地址 物理地址为 10h * 段地址 + 偏移地址

引用变量或标号的偏移地址:offset 变量名或标号名 引用变量或标号的段地址:seg 变量名或标号名


直接寻址与间接寻址

  • 直接寻址方式:[常数]
  • 寄存器间接寻址方式:[寄存器]
  • 寄存器相对寻址方式:[寄存器 + 常数] 或 常数[寄存器]
  • 基址变址寻址方式:[寄存器 + 寄存器] 或 [寄存器][寄存器]
  • 相对基址变址寻址方式:[寄存器 + 寄存器 + 常数] 或 常数[寄存器][寄存器]
  • 示例es:10h[si][bp], es:[bp + si + 10h]

寄存器只允许使用 bx、bp、si、di。若使用的寄存器包含bp,则缺省地址为 ss,否则为 ds。 不可同时使用两个基址寄存器 bx、bp 或两个变址寄存器 si、si。 常数可以出现多次,可以为变量名,编译器会将其替换为该变量的偏移地址。


  • 80386间接寻址一般形式:[寄存器 + 寄存器*n + 常数]

前后两个寄存器可以为 eax、ebx、ecx、edx、esi、edi、esp、ebp 中任意两个,且可以重复出现。n 必须为 1、2、4、8 其中一个。


显卡地址映射

80x25文本模式 - 段地址 = 0B800h - 偏移地址 = (y * 80 + x) * 2 - 每两个字节显示一个字符,低字节为字符的ASCII码,高字节为字符的颜色 - 共有 16 色,每个字符的颜色用一个字节表示,其中低四位为文本色,高四位为背景色 - 0100 表示红色,0010 表示绿色,0001 表示蓝色 - 1100 表示亮红色,1010 表示亮绿色,1001 表示亮蓝色 - 0000 表示黑色,1111 表示白色 - 0111 表示浅灰色,1000 表示深灰色

320x200图形模式 - 段地址 = 0A000h - 偏移地址 = y * 320 + x - 共有 256 色,每个点的颜色用一个字节表示 - 00h-0Fh: 等同16色 - 10h-1Fh: 黑色渐变到白色 - 20h-2Fh: 纯蓝色渐变到纯绿色,再渐变到纯红色


寄存器

通用寄存器

  • eax、ax、ah、al
  • ebx、bx、bh、bl
  • ecx、cx、ch、cl
  • edx、dx、dh、dl

段地址寄存器

  • cs: 代码段寄存器,会初始化为代码段的段地址
  • ss: 堆栈段寄存器,会初始化为堆栈段的段地址
  • ds: 数据段寄存器,会初始化为 PSP (Program Segment Prefix) 的段地址
  • es: 附加段寄存器,会初始化为 PSP (Program Segment Prefix) 的段地址
  • cs 不能用 mov 指令赋值,ds、es、ss 可以用 mov 指令赋值,但操作数必须为寄存器或变量

偏移地址寄存器

  • ip: 指令指针寄存器,会初始化为首条指令的偏移地址
  • sp: 堆栈指针寄存器,会初始化为堆栈段的长度
  • bp: 基址指针寄存器,可用于存放内存中某些数据的偏移地址
  • si: 源变址寄存器,可用于存放内存中源数据区的偏移地址
  • di: 目的变址寄存器,可用于存放内存中目的数据区的偏移地址

标志寄存器

FL 寄存器有状态标志共 6 个,包括 CF、OF、ZF、SF、PF、AF;控制标志共 3 个,包括 DF、IF、TF 第 0、2、4、6、7、8、9、10、11 位分别为 CF、PF、AF、ZF、SF、TF、IF、DF、OF

  • CF: 进位标志,当发生以下情况时,CF 置一
    • 两数相加产生进位
    • 两数相减产生借位
    • 两数相乘的乘积宽度超过被乘数宽度
    • 移位指令最后移出的那一位为一
  • OF: 溢出标志,当发生以下情况时,CF 置一
    • 两个正数相加变负数
    • 两个负数相加变正数
    • 两数相乘的乘积宽度超过被乘数宽度
    • 仅移动一位且移动前的最高位不等于移动后的最高位
  • ZF: 零标志,当运算结果为零时,ZF 置一
  • SF: 符号标志,当运算结果为负时,SF 置一,相当于运算结果的最高位
  • PF: 奇偶标志,当运算结果的低 8 位中有偶数个 1 时,PF 置一
  • AF: 辅助进位标志,当低 4 位向高 4 位产生进位或借位时,AF 置一

  • DF: 方向标志

    • 当 DF = 0 时,字符串操作指令按正方向进行,每次操作后地址加一
    • 当 DF = 1 时,字符串操作指令按反方向进行,每次操作后地址减一
  • IF: 中断允许标志
    • 当 IF = 0 时,禁止硬件中断
    • 当 IF = 1 时,允许硬件中断
  • TF: 跟踪标志
    • 当 TF = 0 时,CPU 进入常规状态
    • 当 TF = 1 时,CPU 进入单步状态,每执行一条指令就自动执行一次 int 01h


端口

  • CPU 通过向端口地址读取和写入数据来控制 I/O 设备
  • 端口地址独立于内存地址,其取值范围为 0000h-0FFFFh
  • 0000h-00FFh: 常用于系统板上的设备,如时钟、定时器、键盘接口、中断控制器和DMA控制器等
  • 0100h-03FFh: 常用于磁盘控制器、显示器、串行口和并行口等接口
  • 0400h-0FFFFh: 常用于用户应用、主板功能和PCI总线

  • 读取端口指令:in al/ax/eax, imm8/dx

  • 写入端口指令:out imm8/dx, al/ax/eax
  • 0-255 号端口可以直接寻址,256-65535 号端口必须使用 dx 间接寻址


CMOS - out 70h, al: 向 70h 端口发送信号,表示要读写某号内存单元 - in al, 71h: 从 71h 端口读取之前访问的内存单元,获得 BCD 码格式的时间信息 - 每个时间信息都用一个字节表示,其中低四位为个位,高四位为十位,即 个位 = 低四位 + 30h, 十位 = 高四位 + 30h - 0h 为秒,2h 为分,4h 为时,6h 为星期(01h-07h 依次表示周日、周一到周六),7h 为日,8h 为月,9h 为年的后两位


键盘 - in al, 60h: 从 60h 号端口获取按键产生的按键编码 - 键盘按下按键发送的按键编码称为为扫描码,每个按键在按下和释放的时候都会产生不同的扫描码,分别称为通码和断码 - 扫描码长度为一个字节,通码的最高位为 0,断码的最高位为 1,即 断码 = 通码 + 80h - 如果是特殊键,则会在扫描码的前缀中包含一个额外的字节 E0h,变为扩展码


指令

算术指令

加法指令

  • add a, b 加法,a = a + b
  • adc a, b 带进位加法,a = a + b + cf
  • inc a 自加1,不影响 CF 标志位,影响 ZF 标志位

减法指令

  • sub a, b 减法,a = a - b
  • sbb a, b 带借位减法,a = a - b - cf
  • dec a 自减1,不影响 CF 标志位,影响 ZF 标志位

  • neg a 取反,会影响CF、ZF、SF等标志位,(neg ax) = (not ax) + 1

  • cmp a, b 做减法运算,仅影响标志位状态
    • a == bZF = 1,若 a≠bZF = 0
    • a < bCF = 1,若 a≥bCF = 0

乘法指令

  • mul/imul a 非符号数/有符号数乘法
    • 当操作数为8位时,ax = al * a
    • 当操作数为16位时,dx:ax = ax * a
    • 当操作数为32位时,edx:eax = eax * a
  • imul a, b 32位模式下可用,a = a * b
    • imul reg16, r/m16
    • imul reg32, r/m32
    • imul reg64, r/m64
  • imul a, b, c 32位模式下可用,a = b * c
    • imul reg16, r/m16, imm8
    • imul reg32, r/m32, imm8
    • imul reg64, r/m64, imm8
    • imul reg16, r/m16, imm16
    • imul reg32, r/m32, imm32
    • imul reg64, r/m64, imm32

除法指令

  • div/idiv a 非符号数/有符号数除法
    • 当操作数为8位时,ax % a,商在 al 中,余数在 ah
    • 当操作数为16位时,dx:ax % a,商在 ax 中,余数在 dx
    • 当操作数为32位时,edx:eax % a,商在 eax 中,余数在 edx

浮点数运算指令

  • FPU共有8个浮点数据寄存器,将指针所处的位置视为逻辑编号0,每压入一个浮点数,指针所处的物理编号减一,其具体值保存在浮点状态寄存器的第11位至第13位
  • dd 为 32 位小数
  • dq 为 64 位小数
  • dt 为 80 位小数

  • fld m32/64/80fp|st(i) 将浮点数压入FPU的寄存器堆栈中

  • fild m16/32/64int 将整数压入FPU的寄存器堆栈中
  • ftp m32/64fp|st(i)st(0) 拷贝到指定位置
  • fstp m32/64fp|st(i)st(0) 拷贝到指定位置,并将其弹出堆栈

  • fadd 浮点数加法

    • fadd m32/64fp : st(0) = st(0) + fp
    • fadd st(0), st(i) : st(0) = st(0) + st(i)
    • fadd st(i), st(0) : st(i) = st(i) + st(0)
  • fsub 浮点数减法,同上
  • fmul 浮点数乘法,同上
  • fdiv 浮点数除法,同上

逻辑运算指令

  • and a,b : a = a & b
  • or a,b : a = a | b
  • xor a,b : a = a ^ b
  • not a : a = ~a
  • test a,b 对两个操作数进行逻辑与运算,并根据运算结果设置相关的标志位,不改变两个操作数
    • SF置结果的最高位
    • 若结果为0,则 ZF 置一
    • 若结果低8位中1的个数是偶数,则 PF 置一
    • CF 清零
    • OF 清零

移位指令

  • shl a, b 逻辑左移指令,低位补0,高位进CF
  • shr a, b 逻辑右移指令,低位进CF,高位补0
  • sal a, b 算数左移指令,低位补0,高位进CF
  • sar a, b 算数右移指令,低位进CF,高位不变

    • 以上四条指令,在移动一位时,OF会受到影响
    • 除SAR以外,OF=最高位和次高位的异或,即正负号改变时,OF = 1
    • 若为SAR,OF = 0
  • rol a, b 循环左移,高位到低位并送CF

  • ror a, b 循环右移,低位到高位并送CF
  • rcl a, b 带进位循环左移,低位补CF,高位进CF
  • rcr a, b 带进位循环右移,低位进CF,高位补CF

十进制调整指令

  • daa 加法的十进制调整,在 al 被做加法后将结果 al 调整为 BCD 码
    • AF == 1al & 0Fh > 09h,则 al += 6h
    • CF == 1al & F0h > 90h,则 al += 60h
  • das 减法的十进制调整,在 al 被做减法后将结果 al 调整为 BCD

    • AF == 1al & 0Fh > 09h,则 al -= 6h
    • CF == 1al & F0h > 90h,则 al -= 60h
  • aaa 加法的 ASCII 调整,在 al 被做加法后连带 ah 一起调整 ax 为非压缩 BCD 码

    • AF == 1al & 0Fh > 09h,则 ax += 106h AF = 1 CF = 1
  • aas 减法的 ASCII 调整,在 al 被做减法后连带 ah 一起调整 ax 为非压缩 BCD 码
    • AF == 1al & 0Fh > 09h,则 ax -= 06h, ah -= 1, al &= 0Fh AF = 1 CF = 1


传送指令

通用传送指令

  • mov dest, src 将 src 赋值给 dest,两个操作数不能全为内存单元。遵循小端规则,在读取时会先读取低位,在写入时会先写入低位
  • push a 将 a 压入栈顶,先送高位,再送低位
    • 操作数只能是 16 位或 32 位的,sp 会减去相应的值
  • pop a 从栈顶弹出数据给 a,先送低位,再送高位
    • 操作数只能是 16 位或 32 位的,sp 会加上相应的值
  • xchg a, b 交换 a 和 b 的值,不能全为内存单元,也不能包含段寄存器

  • pusha 将所有通用寄存器压入堆栈,顺序为 ax cx dx bx sp bp si di(80186)

  • popa 将所有通用寄存器弹出堆栈,顺序为 di si bp sp bx dx cx ax(80186)
  • pushad 将所有32位通用寄存器压入堆栈,顺序为 eax ecx edx ebx esp ebp esi edi(80386)
  • popad 将所有32位通用寄存器弹出堆栈,顺序为 edi esi ebp esp ebx edx ecx eax(80386)

地址传送指令

  • lea r16, mem 装入有效地址,r16 = offset mem。可用于一句完成简单运算和赋值
  • lds r16/32, m16:16/m16:32 装入远指针,ds:r16 = m16:16ds:r32 = m16:32
  • les r16/32, m16:16/m16:32 装入远指针,es:r16 = m16:16es:r32 = m16:32
  • lss r16/32, m16:16/m16:32 装入远指针,ss:r16 = m16:16ss:r32 = m16:32
  • lfs r16/32, m16:16/m16:32 装入远指针,fs:r16 = m16:16fs:r32 = m16:32
  • lgs r16/32, m16:16/m16:32 装入远指针,gs:r16 = m16:16gs:r32 = m16:32

标志寄存器传送指令

  • pushf 将 FL 压入堆栈
  • popf 将 FL 弹出堆栈
  • pushfd 将 EFL 压入堆栈
  • popfd 将 EFL 弹出堆栈
  • lahf 将 FL 存入 ah
  • sahf 将 ah 存入 FL


转移指令

无条件转移指令

  • jmp a:b 用 a 修改 CS,用 b 修改 IP
  • jmp a 用 a 修改 IP
  • jmp label 用标号的CS和IP修改CS和IP

    • CS为代码段寄存器,IP为指令指针寄存器
    • 8086机中,任意时刻,CPU将CS:IP指向的内容当作指令执行
    • 读取一条指令后,IP会加上所读取指令的长度
    • 不可以使用常数
  • 短跳转(Short Jmp): 只能跳转到256字节的范围内,对应机器码为 EB

  • 近跳转(Near Jmp): 可跳至同一段范围内的地址,对应机器码为 E9
  • 远跳转(Far Jmp): 可跳至任意地址,对应机器码为 EA

条件转移指令

  • je/jz label 相等/结果为零,条件为 ZF == 1
  • jne/jnz label 不相等/结果不为零,条件为 ZF == 0
  • js label 结果为负,条件为 SF == 1
  • jns label 结果非负,条件为 SF == 0

  • jg label 大于,条件为 ZF == 0 and SF == OF

  • jge label 大于等于,条件为 SF == OF
  • jl label 小于,条件为 SF != OF
  • jle label 小于等于,条件为 ZF == 1 or SF != OF

  • jb/jnae/jc label 小于/不大于等于/有进位,条件为 CF == 1

  • jnb/jae/jnc label 不小于/大于等于/无进位,条件为 CF == 0
  • jna/jbe label 不大于/小于等于,条件为 CF == 1 or ZF == 1
  • ja/jnbe label 大于/不小于等于,条件为 CF == 0 and ZF == 0

循环指令

  • loop label 将 cx 减一后,若 cx 不为零则跳转
  • loopz label 将 cx 减一后,若 cx 不为零且之前的运算结果为零则跳转
  • loopnz label 将 cx 减一后,若 cx 不为零且之前的运算结果不为零则跳转
  • loopne label 将 cx 减一后,若 cx 不为零且之前的运算结果不为零则跳转

子程序调用与返回指令

  • call label 近调用,将当前的 IP 压入栈中,然后转移
  • call cs:ip 远调用,将当前的 CS 和 IP 压入栈中,然后转移
  • ret 用栈中的数据,修改 IP 的内容,从而实现近转移
  • retf 用栈中的数据,修改 CS 和 IP 中的内容,从而实现远转移

中断指令

  • int n 利用中断类型码,引发中断过程,等效于 IF = 0, TF = 0, pushf, push cs, push ip, jmp (n * 4 + 2):(n * 4)
  • iret 中断返回,会从栈中弹出数据到 CS、IP 和 EFLAGS 寄存器中,等效于 pop ip, pop cs, popf


字符串操作指令

  • rep 重复后面的字符串指令 cx 次
  • repe/repz 当比较相等时继续重复
  • repne/repnz 当比较不相等时继续重复

字符串传送指令

  • movsb 从 ds:[si] 传送一个字节到 es:[di],并以字节为单位移动 si、di
  • movsw 从 ds:[si] 传送一个字到 es:[di],并以字为单位移动 si、di
  • movsd 从 ds:[si] 传送一个双字到 es:[di],并以双字为单位移动 si、di
  • movsq 从 ds:[si] 传送一个四字到 es:[di],并以四字为单位移动 si、di

字符串比较命令

  • cmpsb 比较 ds:[si] 和 es:[di],
  • cmpsw 比较 ds:[si] 和 es:[di],
  • cmpsd 比较 ds:[si] 和 es:[di],
  • cmpsq 比较 ds:[si] 和 es:[di],

字符串扫描命令

  • scasb 比较 al 和 es:[di],并以字节为单位移动 di
  • scasw 比较 ax 和 es:[di],并以字为单位移动 di
  • scasd 比较 eax 和 es:[di],并以双字为单位移动 di

字符串存入命令

  • stosb 将 al 存入 es:[di],并以字节为单位移动 di
  • stosw 将 ax 存入 es:[di],并以字为单位移动 di
  • stosd 将 eax 存入 es:[di],并以双字为单位移动 di

字符串读取命令

  • lodsb 从 es:[di] 读取一个字节至 al,并以字节为单位移动 di
  • lodsw 从 es:[di] 读取一个字至 ax,并以字为单位移动 di
  • lodsd 从 es:[di] 读取一个双字至 eax,并以双字为单位移动 di


转换指令

  • xlat 完成一个字节的查表转换,al = ds:[bx + al]
  • cbw 将 al 符号扩展为 ax
  • cwd 将 ax 符号扩展为 dx:ax

  • cwde 将 ax 符号扩展为 eax(80386)

  • cdq 将 eax 符号扩展为 edx:eax(80386)
  • movsx reg16/32,reg8/reg16/mem8/mem16 符号扩展传送,将8位符号扩展为16位或32位,或者16位符号扩展为32位(80386)
  • movzx reg16/32,reg8/reg16/mem8/mem16 零扩展传送,将8位零扩展为16位或32位,或者16位零扩展为32位(80386)


控制指令

  • cld : DF = 0,自动加一
  • std : DF = 1,自动减一
  • cli : IF = 0,禁止中断发生, 即屏蔽中断
  • sti : IF = 1,允许中断产生
  • clc : CF = 0,无进位/借位
  • stc : CF = 1,有进位/借位
  • nop 无操作,机器码为90h,占用一个字节空间


伪指令

数据定义伪指令

  • [变量名] db/dw/dd/dq/dt 表达式 [注释]
    • db: 字节型变量,每个变量分配 1 个内存单元
    • dw: 字型变量,每个变量分配 2 个内存单元
    • dd: 双字型变量,每个变量分配 4 个内存单元
    • dq: 四字型变量,每个变量分配 8 个内存单元
    • dt: 十字型变量,每个变量分配 10 个内存单元
  • n dup(表达式) 将表达式重复 n

运算符伪指令

  • seg x 获得变量或标号的段地址,编译时会会把 seg a 编译成 seg a - 首段段地址
  • offset x 获得变量或标号的偏移地址

属性说明伪指令

  • 类型 ptr 地址表达式 确定地址表达式的存储单元为指定的类型,即用在地址表达式之前,用于指定或临时改变变量名和标号名的类型
    • byte ptr
    • word ptr
    • dword ptr
    • qword ptr
    • tbyte ptr

段定义与段分配伪指令

  • .386 表示会使用32位寄存器
  • x segment 说明一个段开始
  • x segment use16 表示使用16位地址
  • x segment use32 表示使用32位地址
  • x ends 说明一个段结束
  • assume reg:seg,[reg:seg,...] 建立段和段寄存器之间的联系,放在代码段内和段定义语句之后

程序模块命名与程序结束伪指令

  • end [起始地址标号] 表示程序结束,后为主模块起始地址,汇编时会将起始地址标号的段地址和偏移地址赋给 CS 和 IP


中断

  • CPU在收到中断信息后,根据中断信息中的中断类型码,去中断向量表找到相应中断例程的地址,而后跳转执行。
  • 中断向量表存放着256个中断源所对应的中断处理程序的入口地址,每个地址占四个内存单元,低字放偏移地址,高字放段地址,即 IP = n * 4CS = n * 4 + 2
  • 当中断结束后,通常需要发送 EOI (End of Interrupt) 信号给中断控制器,弹出之前压入堆栈的寄存器中的值,最后再用 iret 恢复CS、IP和EFLAGS:
; 保存和修改中断向量地址
mov ax, 0
mov es, ax

mov ax, es:[9 * 4]
mov [old_9h], ax
mov ax, es:[9 * 4 + 2]
mov [old_9h + 2], ax

mov word ptr es:[9 * 4], offset int_9h
mov word ptr es:[9 * 4 + 2], code
; 退出中断
mov al, 20h
out 20h, al

pop ds
pop cx
pop bx
pop ax
iret

int 21h

int 21h 是 DOS 提供的中断例程,其中包含了 DOS 提供给程序员在编程时调用的子程序

  • ah = 01h 键盘输入并回显,al = 输入字符
  • ah = 02h 显示输出,dl = 输出字符
  • ah = 09h 显示字符串,ds:dx = 串地址$ 结束字符串
  • ah = 0Ah 键盘输入到缓冲区,按回车键结束输入,ds:dx = 缓冲区首地址, (ds:dx) = 缓冲区最大字符数(ds:dx+1) = 实际输入的字符数ds:dx+2 起是输入的字符串
  • ah = 4Ch 带返回码结束,al = 返回码


int 16h

int 16h 是BIOS提供的键盘服务,提供了读取和控制键盘输入的功能 - ah = 00h 读取键盘输入 - 若键盘缓存区为空,则等待键盘输入 - 若键盘缓存区非空,则从键盘缓冲区读取一个键盘输入并将其删除,使 al = 输入的ASCII码ah = 输入的扫描码 - ah = 01h 检测键盘缓存区是否为空,若为空则 ZF = 1,否则 ZF = 0 - ah = 02h 检测键盘各特殊功能键的状态,放入 al 寄存器中,若对应位为 1,则表示该键为按下状态,否则为断开状态


int 10h

int 10h 是BIOS提供的视频服务,提供了屏幕显示、光标操作、颜色设置等与视频相关的功能。

  • ah = 00h 设置显示模式
    • al = 03h 文本模式,文本分辨率80x25,像素分辨率640x200,16色,显卡段地址 B800h
    • al = 12h 图形模式,文本分辨率80x30,像素分辨率640x480,16色,显卡段地址 A000h
    • al = 13h 图形模式,文本分辨率40x25,像素分辨率320x200,256色,显卡段地址 A000h
  • ah = 01h 设置光标形状
  • ah = 02h 设置光标位置
  • ah = 03h 读取光标信息


其他

Turbo Debugger 常用快捷键

  • F2 设置断点
  • F4 运行至光标
  • F7 单步跟踪
  • F8 单步跳过
  • F9 运行程序
  • Ctrl + F2 重载程序
  • Alt + x 退出调试器

  • CPU

    • Ctrl + G 跳转至指定地址
    • Ctrl + O 跳转回程序执行处
    • Ctrl + P 跳转回之前地址
  • Dump
    • Ctrl + N 跳转至下一行
    • Ctrl + G 跳转至指定地址
    • Ctrl + P 跳转回之前地址
    • Ctrl + D 切换显示方式
  • Register
    • Ctrl + I 寄存器加1
    • Ctrl + D 寄存器减1
    • Ctrl + Z 寄存器回0
    • Ctrl + C 寄存器变为指定值
    • Ctrl + R 切换16/32位寄存器


DosBox 调试指令

  • -t 单步执行
  • -p 以汇编指令的格式在内存中写入一条机器指令
  • -q 退出

  • -r 查看所有寄存器的值

  • -r reg 修改寄存器中的值

  • -d 从当前或之前查看位置开始查看内存

  • -d addr 从指定位置开始查看内存
  • -d addr n 从指定位置开始查看内存,到指定偏移地址结束

  • -e addr 从指定位置开始修改内存中的内容,按空格进入下一个,按ENTER结束修改

  • -e addr a b c ... 从指定位置开始,用输入内容修改内存中的内容

  • -a 以汇编指令的格式,从当前位置开始写入机器指令

  • -a addr 以汇编指令的格式,从指定位置开始写入机器指令
  • -u addr 将内存中的机器指令翻译成汇编指令


键盘按键编码

  • Up : 4800h
  • Left : 4B00h
  • Right : 4D00h
  • Down : 5000h

  • Esc : 011Bh

  • Backspace : 0E08h
  • Tab : 0F09h
  • Enter : 1C0Dh
  • Space : 3920h