Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const prefix = "!chip8 ";
- const botname = "BONZCH8 (!chip8)";
- let romList = [];
- const MEMORY_SIZE = 4096;
- const DISPLAY_WIDTH = 64;
- const DISPLAY_HEIGHT = 32;
- const PROGRAM_START = 0x200;
- const FONT_START = 0x50;
- let memory = new Uint8Array(MEMORY_SIZE);
- let V = new Uint8Array(16);
- let I = 0;
- let pc = PROGRAM_START;
- let stack = [];
- let delayTimer = 0;
- let soundTimer = 0;
- let display = new Uint8Array(DISPLAY_WIDTH * DISPLAY_HEIGHT);
- let keys = new Array(16).fill(false);
- let running = false;
- let autoRunInterval = null;
- let autoRunSpeed = 1000;
- const fontSet = [
- 0xF0,0x90,0x90,0x90,0xF0, 0x20,0x60,0x20,0x20,0x70, 0xF0,0x10,0xF0,0x80,0xF0, 0xF0,0x10,0xF0,0x10,0xF0,
- 0x90,0x90,0xF0,0x10,0x10, 0xF0,0x80,0xF0,0x10,0xF0, 0xF0,0x80,0xF0,0x90,0xF0, 0xF0,0x10,0x20,0x40,0x40,
- 0xF0,0x90,0xF0,0x90,0xF0, 0xF0,0x90,0xF0,0x10,0xF0, 0xF0,0x90,0xF0,0x90,0x90, 0xE0,0x90,0xE0,0x90,0xE0,
- 0xF0,0x80,0x80,0x80,0xF0, 0xE0,0x90,0x90,0x90,0xE0, 0xF0,0x80,0xF0,0x80,0xF0, 0xF0,0x80,0xF0,0x80,0x80
- ];
- for (let i = 0; i < fontSet.length; i++) memory[FONT_START + i] = fontSet[i];
- const exampleROM = [0x00, 0xE0, 0x60, 0x00, 0x61, 0x00, 0xA2, 0x02, 0xD0, 0x11, 0x70, 0x08, 0x30, 0x40, 0x12, 0x06];
- function sendMsg(msg) {
- setTimeout(() => {
- socket.emit("talk", { text: msg });
- }, 1100);
- }
- setTimeout(() => { socket.emit("command", { list: ["name", botname] }) }, 1000);
- setTimeout(() => { socket.emit("command", { list: ["name", botname] }) }, 2100);
- setTimeout(() => {
- sendMsg("BONZCH8 is online. The Only Emulator For BonziWorld! Type !chip8 help for commands.");
- setInterval(() => { sendMsg("BONZCH8 is online. The Only Emulator For BonziWorld! Type !chip8 help for commands."); }, 60000);
- }, 3200);
- function loadROM(rom) {
- reset();
- for (let i = 0; i < rom.length; i++) memory[PROGRAM_START + i] = rom[i];
- running = true;
- sendMsg("ROM loaded.");
- }
- function reset() {
- memory.fill(0);
- for (let i = 0; i < fontSet.length; i++) memory[FONT_START + i] = fontSet[i];
- V.fill(0);
- I = 0;
- pc = PROGRAM_START;
- stack = [];
- delayTimer = 0;
- soundTimer = 0;
- display.fill(0);
- keys.fill(false);
- running = false;
- stopAutoRun();
- }
- function stopAutoRun() {
- if (autoRunInterval !== null) {
- clearInterval(autoRunInterval);
- autoRunInterval = null;
- }
- }
- function startAutoRun(intervalMs = autoRunSpeed) {
- stopAutoRun();
- autoRunInterval = setInterval(() => {
- if (!running) {
- stopAutoRun();
- return;
- }
- for (let i = 0; i < 10; i++) cycle();
- sendMsg(renderChip8Braille14x6());
- }, intervalMs);
- }
- function renderChip8Braille14x6() {
- const BRAILLE_WIDTH = 14, BRAILLE_HEIGHT = 6;
- const PIXEL_W = 2, PIXEL_H = 4;
- const VIRTUAL_W = BRAILLE_WIDTH * PIXEL_W;
- const VIRTUAL_H = BRAILLE_HEIGHT * PIXEL_H;
- const scaleX = DISPLAY_WIDTH / VIRTUAL_W;
- const scaleY = DISPLAY_HEIGHT / VIRTUAL_H;
- let out = [];
- for (let by = 0; by < BRAILLE_HEIGHT; by++) {
- let line = "";
- for (let bx = 0; bx < BRAILLE_WIDTH; bx++) {
- let code = 0x2800;
- for (let dy = 0; dy < PIXEL_H; dy++) {
- for (let dx = 0; dx < PIXEL_W; dx++) {
- let vx = bx * PIXEL_W + dx;
- let vy = by * PIXEL_H + dy;
- let chip8x = Math.floor(vx * scaleX);
- let chip8y = Math.floor(vy * scaleY);
- if (chip8x < DISPLAY_WIDTH && chip8y < DISPLAY_HEIGHT) {
- let dot = display[chip8x + chip8y * DISPLAY_WIDTH] ? 1 : 0;
- if (dot) {
- const dotIndex = [
- [0, 1, 2, 6],
- [3, 4, 5, 7]
- ][dx][dy];
- code |= (1 << dotIndex);
- }
- }
- }
- }
- line += String.fromCharCode(code);
- }
- out.push(line);
- }
- return out.join("\n");
- }
- function cycle() {
- let opcode = (memory[pc] << 8) | memory[pc + 1];
- let x = (opcode & 0x0F00) >> 8;
- let y = (opcode & 0x00F0) >> 4;
- let n = opcode & 0x000F;
- let nn = opcode & 0x00FF;
- let nnn = opcode & 0x0FFF;
- pc += 2;
- switch(opcode & 0xF000) {
- case 0x0000:
- if (opcode === 0x00E0) { display.fill(0); }
- else if (opcode === 0x00EE) { pc = stack.pop(); }
- break;
- case 0x1000: pc = nnn; break;
- case 0x2000: stack.push(pc); pc = nnn; break;
- case 0x3000: if (V[x] === nn) pc += 2; break;
- case 0x4000: if (V[x] !== nn) pc += 2; break;
- case 0x5000: if (V[x] === V[y]) pc += 2; break;
- case 0x6000: V[x] = nn; break;
- case 0x7000: V[x] = (V[x] + nn) & 0xFF; break;
- case 0x8000:
- switch (n) {
- case 0x0: V[x] = V[y]; break;
- case 0x1: V[x] |= V[y]; break;
- case 0x2: V[x] &= V[y]; break;
- case 0x3: V[x] ^= V[y]; break;
- case 0x4: {
- let sum = V[x] + V[y];
- V[0xF] = sum > 0xFF ? 1 : 0;
- V[x] = sum & 0xFF;
- } break;
- case 0x5: {
- V[0xF] = V[x] > V[y] ? 1 : 0;
- V[x] = (V[x] - V[y]) & 0xFF;
- } break;
- case 0x6: {
- V[0xF] = V[x] & 0x1;
- V[x] >>= 1;
- } break;
- case 0x7: {
- V[0xF] = V[y] > V[x] ? 1 : 0;
- V[x] = (V[y] - V[x]) & 0xFF;
- } break;
- case 0xE: {
- V[0xF] = (V[x] & 0x80) >> 7;
- V[x] = (V[x] << 1) & 0xFF;
- } break;
- } break;
- case 0x9000: if (V[x] !== V[y]) pc += 2; break;
- case 0xA000: I = nnn; break;
- case 0xB000: pc = nnn + V[0]; break;
- case 0xC000: V[x] = (Math.floor(Math.random() * 0xFF)) & nn; break;
- case 0xD000: {
- let vx = V[x] % DISPLAY_WIDTH;
- let vy = V[y] % DISPLAY_HEIGHT;
- V[0xF] = 0;
- for (let row = 0; row < n; row++) {
- let sprite = memory[I + row];
- for (let col = 0; col < 8; col++) {
- if ((sprite & (0x80 >> col)) !== 0) {
- let xi = vx + col;
- let yi = vy + row;
- if (xi >= DISPLAY_WIDTH || yi >= DISPLAY_HEIGHT) continue;
- let idx = xi + yi * DISPLAY_WIDTH;
- if (display[idx]) V[0xF] = 1;
- display[idx] ^= 1;
- }
- }
- }
- } break;
- case 0xE000:
- if (nn === 0x9E) { if (keys[V[x]]) pc += 2; }
- else if (nn === 0xA1) { if (!keys[V[x]]) pc += 2; }
- break;
- case 0xF000:
- switch (nn) {
- case 0x07: V[x] = delayTimer; break;
- case 0x0A: {
- let key = keys.findIndex(k => k);
- if (key === -1) { pc -= 2; } else { V[x] = key; }
- } break;
- case 0x15: delayTimer = V[x]; break;
- case 0x18: soundTimer = V[x]; break;
- case 0x1E: I = (I + V[x]) & 0xFFF; break;
- case 0x29: I = FONT_START + (V[x] * 5); break;
- case 0x33: {
- memory[I] = Math.floor(V[x] / 100);
- memory[I + 1] = Math.floor((V[x] % 100) / 10);
- memory[I + 2] = V[x] % 10;
- } break;
- case 0x55: for (let i = 0; i <= x; i++) memory[I + i] = V[i]; break;
- case 0x65: for (let i = 0; i <= x; i++) V[i] = memory[I + i]; break;
- } break;
- }
- }
- const help = "__BONZCH8 Commands__\n" +
- "!chip8 help - Show this help\n" +
- "!chip8 load [url|number] - Download a ROM from URL and add it to the list, or load a ROM from the list (e.g. 1)\n" +
- "!chip8 roms - List downloaded ROMs\n" +
- "!chip8 listroms - List downloaded ROMs\n" +
- "!chip8 reset - Reset emulator\n" +
- "!chip8 step - Step one instruction\n" +
- "!chip8 run - Run automatically, displaying every second\n" +
- "!chip8 stop - Stop automatic run, clear display, and halt ROM\n" +
- "!chip8 display - Show display buffer (14x6 braille)\n" +
- "!chip8 key [0-F] [down|up] - Press or release a CHIP-8 key\n" +
- "!chip8 speed <ms> - Set auto-run speed in milliseconds per frame (e.g. 500, 1500)";
- socket.on("talk", function (message) {
- if (!message.text.startsWith(prefix)) return;
- const args = message.text.substring(prefix.length).split(" ");
- const cmd = args[0];
- if (cmd === "help") {
- sendMsg(help);
- } else if (cmd === "load") {
- stopAutoRun();
- if (args[1]) {
- if (args[1].startsWith("http")) {
- fetch(args[1])
- .then(response => response.arrayBuffer())
- .then(buffer => {
- const rom = new Uint8Array(buffer);
- const name = args[1].split("/").pop() || `ROM${romList.length+1}`;
- romList.push({ name, data: rom, url: args[1] });
- sendMsg(`ROM downloaded and added as #${romList.length}: ${name}`);
- })
- .catch(() => sendMsg("Failed to download ROM from URL."));
- } else if (!isNaN(args[1])) {
- const idx = parseInt(args[1], 10) - 1;
- if (romList[idx]) {
- loadROM(romList[idx].data);
- sendMsg(`Loaded ROM #${idx+1}: ${romList[idx].name}`);
- } else {
- sendMsg("No ROM at that number. Use !chip8 roms to list available ROMs.");
- }
- } else {
- sendMsg("Invalid argument. Use a URL or a ROM number.");
- }
- } else {
- loadROM(exampleROM);
- sendMsg("Loaded default example ROM.");
- }
- } else if (cmd === "roms" || cmd === "listroms") {
- if (romList.length === 0) {
- sendMsg("No ROMs downloaded yet.");
- } else {
- let msg = "__ROM List__\n";
- romList.forEach((rom, i) => {
- msg += `${i+1}. ${rom.name} (${rom.url})\n`;
- });
- sendMsg(msg.trim());
- }
- } else if (cmd === "reset") {
- reset();
- sendMsg("Emulator reset.");
- } else if (cmd === "step") {
- stopAutoRun();
- if (!running) { sendMsg("No ROM loaded."); return; }
- cycle();
- sendMsg("Stepped one instruction.\n" + renderChip8Braille14x6());
- } else if (cmd === "run") {
- if (!running) { sendMsg("No ROM loaded."); return; }
- sendMsg("Running automatically. Type !chip8 stop to halt.");
- startAutoRun(autoRunSpeed);
- } else if (cmd === "stop") {
- stopAutoRun();
- running = false;
- display.fill(0);
- sendMsg("Stopped automatic run, cleared display, and halted ROM.\n" + renderChip8Braille14x6());
- } else if (cmd === "display") {
- sendMsg(renderChip8Braille14x6());
- } else if (cmd === "key") {
- if (args.length < 3) {
- sendMsg("Usage: !chip8 key [0-F] [down|up]");
- return;
- }
- let keyStr = args[1].toUpperCase();
- let action = args[2].toLowerCase();
- let keyVal = parseInt(keyStr, 16);
- if (isNaN(keyVal) || keyVal < 0 || keyVal > 0xF) {
- sendMsg("Invalid key. Use 0-9 or A-F.");
- return;
- }
- if (action === "down") {
- keys[keyVal] = true;
- sendMsg("Key " + keyStr + " pressed.");
- } else if (action === "up") {
- keys[keyVal] = false;
- sendMsg("Key " + keyStr + " released.");
- } else {
- sendMsg("Invalid action. Use 'down' or 'up'.");
- }
- } else if (cmd === "speed") {
- if (args.length < 2 || isNaN(args[1]) || parseInt(args[1],10) < 100) {
- sendMsg("Usage: !chip8 speed <milliseconds> (minimum 100)");
- return;
- }
- autoRunSpeed = parseInt(args[1], 10);
- if (autoRunInterval !== null) {
- startAutoRun(autoRunSpeed);
- sendMsg(`Auto-run speed set to ${autoRunSpeed} ms per frame (applied immediately).`);
- } else {
- sendMsg(`Auto-run speed set to ${autoRunSpeed} ms per frame.`);
- }
- } else {
- sendMsg("Unknown CHIP8 command. Type !chip8 help.");
- }
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement