Documentation


Index


Description

This tool allows you to easily program the data for memory bank systems in Survivalcraft. It supports three different use cases: ROM, Selector ROM, and FSM.


Code

Code Format

Within the code field you can write an anonymous arrow function in javascript. The function must take an array of numbers and an optional number as parameters, and it must return an array of numbers. This optional number is the location index when you want many sets of banks with the same inputs each (called Selector ROM on this page) but you wont need it often. The amount of numbers in the input and output arrays and the sizes in bits of each number within the arrays are determined by the "input types" and "output types" text fields. The amount of types within these type fields must match the amount of numbers within their corresponding arrays.

Type Format

(number[], number) => number[]

Format Example

([parameter1, parameter2, parameter3, parameter4], locationIndex) => {
	return [parameter1, parameter2, parameter3, parameter4]
}

Running Code

When you press the run button, the function will be evaluated for every input of your memory bank, the output of the function will be saved as memory bank data. It will be encoded in data types and split over digits.

Relevant Javascript Documentation


Location Index Parameter

The location index parameter represents the index of the memory bank set within the code. This parameter is only needed when you want to generate an array of memory banks with alterations depending on its index (this parameter). On this page, this is referred to as a "Selector ROM".


Code Examples

  1. ROM Calculations

    1. Addition

      locations: 1
      input sizes: 4, 4
      output sizes: 5
      ([x, y]) => {
      	return [x + y];
      }
    2. Subtraction

      locations: 1
      input sizes: 4, 4
      output sizes: 4, 1
      ([x, y]) => {
      	return [x - y];
      }
    3. Subtraction Without Using Types (Sign-Magnitude-Integer Representation)

      locations: 1
      input sizes: 4, 4
      output sizes: INT_5
      ([x, y]) => {
      	const value = x - y;
      	return [Math.abs(value), (value < 0) ? 1:0];
      }
  2. ROM Conversions

    1. 8-bit Hexadecimal To 3-digit Decimal

      locations: 1
      input sizes: 8
      output sizes: 4, 4, 4
      ([x]) => {
      	const digit1 = x % 10;
      	const digit2 = Math.floor(x/10) % 10;
      	const digit3 = Math.floor(x/100) % 10;  
      	return [digit1, digit2, digit3];
      }
    2. 3-digit Decimal Input To 8-bit Hexadecimal

      locations: 1
      input sizes: 4, 4, 4
      output sizes: 8
      ([digit1, digit2, digit3]) => {
      	return [digit1 + digit2 * 10 + digit3 * 100];
      }
    3. Sign-magnitude Integer To Two's Complement Integer

      locations: 1
      input sizes: SM_INT_8
      output sizes: INT_8
      ([x]) => {
      	return [x];
      }
    4. Bit Mask And Shift Left

      locations: 1
      input sizes: 4
      output sizes: 8
      ([x]) => {
      	const bitShiftLeftAmount = 1;
      	const bitMask = 0b1111;
      	return [(x & bitMask)<<bitShiftLeftAmount];
      }
    5. Bit Mask And Shift Right

      locations: 1
      input sizes: 4
      output sizes: 8
      ([x]) => {
      	const bitShiftRightAmount = 1;
      	const bitMask = 0b1111;
      	return [((x & bitMask)<<4)>>bitShiftRightAmount];
      }
  3. FSM Smart Counters

    1. 8-bit Increment-only Counter

      that resets on the maximum value (aka "FF", "1111 1111"). Trigger the memory bank clock inputs to increment. Send the maximum signal (aka "F", "1.5v", "1111") to both input wires to reset it.
      locations: 1
      input sizes: 8
      output sizes: 8
      ([x]) => {
      	if(x == 0xff) return 0;
      	return [x + 1];
      }
    2. 4-bit Counter With Overflow

      Inputs: keep, reset, increment, decrement
      locations: 1
      input sizes: 4, 1, 1
      output sizes: 4
      ([position, isMoving, isDecrementing]) => {
      	const maxPosition = 15;
      	const minPosition = 0;
      	const overflowValue = minPosition;
      	const underflowValue = maxPosition;
      	if (isMoving == 0) {
      		if (isDecrementing != 0) {
      			position = 0;
      		}
      	} else {
      		if(position >= maxPosition && isDecrementing == 0) {
      			position = overflowValue
      		} else if(position <= minPosition && isDecrementing != 0) {
      			position = underflowValue
      		} else {
      			position += (isDecrementing != 0 ? -1 : 1);
      		}
      	}
      	return [position];
      }

      Configurations

      1. 4-bit Counter Without Overflow

        locations: 1
        input sizes: 4, 1, 1
        output sizes: 4
        const maxPosition = 15;
        const minPosition = 0;
        const overflowValue = position;
        const underflowValue = position;
      2. 6-bit Counter FSM With Overflow

        locations: 1
        input sizes: 6, 1, 1
        output sizes: 6
        const maxPosition = 63;
        const minPosition = 0;
        const overflowValue = minPosition;
        const underflowValue = maxPosition;
      3. 6-bit Counter FSM Without Overflow

        locations: 1
        input sizes: 6, 1, 1
        output sizes: 6
        const maxPosition = 63;
        const minPosition = 0;
        const overflowValue = position;
        const underflowValue = position;
    3. 6-bit Pong Ball Controller FSM

      locations: 1
      input sizes: 6, 1, 1
      output sizes: 6, 1
      ([position, isDecrementing, isResetting]) => {
      	const maxPosition = 63;
      	const minPosition = 0;
      	if (isResetting != 0) {
      		position = 0;
      		isDecrementing = 0;
      	} else {
      		if (position >= maxPosition && isDecrementing == 0) {
      			isDecrementing = 1;
      		} else if (position <= minPosition && isDecrementing != 0) {
      			isDecrementing = 0;
      		} else {
      			position += (isDecrementing != 0 ? -1 : 1);
      		}
      	}
      	return [position, isDecrementing];
      }
    4. 2-bit CPU

      with instructions: halt, load, subtract, branch if not zero, and a program to decrement until zero is reached. Given the small instruction set (2-bit gives only four different instructions) you should adjust it per program. Inspired by The Little Man Computer (Online Demo)
      locations: 1
      input sizes: 2, 2, 2, 2, 1
      output sizes: 2, 2, 2, 2, 1
      ([programCounter, accumulator, addressRegister, instructionRegister, isCycleExecute]) => {
      	const assemblyProgram = [
      		{instruction: 1, address: 0}, // load the first value from programData into the accumulator
      		{instruction: 2, address: 1}, // subtract the second value of programData from the accumulator
      		{instruction: 3, address: 0}, // jump (branch) to the first instruction if the accumulator is not zero
      		{instruction: 0, address: 0}, // stop the program
      	];
      	const programData = [3, 1, 0, 0];
      	if (isCycleExecute == 0){
      		isCycleExecute = 1;
      		instructionRegister = assemblyProgram[programCounter].instruction;
      		addressRegister = assemblyProgram[programCounter].address;
      	} else {
      		isCycleExecute = 0;
      		programCounter += 1;
      		switch(instructionRegister){
      			case 0: // halt (aka HLT)
      				programCounter -= 1;
      				isCycleExecute = 0;
      				break;
      			case 1: // load (aka LDA)
      				accumulator = programData[addressRegister];
      				break;
      			case 2: // subtract (aka SUB)
      				accumulator -= programData[addressRegister];
      				break;
      			case 3: // branch if not zero
      				if(accumulator != 0){
      					programCounter = addressRegister;
      				}
      				break;
      			// store (STA) can be implemented with additional ROMs to manage the clock and writing data inputs of the memory banks depending on these parameters
      		}
      	}
      	return [programCounter, accumulator, addressRegister, instructionRegister, isCycleExecute];
      }
  4. Selector ROM

    1. 4-bit Selector

      locations: 16
      input sizes: 4
      output sizes: 4
      ([x], bankPosition) => {
      	return [x == bankPosition ? 0xf : 0x0];
      }

      Configurations

      1. 8-bit Selector

        locations: 256
        input sizes: 8
        output sizes: 4
    2. Horizontal Segment AND-grid Driver For 4-LED Plotters

      locations: 16
      input sizes: 4
      output sizes: 4
      ([x], bankPosition) => {
        const values = [0x3, 0xc];
        const isActive = Math.floor(x/values.length) == bankPosition;
        return [!isActive ? 0x0 : values[x % values.length]];
      }

      Configurations

      1. Vertical Segment AND-grid Driver For 4-LED Plotters

        const values = [0x5, 0xa];
    3. 4-bit Multiplexer

      locations: 16
      input sizes: 4, 4
      output sizes: 4
      ([x, y], bankPosition) => {
      	return [y == bankPosition ? x : 0x0];
      }

Amount Input

The amount input field is a single number that represents the amount of memory bank sets to generate. Usually this should be left as "1" unless you want to generate an array of memory banks with alterations depending on its index. On this page, this is referred to as a "Selector ROM". Note that the number of banks required scales linearly, more information here.


Input Sizes Input

The input sizes input field lets you set the format of the input of your system as a set of segments separated by commas. Each segment can be either be defined by a number, which is its size in bits, or a data type. Note that the number of banks required scales exponentially, more information here.


Output Sizes Input

The output sizes input field lets you set the format of the output of your system as a set of segments separated by commas. Each segment can be either be defined by a number, which is its size in bits, or a data type. Note that the number of banks required scales linearly, more information here.


Data Types

Each value may be split up into multiple smaller parameters of specific sizes in bits. Allowing them to be treated separately. You can use the "input types" and "output types" fields to set the amount of parameters and their types and sizes. This can be done by typing a numbers (sizes in bits) separated by commas, each number is a parameter. Instead of a number you may type a datatype, the code supports the following datatypes:

  1. Unsigned Integer Types
  2. An unsigned integer is a whole number that can not be negative.

    Format

    On this online tool it is formatted as "U_INT_[n]" (without the quotes) (where "[n]" is any number representing the size in bits)

    Unsigned Integer Examples

    Type Bits Range
    U_INT_1 1-bit 0 to 1
    U_INT_2 2-bit 0 to 3
    U_INT_3 3-bit 0 to 7
    U_INT_4 4-bit 0 to 15
    U_INT_5 5-bit 0 to 31
    U_INT_6 6-bit 0 to 63
    U_INT_7 7-bit 0 to 127
    U_INT_8 8-bit 0 to 255
    U_INT_16 16-bit 0 to 65,535
    U_INT_32 32-bit 0 to 4,294,967,295
    U_INT_64 64-bit 0 to 18,446,744,073,709,551,615

    Other Unsigned Integer Forms

    Type Equivalent To Bits Range
    [n] U_INT_[n] n bits 0 to (2ⁿ - 1)
    BYTE U_INT_8 8-bit 0 to 255
    NIBBLE U_INT_4 4-bit 0 to 15
    BIT U_INT_1 1-bit 0 to 1
    FLAG U_INT_1 1-bit 0 to 1
    BOOLEAN U_INT_1 1-bit 0 to 1
    BOOL U_INT_1 1-bit 0 to 1

  3. (Signed) Integer Types
  4. A signed integer (aka integer) is a whole number that can be negative.

    Two's Complement

    This tool uses two's complement to represent negative numbers in binary, here the most significant bit has been negated. For example a four bit number's bits in 2s complement would be valued: 1, 2, 4, -8 instead of 1, 2, 4, 8.

    Format

    on this online tool it is formatted as "INT_[n]" (without the quotes) (where "[n]" is any number representing the size in bits)

    2s Complement Signed Integer Examples

    Type Bits Range
    INT_1 1-bit -1 to 0
    INT_2 2-bit -2 to 1
    INT_3 3-bit -4 to 3
    INT_4 4-bit -8 to 7
    INT_5 5-bit -16 to 15
    INT_6 6-bit -32 to 31
    INT_7 7-bit -64 to 63
    INT_8 8-bit -128 to 127
    INT_16 16-bit -32,768 to 32,767
    INT_32 32-bit -2,147,483,648 to 2,147,483,647
    INT_64 64-bit -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

    Sign-Magnitude Signed Integers

    The tool also supports the Sign-magnitude representation of negative numbers, this is simpler but not reccommended as it has some flaws. Here the sign bit inverts the number by multiplying it by "-1" when it is high. The main reason this tool suports it is so you can convert numbers formatted in sign-magnitude to 2s complement making it easier to input negative numbers. Note: the sign-magnitude representation can represent both "0" and "-0" the latter of which is not a number, the 2s complement on the other hand does not have negative zero and can display an extra negative number.

    Sign-Magnitude Signed Integer Examples

    Type Bits Range
    SM_INT_1 1-bit -0, +0 (Only represents zero)
    SM_INT_2 2-bit -1 to +1
    SM_INT_3 3-bit -3 to +3
    SM_INT_4 4-bit -7 to +7
    SM_INT_5 5-bit -15 to +15
    SM_INT_6 6-bit -31 to +31
    SM_INT_7 7-bit -63 to +63
    SM_INT_8 8-bit -127 to +127
    SM_INT_16 16-bit -32,767 to +32,767
    SM_INT_32 32-bit -2,147,483,647 to +2,147,483,647
    SM_INT_64 64-bit -9,223,372,036,854,775,807 to +9,223,372,036,854,775,807

  5. Floating Point Number Types
  6. Currently unsupported

Optional Parameters

Offset

Currently unsupported


Bank Names

Each memory bank has a code-name like "Bank L0-I0-D0" for the first bank. These names are shown above each bank's generated data and above each bank in the generated circuit. In these names the L, I, and D stand for Location, Input, and Digit respectively. Here is an overview:

Letter Parameter Function If the value was 0 If the value was 1 If the value was 2
L location Which set of banks this bank is part of first set second set third set
I input Which set of 256 input values this bank will handle. input values 0 to 255 input values 256 to 511 input values 512 to 767
D digit Which set of four bits in the output this bank will generate. output bits 0 to 3 output bits 4 to 7 output bits 8 to 11

Data

Whenever memory bank data is generated you may need more than one memory bank to contain it, these are structured by three metrics:

  1. digit: Whenever you need numbers larger than 15 (aka "F") to be output from your memory bank, you will need more digits.
  2. input: Whenever you need numbers larger than 255 (aka "FF") to be input into your memory bank, you will need more banks to handle the higher ranges
  3. location: Whenever you are making a selector ROM system you will need a ROM for each physical location
  4. Using this makes your ROM a selector ROM (a therm I made up but will be using throughout this site)
When designing a system using this tool, consider how many bits you really need as each SurvivalCraft memory bank has an 8-bit address input and a 4-bit output. The amount of memory banks required is:
banks = Locations × floor ( OutputSize 4 ) × 2 max ( 0 , InputSize - 8 )
This calculation should be shown automatically each time data is generated.
As you can see the input size scales exponentially: for one 4-bit-output-and-16-bit-input system you need 256 banks:
(1) × floor ( (4) 4 ) × 2 max ( 0 , (16) - 8 ) = 256


Generated Circuit

A circuit is generated alongside the data. Wire labels refer to the overview table shown after generating, memory bank labels refer to the generated data apart from the labels "4-bit Multiplexer" and "8-bit Selector", these refer to presets with the same name.

Image Artifacts

The image is made up of multiple screenshots from SurvivalCraft stitched together and may cause some slight graphical artifacts as seen below. The circuit part shown in that image may be treated as a single trench with a wire in it.

graphical artifact of two circuit tile images stitched together

Bank Preset labels

If labels such as "4-bit Multiplexer L0" and "8-bit Selector L0" are shown in your generated circuit then your circuit needs more than two input wires, the additional wires are then combined using specially programmed memory banks. The data for these memory banks can be found in the presets section, the presets have the same names as the labels: "4-bit Multiplexer" and "8-bit Selector". The "L" suffix ("L0", "L1", "L2", etc.) of the label refers to the variant of the generated preset to use, this is its location index. Here is how to get their data.

  1. Open the presets page on a new tab
  2. Click "4-bit Multiplexer" or if your label said "8-bit Selector" click that instead
  3. Scroll down and click generate
  4. Scroll down to the "Generated Data" section
  5. Copy the data you need, look at the table below to find which bank to use for the given label suffix. Continue the pattern for lables with "L4" or higher.
    Label Bank
    L0 Bank L0-I0-D0
    L1 Bank L1-I0-D0
    L2 Bank L2-I0-D0
    L3 Bank L3-I0-D0


Common Errors

Unfortunately, there are few error messages available to be shown to you. If the code you wrote in the code input field does not work, you could open your browsers web debugger to view if the site throws any errors in the console. Instead you could also replace the code with one of the example programs to figure out which parts of your code work and which do not.


File Importing

If you wanted to use files you could create a javascript array like shown below.

const myFile = [];
Then you can paste a set of comma-separated-values between the square brackets, to get such values from a file I recommend using this online file to hexadecimal number converter. Leave the "Use 0x and comma as separator (C-like)" setting checked, uncheck "Insert newlines after each 16B" to keep your code block from becoming hard to scroll through, Upload the file you want to import, and click "copy output to clipboard". After which you can paste the data between the brackets in your code block. Now your code can access the file data as an array of bytes (written in hexadecimal).


Similar software

Professionals use Verilog to program the internal wiring of chips. This is remotely similar to this tool:

  1. In both tools, variables have a size defined as a number of bits.
  2. The ROM circuit application of this tool is similar to using the blocking assignment in Verilog.
  3. The FSM circuit application of this tool is similar to using the non-blocking assignment in Verilog. This similarity I found when building FSMs in Verilog.


Contribute

If you have an idea for a feature or bug fix for this site, you can fork it, develop it and make a pull request. If I see it, it works, and I like it, I will add it to this project.