有一个小指令集和一些寻址模式。
这使得 程序员的模型易于学习。
指令集
目前支持 8 条指令:
例子:
inc RA2 // increments register RA2 dec A // decrements accumulator sel WA10 // selects WA10 do #0xBA00 + WA22 > A // adds value 0xBA00 to register WA22, and writes the result to the accumulator do A and #0x5555 -> @ // logical AND between accumulator and value goes to memory do A xnor #0x5555 > RA0 // logical XNOR between accumulator and value goes to register RA0, selects RA0 not RA1 // inverts all the bits in register RA1, selects RA1 jsr A+RA10 // calculates A+RA10, and jumps to this address (subroutine, returns with RET) jmp #loop // jmp to label "loop"
寻址模式
Spoc 支持 3 种寻址模式:
例子:
do #1234 -> A // Immediate addressing: moves value 1234 to accumulator do A -> RA4 // Direct addressing: writes accumulator value to register RA4 do @ -> A // Indirect addressing: reads memory to accumulator do A -> @ // Indirect addressing: writes accumulator to memory
可以对源操作数和目标操作数使用间接寻址。
do @ -> @ do @ + #0x22 -> @ do A or @ -> @
间接寻址与“选定的寄存器”有关(参见下面的“存储器和寄存器文件”段落)。
DO指令
DO 指令是最强大的,因为它可以使用多达 2 个源执行操作,并将结果写入多达 2 个目的地。
要写入 2 个目标,请用逗号分隔它们。
示例:写入 2 个目标时,一个始终是累加器,另一个是寄存器 (RAxx/WAxx) 或存储器 (@)。
do #22 -> A, RA0 do A + #22 -> A, WA1 do A - @ -> A, WA1 do A or RA3 -> WA4, A do @ and #22 -> A, @ do WA6 xor #22 -> A, @
累加器和数据大小
每个 spoc 指令都从可能的大小列表中指定一个数据大小。
Spoc0 具有 4 种有效数据大小:1、8、16 和 32 位。 默认值为 16 位(未指定数据大小时)。
数据大小是通过后缀指令(.bit、.byte、.word 和 .dw)来指定的。
Spoc 还为每个有效数据大小提供了一个累加器。使用 Spoc0,这为我们提供了 4 个累加器。
累加器是独立的(写入一个不会影响其他累加器)。
例子:
do.bit #1 -> A // writes 1 to the 1-bit accumulator inc.byte A // increments the 8-bits accumulator do.word A + #0x1000-> A // adds 0x1000 to the 16-bits accumulator do.dw #0x12DECF80 -> A // writes 0x12DECF80 to the 32-bits accumulator
分支
指令 JMP 用于分支到新的程序位置。
指令 JSR 用于分支到新的程序位置,最终使用指令 RET 从该位置返回。
这些指令可以有条件地执行(借助 C 和 Z 标志,请参阅下一段)。
例子:
jmp #gothere // jumps unconditionally jsr #gotosubroutine // jumps unconditionally to subroutine jmp.Z=0 #gothere // jumps conditionally (if flag Z is 0)
分支指令可以使用计算地址进行分支。 例如,可以有子例程表。
注意:在任何分支指令之后,将取消选择当前寄存器(有关“选定”寄存器的讨论,请参阅后面的内容)。
叠
在使用 JSR 指令之前,请确保初始化 SP(堆栈指针)寄存器。
例:
do #0x0C00 > SP // stack from 0x0C00 to 0x0FFF,enough for a depth of 64 subroutine calls jsr #mysubroutine
堆栈用于存储子例程返回地址。 堆栈使用内存并向上增长(在 Spoc0 中,SP 指针对于每个 JSR 指令递增 16,对于每个 RET 递减 16)。
标志和条件执行
标志用于有条件地执行其他指令。
Spoc0 使用 2 个标志:
Z(零)标志。用于检测零值或“相等”比较。
C(携带)标志。对于无符号数字的“大于”比较很有用(还可以检测无符号添加/子操作的溢出)。
如果上一个操作的结果为 0,则 Z 标志为 0。
如果上一个操作的结果不是 1,则 Z 标志为 0(注意:许多 CPU 采用相反的约定......
标志由所有执行的指令设置。
例子:
do #3 > A dec A // A becomes 0x0002, C is 0, Z is 1 dec A // A becomes 0x0001, C is 0, Z is 1 dec A // A becomes 0x0000, C is 0, Z is 0 dec A // A becomes 0xFFFF, C is 1, Z is 1 dec A // A becomes 0xFFFE, C is 0, Z is 1 dec A // A becomes 0xFFFD, C is 0, Z is 1
您可以执行没有目标的 DO 操作。 在这种情况下,将执行操作,结果将丢失,但标志仍会更新。
例子:
do #3 -> WA0 // writes 3 to register WA0 // now we are going to do 3 subtractions, without saving the results. But the flags are still updated. do WA0-#2 // WA0>2, so we get C=0, Z=1 do WA0-#3 // WA0=3, so we get C=0, Z=0 do WA0-#4 // WA0<4, so we get C=1, Z=1 // now run some conditional instructions jmp.c=0 #mylabel1 // conditional jump, not executed since C=1 add.z=0 WA0 + A > RA2 // conditional addition, not executed since Z=1 jmp.z=1 #mylabel2 // conditional jump, executed since Z=1
最后,如果未执行条件指令,则标志也不会更新。
例:
do #1 > A // A is 0x0001, C is 0, Z is 1 dec.z=0 A // not executed, and flags not changed
将标志作为操作数
进位标志也可以在源操作数中使用(在源操作数中,进位标志可以命名为“C”或“CY”或“carry”)。
例:
do CY -> A do A + #22 + C -> A do A xor CARRY -> RA0
对于算术运算,进位标志值不是有符号扩展的,但对于逻辑运算,它是有符号扩展的(换句话说,对于算术运算,进位值为 0 或 1,对于逻辑运算,进位值为 0 和 0xFFFF)。
例:
do #0 -> A dec A // A=0xFFFF, C=1 do CY + #22 -> A // arithmetic operation, so A=23 do #0 -> A dec A // A=0xFFFF, C=1 do CY xor #0x1111 -> A // logical operation, so A=0xEEEE
存储器和寄存器文件
符号“@”用于表示内存访问。
它与名为“RAxx”和“WAxx”的寄存器结合使用。
内存读取示例(从地址0x200读取):
do #0x0200 -> RA0 do @ -> A // reads memory 0x200, and puts the value in accumulator
内存写入(写入地址0x200)示例:
do #0x0200 -> WA17 do RA3 -> @ // writes content of RA3 to memory 0x200
读取存储器访问的地址由“RAxx”寄存器给出。
写存储器访问的地址由“WAxx”寄存器给出。
寄存器是寄存器文件的一部分,因此您可以使用其中的许多寄存器。
Spoc0 有 32 个 RA 寄存器,命名为 RA0 到 RA31,以及 32 个 WA 寄存器,命名为 WA0 到 WA31。
每个文件的一个寄存器在给定的执行时间被“选择”。 要选择寄存器,您可以使用“sel”指令,也可以写入寄存器。
当指令进行内存访问时,所选寄存器的值用作内存地址。
在每次内存访问期间,寄存器都会自动递增。
例:
do #0x0200 -> RA5 // writes 0x200 to RA5, and selects it do #0x0300 -> WA7 // writes 0x300 to WA7, and selects it // RA5 and WA7 are both selected do @ -> @ // copies the value from memory location 0x200 to memory location 0x300 // now RA5=0x210 and WA7=0x310 do WA7 + #0x20 -> RA6 // RA6 is now selected with the value 0x330 do @ -> A // memory location 0x330 is read and copied to the accumulator sel RA5 // re-select RA5. Since it was non-persistent, its value is back to 0x0200 (see later for explanation on persistent registers)
寄存器 RAxx/WAxx 还可用于存储器访问以外的其他目的。 它们具有与累加器几乎相同的功能,因此可以递增、添加、异或编辑......
它们还可用于子例程参数传递。
内存空间
使用 Spoc0 时,每个寄存器 WAxx 和 RAxx 的宽度为 16 位 (.word),因此它可以寻址 64K。
数据空间是“位可寻址”的。 因此,您可以在没有任何对齐限制的情况下访问它的任何部分。 例如,可以将一个 32 位值写入寻址0xABCD,然后在地址0xABC3写入另一个值,最后从地址0xABC7读取(读取两个值的一部分)。
代码空间通常无法直接访问。 它用于保存要执行的指令。 但是有一个钩子可以访问代码空间,这样你就可以存储表、数据、字符串......
Spoc0 使用寄存器 CS 访问代码空间。
例:
do #GreetingString -> CS // CS points to a string, and selects CS do.byte @ -> A // read the "H" into the accumulator ... GreetingString: data.byte "Hello world!", 13
另一个保留值是“$”。 它代表实际的PC(程序代码位置,当前执行指令的位置)。 这是一个只读值。
例:
jmp $ // forever loop (if you want spoc to "die")
当您使用 blockram 时(如在 Spoc0 中),代码空间是位可寻址的,如果使用串行闪存,则代码空间是字节可寻址的。 这为 Spoc64 提供了 0Kbits 的空间,而使用串行闪存时则为 64KB 的空间。
预留空间
Spoc0 使用一个块 (4Kbits) 作为数据空间,它填充0x0000到0x0FFF的地址。
在此之上,0x1000 0xFFFF的空间是“外部存储器”。它应该用于连接外部外围设备。
此外,保留 blockram 数据空间的前 1Kbit(地址 0x0000 到 0x03FF)来保存寄存器 RAxx/WAxx 的值。 如果你知道自己在做什么,你仍然可以使用它......
保留寄存器
寄存器 WA30/31 和 RA30/31 是保留的。 不要使用它们。
例如,在 Spoc0 中,寄存器 WA31 用作 PC...所以如果你要使用 WA31,你实际上会跳到某个地方......
持久寄存器
当一个寄存器被取消选择,然后在以后重新选择时,它的值可以是“持久的”(它在最后一次自动递增之后恢复该值),也可以是“非持久的”(它恢复最初受影响的值,即在任何内存访问/自动递增之前)。
您可以选择是否希望在选择寄存器时将其持久化(通过使用或不使用“.p”后缀)。
例:
do #0x0200 -> RA5 // RA5 is selected (not persistent) do @ -> A // copies the value from memory location 0x200 to accumulator // RA5 value is now 0x210 do #0x0300 -> RA6.p // RA6 is selected (persistent) do @ -> A // copies the value from memory location 0x300 to accumulator // RA6 value is now 0x310 sel RA5.p // re-selects RA5. Since it was non-persistent, its value is back to 0x0200 // note that it is now persistent! do @ -> A // copies the value from memory location 0x200 to accumulator // RA5 value is now 0x210 sel RA6 // re-selects RA6. Since it was persistent, its value is 0x0310 do @ -> A // copies the value from memory location 0x310 to accumulator // RA6 value is now 0x320 sel RA5 // re-selects RA5 (not persistent). But since it was persistent, its value is 0x0210 do @ -> A // copies the value from memory location 0x210 to accumulator // RA5 value is now 0x220
上一篇:
下一篇: