本文最后更新于:June 30, 2023 pm
本文主要讲了如何设计一个简单的虚拟机
OHVM 虚拟机设计规范
简介
这是我为了学习虚拟机原理和计算机程序运行原理,写的一个很简单的虚拟机demo,可以运行简单的字节码,当然如果我有时间和精力写出来相对完整点的版本,说不定还能搞个小玩具。
OHVM 的含义是 “Only for Happy VM”,就是作者纯粹没有任何其他目的为了开心的产物。该设计本质是为了学习计算机技术,其没有任何生产和使用价值。
基础规范
内存范围
默认内存设定大小为 0-4KB,可以调整参数来扩大。
寄存器分布
一共有 293 个个寄存器.
栈
一共有8层栈空间,每一层有32个寄存器。
虚拟机模型
内存分布
- 程序地址:
程序从 0x0000H 开始。 - 入口地址:
程序入口地址默认是 0x0000H。
注意
0x0H 是一个绝对地址, 真实地址是:1025.
寄存器分布
Those address can’t be accessed by user program.
Name | Symbol | Size | Address(Decimal) |
---|---|---|---|
NullPtr Register | NULL | 1byte | ROM: 0 - 1 |
VM Flag Register | FLAG | 1byte | ROM: 1 - 2 |
Addressing Register | A | 4byte | ROM: 3 - 5 |
ACCumulator | ACC | 4byte | ROM: 6 - 9 |
Program counter | PC | 4byte | ROM: 10 - 13 |
Stack index pointer | SP | 4byte | ROM: 14 - 17 |
Stack deepth pointer | SD | 4byte | ROM: 18 - 21 |
Exception Register | EXCPT | 4byte | ROM: 22 - 25 |
General Register | R{0…31} | 4byte(per) | ROM: 25 - 152 |
Stack Register | S{0…7} | 4byte(per) | ROM: 153 - 378 |
标志位寄存器
Name | Symbol | Size |
---|---|---|
Zero | ZE | 1bit |
Equal | EQ | 1bit |
Execption | EX | 1bit |
Reserved | – | 1bit |
Reserved | – | 1bit |
Reserved | – | 1bit |
Reserved | – | 1bit |
Reserved | – | 1bit |
指令集
Instruction Code | Symbol | Example | Function |
---|---|---|---|
0X0000 | NOP | NOP | No operation, used for delay |
0X0001 | START | START | Start ohvm |
0X0002 | STOP | STOP | Stop ohvm |
0X0003 | RSTR | RSTR | Reset all Register |
0X0004 | TIMER [Rn] | MOVR R0 #5 TIMER R0 |
Start Timer |
0X0005 | GOTO [Rn]/[Label] | GOTO LOOP GOTO R0 |
GOTO ADDress |
0X0006 | CALL [Rn]/[Label] | CALL LOOP CALL R0 |
CALL SUB process |
0X0007 | BACK | BACK | RETURN from SUB process |
0X0008 | JMP [Rn]/[Label] | JMP LOOP | JUMP Address With No Condition |
0X0009 | JZ [ACC] [Rn]/[Label] | JZ LOOP | Compare with ACC, Jump Address if Zero Flag == 1 |
0X000A | JE [ACC] [Rn]/[Label] | JE LOOP | Compare with ACC, Jump Address if Equal Flag == 1 |
0X000B | JX [ACC] [Rn]/[Label] | JX LOOP | Compare with ACC, Jump Address if Excepion Flag == 1 |
0X000C | CMRAE [Rn] | MOV R0 #1 CMRAE |
COMPARE R{x} if equal ACC |
0X000D | CMRAG [Rn] | MOV R0 #1 CMRAG |
COMPARE R{x} if greater than ACC |
0X000E | CMRAL [Rn] | MOV R0 #1 CMRAL |
COMPARE R{x} if little than ACC |
0X000F | CMRRE [Rn] [Rn] | MOV R0 #1 MOV R1 #1 CMRRE R0 R1 |
COMPARE R{x} if equal R{x} |
0X0010 | CMRRG [Rn] [Rn] | MOV R0 #1 MOV R1 #1 CMRRG R0 R1 |
COMPARE R{x} if greater than R{x} |
0X0011 | CMRRL [Rn] [Rn] | MOV R0 #1 MOV R1 #1 CMRRL R0 R1 |
COMPARE R{x} if little than R{x} |
0X0012 | CMASE [Rn] [Sn] | CMASE R0 S0 | COMPARE ACC if equal than Stack |
0X0013 | CMASG [Rn] [Sn] | CMASG R0 S0 | COMPARE ACC if greater than Stack |
0X0014 | CMASL [Rn] [Sn] | CMASL R0 S0 | COMPARE ACC if little than Stack |
0X0015 | CMRSE [Rn] [Sn] | CMRSE R0 S0 | COMPARE R{x} if little than Stack |
0X0016 | CMRSG [Rn] [Sn] | CMRSG R0 S0 | COMPARE R{x} if greater than Stack |
0X0017 | CMRSL [Rn] [Sn] | CMRSL R0 S0 | COMPARE R{x} if little than Stack |
0X0018 | INCA | INCA | INCREASE ACC |
0X0019 | DECA | DECA | DECREASE ACC |
0X001A | INCR [Rn] | INCR R0 | INCREASE Register{x} |
0X001B | DECR [Rn] | DECR R0 | DECREASE Register{x} |
0X001C | ADDAR [Rn] | MOV R0 #1 ADDAR R0 |
ADD ACC and Register{x} |
0X001D | SUBAR [Rn] | MOV R0 #1 SUBAR R0 |
SUB ACC and Register{x} |
0X001E | INCS | INCS | INCREASE Stack |
0X001F | DECS | DECS | DECREASE Stack |
0X0020 | ADDAS [Sn] | ADDAS S0 | ADD ACC and Stack |
0X0021 | SUBAS [Sn] | SUBAS S0 | SUB ACC and Stack |
0X0022 | ANDR [Rn] | MOV R0 #1 ANDR R0 |
ACC AND Register{x} |
0X0023 | AOR [Rn] | MOV R0 #1 AOR R0 |
ACC OR Register{x} |
0X0024 | AXR [Rn] | MOV R0 #1 AXR R0 |
ACC XOR Register{x} |
0X0025 | BSLR [Rn] | MOV R0 #1 BSLR R0 |
BIT Shift left in Register{x} |
0X0026 | BSRR [Rn] | MOV R0 #1 BSRR R0 |
BIT Shift right in Register{x} |
0X0027 | BSLLR [Rn] | MOV R0 #1 BSLLR R0 |
BIT Shift left loop in Register{x} |
0X0028 | BSRLR [Rn] | MOV R0 #1 BSRLR R0 |
BIT Shift right loop in Register{x} |
0X0029 | ANDS [Sn] | ANDS S0 | ACC AND Stack |
0X002A | AOS [Sn] | AOS S0 | ACC OR Stack |
0X002B | AXS [Sn] | AXS S0 | ACC XOR Stack |
0X002C | BSLS [Sn] | BSLS S0 | BIT Shift left in Stack |
0X002D | BSRS [Sn] | BSRS S0 | BIT Shift right in Stack |
0X002E | BSLLS [Sn] | BSLLS S0 | BIT Shift left loop in Stack |
0X002F | BSRLS [Sn] | BSRLS S0 | BIT Shift right loop in Stack |
0X0030 | IMA #[Hex] | IMA #1 | Immediately value to ACC |
0X0031 | IMR [Rn] #[Hex] | IMR R0 #1 | Immediately value to Register |
0X0032 | IMS [Sn] #[Hex] | IMS S1 #1 | Immediately value to Stack |
0X0033 | GET [Rn] | GET R1 | Get value from address |
0X0034 | MVRR [Rn] [Rn] | MVRR R0 R0 | MOVE Register value to another Register{x} |
0X0035 | MVRS [Rn] [Sn] | MVRS R0 S0 | MOVE Register value to Stack |
0X0036 | MVSR [Rn] [Rn] | MVRS S0 R0 | MOVE Stack value to Register |
0X0037 | MVAR [Rn] | MVAR R0 | MOVE ACC value to Register |
0X0038 | INTK | INTK | Wait key interupt |
0X0039 | KEY | KEY | Get key |
0X003A | PLY [Rn] | MOV R0 #1 PLY R0 |
Play dididi sound |
0X003B | PLYS [Rn] | MOV R0 #1 PLYS R0 |
Play a series of sound.Usually have a frequence |
0X003C | GSET [Rn] | MOV R0 #1 GSET R0 |
Graphics resolution setting, [64X32 - 640X320] |
0X003D | CLS | CLS | Clear screen |
0X003E | DPXY [Rn] | MOV R0 #1 DPXY R0 |
Draw 8 * x pixels at (x, y) |
0X003F | DXY [Rn] | MOV R0 #1 DXY R0 |
Draw point at (x, y) |
0X0040 | DCXY [Rn] | MOV R0 #1 DCXY R0 |
Draw char at (x, y) |
0X0041 | DLINE [Rn] | MOV R0 #1 DCXY R0 |
Draw line |
0X0042 | DRECT [Rn] | MOV R0 #1 DCXY R0 |
Draw rectangle |
0X0043 | SCRU | SCRU | Screen scrool up x pixel |
0X0044 | SCRD | SCRD | Screen scrool down x pixel |
0X0045 | SCRL | SCRL | Screen scrool left x pixel |
0X0046 | SCRR | SCRR | Screen scrool right x pixel |
0X0047 | SET [Rn] | SET R0 | Set value to ram |
汇编规范
关键字
- MAIN: 入口标识符
- STOP: 直接退出虚拟机
- BEGIN: 子程序声明标识符
- END: 子程序标签
注意:
关键字不可用于其他地方!
汇编格式
- 入口
用MAIN
标记入口,一个程序必须有一个MAIN
。 - 子程序
用BEGIN {子程序名}: [statement] END
来声明一个子程序,例如声明一个输出字符的子程序:BEGIN OUTPUT_CHAR: ;; END
- 退出
退出用STOP
标签标记,程序只要到STOP
,立即结束所有运行。STOP
- 注释
使用双分号注释:;;
;;这是一行注释
汇编案例
;; Example program
;; main
MAIN:
IA 0 ;; ACC = 0
IR R0 #1 ;; R0 = 1
ADDAR R0 ;; ACC = ACC + R0
CALL DISPLAY ;; Call display
STOP ;;
END
;; display sub process
BEGIN DISPLAY:
DCXY 40, 50, ACC ;; Display value in ACC
END
异常处理
按理来说汇编不应该有异常处理这种设计的,但是我想了下异常处理的本质也是一个 JMP
,于是我自作聪明的设计了这个语法:TRY ... CATCH
.
TRY
;; ....
;; ....
;; ....
CATCH ${Address}
捕获异常时,将设置 EX
寄存器设置为1,并且跳转到 CATCH
指定的代码位置处。
字节码规范
如图:
OHVM Lang
假设已经设计好了一个高级语言叫:OHVM Lang: OHVM Lang。
编译器
我现在技术不到家,写不出来高级语言编译器,本来打算设计一个简单的编译器,把 C 或者是 BASIC 这种基础语法编译成字节码,但是我水平太菜了,搞不定,于是这里只能手动来写字节码了。
如何编译
编译流程:
┌───────────────────────┐
│ BASIC Source code │
│ │
└──────────┬────────────┘
│
▼
┌───────────────────────┐
│ OHVM ASM code │
│ │
└──────────┬────────────┘
│
┌──────────▼────────────┐
│ │
│ OHVM Byte code │
└───────────────────────┘
假设编译器已经实现好了,应该按照下面的方式来使用:
# 要编译这个文件:hello_world.oohvms
oohvmcc hello_world.oohvms
# 编译完成后,应该有下面这几个文件:
# hello_world.oohvmasm hello_world.oohvmbc
oohvms 为源代码文件;oohvmasm 为汇编代码;oohvmbc 为字节码文件
任务规划
- 基础指令集设计
- 项目结构设计
- 虚拟机内核设计
- 汇编器设计
- 高级程序语言设计
- 程序编译器设计
资源链接
- Blog: https://openlab.ezlinker.cn
- Mail: cnwwhai@gmail.com
参考
《计算机程序运行原理》
《C语言核心技术》
《Linux内核开发》
YACC,LEX 等基础官方文档(实现编译器相关)
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!