我用JavaScript (source)写了一个Chip-8模拟器,并制作了可播放的浏览器版本here。
它包含一个HTML选择:
<select>
<option value="PONG">PONG</option>
<option value="TETRIS">TETRIS</option>
</select>
每次选择从文件中加载ROM的方式:
document.querySelector('select').addEventListener('change', event => {
const rom = event.target.value
loadRom(rom)
})
此loadRom
函数获取ROM,将其转换为有用的形式,然后将其加载到CPU类的实例中。 cpu
有一个获取,解码,执行周期,该周期由step()
调用。我创建了此cycle
(主要)函数以在setTimeout中对其进行调用。
const loadRom = async rom => {
const response = await fetch(`./roms/${rom}`)
const arrayBuffer = await response.arrayBuffer()
const uint8View = new Uint8Array(arrayBuffer);
const romBuffer = new RomBuffer(uint8View)
cpu.interface.clearDisplay()
cpu.load(romBuffer)
let timer = 0
async function cycle() {
timer++
if (timer % 5 === 0) {
cpu.tick()
timer = 0
}
await cpu.step()
setTimeout(cycle, 3)
}
cycle()
}
这很好,直到我用select加载新的ROM。现在,循环变得更加复杂,游戏的运行速度提高了一倍。每次加载新的ROM时,它都会再次复合并创建一个新的循环。
我如何创建一个无限循环,但要停止它并开始一个全新的循环而又不加扰?
答案 0 :(得分:1)
首先,使当前超时为持久变量,然后在调用clearTimeout
之前立即使用它来调用loadRom
。如果尚未加载任何内容,则clearTimeout
不会执行任何操作。
但是因为您也有await
,所以您需要检查在await
进行期间是否装入了新的rom。实现此目的的一种方法是使用另一个持久变量,使用当前的romBuffer
-如果它与函数闭包中的romBuffer
不同,则另一个rom已启动,因此请立即返回(并且不要递归地创建超时)。
let timeout;
let currentRomBuffer;
const loadRom = async rom => {
const response = await fetch(`./roms/${rom}`)
const arrayBuffer = await response.arrayBuffer()
const uint8View = new Uint8Array(arrayBuffer);
const romBuffer = new RomBuffer(uint8View)
currentRomBuffer = romBuffer;
cpu.interface.clearDisplay()
cpu.load(romBuffer)
let timer = 0
async function cycle() {
timer++
if (timer % 5 === 0) {
cpu.tick()
timer = 0
}
await cpu.step();
if (romBuffer !== currentRomBuffer) {
return;
}
timeout = setTimeout(cycle, 3);
}
cycle()
};
答案 1 :(得分:0)
尝试使用setInerval
并跟踪手柄。
然后在加载新(或相同)ROM时将其清除。
const loadRom = async rom => {
const response = await fetch(`./roms/${rom}`)
const arrayBuffer = await response.arrayBuffer()
const uint8View = new Uint8Array(arrayBuffer);
const romBuffer = new RomBuffer(uint8View)
cpu.interface.clearDisplay()
cpu.load(romBuffer)
// if rom is loaded, clear it!
if(this.lastLoad)
clearInterval(this.lastLoad);
let timer = 0
async function cycle() {
timer++
if (timer % 5 === 0) cpu.tick()
await cpu.step()
}
// keep track the handle.
this.lastLoad = setInterval(cycle, 3);
}