# vspm-ts **Repository Path**: aimaier4869/vspm-ts ## Basic Information - **Project Name**: vspm-ts - **Description**: 非常简单原型机 VSPM 动画演示 - **Primary Language**: TypeScript - **License**: MIT - **Default Branch**: main - **Homepage**: http://sswa.top/vspm-ts - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-09-05 - **Last Updated**: 2024-03-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: CPU, 计算机系统, 操作系统, animejs ## README # 非常简单原型机 VSPM ![](assets/1694416738.png) ## 一、 快速开始 1. 克隆仓库 ```bash git clone https://gitee.com/aimaier4869/vspm-ts.git ``` 2. 进入项目目录并安装依赖 ```bash cd vspm-ts yarn ``` 3. 开始开发 ```bash yarn dev ``` 4. 打包 ```bash yarn build ``` ## 二、开发指南 ### 1. 目录结构 ```bash / ├─public │ initialData.js # ram和reg的初始数据 └─src │ index.ts # 项目入口文件 │ style.css # vite配置 │ vite-env.d.ts ├─assets # 静态资源目录(图片、音频等) │ 0908170018.jpg │ bg.jpg ├─css # 样式文件 │ baseModule.scss # cpu每个组件相关的样式 │ clk.scss # 时钟相关的样式 │ common.scss # 通用样式,如按钮、模态框等 │ index.scss # 样式入口文件,页面布局相关的样式 │ shape.scss # 页面中的黑色箭头相关样式 └─ts # ts文件 │ clk.ts # 时钟控制逻辑 │ index.ts # 入口文件 │ init.ts # 初始化所有配置、组件、页面交互逻辑等 │ types.ts # 项目中通用的类型声明 ├─config # 配置文件 │ edgeConfig.ts # 边沿配置,上升沿下降沿持续时常,页面中波形的长度等 │ runtimeConfig.ts # 用来程序运行后控制页面中的一些元素的变量,比如按钮的显示隐藏、禁止点击按钮等 │ sizeConfig.ts # 尺寸信息、每个组件在画布中的尺寸及位置 ├─dom # 交互相关逻辑 │ listener.ts # 事件注册,如各种按钮的点击事件等 │ tooltip.ts # tooltip相关逻辑 ├─module # 组件相关逻辑 │ au.ts │ bus.ts │ conSignal.ts │ decoder.ts │ index.ts │ input.ts │ ir.ts │ mutiplexer21.ts │ mutiplexer31.ts │ output.ts │ pc.ts │ psw.ts │ ram.ts │ reg.ts │ sm.ts ├─shape # 画图相关逻辑 │ arrow.ts # 画箭头,已废弃,换成:黑色箭头使用css画,白色箭头单独画 │ bus.ts # 画总线、白色箭头等 │ edge.ts # 画时钟波形 └─utils # 工具函数 index.ts # 通用的工具函数集合 logger.ts # 记录器,用来记录一个周期结束后的组件状态 ``` ### 2. 生命周期 ![](./assets/flow.drawio.svg) #### 2.1 初始化 首先初始化所有内容,从`src/ts/init.ts`开始 ```ts // src/ts/init.ts /** * 初始化 */ function init() { /** * 1. 首先初始化每个组件的尺寸信息 */ initSizeConfig() /** * 2. 初始化边沿配置,如波形的长度、上升沿下降沿的持续时间等 */ initEdgeConfig() /** * 3. 画总线的图形,根据尺寸配置画出总线的图形 */ initBusShape() /** * 4. 画出上升沿和下降沿的波形图,根据边沿配置画出波形图 */ initEdge() /** * 5. 初始化每个组件 */ for (const module of modules) { /** * 5.1 初始化组件 */ module.init() /** * 5.2 给组件状态的图标添加点击事件,组件的状态是可以通过点击图标来切换的 */ // @ts-ignore if (module.state) { // @ts-ignore registerBoolClickEvent(module) } } /** * 6. 注册事件 */ initDomListener() /** * 7. 初始化tooltip */ initTooltip() } ``` #### 2.2 组件 1. 初始化完成后,根据不同的点击事件就可以处理不同的逻辑了。每个组件逻辑文件中都导出了一个对象,这个对象就表示该组件。每个组件对象的类型如下: ```ts // src/ts/types.ts /** * 模块对象 */ export interface IModule { /** * 模块名称 */ name: ModuleKeyType /** * 初始化函数 */ init: () => void /** * 当状态发生变化时执行的函数 */ change?: () => Promise /** * 下降沿时执行的函数 */ neg?: () => Promise /** * 上升沿时执行的函数 */ rise?: () => Promise } /** * 含有状态的模块 */ export interface IModuleWithState extends IModule { /** * 模块状态 */ state: K /** * 设置模块状态的函数 */ setState: SetStateFunc } ``` 2. 以 pc 模块为例: ```ts // src/ts/module/pc.ts /** * pc模块 */ export const pc: IPc = { name: 'pc', state: { in_pc: 1, ld_pc: 0, addr: 0, }, async setState(prop, value) { // 根据prop改变state中的值 }, async neg() { // 在时钟下降沿触发时pc模块要做到事情以及页面中相关的动画 }, init() { // 做一些初始化操作 }, } ``` pc 模块中提供的 `init` 方法会在初始化时调用,`neg` 方法在时钟下降沿时被时钟调用。 #### 2.3 时钟 跟普通模块同理,在 `src/ts/clk.ts` 中导出了一个时钟对象,通过该对象可以操控时钟。 它提供了一个比较关键的函数 `step`,即下一步,该函数做的事情非常简单,找到所有模块的与当前周期对应的函数,然后调用,调用完成后把时钟里的周期指针往后移动。 理解了 `step` 函数后,理解其他函数就很简单了,`run` 函数就是不停的调用 `step`,`neg`、`rise`这两个函数就是调用所有模块的对应函数。 ```ts // src/ts/clk.ts export const clk: IClk = { // 周期 cycles: ['low', 'rise', 'high', 'neg'], // 是否正在运行 running: false, // 当前周期的索引 current: { _value: 0, val() { return this._value }, async get() { return this._value }, async set(v) { if (v > 3) { v = v - 4 } this._value = v return true }, }, // 执行当前 async step() { // 获取当前周期的索引 const currentIndex = this.current.val() // 获取当前周期 const currentCycle = this.cycles[currentIndex] const peomiseArr: Promise[] = [] // 调用所有模块的当前周期函数 for (const element of modules) { if (element[currentCycle]) { peomiseArr.push(element[currentCycle]()) } } await Promise.all(peomiseArr) let step = 1 if (currentCycle == 'neg' || currentCycle == 'rise') { step = 2 } // 指向下一个周期 await this.current.set(currentIndex + step) return }, // 手动触发一次上升沿 async rise() { const peomiseArr: Promise[] = [] // 调用所有模块的rise函数 for (const element of modules) { if (element['rise']) { peomiseArr.push(element['rise']()) } } await Promise.all(peomiseArr) }, // 手动触发一次下降沿 async neg() { const peomiseArr: Promise[] = [] // 调用所有模块的neg函数 for (const element of modules) { if (element['neg']) { peomiseArr.push(element['neg']()) } } await Promise.all(peomiseArr) }, // 运行 async run() { this.running = true await this.step() if (this.running) { this.run() } }, // 停止 stop() { this.running = false }, } ```