From 0bdab6d0af4910b1cb96fefc8b1b3601f32c8d76 Mon Sep 17 00:00:00 2001 From: Brendan Zagaeski <4934473+brendanzagaeski@users.noreply.github.com> Date: Sun, 5 May 2019 21:30:21 -0700 Subject: [PATCH] Update fi.html to contain the interpreter --- fi.html | 859 +++++++++++++++----------------------------------------- 1 file changed, 221 insertions(+), 638 deletions(-) diff --git a/fi.html b/fi.html index 29a5dd4..1aa53b1 100644 --- a/fi.html +++ b/fi.html @@ -22,354 +22,19 @@ ); }); -String.prototype.h = (function (ii) -{ - return String.prototype.concat ( - `0x`, - this.charCodeAt (ii * 4 + 3).toString (0x10).padStart (2, `0`), - this.charCodeAt (ii * 4 + 2).toString (0x10).padStart (2, `0`), - this.charCodeAt (ii * 4 + 1).toString (0x10).padStart (2, `0`), - this.charCodeAt (ii * 4).toString (0x10).padStart (2, `0`), - ` ` - ); -}); - -/////////////////////////////////// -// ELF header and program header // -/////////////////////////////////// - -// https://refspecs.linuxfoundation.org/elf/gabi4+/contents.html - -// let Elf64_Addr = 8; -// let Elf64_Off = 8; -// let Elf64_Half = 2; -// let Elf64_Word = 4; -// let Elf64_Xword = 8; -// let EI_NIDENT = 0x10; - -// Elf64_Ehdr - -let p = String.prototype.concat ( - `\x7F`, // e_ident[ELFMAG0] - `E`, // e_ident[ELFMAG1] - `L`, // e_ident[ELFMAG2] - `F`, // e_ident[ELFMAG3] - `\x02`, // e_ident[EI_CLASS] is ELFCLASS64 - `\x01`, // e_ident[EI_DATA] is ELFDATA2LSB - `\x01`, // e_ident[EI_VERSION] is EV_CURRENT - `\x00`, // e_ident[EI_OSABI] is ELFOSABI_NONE - `\x00`, // e_ident[EI_ABIVERSION] is *unspecified* - `\x00`.repeat (7), // e_ent[EI_PAD] through e_ident[EI_NIDENT - 1] are all 0 - `\x02\x00`, // Elf64_Half e_type is ET_EXEC (executable file) - `\x3E\x00`, // Elf64_Half e_machine is EM_X86_64 - `\x01\x00\x00\x00`, // Elf64_Word e_version is EV_CURRENT - `\x00\x10\x40\x00\x00\x00\x00\x00`, // Elf64_Addr e_entry is at virtual memory address 0x401000 - `\x40\x00\x00\x00\x00\x00\x00\x00`, // Elf64_Off e_phoff is directly after this ELF header - `\x00`.repeat (8), // Elf64_Off e_shoff is 0: No section header table - `\x00`.repeat (4), // Elf64_Word e_flags - `\x40\x00`, // Elf64_Half e_ehsize: The ELF header is 0x40 bytes - `\x38\x00`, // Elf64_Half e_phentsize: Program headers are 0x38 bytes - `\x01\x00`, // Elf64_Half e_phnum: One program header - `\x00\x00`, // Elf64_Half e_shentsize - `\x00\x00`, // Elf64_Half e_shnum - `\x00\x00`, // Elf64_Half e_shstrndx is SHN_UNDEF - -// Elf64_Phdr - - `\x01\x00\x00\x00`, // Elf64_Word p_type is PT_LOAD - `\x07\x00\x00\x00`, // Elf64_Word p_flags is PF_X ^ PF_W ^ PF_R === 1 ^ 2 ^ 4 - `\x00`.repeat (8), // Elf64_Off p_offset is 0, at the beginning of the file - `\x00\x00\x40\x00\x00\x00\x00\x00`, // Elf64_Addr p_vaddr is at 0x400000 to follow conventional segment arrangement - `\x00`.repeat (8), // Elf64_Addr p_paddr (System V ignores this) - `\x00\xA0\x00\x00\x00\x00\x00\x00`, // Elf64_Xword p_filesz is 0xA pages - `\x00\x00\x02\x00\x00\x00\x00\x00`, // Elf64_Xword p_memsz is 0x20 pages - `\x00\x10\x00\x00\x00\x00\x00\x00` // Elf64_Xword p_align is 0x1000 -); - -//////////////////////////////////////////// -// Intel 64 instruction names and helpers // -//////////////////////////////////////////// - -// https://software.intel.com/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-2a-2b-2c-and-2d-instruction-set-reference-a-z - -let ADD_FTCH_0x03 = `\x03`; -let AND_FTCH_0x23 = `\x23`; -let XOR_FTCH_0x33 = `\x33`; -let JZ_0x74_0xXX = `\x74`; -let JNZ_0x75_0xXX = `\x75`; -let MOV_STOR_0x89 = `\x89`; -let MOV_FTCH_0x8B = `\x8B`; -let POP_0x8F_0 = `\x8F`; -let MOV_IMMD_0xC7_0 = `\xC7`; -let SHL_0xD1_4 = `\xD1`; -let SHR_0xD1_5 = `\xD1`; -let JMP_0xFF_4 = `\xFF`; -let PUSH_0xFF_6 = `\xFF`; - -let REG_0b11 = 0b11; -let MEM_0b00 = 0b00; - -let modRM = (function (mod, reg, rm) -{ - return String.fromCharCode (mod << 6 ^ reg << 3 ^ rm); -}); - -/////////////////////////////////////// -// Runtime setup for Forth-like code // -/////////////////////////////////////// - -p = p.concat ( - // e_entry is at byte address 0x1000 in the file - `\x00`.repeat (0x1000 - p.length), - - // Initialize register 1 to a constant value of positive 4 - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b001), `\x04\x00\x00\x00`, - // Initialize register 3 to a constant value of negative 4 - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b011), `\xFC\xFF\xFF\xFF`, - // Initialize the stack pointer - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b110), `\x00\x00\x42\x00`, - // Jump to the compiler - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b111), `\x00\x20\x40\x00`, - JMP_0xFF_4 , modRM (REG_0b11, 4, 0b111) -); - -/////////////////////////////////////////////////////////////////////////////// -// Instructions to dump the stack to standard output at the end of execution // -/////////////////////////////////////////////////////////////////////////////// - -// System calling conventions: -// https://github.com/hjl-tools/x86-psABI/blob/5f35093b24b17fed14ab99eea8fe162f6212e28b/kernel.tex#L25-L45 -// -// System call numbers: -// https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl - -p = p.concat ( - `\x00`.repeat (0x1100 - p.length), - - // Do a `dup` to copy the top of the stack into memory - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b011), - MOV_STOR_0x89 , modRM (MEM_0b00, 0b111, 0b110), - // Move the stack pointer down once more so that the loop will write out the - // topmost value from the stack before it breaks - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b011), - - // Select the `write ()` syscall - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b000), `\x01\x00\x00\x00`, - // Use file descriptor 1 - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b111), `\x01\x00\x00\x00`, - // Start at the bottom of the stack, so first save the address of the top of - // the stack to a scratch register - MOV_FTCH_0x8B , modRM (REG_0b11, 0b001, 0b110), - // Set the starting address to the bottom of the stack - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b110), `\xF8\xFF\x41\x00`, - // Set the write size to 4 bytes - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b010), `\x04\x00\x00\x00`, - - // Save the address of the bottom of the stack so we can do a destructive XOR - // compare - PUSH_0xFF_6 , modRM (REG_0b11, 6, 0b001), - XOR_FTCH_0x33 , modRM (REG_0b11, 0b001, 0b110), - // If we're at the top of the stack, we're done - JZ_0x74_0xXX , `\x12`, - // Otherwise restore the address of the bottom of the stack - POP_0x8F_0 , modRM (REG_0b11, 0, 0b001), - // Re-save it because the syscall overwrites it - PUSH_0xFF_6 , modRM (REG_0b11, 6, 0b001), - // Write the current 4 bytes - `\x0F\x05`, // SYSCALL - // Restore the address of the bottom of the stack again - POP_0x8F_0 , modRM (REG_0b11, 0, 0b001), - // Decrement the current position to the next 4 bytes - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b011), - // Re-select the `write ()` syscall - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b000), `\x01\x00\x00\x00`, - // Loop (using a conditional jump to get relative addressing without having - // to introduce another JMP opcode) - JNZ_0x75_0xXX , `\xE8`, - - // Now do the `exit ()` syscall - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b000), `\x3C\x00\x00\x00`, - XOR_FTCH_0x33 , modRM (REG_0b11, 0b111, 0b111), - `\x0F\x05` // SYSCALL -); - -///////////////////////////////////////////// -// Forth-like word definitions in Intel 64 // -///////////////////////////////////////////// - -let __dup = String.prototype.concat ( - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b011), - MOV_STOR_0x89 , modRM (MEM_0b00, 0b111, 0b110) -); - -let _dup = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - __dup -); - -let __drop = String.prototype.concat ( - MOV_FTCH_0x8B , modRM (MEM_0b00, 0b111, 0b110), - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b001) -); - -let _drop = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - __drop -); - -let _over = String.prototype.concat ( - `\x0F\x1F\x84\x00\x00\x00\x00\x00`, // nop - MOV_FTCH_0x8B , modRM (MEM_0b00, 0b010, 0b110), - __dup, - MOV_FTCH_0x8B , modRM (REG_0b11, 0b111, 0b010) -); - -let _push = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - `\x0F\x1F\x40\x00`, // nop - PUSH_0xFF_6 , modRM (REG_0b11, 6, 0b111), - __drop -); - -let _pop = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - `\x0F\x1F\x40\x00`, // nop - __dup, - POP_0x8F_0 , modRM (REG_0b11, 0, 0b111) -); - -let _jcc_prep = String.prototype.concat ( - // Save the top of the stack and the second position in scratch registers - MOV_FTCH_0x8B , modRM (REG_0b11, 0b010, 0b111), - MOV_FTCH_0x8B , modRM (MEM_0b00, 0b000, 0b110), - // Drop 2 from the top of the stack - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b001), - MOV_FTCH_0x8B , modRM (MEM_0b00, 0b111, 0b110), - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b001), - // Test the value that was saved from the second position in the stack - AND_FTCH_0x23 , modRM (REG_0b11, 0b000, 0b000), -); - -let _jz = String.prototype.concat ( - _jcc_prep, - JNZ_0x75_0xXX , `\x02`, - JMP_0xFF_4 , modRM (REG_0b11, 4, 0b010) -); - -let _jnz = String.prototype.concat ( - _jcc_prep, - JZ_0x74_0xXX , `\x02`, - JMP_0xFF_4 , modRM (REG_0b11, 4, 0b010) -); - -let _jump = String.prototype.concat ( - `\x0F\x1F\x84\x00\x00\x00\x00\x00`, // nop - MOV_FTCH_0x8B , modRM (REG_0b11, 0b010, 0b111), - __drop, - JMP_0xFF_4 , modRM (REG_0b11, 4, 0b010) -); - -let _add = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - ADD_FTCH_0x03 , modRM (MEM_0b00, 0b111, 0b110), - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b001) -); - -let _shl = String.prototype.concat ( - `\x0F\x1F\x84\x00\x00\x00\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - SHL_0xD1_4 , modRM (REG_0b11, 4, 0b111), -); - -let _shr = String.prototype.concat ( - `\x0F\x1F\x84\x00\x00\x00\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - SHR_0xD1_5 , modRM (REG_0b11, 5, 0b111), -); - -let _or = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - XOR_FTCH_0x33 , modRM (MEM_0b00, 0b111, 0b110), - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b001) -); - -let _and = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - AND_FTCH_0x23 , modRM (MEM_0b00, 0b111, 0b110), - ADD_FTCH_0x03 , modRM (REG_0b11, 0b110, 0b001) -); - -let _ftch = String.prototype.concat ( - `\x0F\x1F\x84\x00\x00\x00\x00\x00`, // nop - `\x66\x0F\x1F\x44\x00\x00`, // nop - MOV_FTCH_0x8B , modRM (MEM_0b00, 0b111, 0b111) -); - -let _exit = String.prototype.concat ( - `\x0F\x1F\x84\x00\x00\x00\x00\x00`, // nop - // Jump to the instructions at 0x401100 that dump the stack and exits - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b010), `\x00\x11\x40\x00`, - JMP_0xFF_4 , modRM (REG_0b11, 4, 0b010) -); - -let _ftchP = String.prototype.concat ( - `\x66\x0F\x1F\x44\x00\x00`, // nop - __dup, - MOV_IMMD_0xC7_0, modRM (REG_0b11, 0, 0b111), -); - -//////////////////////////////////////////// -// Parser and compiler in Forth-like code // -//////////////////////////////////////////// +/////////////////////////////////////////////// +// Parser and interpreter in Forth-like code // +/////////////////////////////////////////////// // Comments for the Forth-like code are kept in a separate section later in the // file so that cursor positions in the code line up with byte positions in the // output executable. -p = p.concat (`\x00`.repeat (0x2000 - p.length), -`0x00400000 -0x00000064 -0x00402050 -0x00402100 -jump -0xFFFFE000 -add -dup -push -0x00400064 -0x00001F9C -0x00402300 -0x00402100 -jump - - -push -2/ -2/ -0xFFFFFFFF -add -push -dup -0x00000004 -add -push -@ -pop -pop -dup -0x00402130 -jnz -drop -drop -pop +let p = +`0x00402300 jump - + 0x00404000 push pop @@ -391,45 +56,11 @@ jz dup @ -0x000000FF -and -0x00000022 -or -0x00402530 -jnz - dup - 0x00000010 - add - push - 0x00000010 - 0x00402320 - 0x00402100 - jump -dup -@ 0x00402C80 jnz -0x00000010 -add -push -0x00000000 -0x00000000 -0x00000000 -0x00000000 -pop -pop -0xFFFFC000 -add -0x00402690 -over -0x00402100 -jnz - drop - drop - drop exit - + 0x00000010 0xFFFFFFFF add @@ -454,15 +85,11 @@ jnz drop push -0x00000000 -0x00000000 -0x00000000 -0x00000000 pop 0x00402330 jump - + 0x00000002 add push @@ -501,15 +128,12 @@ 0x004029E0 jnz drop -${_ftchP.h (0)} -${_ftchP.h (1)} -${_ftchP.h (2)} pop pop 0x00402330 jump - + push 0x00403001 pop @@ -534,182 +158,130 @@ "dup " -${_dup.h (0) } -${_dup.h (1) } -${_dup.h (2) } -${_dup.h (3) } +dup 0x00402320 jump - + "drop " -${_drop.h (0) } -${_drop.h (1) } -${_drop.h (2) } -${_drop.h (3) } +drop 0x00402320 jump - + "over " -${_over.h (0) } -${_over.h (1) } -${_over.h (2) } -${_over.h (3) } +over 0x00402320 jump - + "push " -${_push.h (0) } -${_push.h (1) } -${_push.h (2) } -${_push.h (3) } +pop +over +push +push +drop 0x00402320 jump - + "pop " -${_pop.h (0) } -${_pop.h (1) } -${_pop.h (2) } -${_pop.h (3) } +pop +pop +over +push +push +drop +pop 0x00402320 jump - + "jz " -${_jz.h (0) } -${_jz.h (1) } -${_jz.h (2) } -${_jz.h (3) } +push +0x00403590 +jnz + pop + pop + drop + 0x00402330 + jump +pop +drop 0x00402320 jump - + "jnz " -${_jnz.h (0) } -${_jnz.h (1) } -${_jnz.h (2) } -${_jnz.h (3) } +push +0x00403690 +jz + pop + pop + drop + 0x00402330 + jump +pop +drop 0x00402320 jump - + "jump " -${_jump.h (0) } -${_jump.h (1) } -${_jump.h (2) } -${_jump.h (3) } -0x00402320 +pop +drop +0x00402330 jump - + "add " -${_add.h (0) } -${_add.h (1) } -${_add.h (2) } -${_add.h (3) } +add 0x00402320 jump - + "2* " -${_shl.h (0) } -${_shl.h (1) } -${_shl.h (2) } -${_shl.h (3) } +2* 0x00402320 jump - + "2/ " -${_shr.h (0) } -${_shr.h (1) } -${_shr.h (2) } -${_shr.h (3) } +2/ 0x00402320 jump - + "or " -${_or.h (0) } -${_or.h (1) } -${_or.h (2) } -${_or.h (3) } +or 0x00402320 jump - + "and " -${_and.h (0) } -${_and.h (1) } -${_and.h (2) } -${_and.h (3) } +and 0x00402320 jump - + "@ " -${_ftch.h (0) } -${_ftch.h (1) } -${_ftch.h (2) } -${_ftch.h (3) } +@ 0x00402320 jump - + "exit " -${_exit.h (0) } -${_exit.h (1) } -${_exit.h (2) } -${_exit.h (3) } -0x00402320 -jump - -\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`.replace (/\x0A/g, ` `) -); +exit + + +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`.replace (/\x0A/g, ` `); ////////////////////////////////// // Comments for Forth-like code // ////////////////////////////////// - - - -// Load 0x64 bytes of the ELF header onto the stack. - -// Use the two's complement to subtract 0x2000 from the p_filesz value. Each round of compilation consumes a 0x2000-byte block of source code, so the file gets shorter by 0x2000 bytes each time. - -// Save a copy of the new p_filesz value for later. - - -// Load the remainder of the ELF header, the register initialization code, and the `_exit` code block onto the stack. - - - - -// // Load up to a specified number of bytes onto the stack, in 32-bit increments // First save the return address. -// Divide by 4 to get the floor of the number of 32-bit values to load. - -// (Start of the loop) - - - - - - - - - - - -// If the loop counter is not zero, loop. - - -// Restore the return address. - +// This jump and the spaces that follow it are a little silly. They are only here so that the other jump addresses will all be the same as in the compiler. // // Main parsing loop // First initialize the source address to point to the input program. @@ -733,43 +305,9 @@ // If the character is `0`, jump into reading a number. - - - -// Compare to 0x22 (ASCII `"`). - - - // Use the source address as the copy src address. - - // Increment the source address to the next 0x10-byte block. - - // Prepare to load 0x10 bytes. - // Set the return address for the multi-byte loader. - - // Jump to the multi-byte loader. - - // If the value isn't ` `, `0`, `"`, or 0x00000000 (null), // then read it as a word. -// If the value is 0x00000000, increment the source address by 0x10 bytes. - - -// Load 0x10 bytes of zero onto the stack under the source address. - - - - -// Restore the new p_filesz size. - -// Subtract the 0x4000 bytes that have been added so far. -// Set the return address for the multi-byte loader. - - -// If the result after subtracting 0x4000 is not zero, load that remaining number of bytes to get up to p_filesz. - // Otherwise drop the arguments to the multi-byte loader. - - -// And then exit. +// If the value is 0x00000000, exit. // // Read whitespace // First initialize the loop counter to 0x10. @@ -796,10 +334,6 @@ // Loop if the loop counter is not 0. // Otherwise, drop the loop counter. -// Load 0x10 bytes of zero on the stack under the source address. - - - // Jump back to the main loop. @@ -843,10 +377,7 @@ // If the loop counter is not zero, loop. // Otherwise, drop the loop counter. -// Load the Intel 64 definition of `_ftchP`. - - -// Restore the accumulator to be the input to `_ftchP`. +// Restore the accumulator. // Restore the source address. @@ -975,24 +506,6 @@ - - - - - - - - - - - - - - - - - - @@ -1016,9 +529,9 @@ // End comments // ////////////////// -///////////////////////////////////////////// -// Parser and compiler in JavaScript setup // -///////////////////////////////////////////// +//////////////////////////////////////////////// +// Parser and interpreter in JavaScript setup // +//////////////////////////////////////////////// let hello = String.prototype.concat ( // l l e H @@ -1032,13 +545,11 @@ `exit ` ); -p = p.concat ( - // The third-order compiler, to be compiled to Intel 64 instructions by the - // second-order compiler in the Forth-like language - p.slice (0x2000, 0x4000), - - // The fourth-order compiler - p.slice (0x2000, 0x4000), +p = String.prototype.concat ( + // The second-order interpreter, to be interpreted by JavaScript + p.replace (/0x00404000/, `0x00002000`) + .replace (/0x00402(...)/g, `0x00000$1`) + .replace (/0x00403(...)/g, `0x00001$1`), // The final "Hello, world!" program hello, ` `.repeat (0x1FF0 - hello.length), `\x00`.repeat (0x10), @@ -1046,28 +557,145 @@ ); let a = ``; -let pc = 0x2000; +let b = ``; +let pc = 0; +let done = false; + +/////////////////////////////////////////////// +// Forth-like word definitions in JavaScript // +/////////////////////////////////////////////// + +let _dup = (function () +{ + a = a.slice (0, 4).concat (a); +}); + +let _drop = (function () +{ + a = a.slice (4); +}); + +let _over = (function () +{ + a = a.slice (4, 8).concat (a); +}); -/////////////////////////////////////// -// Parser and compiler in JavaScript // -/////////////////////////////////////// +let _push = (function () +{ + b = a.slice (0, 4).concat (b); + _drop (); +}); + +let _pop = (function () +{ + a = b.slice (0, 4).concat (a); + b = b.slice (4); +}); + +let _jz = (function () +{ + let x = a.getUint32 (0); + let y = a.getUint32 (4); + _drop (); + _drop (); + if (y !== 0) { + return; + } + pc = x; +}); + +let _jnz = (function () +{ + let x = a.getUint32 (0); + let y = a.getUint32 (4); + _drop (); + _drop (); + if (y === 0) { + return; + } + pc = x; +}); + +let _jump = (function () +{ + pc = a.getUint32 (0); + _drop (); +}); + +let _add = (function () +{ + a = String.fromUint32 (a.getUint32 (0) + a.getUint32 (4)) + .concat (a.slice (8)); +}); + +let _shl = (function () +{ + a = String.fromUint32 (a.getUint32 (0) << 1) + .concat (a.slice (4)); +}); + +let _shr = (function () +{ + a = String.fromUint32 (a.getUint32 (0) >> 1) + .concat (a.slice (4)); +}); + +let _or = (function () +{ + a = String.fromUint32 (a.getUint32 (0) ^ a.getUint32 (4)) + .concat (a.slice (8)); +}); + +let _and = (function () +{ + a = String.fromUint32 (a.getUint32 (0) & a.getUint32 (4)) + .concat (a.slice (8)); +}); + +let _ftch = (function () +{ + let x = a.getUint32 (0); + a = p.slice (x, x + 4).concat (a.slice (4)); +}); + +let _exit = (function () +{ + let acc = ``; + a = a.slice (0, 4).concat (a); + let i = a.length; + while (i > 0) { + acc = acc.concat (a.slice (i, i + 4)); + i -= 4; + } + done = true; + document.location = `data:application/octet-stream;base64,${btoa (acc)}`; +}); + +let _ftchP = (function (x) +{ + a = String.fromUint32 (x).concat (a); +}); + +////////////////////////////////////////// +// Parser and interpreter in JavaScript // +////////////////////////////////////////// let words = String.prototype.concat ( - `"dup " `, `_dup `, `\x00`.repeat (0xE0), - `"drop" `, `_drop `, `\x00`.repeat (0xE0), - `"over" `, `_over `, `\x00`.repeat (0xE0), - `"push" `, `_push `, `\x00`.repeat (0xE0), - `"pop " `, `_pop `, `\x00`.repeat (0xE0), - `"jz " `, `_jz `, `\x00`.repeat (0xE0), - `"jnz " `, `_jnz `, `\x00`.repeat (0xE0), - `"jump" `, `_jump `, `\x00`.repeat (0xE0), - `"add " `, `_add `, `\x00`.repeat (0xE0), - `"2* " `, `_shl `, `\x00`.repeat (0xE0), - `"2/ " `, `_shr `, `\x00`.repeat (0xE0), - `"or " `, `_or `, `\x00`.repeat (0xE0), - `"and " `, `_and `, `\x00`.repeat (0xE0), - `"@ " `, `_ftch `, `\x00`.repeat (0xE0), - `"exit" `, `_exit `, `\x00`.repeat (0xE0), + `"dup " `, `_dup (); `, `\x00`.repeat (0xE0), + `"drop" `, `_drop (); `, `\x00`.repeat (0xE0), + `"over" `, `_over (); `, `\x00`.repeat (0xE0), + `"push" `, `_push (); `, `\x00`.repeat (0xE0), + `"pop " `, `_pop (); `, `\x00`.repeat (0xE0), + `"jz " `, `_jz (); `, `\x00`.repeat (0xE0), + `"jnz " `, `_jnz (); `, `\x00`.repeat (0xE0), + `"jump" `, `_jump (); `, `\x00`.repeat (0xE0), + `"add " `, `_add (); `, `\x00`.repeat (0xE0), + `"2* " `, `_shl (); `, `\x00`.repeat (0xE0), + `"2/ " `, `_shr (); `, `\x00`.repeat (0xE0), + `"or " `, `_or (); `, `\x00`.repeat (0xE0), + `"and " `, `_and (); `, `\x00`.repeat (0xE0), + `"@ " `, `_ftch (); `, `\x00`.repeat (0xE0), + `"exit" `, `_exit (); `, `\x00`.repeat (0xE0), ); let whitespace = (function () @@ -1081,7 +709,6 @@ } pc++; } while (j !== 0); - a = `\x00\x00\x00\x00`.repeat (4).concat (a); }); let number = (function () @@ -1100,10 +727,7 @@ } acc ^= y; } while (j !== 0); - a = _ftchP.slice (0, 4).concat (a); - a = _ftchP.slice (4, 8).concat (a); - a = _ftchP.slice (8, 0xC).concat (a); - a = String.fromUint32 (acc).concat (a); + _ftchP (acc); }); let word = (function () @@ -1120,29 +744,9 @@ } while (true); pc += 4; j += 0xF; - let w = eval (words.slice (j, j + 0x10)); - a = w.slice (0, 4).concat (a); - a = w.slice (4, 8).concat (a); - a = w.slice (8, 0xC).concat (a); - a = w.slice (0xC).concat (a); + eval (words.slice (j, j + 0x10)); }); -let load = (function (ys, n) -{ - let j = 0; - n >>= 2; - do { - n--; - let x = ys.getUint32 (j); - j += 4; - a = String.fromUint32 (x).concat (a); - } while (n !== 0); -}); - -load (p, 0x64); -let p_filesz = a.getUint32 (0); -load (p.slice (0x64), 0x1F9C); - do { let x = p.getUint32 (pc) & 0xFF; if ((x ^ 0x20) === 0) { @@ -1154,32 +758,11 @@ number (); continue; } - x = p.getUint32 (pc) & 0xFF; - if ((x ^ 0x22) === 0) { - load (p.slice (pc), 0x10); - pc += 0x10; - continue; - } x = p.getUint32 (pc); if (x) { word (); continue; } - pc += 0x10; - a = `\x00\x00\x00\x00`.repeat (4).concat (a); - p_filesz -= 0x4000; - if (p_filesz !== 0) { - load (p.slice (pc), p_filesz); - } break; -} while (true); - -let acc = ``; -a = a.slice (0, 4).concat (a); -let i = a.length - 4; -while (i > 0) { - acc = acc.concat (a.slice (i, i + 4)); - i -= 4; -} -document.location = `data:application/octet-stream;base64,${btoa (acc)}`; +} while (!done);