快速理解Z80汇编指令

  • 快速理解Z80GB汇编指令
    文/神奇误差


    Gameboy 的核心是一块Z80指令兼容的微处理器(下文以Z80GB指代)。Z80GB设有16根地址线和8条数据线。8条数据线的意思是Z80GB可以一口气处理8位(即一个字节)的数据,因此具有8条数据线的处理器一般也被称为8位处理器。16根地址线代表了Z80GB的寻址能力是2的16次方,即64KB的地址空间。
    2^16=2^6×2^10=64×2^10=64KB
    如果对数据,地址之类的概念不太熟悉,似乎一下就懵了,下面我们简要说明如下。
    又不准确不清晰的地方欢迎跟帖留言 :D:D


    问题的来源
    将电池正负极接上小灯泡,中间接上开关。这个小电路只有两种状态,开关闭合,灯泡被点亮,开关断开,灯泡失去电力供应而熄灭。如果只能用两种状态来描绘世界,那无非就是高和矮,胖和瘦,黑和白等。如果用两个数字描绘世界,只能是0和1。这个世界这么美好,0和1怎么能表达得清楚?但是用电气设备表达开和关,灯泡点亮熄灭这样的状态是再容易不过的事情。如何解决?
    我们引入两个小灯泡。
    如果灯泡熄灭时代表0,灯泡被点亮时代表1。两个小灯泡全亮的时候就是11,左边的被点亮的时候是10,那么将2个灯泡并排放在一起就可以表达,00,01,10,11四种情况。假如我们定义00代表字母a,01代表字母b,10代表字母c,11代表字母d。当灯泡的点亮熄灭组合顺次为10,01,00时,我们就知道这两个小灯泡想表达的是cba,它们可能想打篮球了。
    两个小灯泡似乎还是不够,再引入6个小灯泡。这下总共有8只小灯泡!将8个小灯泡依次排开,这些它们可以表达00000000,00000001,00000010,。。。,11111110,11111111总共128种不同的信息啦。考虑到英文字母只有26个,现在这8个灯泡只要依次点亮变换,已经足够一个文学家用英语描绘整个世界。
    Gameboy就是这样采用0和1进行工作的,它里面包含了数量庞大的和灯泡开关类似的小元件来不停地运算工作。


    一点点数学知识
    为了便于交流,将类似00110001这样的8位数据称为一个字节,即一个字节包含8位数据。如果足够细心,可以观察到,一个字节的高四位(最左边的四位,如前例中的0011)和一个字节的低四位(最右边的四位,如前例中的0001)是类似的,它们是四位数据,最多都可以表示16个不同的信息,如下


    图1 二进制和十六进制


    如果用A代表10,B代表11,C代表12,。。。,F代表15。则类似00110001这样字节实际代表的就是31H,或如00111100代表的就是3CH。这里H表示的是16进制。实际上,用0~F进行计数,就是16进制计数法则。汇编语言中,需要经常使用16进制。
    如果给出16进制的FFH,可以通过查上表得知,其实就是11111111。用0~1进行计数,是2进制计数法则。常用的是0~9计数,这是10进制计数法则。本文开头提到了Z80GB的寻址空间是64KB。这里的B表示的是字节(Byte),K表示的是千。由于计算机是基于2进制的,计算机界所谓的千指的是2的10次方,即1024。
    1MB = 1024 KB
    1KB = 1024B
    1B = 8 b(即,1Byte = 8 bit。Byte: 字节; Bit:位)
    1MB=1024KB=1024x1024B=1048576B
    2MB=2048KB=2048x1024B=2097152B=1FFFFFH


    如果用16进制表达64KB的寻址空间是多少呢?
    不妨打开windows自带的计算器(附件->计算器),修改查看方式为编程模式(图1),在10进制(DEC)下输入65535(这是2的16次方的值减1,我们从0开始计数),然后切换为十六进制(HEX)看看是多少?



    图 2 计算器
    正确的答案是FFFFH。以后关于进制的换算,经常需要这台计算器来帮忙。


    Gameboy中的处理器
    Gameboy中的核心芯片LR35902包含了Z80核心。这个核心可以称作Z80GB,它类似Z80(如图2所示),Z80GB这样的处理器是一种电气设备,它有若干引脚(28根或40根,叫做28pin或40pin)。按照功能不同有供电引脚,晶振引脚,地址线引脚,数据线引脚等等。



    图3 一个具有40个引脚的Z80芯片
    对于数据引脚,每一个引脚都可以接收或者发出一个电压信号。如果给引脚接入电压较高的信号(3.3伏或5伏),称之为引脚读入了高电平。如果给引脚接入电压较低的信号(低于3伏),称之为引脚读入了低电平。如果用高电平表示数据1,用低电平表示数据0。那么我们可以说某引脚读入了数据1或者读入了数据0。反之亦然。我们也可以说某引脚输出了数据0或者输出了数据1。
    给Z80GB的8根数据线依次接入00000011(从高位到低位)信号,相当于传递给Z80GB字符‘c’的信息(还记得前面那个cba的例子?)。


    存储器(RAM和ROM)
    前文说到,数据可以用不同的0和1组合来表达。Gameboy用存储器来储存数据,数据以字节的形式在存储器中码放。每一个字节都有一个地址,顺序排放。就像住在一栋大楼的家庭,每一户都有自己的门牌号,每一户人家就是一个字节,门牌号就是对应的地址。存储器有自己的地址线(假设也有16条,真实情况可能是12条或者8条)和数据线(8条),当信号沿着地址线传进存储器,则存储器根据对应的地址,从数据线传出数据。如果,0000 0000 0000 0001 (写作16进制0001H)按照对应关系一位信号传给一条地址线,则存储器就明白外界想要地址在0001H的字节了。之后存储器将位于0001H的字节信息(8位)依次分配给8条不同的数据线,送出。


    为了有效地区别数据,经常用0x0000这样的形式来表示地址0000H,那么0x9700就代表地址9700H.


    CPU和存储器一起工作
    CPU只是一个执行器,它不存储任何东西。给CPU上电后,它从存储器的0000H地址取得第一个字节作为指令,根据指令进行运算或到某地址继续取指令或者数据。CPU开始周而复始的工作。


    了解了数据和地址之后,在此简单介绍Z80GB的汇编指令
    说处理器不存储任何东西是不严谨的,在工作时,Z80GB有A,B,C,D,E,F,HL等若干寄存器(每个大小是1个字节或2字节)和一些标志寄存器。CPU也有自己的指令集,会根据读到的数据进行相应判断。
    当CPU读到指令80H时,这就是CPU的
    ADD A,B
    指令,意思是将寄存器B中的数值和寄存器A中的数据加和,然后将结果放入寄存器A中。
    当CPU读到指令3EH时,这就是CPU的
    LD A,n
    指令,意思是将3EH指令后面紧跟的数据放入寄存器A中。
    一个很有趣的问题时,CPU是如何分辨谁是数据,谁是指令,什么时候80H是指令,什么时候80H是数据?对于Z80GB来说,通电后,从0x0000读取到的第一个数据作为指令,执行完这条指令后,取下一个指令,顺序执行。举个例子,如果在0x0000存放的数据是80H,那么在0x0001存放的数据会被Z80GB视为指令。如果在0x0000存放的数据是3EH,那么在0x0001存放的数据会被视为数据。依次类推。
    顺带提一句,Gameboy会通过跳转指令,在0x0150开始真正的程序。


    两个很有用的Gameboy Z80指令集资料
    http://pastraiser.com/cpu/gameboy/gameboy_opcodes.html
    http://goldencrystal.free.fr/GBZ80Opcodes.pdf

  • "aaa25295811" wrote:

    这篇看的不是很明白 感觉大致意思就是给存储器和CPU下达数据,指令告诉他们什么时候该做什么的意思么?16进制换算结果就是其执行程序?


    大体没错。


    数据都是储存在存储器中。
    数据按8位一个单元(字节)依次码放,每一个单元(字节)的数据都有对应的地址。举个不太严谨的例子,存储器中开头32位数据为10101111110000110101000000000001.....,按照8位组成一个字节,每个字节一个地址,各字节依次码放的原则,这32位数据可以写成如下形式:



     
    现在请打开下述Z80GB 操作码查询表 的链接,然后继续
    http://pastraiser.com/cpu/gameboy/gameboy_opcodes.html


    CPU的基本工作流程是取指令,执行指令,取指令,执行指令。Z80GB一次取一个字节,因为它只有8根数据线,一次能读入8位即一个字节的指令/数据。

    CPU上电之后,从存储器中读取的第一个字节作为指令。
    从0x0000地址读入第一个字节10101111B(为了描述方便,我们用16进制表示为AFH)。AFH指令在Z80GB 操作码查询表的Ax行,xF列。
    查得这是XOR A指令,CPU执行指令。异或逻辑计算的结果将使寄存器A中的值变为零。


    接下来CPU顺序移到下一个位置,0x0001,继续取指令。
    取到C3H,查询Cx行,x3列,查得这是JP a16指令。
    JP a16指令有一个参数,这个参数是一个16位的地址。a16意思是address 16bit,很好记。
    16位的地址就是两个字节。那么这两个字节从哪里找?
    按照指令集规定,这两个字节按照“低位在前高位在后”的原则顺序存放在C3H所在地址之后。


    CPU继续移到下一个位置,0x0002,取到数据50H(注意,现在CPU已经明白0x0002,0x0003储存的不是指令而是0x0001所需要的参数)。


    CPU继续移到下一个位置,0x0003,取到数据01H。


    则现在指令完整,为C3H 50H 01H,写作汇编指令即

    Quote

    JP 5001H


    按照低位在前,高位在后的原则,对调5001H得到0150H。真实的地址其实是0x0150。
    JP指令是跳转命令(英文跳跃Jump的缩写),JP使CPU跳转到其参数指定的地址。
    现在CPU执行指令,JP 5001H,指令执行的结果是CPU跳转到0x0150地址。


    接下来CPU从0x0150取一个字节作为指令,周而复始。


    所以CPU永远不知道哪一条数据是指令,哪一条数据是数据,直到它走到那条数据所在的地址门前,有点拗口。


    只有一个例外: 0x0000地址存储的8位数据它永远都是指令;它也是CPU工作的起点。


    有时候为了节约空间,通过一定的编程技巧,有的地址存储的字节前几步可能还是指令,过了一会儿就会被当作数据处理,这是后话。




    --------
    题外话,这个论坛的消息提醒和其它的不太一样。
    在论坛左下角可以选择订阅主题,或者收藏主题。
    订阅主题:该主题有新回复会得到电邮通知
    收藏主题:该主题有新回复可以在 用户控制面板,收藏界面看到



    撰写帖子时,也可以勾选
    有人回复时,给我发送一封Email
    这样有新回复可以得到一封电邮通知。(在未查看新回复前,电邮不会再提醒)


  • 可以换个靠谱点儿的传上来啊,哈哈


    那些准备资料看的怎么样,主要是了解一下Gameboy的工作机制。


    :arrow: 有问题我们可以一起在论坛发文讨论。


    这段时间主要是等大家的反馈。


    队友们准备得差不多就可以开工了,在实践中消灭各种拦路虎。

Join us

Have your own thoughts on this discussion? Wanna help others, avoid the mistakes you met before? Wanna contribute more to this or other topic? Just join us! Registration is free. Join us!