Our server costs ~$56 per month to run. Please consider donating or becoming a Patron to help keep the site running. Help us gain new members by following us on Twitter and liking our page on Facebook!
Current time: November 21, 2024, 9:33 am

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
PicoBlaze Simulator in JavaScript
#51
RE: PicoBlaze Simulator in JavaScript
bennyboy Wrote:This isn't something we have to philosophize on-- go ahead and make a simple switch statement and check the assembled code.
Do you happen to know how to do that with JavaScript? I know JavaScript is, in modern engines, first compiled to a superset of WebAssembly, but I have no idea how to see that WebAssembly, and I also don't know how to see the assembly code that WebAssembly is compiled to.
bennyboy Wrote:Anyway, your js code is calculating [codebyte] again and again in the conditional check of every single "if" statement, which is not the same as what you have in the above example.
The compiler probably optimizes that away, right? And even if it doesn't, the "and" operation takes a very small amount of time.
bennyboy Wrote:Does it say why?
Allegedly, using switch-case is a sign you do not have enough subclasses. If a switch-case is used again and again in methods of some class (in different methods), it's a sign that by which the switch-case branches are logically different classes.
HappySkeptic Wrote:A map that calls different pieces of code.
You mean, like virtual methods?
bennyboy Wrote:More readable code can reduce mistakes, and allow other programmers to understand it quickly: a potential savings of hours of real-life human time instead of a few milliseconds of CPU time.
In our "Razvoj Programske Podrške po Objektno Orijentiranim Načelima" classes, we were also taught clean and idiomatic code usually results in a faster compiled program, because the compiler is optimized to work with idiomatic code.
bennyboy Wrote:I'm assuming you're using JavaScript because this is meant to run online. Did I miss (or forget) a link to a webpage that is actually running this? Do you have sample code that you feel is executing too slowly?
It is on my website. The example code that executes unacceptably slow is my "Decimal to Binary" example program:
Code:
;WARNING: I have tried to run this on
;         real PicoBlaze, but failed.
;         Now, I did not have time to
;         test whether a simpler
;         example program using UART
;         would work, to see if it
;         is a problem with this
;         program, or if it is maybe
;         a problem with the way I
;         set up PicoBlaze. I have
;         opened a
;         StackOverflow question
;         about it, but, thus far, I
;         received no response.


;This is an example program that uses
;UART, the interface that PicoBlaze uses
;for connecting to terminals (a DOS-like
;user interface, with a keyboard and a
;screen capable of displaying text).
;It loads base-10 integer numbers from
;the terminal, converts them into binary,
;and then prints the binary
;representations back onto the terminal.
;Example input would be:

;1
;2
;4
;8
;15
;127
;255
;

;And the expected output is:

;1_(10)=1_(2)
;2_(10)=10_(2)
;4_(10)=100_(2)
;8_(10)=1000_(2)
;15_(10)=1111_(2)
;127_(10)=1111111_(2)
;255_(10)=11111111_(2)
;

;Note that you need to click the
;"Enable UART" button in order to use it.
;Also, the trailing empty line in the
;input is necessary for the result to be
;printed.

;Now follows some boilerplate code
;we use in our Computer Architecture
;classes...
CONSTANT LED_PORT,         00
CONSTANT HEX1_PORT,        01
CONSTANT HEX2_PORT,        02
CONSTANT UART_TX_PORT,     03
CONSTANT UART_RESET_PORT,  04
CONSTANT SW_PORT,          00
CONSTANT BTN_PORT,         01
CONSTANT UART_STATUS_PORT, 02
CONSTANT UART_RX_PORT,     03
; Tx data_present
CONSTANT U_TX_D, 00000001'b
; Tx FIFO half_full
CONSTANT U_TX_H, 00000010'b
; TxFIFO full
CONSTANT U_TX_F, 00000100'b
; Rxdata_present
CONSTANT U_RX_D, 00001000'b
; RxFIFO half_full
CONSTANT U_RX_H, 00010000'b
; RxFIFO full
CONSTANT U_RX_F, 00100000'b

ADDRESS 000
START:
;At the beginning, the number is 0.
load s0, 0
;And we are storing its string
;representation at the beginning
;of RAM.
namereg s3, pointer
load pointer, 0
;Now follows a loop to load
;the digits of the number.
loading_the_number:
 ;Load a character from the UART
 ;terminal.
 call UART_RX
 ;Check whether the character is a digit.
   compare s9, "0"
   ;If it is not a digit, jump to the
   ;part of the program for printing
   ;the number you have got.
   jump c,print_the_number
   compare s9, "9" + 1
   ;Suggestion by a StackExchange user.
   jump nc, print_the_number
 ;If it is a digit, store it into RAM.
 store s9, (pointer)
 add pointer, 1
 ;Multiply the number you have got by 10.
 load sf, s0
 call multiply_by_10
 load s0, se
 ;Then, convert the digit from ASCII
 ;into binary.
 sub s9, "0"
 ;And then add it to the number you
 ;have got.
 add s0, s9
 call c, abort ;In case of overflow.
 jump loading_the_number ;Repeat until a
                         ;non-digit is
                         ;loaded.
print_the_number:
;If there are no digits to be printed,
;do not print anything.
sub pointer, 0
jump z, START
print_the_decimal:
load s4, pointer
load pointer, 0
printing_the_decimal_loop:
 compare pointer, s4
 jump nc, end_of_printing_the_decimal
 fetch s9, (pointer)
 ;Do some basic sanity check: Is the
 ;character you are printing indeed
 ;a decimal digit?
   compare s9, "0"
   call    c , abort
   compare s9, "9" + 1
   call    nc, abort
 ;If it is indeed a decimal digit,
 ;print it.
 call UART_TX
 add pointer,1
 jump printing_the_decimal_loop
end_of_printing_the_decimal:
;After you have repeated the decimal
;number, print the string "_(10)=".
load s9, "_"
call UART_TX
load s9, "("
call UART_TX
load s9, "1"
call UART_TX
load s9, "0"
call UART_TX
load s9, ")"
call UART_TX
load s9, "="
call UART_TX
;If the number to be printed is
;equal to zero, print 0.
sub s0, 0
jump nz, print_the_binary
load s9, "0"
call UART_TX
jump end_of_printing_loop
print_the_binary:
;Make the pointer point to the
;beginning of RAM.
load pointer, 0
;Now goes a loop which stores the binary
;representation of the number we have
;got into RAM, but reversed.
beginning_of_converting_to_binary:
 sub   s0, 0
 jump  z , end_of_converting_to_binary
 load  s9, "0"
 sr0   s0
 jump nc, store_digit_to_memory
 add   s9, 1
 store_digit_to_memory:
 store s9, (pointer)
 add   pointer, 1
 jump beginning_of_converting_to_binary
end_of_converting_to_binary:
;Do some basic sanity check, such as that
;the pointer does not point to zero.
compare pointer, 0
call z, abort ;Something went wrong
             ;so end the program.
;Check whether there are more than 8 bits.
compare pointer,9
call nc, abort
;Now goes a loop which will print
;the binary number in RAM, with digits
;in the correct order. The pointer now
;points at a memory location right after
;the binary number (not at the last digit,
;but after it).
beginning_of_printing_loop:
 sub   pointer, 1
 jump  c      , end_of_printing_loop
 fetch s9     , (pointer)
 ;Do some basic sanity check:
 ;Is the character the pointer points to
 ;indeed a binary digit?
   compare s9, "0"
   jump    z , memory_is_fine
   compare s9, "1"
   jump    z , memory_is_fine
   call abort ;Something went wrong,
              ;so end the program.
 memory_is_fine:
 ;If everything is fine, print that
 ;digit.
 call UART_TX
 ;Repeat until you have printed all
 ;digits of the binary number
 ;stored in RAM.
 jump beginning_of_printing_loop
end_of_printing_loop:
;After you have printed that binary
;number, print the string "_(2)" and
;a new-line.
load s9, "_"
call UART_TX
load s9, "("
call UART_TX
load s9, "2"
call UART_TX
load s9, ")"
call UART_TX
load s9, a ;newline character, 0xa=='\n'.
call UART_TX
;The program runs in an infinite loop...
JUMP START

multiply_by_10:
 load se, sf
 add  se, se
 call c , abort
 add  se, se
 call c , abort
 add  se, sf
 call c , abort
 add  se, se
 call c , abort
return

abort:
 load s9, "E"
 call UART_TX
 load s9, "R"
 call UART_TX
 load s9, "R"
 call UART_TX
 load s9, "O"
 call UART_TX
 load s9, "R"
 call UART_TX
 load s9, "!"
 call UART_TX
 load s9, a ;newline
 call UART_TX
 infinite_loop:
 jump infinite_loop
return

;Now follows some boilerplate code
;we use in our Computer Architecture
;classes...
UART_RX:
 INPUT sA, UART_STATUS_PORT
 TEST  sA, U_RX_D
 JUMP  Z , UART_RX
 INPUT s9, UART_RX_PORT
RETURN

UART_TX:
 INPUT  sA, UART_STATUS_PORT
 TEST   sA, U_TX_F
 JUMP   NZ, UART_TX
 OUTPUT s9, UART_TX_PORT
RETURN
#52
RE: PicoBlaze Simulator in JavaScript
So, @bennyboy , I have tried to convert that huge if-else to switch-case. However, the simulator stopped working then. Can you figure out what is going on?
Code:
"use strict";
function simulateOneInstruction() {
  try {
    PC = PC %
         4096; // If you are at the end of a program, and there is no "return"
    // there, jump to the beginning of the program. I think that's
    // how PicoBlaze behaves, though I haven't tried it.
    if (breakpoints.includes(machineCode[PC].line)) {
      alert("Reached breakpoint on the line #" + machineCode[PC].line + ".");
      if (playing)
        clearInterval(simulationThread);
      playing = false;
      document.getElementById("fastForwardButton").disabled = false;
      document.getElementById("singleStepButton").disabled = false;
      document.getElementById("UART_INPUT").disabled = false;
      document.getElementById("playImage").style.display = "inline";
      document.getElementById("pauseImage").style.display = "none";
    }
    document.getElementById("PC_label_" + formatAsAddress(PC)).innerHTML = "";
    const currentDirective = parseInt(machineCode[PC].hex, 16);
    // "bennyboy" from "atheistforums.org" thinks my program can be
    // speeded up by using a switch-case instead of the large if-else (that a
    // switch-case would compile into a more efficient assembly code), so it
    // would be interesting to investigate whether that's true:
    // https://atheistforums.org/thread-61911-post-2112817.html#pid2112817
    let port, firstRegister, secondRegister, firstValue, secondValue, result,
        value;
    switch (currentDirective & 0xff000) {
    //    if ((currentDirective & 0xff000) === 0x00000) {
    case 0x00000:
      // LOAD register,register
      registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
          registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x01000) {
      break;
    case 0x01000:
      // LOAD register,constant
      registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
          parseInt(machineCode[PC].hex.substr(3), 16);
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x17000) {
      break;
    case 0x17000:
      // STAR register,constant ;Storing a constant into an inactive register
      registers[!regbank | 0 /*That is how you convert a boolean to an integer
                                        in JavaScript.*/
      ][parseInt(machineCode[PC].hex[2], 16)] =
          parseInt(machineCode[PC].hex.substr(3), 16);
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x16000) {
      break;
    case 0x16000:
      // STAR register,register ;Copying from an active register into an
      // inactive one.
      registers[!regbank | 0][parseInt(machineCode[PC].hex[2], 16)] =
          registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x2e000) {
      break;
    case 0x2e000:
      // STORE register,(register) ;Store the first register at the memory
      // location where the second register points to.
      memory[registers[regbank][parseInt(machineCode[PC].hex[3], 16)]] =
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
      document
          .getElementById(
              "memory_" +
              formatAsByte(
                  registers[regbank][parseInt(machineCode[PC].hex[3], 16)]))
          .innerHTML = formatAsByte(
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)]);
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x2f000) {
      break;
    case 0x2f000:
      // STORE register,memory_address ;Copy a register onto a memory address.
      memory[parseInt(machineCode[PC].hex.substr(3), 16)] =
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
      document.getElementById("memory_" + machineCode[PC].hex.substr(3))
          .innerHTML = formatAsByte(
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)]);
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x0a000) {
      break;
    case 0x0a000:
      // FETCH register,(register) ;Dereference the pointer in the second
      // register.
      registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
          memory[registers[regbank][parseInt(machineCode[PC].hex[3], 16)]];
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x0b000) {
      break;
    case 0x0b000:
      // FETCH register,memory_address ;Copy the value at memory_address to the
      // register.
      registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
          memory[parseInt(machineCode[PC].hex.substr(3), 16)];
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x08000) {
      break;
    case 0x08000:
      // INPUT register,(register) ;Read a byte from a port specified by a
      // register.
      /*const*/ port = registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
      if ((port === 2 || port === 3) && is_UART_enabled) {
        if (port === 3) {
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
              document.getElementById("UART_INPUT")
                  .value.charCodeAt(currentlyReadCharacterInUART);
          currentlyReadCharacterInUART++;
        } else
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
              currentlyReadCharacterInUART <
                      document.getElementById("UART_INPUT").value.length
                  ? 0b00001000 /*U_RX_D*/
                  : 0;
      } else
        registers[regbank][parseInt(machineCode[PC].hex[2], 16)] = parseInt(
            document
                .getElementById("input_" +
                                formatAsByte(registers[regbank][parseInt(
                                    machineCode[PC].hex[3], 16)]))
                .value,
            16);
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x09000) {
      break;
    case 0x09000:
      // INPUT register, port_number
      /*const*/ port = parseInt(machineCode[PC].hex.substr(3), 16);
      if ((port === 2 || port === 3) && is_UART_enabled) {
        if (port === 3) {
          // UART_RX_PORT
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
              document.getElementById("UART_INPUT")
                  .value.charCodeAt(currentlyReadCharacterInUART);
          currentlyReadCharacterInUART++;
        } else if (port === 2)
          // UART_STATUS_PORT
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
              currentlyReadCharacterInUART <
                      document.getElementById("UART_INPUT").value.length
                  ? 0b00001000 /*U_RX_D*/
                  : 0;
        else {
          alert(
              "Internal simulator error: The simulator got into a forbidden state!");
          stopSimulation();
        }
      } else
        registers[regbank][parseInt(machineCode[PC].hex[2], 16)] = parseInt(
            document.getElementById("input_" + machineCode[PC].hex.substr(3))
                .value,
            16);
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x2c000) {
      break;
    case 0x2c000:
      // OUTPUT register,(register) ;Output the result of the first register to
      // the port specified by the second register.
      /*const*/ port = registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
      /*const*/ value =
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
      if ((port === 3 || port === 4) && is_UART_enabled) {
        if (port === 3)
          // UART_TX_PORT
          document.getElementById("UART_OUTPUT").innerText +=
              String.fromCharCode(value);
        else if (port === 4)
          // UART_RESET_PORT
          document.getElementById("UART_OUTPUT").innerText = "";
        else {
          alert(
              "Internal simulator error: The simulator got into a forbidden state!");
          stopSimulation();
        }
      } else
        output[registers[regbank][parseInt(machineCode[PC].hex[3], 16)]] =
            registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
      displayOutput();
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x2d000) {
      break;
    case 0x2d000:
      // OUTPUT register, port_number
      /*const*/ port = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ value =
          registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
      if ((port === 3 || port === 4) && is_UART_enabled) {
        if (port === 3)
          // UART_TX_PORT
          document.getElementById("UART_OUTPUT").innerText +=
              String.fromCharCode(value);
        else if (port === 4)
          // UART_RESET_PORT
          document.getElementById("UART_OUTPUT").innerText = "";
        else {
          alert(
              "Internal simulator error: The simulator got into a forbidden state!");
          stopSimulation();
        }
      } else {
        output[parseInt(machineCode[PC].hex.substr(3), 16)] =
            registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
        displayOutput();
      }
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x2b000) {
      break;
    case 0x2b000:
      // OUTPUTK constant, port_number
      /*const*/ value = parseInt(machineCode[PC].hex.substr(2, 2), 16);
      /*const*/ port = parseInt(machineCode[PC].hex[4], 16);
      if ((port === 3 || port === 4) && is_UART_enabled) {
        if (port === 3)
          // UART_TX_PORT
          document.getElementById("UART_OUTPUT").innerText +=
              String.fromCharCode(value);
        else if (port === 4)
          // UART_RESET_PORT
          document.getElementById("UART_OUTPUT").innerText = "";
        else {
          alert(
              "Internal simulator error: The simulator got into a forbidden state!");
          stopSimulation();
        }
      } else {
        output[parseInt(machineCode[PC].hex[4], 16)] =
            parseInt(machineCode[PC].hex.substr(2, 2), 16);
        displayOutput();
      }
      PC++;
      /*    } else if (currentDirective === 0x37000) {
            // REGBANK A
            regbank = 0;
            PC++;
          } else if (currentDirective === 0x37001) {
            // REGBANK B
            regbank = 1;
            PC++;
      */
      break;
    case 0x37000:
      if (currentDirective % 2 === 0)
        regbank = 0;
      else
        regbank = 1;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x22000) {
      break;
    case 0x22000:
      // JUMP label
      PC = parseInt(machineCode[PC].hex.substr(2), 16);
      /*    } else if (machineCode[PC].hex.substr(0, 2) === "14" &&
                     machineCode[PC].hex.substr(3) === "80") {
            // HWBUILD register
            flagC[regbank] =
                1; // Have a better idea? We can't simulate all of what this
         directive
            // does, but we can simulate this part of it.
            PC++;
      */
      break;
    case 0x14000:
      if ((currentDirective & 0x000ff) === 0x00080) {
        flagC[regbank] = 1;
      }
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x10000) {
      break;
    case 0x10000:
      // ADD register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      if ((firstValue + secondValue) % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (firstValue + secondValue > 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] += secondValue;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x11000) {
      break;
    case 0x11000:
      // ADD register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      if ((firstValue + secondValue) % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (firstValue + secondValue > 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] += secondValue;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x12000) {
      break;
    case 0x12000:
      // ADDCY register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue + secondValue + flagC[regbank];
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result > 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x13000) {
      break;
    case 0x13000:
      // ADDCY register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue + secondValue + flagC[regbank];
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result > 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x18000) {
      break;
    case 0x18000:
      // SUB register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue - secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x19000) {
      break;
    case 0x19000:
      // SUB register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue - secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x1a000) {
      break;
    case 0x1a000:
      // SUBCY register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue - secondValue - flagC[regbank];
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x1b000) {
      break;
    case 0x1b000:
      // SUBCY register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue - secondValue - flagC[regbank];
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x03000) {
      break;
    case 0x03000:
      // AND register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue & secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x02000) {
      break;
    case 0x02000:
      // AND register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue & secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x04000) {
      break;
    case 0x04000:
      // OR register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue | secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x05000) {
      break;
    case 0x05000:
      // OR register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue | secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x06000) {
      break;
    case 0x06000:
      // XOR register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue ^ secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x07000) {
      break;
    case 0x07000:
      // XOR register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue ^ secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      registers[regbank][firstRegister] = result;
      PC++;
      /*    } else if ((currentDirective & 0xff000) === 0x0c000 ||
                     (currentDirective & 0xff000) === 0x0e000) {
      */
      break;
    case 0x0c000:
    case 0x0e000:
      // TEST register, register ;The same as "AND", but does not store the
      // result (only the flags). I am not sure if there is a difference between
      // "0c" and "0e", they appear to be the same.
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue & secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      // registers[regbank][firstRegister] = result;
      PC++;
      /*    } else if ((currentDirective & 0xff000) === 0x0d000 ||
                     (currentDirective & 0xff000) === 0x0f000) {*/
      break;
    case 0x0d000:
    case 0x0f000:
      // TEST register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue & secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result % 256 === 255)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      // registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x1c000) {
      break;
    case 0x1c000:
      // COMPARE register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue - secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      // registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x1d000) {
      break;
    case 0x1d000:
      // COMPARE register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue - secondValue;
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      // registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x1e000) {
      break;
    case 0x1e000:
      // COMPARECY register, register
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      /*const*/ result = firstValue - secondValue - flagC[regbank];
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      // registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x1f000) {
      break;
    case 0x1f000:
      // COMPARECY register, constant
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
      /*const*/ result = firstValue - secondValue - flagC[regbank];
      if (result % 256 === 0)
        flagZ[regbank] = 1;
      else
        flagZ[regbank] = 0;
      if (result < 0)
        flagC[regbank] = 1;
      else
        flagC[regbank] = 0;
      // registers[regbank][firstRegister] = result;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x14000) {
      break;
    case 0x14000:
      // Bit-shifting operations...
      /*const*/ registerIndex = parseInt(machineCode[PC].hex[2], 16);
      /*let*/ registerValue = registers[regbank][registerIndex];
      console.log("DEBUG: Shifting the bits in register s" +
                  registerIndex.toString(16));
      const set_flags_after_shift_left = () => {
        flagC[regbank] = (registerValue > 255) | 0;
        flagZ[regbank] = (registerValue % 256 === 0) | 0;
      };
      const set_flags_before_shift_right = () => {
        flagC[regbank] = registerValue % 2;
        flagZ[regbank] = (Math.floor(registerValue / 2) === 0) | 0;
      };
      switch (machineCode[PC].hex.substr(3)) {
      case "06": // SL0
        registerValue <<= 1;
        set_flags_after_shift_left();
        break;
      case "07": // SL1
        registerValue = (registerValue << 1) + 1;
        set_flags_after_shift_left();
        break;
      case "04": // SLX
        registerValue = (registerValue << 1) + (registerValue % 2);
        set_flags_after_shift_left();
        break;
      case "00": // SLA
        registerValue = (registerValue << 1) + flagC[regbank];
        set_flags_after_shift_left();
        break;
      case "02": // RL
        registerValue = (registerValue << 1) + Math.floor(registerValue / 128);
        set_flags_after_shift_left();
        break;
      case "0e": // SR0
        set_flags_before_shift_right();
        registerValue >>= 1;
        break;
      case "0f": // SR1
        set_flags_before_shift_right();
        registerValue = (registerValue >> 1) + 128;
        break;
      case "0a": // SRX
        set_flags_before_shift_right();
        registerValue =
            (registerValue >> 1) + Math.floor(registerValue / 128) * 128;
        break;
      case "08": // SRA
        const oldFlagC = flagC[regbank];
        set_flags_before_shift_right();
        registerValue = (registerValue >> 1) + oldFlagC;
        break;
      case "0c": // RR
        set_flags_before_shift_right();
        registerValue = (registerValue >> 1) + 128 * (registerValue % 2);
        break;
      default:
        alert('The instruction "' + machineCode[PC].hex +
              '", assembled from line #' + machineCode[PC].line +
              ", hasn't been implemented yet, sorry about that!");
      }
      registers[regbank][registerIndex] = registerValue;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x32000) {
      break;
    case 0x32000:
      // JUMP Z, label
      if (flagZ[regbank])
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x36000) {
      break;
    case 0x36000:
      // JUMP NZ, label
      if (!flagZ[regbank])
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x3a000) {
      break;
    case 0x3a000:
      // JUMP C, label
      if (flagC[regbank])
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x3e000) {
      break;
    case 0x3e000:
      // JUMP NC, label
      if (!flagC[regbank])
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x26000) {
      break;
    case 0x26000:
      // JUMP@ (register, register) ; Jump to the address pointed by the
      // registers (something like function pointers, except that "return" won't
      // work).
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      PC = (firstValue % 16) * 256 + secondValue;
      //    } else if ((currentDirective & 0xff000) === 0x20000) {
      break;
    case 0x20000:
      // CALL functionName
      callStack.push(PC);
      PC = parseInt(machineCode[PC].hex.substr(2), 16);
      //    } else if ((currentDirective & 0xff000) === 0x30000) {
      break;
    case 0x30000:
      // CALL Z, functionName ; Call the function only if the Zero Flag is set.
      if (flagZ[regbank]) {
        callStack.push(PC);
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x34000) {
      break;
    case 0x34000:
      // CALL NZ, functionName ; Call the function only if the Zero Flag is not
      // set.
      if (!flagZ[regbank]) {
        callStack.push(PC);
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x38000) {
      break;
    case 0x38000:
      // CALL C, functionName ; Call the function only if the Carry Flag is set.
      if (flagC[regbank]) {
        callStack.push(PC);
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x3c000) {
      break;
    case 0x3c000:
      // CALL NC, functionName ; Call the function only if the Carry Flag is not
      // set.
      if (!flagC[regbank]) {
        callStack.push(PC);
        PC = parseInt(machineCode[PC].hex.substr(2), 16);
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x24000) {
      break;
    case 0x24000:
      // CALL@ (register, register) ; Jump the function pointed by the function
      // pointer stored in the registers.
      /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
      /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
      /*const*/ firstValue = registers[regbank][firstRegister];
      /*const*/ secondValue = registers[regbank][secondRegister];
      callStack.push(PC);
      PC = (firstValue % 16) * 256 + secondValue;
      //    } else if ((currentDirective & 0xff000) === 0x25000) {
      break;
    case 0x25000:
      // RETURN
      if (callStack.length)
        PC = callStack.pop() + 1;
      else {
        if (playing)
          clearInterval(simulationThread);
        alert("The program exited!");
      }
      //    } else if ((currentDirective & 0xff000) === 0x31000) {
      break;
    case 0x31000:
      // RETURN Z ; Return from a function only if the Zero Flag is set.
      if (flagZ[regbank]) {
        if (callStack.length)
          PC = callStack.pop() + 1;
        else {
          if (playing)
            clearInterval(simulationThread);
          alert("The program exited!");
        }
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x35000) {
      break;
    case 0x35000:
      // RETURN NZ ; Return from a function only if the Zero Flag is not set.
      if (!flagZ[regbank]) {
        if (callStack.length)
          PC = callStack.pop() + 1;
        else {
          if (playing)
            clearInterval(simulationThread);
          alert("The program exited!");
        }
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x39000) {
      break;
    case 0x39000:
      // RETURN C ; Return from a function only if the Carry Flag is set.
      if (flagC[regbank]) {
        if (callStack.length)
          PC = callStack.pop() + 1;
        else {
          if (playing)
            clearInterval(simulationThread);
          alert("The program exited!");
        }
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x3d000) {
      break;
    case 0x3d000:
      // RETURN NC ; Return from a function only if the Carry Flag is not set.
      if (!flagC[regbank]) {
        if (callStack.length)
          PC = callStack.pop() + 1;
        else {
          if (playing)
            clearInterval(simulationThread);
          alert("The program exited!");
        }
      } else
        PC++;
      //    } else if ((currentDirective & 0xff000) === 0x28000) {
      break;
    case 0x28000:
      // INTERRUPT ENABLE|DISABLE
      flagIE = machineCode[PC].hex[4] | 0;
      PC++;
      //    } else if ((currentDirective & 0xff000) === 0x29000) {
      break;
    case 0x29000:
      // RETURNI ENABLE|DISABLE
      flagIE = machineCode[PC].hex[4] | 0;
      if (callStack.length)
        PC = callStack.pop() + 1;
      else {
        if (playing)
          clearInterval(simulationThread);
        alert("The program exited!");
      }
      //    } else {
      break;
    default:
      alert(
          'Sorry about that, the simulator currently does not support the instruction "' +
          machineCode[PC].hex + '" (' + currentDirective + " & " + 0xff000 +
          " = " + (currentDirective & 0xff000) + "), assembled from line #" +
          machineCode[PC].line + ".");
      stopSimulation();
    }
    displayRegistersAndFlags();
    document.getElementById("PC_label_" + formatAsAddress(PC)).innerHTML =
        "-&gt;";
  } catch (error) {
    if (playing)
      clearInterval(simulationThread);
    alert("The simulator crashed! Error: " + error.message);
  }
}
#53
RE: PicoBlaze Simulator in JavaScript
(August 18, 2022 at 6:43 am)FlatAssembler Wrote: HappySkepticA map that calls different pieces of code.
You mean, like virtual methods

You don't use a switch statement to handle polymorphism.  That is the one "bad" use of a switch statement that I agree with.

Virtual methods are implemented by pointers to function calls.  The pointer gets set at object instantiation based on the type of object instantiated.  Each object instance has a virtual function table (if it has at least one virtual method).  There is one pointer in the table for each virtual method.

A map is different. A map of function pointers would allow one to find the function pointer based on a Key of some type (string or int, for instance).  Internally a Map uses a a tree or a hashtable, but you don't care how its implemented.

If you haven't used a map yet, you need to learn about them.  ES6 javascript has maps.  Any regular Object in javascript is a map with "string" as the type of key (though it has performance issues if you constantly change its contents).  However, a generic Map could use any type as the key (as long as the type is hashable and can be tested for equality).

In Flash, the name Dictionary was used instead of Map.  In some ways I like that description.
#54
RE: PicoBlaze Simulator in JavaScript
(August 18, 2022 at 9:08 am)FlatAssembler Wrote: So, @bennyboy , I have tried to convert that huge if-else to switch-case. However, the simulator stopped working then. Can you figure out what is going on?
Code:
"use strict";
function simulateOneInstruction() {
 try {
   PC = PC %
        4096; // If you are at the end of a program, and there is no "return"
   // there, jump to the beginning of the program. I think that's
   // how PicoBlaze behaves, though I haven't tried it.
   if (breakpoints.includes(machineCode[PC].line)) {
     alert("Reached breakpoint on the line #" + machineCode[PC].line + ".");
     if (playing)
       clearInterval(simulationThread);
     playing = false;
     document.getElementById("fastForwardButton").disabled = false;
     document.getElementById("singleStepButton").disabled = false;
     document.getElementById("UART_INPUT").disabled = false;
     document.getElementById("playImage").style.display = "inline";
     document.getElementById("pauseImage").style.display = "none";
   }
   document.getElementById("PC_label_" + formatAsAddress(PC)).innerHTML = "";
   const currentDirective = parseInt(machineCode[PC].hex, 16);
   // "bennyboy" from "atheistforums.org" thinks my program can be
   // speeded up by using a switch-case instead of the large if-else (that a
   // switch-case would compile into a more efficient assembly code), so it
   // would be interesting to investigate whether that's true:
   // https://atheistforums.org/thread-61911-post-2112817.html#pid2112817
   let port, firstRegister, secondRegister, firstValue, secondValue, result,
       value;
   switch (currentDirective & 0xff000) {
   //    if ((currentDirective & 0xff000) === 0x00000) {
   case 0x00000:
     // LOAD register,register
     registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
         registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x01000) {
     break;
   case 0x01000:
     // LOAD register,constant
     registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
         parseInt(machineCode[PC].hex.substr(3), 16);
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x17000) {
     break;
   case 0x17000:
     // STAR register,constant ;Storing a constant into an inactive register
     registers[!regbank | 0 /*That is how you convert a boolean to an integer
                                       in JavaScript.*/
     ][parseInt(machineCode[PC].hex[2], 16)] =
         parseInt(machineCode[PC].hex.substr(3), 16);
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x16000) {
     break;
   case 0x16000:
     // STAR register,register ;Copying from an active register into an
     // inactive one.
     registers[!regbank | 0][parseInt(machineCode[PC].hex[2], 16)] =
         registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x2e000) {
     break;
   case 0x2e000:
     // STORE register,(register) ;Store the first register at the memory
     // location where the second register points to.
     memory[registers[regbank][parseInt(machineCode[PC].hex[3], 16)]] =
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
     document
         .getElementById(
             "memory_" +
             formatAsByte(
                 registers[regbank][parseInt(machineCode[PC].hex[3], 16)]))
         .innerHTML = formatAsByte(
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)]);
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x2f000) {
     break;
   case 0x2f000:
     // STORE register,memory_address ;Copy a register onto a memory address.
     memory[parseInt(machineCode[PC].hex.substr(3), 16)] =
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
     document.getElementById("memory_" + machineCode[PC].hex.substr(3))
         .innerHTML = formatAsByte(
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)]);
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x0a000) {
     break;
   case 0x0a000:
     // FETCH register,(register) ;Dereference the pointer in the second
     // register.
     registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
         memory[registers[regbank][parseInt(machineCode[PC].hex[3], 16)]];
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x0b000) {
     break;
   case 0x0b000:
     // FETCH register,memory_address ;Copy the value at memory_address to the
     // register.
     registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
         memory[parseInt(machineCode[PC].hex.substr(3), 16)];
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x08000) {
     break;
   case 0x08000:
     // INPUT register,(register) ;Read a byte from a port specified by a
     // register.
     /*const*/ port = registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
     if ((port === 2 || port === 3) && is_UART_enabled) {
       if (port === 3) {
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
             document.getElementById("UART_INPUT")
                 .value.charCodeAt(currentlyReadCharacterInUART);
         currentlyReadCharacterInUART++;
       } else
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
             currentlyReadCharacterInUART <
                     document.getElementById("UART_INPUT").value.length
                 ? 0b00001000 /*U_RX_D*/
                 : 0;
     } else
       registers[regbank][parseInt(machineCode[PC].hex[2], 16)] = parseInt(
           document
               .getElementById("input_" +
                               formatAsByte(registers[regbank][parseInt(
                                   machineCode[PC].hex[3], 16)]))
               .value,
           16);
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x09000) {
     break;
   case 0x09000:
     // INPUT register, port_number
     /*const*/ port = parseInt(machineCode[PC].hex.substr(3), 16);
     if ((port === 2 || port === 3) && is_UART_enabled) {
       if (port === 3) {
         // UART_RX_PORT
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
             document.getElementById("UART_INPUT")
                 .value.charCodeAt(currentlyReadCharacterInUART);
         currentlyReadCharacterInUART++;
       } else if (port === 2)
         // UART_STATUS_PORT
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)] =
             currentlyReadCharacterInUART <
                     document.getElementById("UART_INPUT").value.length
                 ? 0b00001000 /*U_RX_D*/
                 : 0;
       else {
         alert(
             "Internal simulator error: The simulator got into a forbidden state!");
         stopSimulation();
       }
     } else
       registers[regbank][parseInt(machineCode[PC].hex[2], 16)] = parseInt(
           document.getElementById("input_" + machineCode[PC].hex.substr(3))
               .value,
           16);
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x2c000) {
     break;
   case 0x2c000:
     // OUTPUT register,(register) ;Output the result of the first register to
     // the port specified by the second register.
     /*const*/ port = registers[regbank][parseInt(machineCode[PC].hex[3], 16)];
     /*const*/ value =
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
     if ((port === 3 || port === 4) && is_UART_enabled) {
       if (port === 3)
         // UART_TX_PORT
         document.getElementById("UART_OUTPUT").innerText +=
             String.fromCharCode(value);
       else if (port === 4)
         // UART_RESET_PORT
         document.getElementById("UART_OUTPUT").innerText = "";
       else {
         alert(
             "Internal simulator error: The simulator got into a forbidden state!");
         stopSimulation();
       }
     } else
       output[registers[regbank][parseInt(machineCode[PC].hex[3], 16)]] =
           registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
     displayOutput();
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x2d000) {
     break;
   case 0x2d000:
     // OUTPUT register, port_number
     /*const*/ port = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ value =
         registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
     if ((port === 3 || port === 4) && is_UART_enabled) {
       if (port === 3)
         // UART_TX_PORT
         document.getElementById("UART_OUTPUT").innerText +=
             String.fromCharCode(value);
       else if (port === 4)
         // UART_RESET_PORT
         document.getElementById("UART_OUTPUT").innerText = "";
       else {
         alert(
             "Internal simulator error: The simulator got into a forbidden state!");
         stopSimulation();
       }
     } else {
       output[parseInt(machineCode[PC].hex.substr(3), 16)] =
           registers[regbank][parseInt(machineCode[PC].hex[2], 16)];
       displayOutput();
     }
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x2b000) {
     break;
   case 0x2b000:
     // OUTPUTK constant, port_number
     /*const*/ value = parseInt(machineCode[PC].hex.substr(2, 2), 16);
     /*const*/ port = parseInt(machineCode[PC].hex[4], 16);
     if ((port === 3 || port === 4) && is_UART_enabled) {
       if (port === 3)
         // UART_TX_PORT
         document.getElementById("UART_OUTPUT").innerText +=
             String.fromCharCode(value);
       else if (port === 4)
         // UART_RESET_PORT
         document.getElementById("UART_OUTPUT").innerText = "";
       else {
         alert(
             "Internal simulator error: The simulator got into a forbidden state!");
         stopSimulation();
       }
     } else {
       output[parseInt(machineCode[PC].hex[4], 16)] =
           parseInt(machineCode[PC].hex.substr(2, 2), 16);
       displayOutput();
     }
     PC++;
     /*    } else if (currentDirective === 0x37000) {
           // REGBANK A
           regbank = 0;
           PC++;
         } else if (currentDirective === 0x37001) {
           // REGBANK B
           regbank = 1;
           PC++;
     */
     break;
   case 0x37000:
     if (currentDirective % 2 === 0)
       regbank = 0;
     else
       regbank = 1;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x22000) {
     break;
   case 0x22000:
     // JUMP label
     PC = parseInt(machineCode[PC].hex.substr(2), 16);
     /*    } else if (machineCode[PC].hex.substr(0, 2) === "14" &&
                    machineCode[PC].hex.substr(3) === "80") {
           // HWBUILD register
           flagC[regbank] =
               1; // Have a better idea? We can't simulate all of what this
        directive
           // does, but we can simulate this part of it.
           PC++;
     */
     break;
   case 0x14000:
     if ((currentDirective & 0x000ff) === 0x00080) {
       flagC[regbank] = 1;
     }
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x10000) {
     break;
   case 0x10000:
     // ADD register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     if ((firstValue + secondValue) % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (firstValue + secondValue > 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] += secondValue;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x11000) {
     break;
   case 0x11000:
     // ADD register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     if ((firstValue + secondValue) % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (firstValue + secondValue > 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] += secondValue;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x12000) {
     break;
   case 0x12000:
     // ADDCY register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue + secondValue + flagC[regbank];
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result > 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x13000) {
     break;
   case 0x13000:
     // ADDCY register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue + secondValue + flagC[regbank];
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result > 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x18000) {
     break;
   case 0x18000:
     // SUB register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue - secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x19000) {
     break;
   case 0x19000:
     // SUB register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue - secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x1a000) {
     break;
   case 0x1a000:
     // SUBCY register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue - secondValue - flagC[regbank];
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x1b000) {
     break;
   case 0x1b000:
     // SUBCY register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue - secondValue - flagC[regbank];
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x03000) {
     break;
   case 0x03000:
     // AND register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue & secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x02000) {
     break;
   case 0x02000:
     // AND register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue & secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x04000) {
     break;
   case 0x04000:
     // OR register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue | secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x05000) {
     break;
   case 0x05000:
     // OR register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue | secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x06000) {
     break;
   case 0x06000:
     // XOR register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue ^ secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x07000) {
     break;
   case 0x07000:
     // XOR register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue ^ secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     registers[regbank][firstRegister] = result;
     PC++;
     /*    } else if ((currentDirective & 0xff000) === 0x0c000 ||
                    (currentDirective & 0xff000) === 0x0e000) {
     */
     break;
   case 0x0c000:
   case 0x0e000:
     // TEST register, register ;The same as "AND", but does not store the
     // result (only the flags). I am not sure if there is a difference between
     // "0c" and "0e", they appear to be the same.
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue & secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     // registers[regbank][firstRegister] = result;
     PC++;
     /*    } else if ((currentDirective & 0xff000) === 0x0d000 ||
                    (currentDirective & 0xff000) === 0x0f000) {*/
     break;
   case 0x0d000:
   case 0x0f000:
     // TEST register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue & secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result % 256 === 255)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     // registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x1c000) {
     break;
   case 0x1c000:
     // COMPARE register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue - secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     // registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x1d000) {
     break;
   case 0x1d000:
     // COMPARE register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue - secondValue;
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     // registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x1e000) {
     break;
   case 0x1e000:
     // COMPARECY register, register
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     /*const*/ result = firstValue - secondValue - flagC[regbank];
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     // registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x1f000) {
     break;
   case 0x1f000:
     // COMPARECY register, constant
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = parseInt(machineCode[PC].hex.substr(3), 16);
     /*const*/ result = firstValue - secondValue - flagC[regbank];
     if (result % 256 === 0)
       flagZ[regbank] = 1;
     else
       flagZ[regbank] = 0;
     if (result < 0)
       flagC[regbank] = 1;
     else
       flagC[regbank] = 0;
     // registers[regbank][firstRegister] = result;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x14000) {
     break;
   case 0x14000:
     // Bit-shifting operations...
     /*const*/ registerIndex = parseInt(machineCode[PC].hex[2], 16);
     /*let*/ registerValue = registers[regbank][registerIndex];
     console.log("DEBUG: Shifting the bits in register s" +
                 registerIndex.toString(16));
     const set_flags_after_shift_left = () => {
       flagC[regbank] = (registerValue > 255) | 0;
       flagZ[regbank] = (registerValue % 256 === 0) | 0;
     };
     const set_flags_before_shift_right = () => {
       flagC[regbank] = registerValue % 2;
       flagZ[regbank] = (Math.floor(registerValue / 2) === 0) | 0;
     };
     switch (machineCode[PC].hex.substr(3)) {
     case "06": // SL0
       registerValue <<= 1;
       set_flags_after_shift_left();
       break;
     case "07": // SL1
       registerValue = (registerValue << 1) + 1;
       set_flags_after_shift_left();
       break;
     case "04": // SLX
       registerValue = (registerValue << 1) + (registerValue % 2);
       set_flags_after_shift_left();
       break;
     case "00": // SLA
       registerValue = (registerValue << 1) + flagC[regbank];
       set_flags_after_shift_left();
       break;
     case "02": // RL
       registerValue = (registerValue << 1) + Math.floor(registerValue / 128);
       set_flags_after_shift_left();
       break;
     case "0e": // SR0
       set_flags_before_shift_right();
       registerValue >>= 1;
       break;
     case "0f": // SR1
       set_flags_before_shift_right();
       registerValue = (registerValue >> 1) + 128;
       break;
     case "0a": // SRX
       set_flags_before_shift_right();
       registerValue =
           (registerValue >> 1) + Math.floor(registerValue / 128) * 128;
       break;
     case "08": // SRA
       const oldFlagC = flagC[regbank];
       set_flags_before_shift_right();
       registerValue = (registerValue >> 1) + oldFlagC;
       break;
     case "0c": // RR
       set_flags_before_shift_right();
       registerValue = (registerValue >> 1) + 128 * (registerValue % 2);
       break;
     default:
       alert('The instruction "' + machineCode[PC].hex +
             '", assembled from line #' + machineCode[PC].line +
             ", hasn't been implemented yet, sorry about that!");
     }
     registers[regbank][registerIndex] = registerValue;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x32000) {
     break;
   case 0x32000:
     // JUMP Z, label
     if (flagZ[regbank])
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x36000) {
     break;
   case 0x36000:
     // JUMP NZ, label
     if (!flagZ[regbank])
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x3a000) {
     break;
   case 0x3a000:
     // JUMP C, label
     if (flagC[regbank])
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x3e000) {
     break;
   case 0x3e000:
     // JUMP NC, label
     if (!flagC[regbank])
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x26000) {
     break;
   case 0x26000:
     // JUMP@ (register, register) ; Jump to the address pointed by the
     // registers (something like function pointers, except that "return" won't
     // work).
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     PC = (firstValue % 16) * 256 + secondValue;
     //    } else if ((currentDirective & 0xff000) === 0x20000) {
     break;
   case 0x20000:
     // CALL functionName
     callStack.push(PC);
     PC = parseInt(machineCode[PC].hex.substr(2), 16);
     //    } else if ((currentDirective & 0xff000) === 0x30000) {
     break;
   case 0x30000:
     // CALL Z, functionName ; Call the function only if the Zero Flag is set.
     if (flagZ[regbank]) {
       callStack.push(PC);
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x34000) {
     break;
   case 0x34000:
     // CALL NZ, functionName ; Call the function only if the Zero Flag is not
     // set.
     if (!flagZ[regbank]) {
       callStack.push(PC);
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x38000) {
     break;
   case 0x38000:
     // CALL C, functionName ; Call the function only if the Carry Flag is set.
     if (flagC[regbank]) {
       callStack.push(PC);
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x3c000) {
     break;
   case 0x3c000:
     // CALL NC, functionName ; Call the function only if the Carry Flag is not
     // set.
     if (!flagC[regbank]) {
       callStack.push(PC);
       PC = parseInt(machineCode[PC].hex.substr(2), 16);
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x24000) {
     break;
   case 0x24000:
     // CALL@ (register, register) ; Jump the function pointed by the function
     // pointer stored in the registers.
     /*const*/ firstRegister = parseInt(machineCode[PC].hex[2], 16);
     /*const*/ secondRegister = parseInt(machineCode[PC].hex[3], 16);
     /*const*/ firstValue = registers[regbank][firstRegister];
     /*const*/ secondValue = registers[regbank][secondRegister];
     callStack.push(PC);
     PC = (firstValue % 16) * 256 + secondValue;
     //    } else if ((currentDirective & 0xff000) === 0x25000) {
     break;
   case 0x25000:
     // RETURN
     if (callStack.length)
       PC = callStack.pop() + 1;
     else {
       if (playing)
         clearInterval(simulationThread);
       alert("The program exited!");
     }
     //    } else if ((currentDirective & 0xff000) === 0x31000) {
     break;
   case 0x31000:
     // RETURN Z ; Return from a function only if the Zero Flag is set.
     if (flagZ[regbank]) {
       if (callStack.length)
         PC = callStack.pop() + 1;
       else {
         if (playing)
           clearInterval(simulationThread);
         alert("The program exited!");
       }
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x35000) {
     break;
   case 0x35000:
     // RETURN NZ ; Return from a function only if the Zero Flag is not set.
     if (!flagZ[regbank]) {
       if (callStack.length)
         PC = callStack.pop() + 1;
       else {
         if (playing)
           clearInterval(simulationThread);
         alert("The program exited!");
       }
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x39000) {
     break;
   case 0x39000:
     // RETURN C ; Return from a function only if the Carry Flag is set.
     if (flagC[regbank]) {
       if (callStack.length)
         PC = callStack.pop() + 1;
       else {
         if (playing)
           clearInterval(simulationThread);
         alert("The program exited!");
       }
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x3d000) {
     break;
   case 0x3d000:
     // RETURN NC ; Return from a function only if the Carry Flag is not set.
     if (!flagC[regbank]) {
       if (callStack.length)
         PC = callStack.pop() + 1;
       else {
         if (playing)
           clearInterval(simulationThread);
         alert("The program exited!");
       }
     } else
       PC++;
     //    } else if ((currentDirective & 0xff000) === 0x28000) {
     break;
   case 0x28000:
     // INTERRUPT ENABLE|DISABLE
     flagIE = machineCode[PC].hex[4] | 0;
     PC++;
     //    } else if ((currentDirective & 0xff000) === 0x29000) {
     break;
   case 0x29000:
     // RETURNI ENABLE|DISABLE
     flagIE = machineCode[PC].hex[4] | 0;
     if (callStack.length)
       PC = callStack.pop() + 1;
     else {
       if (playing)
         clearInterval(simulationThread);
       alert("The program exited!");
     }
     //    } else {
     break;
   default:
     alert(
         'Sorry about that, the simulator currently does not support the instruction "' +
         machineCode[PC].hex + '" (' + currentDirective + " & " + 0xff000 +
         " = " + (currentDirective & 0xff000) + "), assembled from line #" +
         machineCode[PC].line + ".");
     stopSimulation();
   }
   displayRegistersAndFlags();
   document.getElementById("PC_label_" + formatAsAddress(PC)).innerHTML =
       "-&gt;";
 } catch (error) {
   if (playing)
     clearInterval(simulationThread);
   alert("The simulator crashed! Error: " + error.message);
 }
}
Sorry, it's a bit too much for me to wade through.  I like the improved appearance with `switch`-- it really makes the code a lot more comfortable to read. After googling more about `switch` vs `if/else if` I'm not 100% confident that this will make much of a differnece, if any, in the speed of your simulation.

One thing I dislike about JavaScript is that if it encounters an error, it basically just aborts-- whereas C# will display the entire call stack, give detailed error messages, and so on.  So you'll have to start writing debug messages to console, adding breakpoints, and so on, to find the fail point.  (I admit thought that I don't know much about setting up a JS development environment, so maybe getting error messages is easy but I just don't know)
#55
RE: PicoBlaze Simulator in JavaScript
Please learn to use the Developer (F12) console in Chrome.

Performance testing your Decimal to Binary when using FastForward shows that the time is almost 100% your setting (and the rendering) of DOM elements. I haven't tested the compiler - I presume that isn't what you are complaining about.

Also, your use of a timer to instantiate the next instruction is a good idea "IF" you want every instruction to show on screen, but terrible if you want performance. The timer event will go in the back of the queue, and the DOM update work will be done first.

Looking at the source after the profiling in the F12 dev console even shows timings for certain hot spots. It is all about changing the innerHtml or innerText of your DOM elements (and the Paint time to render them).

You can even debug through the run to make sure you aren't doing the same work 100x or some other bad thing, but otherwise it looks like your html work.

In "real" programs, one would do as much work as possible in one frame, and then do the DOM update once per frame. This is done by calling requestAnimationFrame(), and only doing the DOM work on that callback (and only doing DOM work if at least one command has been executed since the last update).

You could still use your timer for scheduling each instruction, but it would run 1000x faster than current. A timer for each instruction is not fast either, because of the overhead in scheduling the microtask, but is nowhere near the performance problem you have now.

One other note - I don't know if this is a problem or not in your GUI, but don't use the DOM as your state. If you do, you can't get around adjusting it on every instruction. The DOM is a view of your state, not the state itself. So, you should be able to run 1000 instructions before updating it. If your design doesn't allow that, you haven't designed it well.

Also - logging to the console slows things down, especially when that console is open, but slightly even when its not. Make sure you only log at spots where you don't care about performance. Often I'll put some global "enableLogging" flag, and only log if that is on (if I'm not already using a proper logging framework).
#56
RE: PicoBlaze Simulator in JavaScript
(August 17, 2022 at 3:34 pm)bennyboy Wrote: Now, personally, I don't think that this difference will matter much unless you have hamsters running the code, but it seems like a lot of unnecessary operations to me

Yup, it's hamsters Cool
#57
RE: PicoBlaze Simulator in JavaScript
bennyboy Wrote:Sorry, it's a bit too much for me to wade through.
I found one error, namely, the bit-shifting operations were not running at all because the 0x14000 was mentioned two times in "case" (and, apparently, the SpiderMonkey engine in Firefox did not warn me about that, it simply went with when it was mentioned first, in HWBUILD). But the examples still don't run correctly even after I fixed that. Well, if you want to, you can fork me on GitHub and make a Pull Request in which you fix the rest. I will not spend my time doing that myself, since it seems quite obvious now it will not speed up my simulator significantly.
HappySkeptic Wrote:Performance testing your Decimal to Binary when using FastForward shows that the time is almost 100% your setting (and the rendering) of DOM elements.
Well, I must say I strongly suspected that to be the case.
HappySkeptic Wrote:Yup, it's hamsters
What does that mean?
#58
RE: PicoBlaze Simulator in JavaScript
(August 18, 2022 at 12:00 pm)FlatAssembler Wrote:
HappySkeptic Wrote:Yup, it's hamsters
What does that mean?

It is a joke.  It means there are a group of hamsters are doing the calculations somewhere in the computer (as that would be about the same speed).

In your case, the DOM is taking the place of the hamsters, slowing everything down.  I've explained how you would fix it, if you care to.
#59
RE: PicoBlaze Simulator in JavaScript
(August 18, 2022 at 12:12 pm)HappySkeptic Wrote:
(August 18, 2022 at 12:00 pm)FlatAssembler Wrote: What does that mean?

It is a joke.  It means there are a group of hamsters are doing the calculations somewhere in the computer (as that would be about the same speed).

In your case, the DOM is taking the place of the hamsters, slowing everything down.  I've explained how you would fix it, if you care to.

OK, I will try to fix that some time later.
#60
RE: PicoBlaze Simulator in JavaScript
By the way, I was considering making a social network where people can share assembly-language PicoBlaze programs and comment on each other's assembly-language PicoBlaze programs. I have already made a (not yet perfect) PicoBlaze Assembler and PicoBlaze Simulator. Where should I continue from here? I suppose the front-end is mostly made now, now I should make a back-end. And I know next to nothing about back-end development. So, what should I learn? Do you have some framework to recommend that will help me significantly to make that?



Possibly Related Threads...
Thread Author Replies Views Last Post
  A weird bug in the preprocessor of PicoBlaze Simulator in JavaScript FlatAssembler 81 9180 December 19, 2023 at 4:46 pm
Last Post: BrianSoddingBoru4
  Reformatting tools for JavaScript FlatAssembler 0 449 June 14, 2020 at 10:13 am
Last Post: FlatAssembler
  Anatomy of religion in RELIGION SIMULATOR game gravitysoftware 12 3369 February 8, 2015 at 12:52 pm
Last Post: c172
  Analysis of a Facebook social engineering/Javascript "hack" Autumnlicious 4 3491 March 2, 2011 at 9:29 am
Last Post: fr0d0



Users browsing this thread: 3 Guest(s)