应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

用JavaScript编写8位机仿真器

  WhatisChip8?什么是8位机
  在开始这个项目之前,我从未听说过8位机,所以我想大多数人也没有听说过,除非他们已经进入了模拟器。Chip8是一种非常简单的解释型编程语言,于20世纪70年代为业余计算机开发。人们编写了基本的Chip8程序,模仿当时流行的游戏,如Pong,俄罗斯方块,太空侵略者,可能还有其他独特的游戏,失去了时间的歼灭。
  玩这些游戏的虚拟机实际上是Chip8解释器,而不是技术上的模拟器,因为模拟器是模拟特定机器硬件的软件,而Chip8程序不与任何特定硬件相关联。通常,Chip8解释器用于图形计算器。
  尽管如此,它已经足够接近于成为模拟器,对于任何想要学习如何构建模拟器的人来说,它通常是开始的项目,因为它比创建NES模拟器或除此之外的任何东西都要简单得多。对于许多CPU概念来说,这也是一个很好的起点,比如内存、堆栈和IO,这些都是我在JavaScript运行时无限复杂的世界中每天要处理的事情。WhatGoesIntoaChip8Interpreter?8位机解释器
  我必须做很多预习才能开始理解我正在做什么,因为我以前从未学习过计算机科学的基础知识。所以我写了理解位,字节,基,并在JavaScript中编写十六进制转储,其中大部分内容。
  总而言之,该文章有两个主要要点:位和字节位是二进制数字0或1,真或假,开或关。八位是一个字节,它是计算机使用的基本信息单位。数字基数十进制是我们最常处理的基数系统,但计算机通常使用二进制(基数2)或十六进制(基数16)。二进制的1111、十进制的15和十六进制的f都是相同的数字。
  CPU是执行程序指令的计算机的主处理器。在这种情况下,它由下面描述的各种状态位以及包含获取,解码和执行步骤的指令周期组成。Memory内存Programcounter计算计算器Registers寄存器Indexregister寄存器索引Stack栈Stackpointer栈指针Keyinput输入Graphicaloutput图像输出Timers时钟Memory内存
  8位计算机,可以访问高达4千字节的内存(RAM)。(这是软盘上存储空间的0。002。CPU中的绝大多数数据都存储在内存中。
  4kb是4096字节,JavaScript有一些有用的类型化数组,比如Uint8Array,它是某个元素的固定大小的数组在这种情况下是8位。letmemorynewUint8Array(4096)
  您可以像访问和使用此数组一样访问和使用此数组,从内存〔0〕到内存〔4095〕,并将每个元素设置为最大255的值。任何高于此值的内容都将回退到该值(例如,内存〔0〕300将导致内存〔0〕255)。
  Programcounter程序计数器
  程序计数器将当前指令的地址存储为16位整数。Chip8中的每条指令都将在完成后更新程序计数器(PC),以进入下一条指令,方法是访问以PC为索引的存储器。
  在8位机内存布局中,内存中0x1FF0x000是保留的,因此它从0x200开始。letPC0x200memory〔PC〕willaccesstheaddressofthecurrentinstruvtion
  您会注意到内存阵列是8位的,而PC是16位整数,因此两个程序代码将被组合成一个大的字节序操作码。Registers寄存器
  存储器通常用于长期存储和程序数据,因此寄存器作为一种短期存储器存在,用于即时数据和计算。8位机有16个8位寄存器。它们被称为V0到VF。letregistersnewUint8Array(16)Indexregister索引寄存器
  有一个特殊的16位寄存器,用于访问内存中的特定点,称为I。I寄存器通常用于读取和写入内存,因为可寻址内存也是16位的。letI0Stack栈
  8位机能够进入子例程,以及一个用于跟踪返回位置的堆栈。堆栈是16个16位值,这意味着程序在经历堆栈溢出之前可以进入16个嵌套的子例程。letstacknewUint16Array(16)Stackpointer栈指针
  堆栈指针(SP)是一个8位整数,指向堆栈中的某个位置。即使堆栈是16位,它也只需要是8位,因为它只引用堆栈的索引,因此只需要0彻底15。letSP1stack〔SP〕willaccessthecurrentreturnaddressinthestackTimers定时器
  8位机能够发出光荣的一声蜂鸣声。说实话,我没有费心为音乐实现实际输出,尽管CPU本身都设置为与它正确接口。有两个计时器,都是8位寄存器一个声音计时器(ST)用于决定何时发出蜂鸣声,一个延迟计时器(DT)用于在整个游戏中对某些事件进行计时。它们在60Hz下倒计时。letDT0letST0Keyinput输入
  8位机的设置是为了与惊人的十六进制键盘接口。它看起来像这样:123C456D789EA0BF
  在实践中,似乎只使用了几个键,你可以将它们映射到你想要的任何4x4网格,但它们在游戏之间非常不一致。Graphicaloutput图形输出
  8位机使用单色64x32分辨率显示器。每个像素要么打开,要么关闭。
  可以保存在内存中的精灵为8x158个像素宽x15个像素高。Chip8还附带了字体集,但它只包含十六进制键盘中的字符,因此总体上不是最有用的字体集。CPU
  将它们放在一起,您将获得CPU状态。
  CPUclassCPU{constructor(){this。memorynewUint8Array(4096)this。registersnewUint8Array(16)this。stacknewUint16Array(16)this。ST0this。DT0this。I0this。SP1this。PC0x200}}DecodingChip8Instructions指令解码
  8位机有36条指令。此处列出了所有说明。所有指令的长度均为2个字节(16位)。每条指令都由操作码(操作码)和操作数(操作码)编码,操作数据作。
  如两个操作x1y2ADDx,y
  其中ADD是操作码,x、y是操作数。这种类型的语言称为汇编语言。此指令将映射到:xxy
  使用此指令集,我必须将此数据存储在16位中,因此每个指令最终都是从0x0000到0xffff的数字。这些集合中的每个数字位置都是一个半字节(4位)。
  那么我怎么能从nnnn到像ADDx,y这样的东西,这更容易理解呢?好吧,我将首先查看Chip8中的一条指令,它与上面的例子基本相同:
  Instruction
  Description
  8xy4
  ADDVx,Vy
  那么,我们在这里处理的是什么呢?有一个关键字ADD和两个参数Vx和Vy,我们在上面建立的它们是寄存器。
  如操作指令:ADD(add)SUB(subtract)JP(jump)SKP(skip)RET(return)LD(load)
  数据类型:地址(I)寄存器(Vx,Vy)常数(NorNNfornibbleorbyte)
  下一步是找到一种方法将16位操作码解释为这些更易于理解的指令。BitMasking位操作constopcode0x8124constmask0xf00fconstpattern0x8004constisMatch(opcodemask)patterntrueconstx(0x81240x0f00)81(0x81240x0f00)is100000000inbinaryrightshiftingby8(8)willremove8zeroesfromtherightThisleavesuswith1consty(0x81240x00f0)42(0x81240x00f0)is100000inbinaryrightshiftingby4(4)willremove4zeroesfromtherightThisleavesuswith10,thebinaryequivalentof2constinstruction{id:ADDVXVY,name:ADD,mask:0xf00f,pattern:0x8004,arguments:〔{mask:0x0f00,shift:8,type:R},{mask:0x00f0,shift:4,type:R},〕,}
  Disassemblerfunctiondisassemble(opcode){FindtheinstructionfromtheopcodeconstinstructionINSTRUCTIONSET。find((instruction)(opcodeinstruction。mask)instruction。pattern)Findtheargument(s)constargsinstruction。arguments。map((arg)(opcodearg。mask)arg。shift)Returnanobjectcontainingtheinstructiondataandargumentsreturn{instruction,args}}ReadingtheROM
  由于我们将此项目视为仿真器,因此每个8位程序文件都可以被视为一个ROM。ROM只是二进制数据,我们正在编写程序来解释它。我们可以把Chip8CPU想象成一个虚拟游戏机,而一个8位ROM是一个虚拟游戏卡带。
  RomBuffer。jsclassRomBuffer{param{binary}fileContentsROMbinaryconstructor(fileContents){this。data〔〕ReadtherawdatabufferfromthefileconstbufferfileContentsCreate16bitbigendianopcodesfromthebufferfor(leti0;ibuffer。length;i2){this。data。push((buffer〔i〕8)(buffer〔i1〕0))}}}
  指令周期获取、解码、执行
  现在,我已经准备好解释指令集和游戏数据。CPU只需要对它做点什么。指令周期包括三个步骤获取、解码和执行。Fetch获取数据Decode译码Execute执行
  FetchGetaddressvaluefrommemoryfunctionfetch(){returnmemory〔PC〕}
  DecodeDecodeinstructionfunctiondecode(opcode){returndisassemble(opcode)}
  ExecuteExecuteinstructionfunctionexecute(instruction){const{id,args}instructionswitch(id){caseADDVXVY:Performtheinstructionoperationregisters〔args〔0〕〕registers〔args〔1〕〕UpdateprogramcountertonextinstructionPCPC2breakcaseSUBVXVY:etc。。。}}
  CPU。jsclassCPU{constructor(){this。memorynewUint8Array(4096)this。registersnewUint8Array(16)this。stacknewUint16Array(16)this。ST0this。DT0this。I0this。SP1this。PC0x200}Loadbufferintomemoryload(romBuffer){this。reset()romBuffer。forEach((opcode,i){this。memory〔i〕opcode})}Stepthrougheachinstructionstep(){constopcodethis。fetch()constinstructionthis。decode(opcode)this。execute(instruction)}fetch(){returnthis。memory〔this。PC〕}decode(opcode){returndisassemble(opcode)}execute(instruction){const{id,args}instructionswitch(id){caseADDVXVY:this。registers〔args〔0〕〕this。registers〔args〔1〕〕this。PCthis。PC2break}}}CreatingaCPUInterfaceforIO输入输出
  所以现在我有了这个CPU,它正在解释和执行指令并更新它自己的所有状态,但我现在还不能用它做任何事情。为了玩游戏,你必须看到它并能够与之互动。
  这就是输入输出或IO的用武之地。IO是CPU与外部世界之间的通信。输入是CPU接收的数据输出是从CPU发送的数据
  CpuInterface。jsAbstractCPUinterfaceclassclassCpuInterface{constructor(){if(new。targetCpuInterface){thrownewTypeError(Cannotinstantiateabstractclass)}}clearDisplay(){thrownewTypeError(Mustbeimplementedontheinheritedclass。)}waitKey(){thrownewTypeError(Mustbeimplementedontheinheritedclass。)}getKeys(){thrownewTypeError(Mustbeimplementedontheinheritedclass。)}drawPixel(){thrownewTypeError(Mustbeimplementedontheinheritedclass。)}enableSound(){thrownewTypeError(Mustbeimplementedontheinheritedclass。)}disableSound(){thrownewTypeError(Mustbeimplementedontheinheritedclass。)}}classCPU{Initializetheinterfaceconstructor(cpuInterface){this。interfacecpuInterface}execute(instruction){const{id,args}instructionswitch(id){caseCLS:Usetheinterfacewhileexecutinganinstructionthis。interface。clearDisplay()}}Screen显示
  屏幕的分辨率为64像素宽x32像素高。因此,就CPU和接口而言,它是一个64x32的位网格,这些位要么打开要么关闭。要设置一个空屏幕,我可以制作一个零的3D数组来表示所有关闭的像素。帧缓冲区是内存的一部分,其中包含将呈现到显示器上的位图图像。
  MockCpuInterface。jsInterfacefortestingclassMockCpuInterfaceextendsCpuInterface{constructor(){super()Storethescreendataintheframebufferthis。frameBufferthis。createFrameBuffer()}Create3DarrayofzeroescreateFrameBuffer(){letframeBuffer〔〕for(leti0;i32;i){frameBuffer。push(〔〕)for(letj0;j64;j){frameBuffer〔i〕。push(0)}}returnframeBuffer}Updateasinglepixelwithavalue(0or1)drawPixel(x,y,value){this。frameBuffer〔y〕〔x〕value}}
  在DRW函数中,CPU将循环遍历它从内存中提取的子画面,并更新子画面中的每个像素(为简洁起见,省略了一些细节)。caseDRWVXVYN:Theinterpreterreadsnbytesfrommemory,startingattheaddressstoredinIfor(leti0;iargs〔2〕;i){letlinethis。memory〔this。Ii〕Eachbyteisalineofeightpixelsfor(letposition0;position8;position){。。。Getvalue,x,andy。。。this。interface。drawPixel(x,y,value)}}
  clearDisplay()函数是将用于与屏幕交互的唯一其他方法。这是与屏幕交互所需的所有CPU接口。Keys按键1234QWERASDFZXCVprettierignoreconstkeyMap〔1,2,3,4,q,w,e,r,a,s,d,f,z,x,c,v〕
  按键按下状态。this。keys00b1000000000000000Vispressed(keyMap〔15〕,orindex15)0b00000000000000111and2arepressed(index0,1)0b0000000000110000QandWarepressed(index4,5)caseSKPVX:SkipnextinstructionifkeywiththevalueofVxispressedif(this。interface。getKeys()(1this。registers〔args〔0〕〕)){Skipinstruction}else{Gotonextinstruction}Screen显示器
  对于所有实现,包含屏幕数据位图的帧缓冲区都是相同的,但屏幕与每个环境的接口方式将不同。
  带着祝福,我只是定义了一个屏幕对象:this。screenblessed。screen({smartCSR:true})
  并在像素上使用fillRegion或clearRegion,并使用完整的unicode块进行填充,使用帧缓冲区作为数据源。drawPixel(x,y,value){this。frameBuffer〔y〕〔x〕valueif(this。frameBuffer〔y〕〔x〕){this。screen。fillRegion(this。color,,x,x1,y,y1)}else{this。screen。clearRegion(x,x1,y,y1)}this。screen。render()}Keys按键
  按键处理程序与我对DOM的期望没有太大区别。如果按下某个键,处理程序将传递该键,然后我可以使用该键查找索引并使用已按下的任何其他新键更新keys对象。this。screen。on(keypress,(,key){constkeyIndexkeyMap。indexOf(key。full)if(keyIndex){this。setKeys(keyIndex)}})setInterval((){Emulateakeyupeventtoclearallpressedkeysthis。resetKeys()},100)Entrypoint入口点
  terminal。jsterminal。jsconstfsrequire(fs)const{CPU}require(。。classesCPU)const{RomBuffer}require(。。classesRomBuffer)const{TerminalCpuInterface}require(。。classesinterfacesTerminalCpuInterface)RetrievetheROMfileconstfileContentsfs。readFileSync(process。argv。slice(2)〔0〕)InitializetheterminalinterfaceconstcpuInterfacenewTerminalCpuInterface()InitializetheCPUwiththeinterfaceconstcpunewCPU(cpuInterface)ConvertthebinarycodeintoopcodesconstromBuffernewRomBuffer(fileContents)Loadthegamecpu。load(romBuffer)functioncycle(){cpu。step()setTimeout(cycle,3)}cycle()

日本女演员山本麻衣的时尚时刻女演员山本麻衣现身在代代木国立体育场第一体育馆(东京都涩谷区)举行的大型时尚活动第30届MynaviTokyoGirlsCollection(TGC)2020SPRINGSUM……烟台VS徐州究竟谁更胜一筹?烟台山东省地级市,2021年GDP为8711。8亿元。徐州江苏省地级市,2021年GDP为8117。4亿元。单论GDP很明显烟台更胜一筹。烟台面积大约13864。5km2人口大……最冷世界杯巴西葡萄牙今夜登场,32强全部亮相,还会更冷吗?冷门跌爆的世界杯,今夜会再度迎来意外吗?北京时间今夜明晨,世界杯小组赛迎来首轮最后四场比赛:葡萄牙对阵加纳,五星巴西迎战塞尔维亚,瑞士对阵喀麦隆,乌拉圭对阵韩国。截……走心文案,触碰内心深处01hr不要羡慕时间自由的人因为别人在用你看不懂的方式赚取你想象不到的财富门外的人永远不知道他错过了什么门里的人永远懊悔进来得太晚。。。。。。02……华为Mate50系列被爆支持北斗短报文,抢先苹果实现卫星通信9月6日,华为将举办Mate50系列及全场景秋季新品发布会。今天(9月2日),华为官方发布了常务董事、终端BGCEO、智能汽车解决方案BUCEO余承东的快问快答视频,透露了关于……2022世界杯足球预测分析今年的世界杯。比较特别,如果是往届已经踢完了。由于地理位置的特殊性,举办日期也是变到了11月份,因为卡塔尔的夏季4050度的高温让球员容易中暑受不了,感觉欧洲的人这边踢球会有点……不和谐!杜锋为付豪赵继伟庆生,14大巨星中只有郭艾伦直接消失北京时间8月26日,中国男篮世预赛面对哈萨克斯坦的比赛,虽然赢下对手12分,但是比赛的内容真的很丑陋。不过在赛后男篮也是迎来了喜讯,杜锋亲自带队给付豪、赵继伟庆祝生日,结……在津巴布韦发现非洲最古老的恐龙骨骼一个国际古生物学家团队发现了他们认为是在非洲发现的最古老的恐龙骨架。据弗吉尼亚理工大学周三发布的消息,在津巴布韦北部发现了名叫拉希姆比龙(Mbiresaurusraathi)的……地球上大陆的形成,与陨石撞击有关?大陆的形成地球是目前已知的唯一拥有大陆的行星,这些巨大的陆地为人类以及地球上众多的其他生命提供了家园。然而,一些与大陆有关的基本问题,却仍然没有确切答案。例如,它们是如何……人民币兑美元汇率大涨与股市上涨对我国及世界经济的影响11月14日,人民币兑美元中间价为7。0899,上调1008个基点,中间价升值至2022年9月27日以来最高,人民币兑美元汇率大涨,升幅创2005年7月22日以来最大。近……最重要的从来不是行为,而是念头上次写了我多年前在厦门无意玩买了花茶被坑的事:1。2元一克觉得便宜,最后换算发现是600一斤。好多朋友跑来和我说遇到过同样的事,但基本都是选择了买了算了,毕竟东西都装到袋子里了……治愈生活的18件小事(值得收藏)知乎上有个问答:请用一个字来形容你现在的生活状态。很多人的回答是:累。现如今,快节奏、高压力的生活,让很多人如同上紧了发条的时钟一般,终日处在忙碌和焦虑之中。……
华为天才少年稚晖君确认离职本人声明不会做全职up近段时间,有传言称华为天才少年稚晖君已然离职,12月27日,其本人在微博更新状态,确认了此事。稚晖君还表示,接下来会开启一段新的事业,去做更有挑战的事情,同时他也否认做全……闲时透过杯子,窥探生活从前,我特别爱囤杯子,像陶瓷杯,玻璃杯,塑料杯等,喜欢它们别出心裁的设计,还有各种可爱的造型。我在上高中时,那时同桌的女生送我个陶瓷杯,软胶的盖子轻轻一提就能打开,雪人的……新人如何在TikTok上开启第一场直播?小白要在TikTok开启第一场直播,其实并不难,一部手机就可以啦01找对标账号都说最好的老师是竞争对手,那么你就新注册一个全新的账号去搜索TikTok的直播。……广东9985辽宁还有惊喜,杜锋用活周鹏接班人,20岁新星横空9985,本赛季首场辽粤大战,最终广东队还是笑到了最后,以14分的优势战胜辽宁队,或许出乎了不少球迷的预料。毕竟,对于辽宁队来说,上赛季五次面对广东队,实现横扫。没想到再次碰面……大步迈进,一路向前人生漫长,前方的路崎岖不平,有时甚至还是一道深深的沟壑。我们蹒跚地走着,向上爬着,只渴望能走过这条道,跨过这道坎。虽然不像贝多芬拥有着斗争命运的勇气与决心,但我们需要怀着……马卡评最激动人心冬窗C罗领衔菲利克斯苏亚雷斯等在列直播吧12月26日讯随着卡塔尔世界杯的结束,一月的冬季转会市场即将开放,《马卡报》撰文分析了包括C罗、菲利克斯、苏亚雷斯、伊斯科在内等多名球星的未来去向。C罗(自由身)……百億百益健康讲堂衰老坚持特定生活方式,8周年轻3岁近期,著名的《衰老》杂志刊登了一项关于特定生活方式对生物年龄影响的研究。他们选取了43位年龄在5072岁之间的健康成年男性,进行了为期8周的生活方式干预,发现如果能坚持健康的生……12个交易日8个涨停,彩虹集团电热毯在手订单约133万元【大河报大河财立方】(记者吴春波)10月14日,电热毯概念股彩虹集团涨停板行情结束,并于当日收跌3,报40。7元股,对应市值42。87亿元。此前一日,在其交易所问询函回复……果然还是十三香,小米13明日首销,或遭遇老问题都说十三香,如果对比iPhone13和iPhone14的话,你会觉得iPhone13的升级还是很有诚意的。如果现在你说买iPhone13还是iPhone14,我肯定首推iPho……真不讨喜新版PSPlus使订阅用户减少近两百万人次PlayStation今年陆续于各国推出了全新的PSPlus订阅规则,不过显然有许多玩家并不喜欢新的订阅方案,因为根据官方最新财务报告显示,光是今年7月至9月期间,PSPlus……五菱AirEV定名晴空续航300km或于年内上市2022年11月2日,五菱汽车正式公布五菱首款新能源全球车Airev的中文命名,新车定名晴空,提供白、蓝、灰、咖四种车身颜色供消费者选择。此前,AirEV已于印度尼西亚上……中国联通回应与腾讯设立混改新公司中国联通回应与腾讯设立混改新公司不要过多解读中国联通和腾讯的混改,也不要将其与当年的公私合营联系起来。肯定不是收购民企的事情,是国企和民企的正常合作,也是这么多年来的惯例。……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网