Eight Bit Computer

Welcome to the documentation for the 8 bit computer project!

Overview

Usage

Setup

Inputting Programs

Running and Resetting

Modules

Register

A register stores an 8 bit number. In the computer registers are connected to the bus and can read a value from the bus or assert a value onto the bus. They also provide an output which is the currently stored value.

As well as the data oriented ACC, A, B and C registers, the control oriented instruction register, stack pointer and memory address register are all just registers. The memory address register only needs to read from the bus and not assert values onto it so it’s slightly simpler.

Interface and Operation

This is the interface of a register:

_images/register_block.png

This is how it operates:

Name Bit width Description
data 8 Reads bits from, or asserts bits onto this connection
contents 8 Always outputs the current value held in the register
clock 1 Clock signal from the clock module
input_enable 1 While high, the register stores the value on data on a rising clock edge
output_enable 1 While high, the register asserts it’s content onto data

Implementation

  • A 74HCT377 is used to store the contents of the register.
  • A 74HCT245 is used to provide a tri-state output to allow asserting values onto the bus, or not.
  • LEDs with current limiting resistors are used to display the current contents.
  • A 74HCT04 is used to invert the incoming input_enable and output_enable signals to drive the active low inputs on the 74HCT377 and 74HCT245.

The electronics are laid out on the breadboard like so:

_images/register_bus_left_bb.png

Due to the central bus in the layout of the computer it’s convenient to also have a version where the connection to the bus is on the right:

_images/register_bus_right_bb.png

An input only register like the memory address register is simpler:

_images/register_input_only_right_bb.png

2:1 Selector

The 2:1 selector outputs one of two 8 bit inputs. Also known as a multiplexer.

Interface and Operation

This is the interface of a 2:1 selector:

_images/two_to_one_block.png

This is how it operates:

Name Bit width Description
A 8 First input
B 8 Second input
out 8 Output, either A or B.
A/B 1 When low, A is output, when high, B is output

Implementation

  • Two 74HCT157s are used to choose between the two inputs.
  • LEDs with current limiting resistors are used to display the output.

Due to 2:1 selectors being required in different parts of the computer, the layout on the breadboard of A, B and out sometimes need to differ.

This is the A B out layout:

_images/two_to_one_A_B_O_bb.png

This is the out A B layout:

_images/two_to_one_O_A_B_bb.png

Different coloured wires are used in the Fritzing images above to help track the A, B and out bits. When creating the computer only green wire was available :(.

Program Counter

The program counter stores an eight bit number used to track the current position/instruction in program memory.

It’s similar to a regular register but has some extra functionality:

  • It can add one to it’s current value.
  • It’s value can be asynchronously reset to 0.

Interface and Operation

This is the interface to the program counter:

_images/program_counter_block.png

This is how it operates:

Name Bit width Description
data 8 Reads bits from, or asserts bits onto this connection.
contents 8 Always outputs the current value held in the program counter.
clock 1 Clock signal from the clock module.
input_enable 1 While high, the program counter stores the value on data on a rising clock edge.
count 1 While high, the program counter increments the stored value by one on a rising clock edge.
output_enable 1 While high, the program counter asserts it’s content onto data.

Implementation

  • Two 74HCT161s are used to store, increment and clear the 8 bit value.
  • A 74HCT245 is used to provide a tri-state output to allow asserting values onto the bus, or not.
  • A 74HCT04 is used to invert the incoming input_enable, output_enable and count signals to drive the active low inputs on the 74HCT161s and 74HCT245.
  • LEDs with current limiting resistors are used to display the current contents.

The electronics are laid out on the breadboard like so:

_images/program_counter_bb.png

Clock

The clock produces signals that synchronise the operation of all the other modules in the computer.

It can drive the operation of the computer step by step for debugging or at an arbitrary speed.

Interface and Operation

This is the interface to the clock:

_images/clock_block.png

And this is how it operates:

Name Bit width Description
auto/manual 1 When low, the clock signals are advanced manually with manual_input. When high, clock singals are advanced automatically.
manual_clock_input 1 High/low transitions here will advance the clock signals.
halt 1 While high, bring both of the clock signals low and stop them advancing.
reset 1 While high, bring both of the clock signals low and stop them advancing.
data_clock 1 Alternates between high and low, driving the data transitions in the computer.
control_clock 1 Alternates between high and low, driving the control signal transitions in the computer.
Control and Data Clocks

Typically the clocks in other computers invert data_clock to create control_clock, like this:

_images/inverted_data_clock.png

This is not suitable for this computer due to some restrictions on the 74HCT161 used in the Program Counter. Here is the note from the datasheet:

Note

  1. The High-to-Low transition of PE or TE on the ’HC/HCT161 and the ’HC/HCT163 should only occur while CP is HIGH for conventional operation.
  2. The Low-to-High transition of SPE on the ’HC/HCT161 and SPE or MR on the ’HC/HCT163 should only occur while CP is HIGH for conventional operation.

This means:

  • PE going from high to low (i.e. disabling counting) must happen while data_clock is high.
  • SPE going from low to high (i.e. disabling parallel load mode) must happen while data_clock is high.

The inverted clock method doesn’t satisfy this constraint as control signal changes (which happen a short delay after the rising edge of control_clock) would occur after data_clock had gone low. The delay is introduced by the EEPROMs in the Control Unit settling after a new instruction/flag/micro-step value goes onto their address lines. This demonstrates the problem using the PE control (_SPE is the same):

_images/inverted_data_clock_problem.png

To satisfy this constraint, the two clock signals proceed like this:

_images/offset_clock_signals.png

This means data_clock is still high when PE/count_enable changes:

_images/offset_clock_signals_fix.png
Halt and reset

Once halt or reset go high, both data_clock and control_clock immediately go low. Once halt and reset go low again after either becomes high, data_clock should be the first to go high and the then sequence continues. This is to ensure correct timing gaps are left when operation of the computer resumes.

Implementation

Even duty cycle

The 555 astable circuit used does not always output a signal with an even duty cycle. To fix this, the output from the 555 is fed into the clock of a JK flip flop configured to toggle. This halves the frequency of the astable output but guarantees it’s at a 50% duty cycle.

Block diagram

Logically the clock is implemented as follows:

_images/clock_schematic_ideal.png

From left to right:

  • Manual and 555 clock signals.
  • Feed the 555 into a JK flip flop configured to toggle to achieve even duty cycle.
  • Multiplex to choose the manual or auto clock.
  • Halt and reset signals.
  • Safe Clock Enable. This ensures correct timing of the clock after reset is released.
  • Two JK flip flops configured to toggle, one fed with the inverse of the gated clock signal to be the delayed signal for the control clock.

However, in reality the layout is equivalent, but a little more complex due to implementation details in the other chips (active low inputs) and trying to make the best use of space and gates available on chips:

_images/clock_schematic_reality.png
Hardware

There is also some debouncing that happens on the clock for the manual signals.

The following electronics are used:

  • A 555 and accompanying resistors and capacitors to generate the auto clock signal.
  • A 74HCT109 to get an even duty cycle from the 555.
  • A 74HCT14 and accompanying resistors and capacitors to debounce the manual inputs.
  • A 74HCT00 to create a multiplexer to select between the manual and auto clock signals.
  • A 74HCT02 and 74HCT00 to create the safe clock gate and some additional signal inverting.
  • Another 74HCT109 to provide the last 2 toggles for the clocks.

The components are laid out on the breadboard like so:

_images/clock_bb.png

The clock module is the first in the list to be redesigned :).

RAM

The RAM provides 256 bytes of program memory (instruction bytes) and 256 bytes of data memory (global variables and the stack).

Interface and Operation

This is the interface of the RAM:

_images/ram_block.png

This is how it operates:

Name Bit width Description
input_enable 1 While high, the data on data_in will be stored at the currently selected address on a rising clock edge.
output_enable 1 While high, the RAM asserts the data at the currently selected address onto data_out.
data_clock 1 Clock signal from the clock module (data_clock).
setup_clock 1 Clock signal from the manual setup switch.
run_setup 1 When low, the RAM is in setup mode, when high, run mode.
control_unit_select_data_memory 1 Program/data memory selection switch from the control unit.
setup_select_data_memory 1 Program/data memory selection switch from the manual setup switch.
data_in 8 Data to be stored is read from here.
data_out 8 Data at current address is output here.
address 8 Index in memory to read from or write to.

Implementation

Combined Input/Output pins

Unlike the 74LS189 chip that Ben Eater uses for his RAM, the 6116SA used in this computer has combined input and output pins. This means that care is required not to apply signals to those pins at incorrect times. A tri state buffer is used on the input to achieve this. The connections from the input/output pins on the RAM chip and the 74HCT245 tri state buffers are arranged like this:

_images/input_output_buffers.png
Timing for Safely Writing Data

Most of the time the RAM chip is in read mode. This means that the chip is asserting the data stored in the address currently specified on the address pins to the input/output pins. When it’s time to write, the chip needs to switch to write mode. To simplify control circuitry, only the write enable (active low) input on the 6116SA is used to control switching between read and write mode (output enable (active low) is available on the chip as well).

It takes time for the chip to switch modes and the pins to change from output to input. This time is referred to as tWHZ in the data-sheet and the following note applies:

Note

  1. During this period, the I/O pins are in the output state and the input signals must not be applied.

According to the data-sheet tWHZ is at most 7ns for the 6116SA15 being used.

As well as the time taken for the pins to change state, we need to ensure that the overall write pulse width is a certain length due to another note in the data-sheet (which applies because _OE will indeed be low):

Note

  1. _OE is continuously HIGH. If _OE is LOW during a _WE controlled write cycle, the write pulse width must be the larger of tWP or (tWHZ + tDW) to allow the I/O drivers to turn off and data to be placed on the bus for the required tDW. If _OE is HIGH during a _WE controlled write cycle, this requirement does not apply and the write pulse is the specified tWP. For a _CS controlled write cycle, _OE may be LOW with no degradation to tCW.

tWP is listed as 12ns, but tWHZ + tDW (7ns + 12ns) is greater at 19ns so the write pulse needs to be at least that long.

The chip is also not edge triggered - it will write data to the specified address as long as it is in write mode. This means that we need to do our own edge detection and keep the write pulse as short as possible. See the Edge Detection section for more details.

Once the write is finished, the chip can go instantly back into read mode (as tDH is 0ns).

So with all this in mind, this is how the desired control signals would look (assuming a write is to take place).

_images/ideal_ram_write_control.png

The output enable on the 74HCT245 and write enable on the 6116SA are active low so the final circuit looks like this:

_images/write_signal_timing.png

(This can be seen in Falstad Circuit Simulator by loading the saved session)

From left to right:

  • The capacitor, resistor and inverting Schmitt trigger on the bottom form the edge detector that produces a low pulse when a rising edge is detected.
  • The three inverting Schmitt triggers on top serve to delay and invert the clock signal (using propagation delay).
  • The two OR gates ensure that once the low pulse from the detected rising edge finishes, both control signals go high at the same time.

The propagation delay of the inverting Schmitt triggers (inside a 74HCT14) is approximately 20ns, to a maximum of approximately 40ns. With the 3 Schmitt triggers on top, the inversion of the now positive clock is delayed between 60ns and 120ns.

The resistor and capacitor values are chosen so that the time from the voltage between the capacitor and the resistor going high, to the voltage going below the negative going threshold of the Schmitt trigger is at least 112ns. That is:

  • 120ns for the longest possible delay for the top three Schmitt triggers.
  • Subtract 20ns for the shortest propagation delay from the edge detect Schmitt trigger.
  • 12ns to satisfy tDW

The 7ns required for tWHZ is amply catered for by the chained Schmitt triggers.

The final control signals look like this:

_images/ram_write_signals.png
Run/Setup Mode

The RAM needs to accessed by the computer while running (run mode) and by the user during setup (setup mode). To achieve this, the follwing inputs to the RAM all need to be driven by either the computer itself, or the user:

  • data_in
  • address
  • input_enable
  • select_data_memory
  • clock

The run_setup switch decides which input will be fed to the RAM.

data_in and address are connected to 2:1 Selectors.

The remaining input_enable, prog_data_mem_select and clock are all connected to a 74HCT157 Quad 2 to 1 line data selector. They are set up as follows:

_images/run_setup_mode.png

From left to right:

  • Multiplexers to select between run and setup control signals
  • Safe Clock Enable
  • Outputs to the rest of the RAM.

When in run mode:

  • data_in - connected to the bus.
  • address - connected to the output of the memory address register.
  • input_enable - connected to ram_in from the Control Unit.
  • select_data_memory - connected to ram_sel_data_mem from the Control Unit.
  • clock - connected to data_clock from the Clock.

When in setup mode, all of the above are connected to switches that the user controls, apart from input_enable which is held high.

Hardware

The following electronics are used:

  • A 6166SA15 RAM chip to hold all the data.
  • 2 x 74HCT245 for controlling input and output to and from the RAMs IO pins.
  • A 74HCT157 to choose between the inputs in run and setup mode.
  • A 74HCT02 containing NOR gates to build the Safe Clock Enable.
  • A 74HCT08 containing AND gates to build the Safe Clock Enable and isolate the resistor and capacitor circuit from the other logic gates.
  • A 74HCT14 containing Schmitt triggers to edge detect and delay the clock.
  • A 74HCT32 for the OR gates that are used in the edge detection and delay circuit.

They are laid out on the breadboards as follows:

_images/ram_bb.png

Control Unit

The control unit interprets the instruction byte and flag bits. It sets control signals in the correct sequence to operate the other modules in the computer to complete the current instruction. Steps in the sequence are kept in order with an internal microcode step counter.

Interface and Operation

This is the interface of the Control Unit:

_images/control_unit_block.png

This is how it operates:

Name Bit width Description
instruction_byte 8 Specifies the current instruction.
flags 4 The flag bits from the ALU, control conditional instructions.
clock 1 The control_clock from the clock module.
reset 1 While high, sets the microcode step count to 0.
acc_in 1 Instructs the accumulator register to store the value on the bus.
acc_out 1 Instructs the accumulator register to assert it’s content onto the bus.
a_in 1 Instructs the A register to store the value on the bus.
a_out 1 Instructs the A register to assert it’s content onto the bus.
b_in 1 Instructs the B register to store the value on the bus.
b_out 1 Instructs the B register to assert it’s content onto the bus.
c_in 1 Instructs the C register to store the value on the bus.
c_out 1 Instructs the C register to assert it’s content onto the bus.
alu_store_result 1 Instructs the ALU to store the current result.
alu_store_flags 1 Instructs the ALU to store the current flags.
alu_out 1 Instructs the ALU register to assert it’s stored result onto the bus.
alu_a_is_bus 1 When low, the A input to the ALU is the accumulator register, when high, its the bus.
alu_s0 1 The S0 input for selecting ALU operations.
alu_s1 1 The S1 input for selecting ALU operations.
alu_s2 1 The S2 input for selecting ALU operations.
alu_s3 1 The S3 input for selecting ALU operations.
alu_m 1 The M input for the ALU.
alu_c_in 1 The carry in input for the ALU.
mar_in 1 Instructs the memory address register to store the value on the bus.
ram_in 1 Instructs the RAM to store the value on the bus.
ram_out 1 Instructs the RAM to assert it’s content onto the bus.
ram_sel_data_mem 1 When low, the RAM will operate on program memory, when high, data memory.
sp_in 1 Instructs the stack pointer to store the value on the bus.
sp_out 1 Instructs the stack pointer to assert it’s content onto the bus.
pc_in 1 Instructs the program counter to store the value on the bus.
pc_out 1 Instructs the program counter to assert it’s content onto the bus.
pc_count 1 Instructs the program counter to increment it’s value by one.
ir_in 1 Instructs the instruction register to store the value on the bus.
cu_step_reset 1 Resets the microcode step counter to 0.
clock_halt 1 Halts the clock module (and thus the entire computer).

A single instruction needs multiple steps (data transfers between modules, or operations on data by a module) to complete. See the Micro Code section for more details.

Implementation

EEPROMs

The control signals to set are decided by a large combinatorial logic setup. The instruction byte, flag bits and microcode step bits form the input and the varying control signals are the output.

There are 4 EEPROM chips, they are all fed the same input as an address. Each EEPROM is programmed so that at the address corresponding to the input bits, 8 of the control signals are stored as data. Each EEPROM holds an 8 bit wide “slice” of the output control signals.

This is visible in the logic block diagram:

_images/logic_block.png

The EEPROMs are in a vertical line, all fed the same inputs and each EEPROM outputs it’s own slice.

Mirror Registers

As per the video by James Bates, mirror registers are used to store the instruction byte and flag bits on a rising control_clock. This is so that the control signals remain constant when the data_clock rises.

The instruction byte and flags bits partly determine the control signals, having the control signals change while the clock is rising could lead to unpredictable results.

The circled area below demonstrates the problem. The rising data clock and control signals change at almost same time. The EEPROM outputs settling and whatever propagation delay happens to have accumulated elsewhere in the computer affect the exact timing:

_images/instruction_change_problem.png

With the mirror register we can control the time that the control signals change to be at a safe distance from the rising data_clock edge:

_images/instruction_change_fixed.png
Microcode Steps

A 4 bit counter is used to keep track of the microcode steps in each instruction. There are a maximum of 8 steps in any one instruction so only 3 bits are used. The count is increased on each rising control_clock. The control unit can reset the count before the 8th step is reached to save executing “empty” micro cycles. This is achieved synchronously enabling the parallel load functionality of the 74HCT161. For more details about the microcode steps see the Micro Code section.

Hardware

The following electronics are used:

  • A 74HCT377 is used for the mirror instruction register.
  • A 74HCT173 is used for the mirror flag bits.
  • A 74HCT04 is used to invert the incoming control signal that causes the counter to reset and the master reset signal.
  • A 74HCT161 is used to provide the counting, loading, and resetting behaviour (much like the program counter).
  • A 74HCT138 is used to convert the 3 bit binary number of the microcode step to an individual signal for each step (purely for aesthetic/blinky blinky reasons :))
  • 4 x AT28C256 EEPROM chips are used to hold the combinational logic/microcode.
  • LEDs and resistors are used to provide display of the data.

They are laid out as follows:

  • Step counter, mirror flags and mirror instruction byte.
  • Display of the above and microcode step format conversion.
  • EEPROMs 2 and 3
  • EEPROMs 0 and 1
  • Control signal output and display.
_images/control_unit_bb.png

Arithmetic Logic Unit

The Arithmetic Logic Unit (ALU) performs arithmetical and logical operations using one or both of it’s inputs. The result is stored internally when desired and the value of that internal storage is also output when desired.

As well as calculating the result, pieces of information (flags) about the result or the two inputs are also stored internally when desired and the state of that internal storage is always output.

Interface and Operation

This is the interface of the ALU:

_images/alu_block.png

This is how it operates:

Name Bit width Description
carry_in 1 If high, supply a carry in to the arithmetical operation. For subtraction operations this becomes an active low borrow_in.
M 1 Choose between arithmetical and logical operations.
S0-3 4 Specify which arithmetical or logical operation.
store_result 1 While high, the result of the operation will be stored internally on a rising clock egde.
store_flags 1 While high, flags resulting from the current operation/inputs/result will be stored internally on a rising clock egde.
output_stored_result 1 Assert the value of the stored result onto the result_stored connection.
clock 1 A rising edge triggers result and flag storage if enabled.
A 8 The A input to the operation.
B 8 The B input to the operation.
result_live 8 The result of the current operation with the current inputs.
result_stored 8 The value of the stored result while output_stored_result is high, not connected otherwise.
flags_live 4 Flags resulting from the current operation/inputs/result.
flags_stored 4 The value of the stored flags.

carry_in, M, and S0-3 are used to select from the available operations (from the datasheet).:

_images/operation_select.jpg

Not all of the operations are available for use in the computer, see the Language section for more details on which are available.

The output flags are:

Name Description
zero The output of the ALU is zero (All bits 0)
negative The output of the ALU is negative (when the 8 bits are read in 2’s compliment form. Also the same as the most significant bit being 1)
carry_borrow If the operation was an addition and the value is high, there was a carry bit, if low, no carry. If the operation was a subtraction and the value is low, there was a borrow, if high, no borrow.
equality When the ALU is in the appropriate mode, this flag indicates the A and B inputs are equal.

Implementation

The logical arrangement of the ALU is like this:

_images/alu_logic_layout.png

A Safe Clock Enable circuit is only required in the Logisim version. It’s natively implemented in the 74HCT377 and 74HCT173 chips.

OR gates are used to OR all the bits together and then the result inverted to calculate the zero flag.

The most significant bit of the output is the negative flag.

The carry_borrow flag and the equality flag are output from the ALU (the carryborrow flag is inverted).

The following electronics are used:

  • 2 x 74LS181 are used for the arithmetic and logic operations.
  • A 74CHT00 is used to AND the two A=B outputs and invert carry_borrow_out.
  • A 74HCT173 is used to store the flags.
  • 2 x 74HCT32s are used to OR all the bits to check if it’s zero.
  • A 74HCT04 is used to invert the result of the OR zero check, the store_flags control signal and the carry_borrow input signal.
  • A 74HCT245 is used to provide tri-state buffering for result_stored to go onto the bus.
  • A 74HCT377 stores the result of the operation from result_live.
  • Another 74HCT04 is used to invert the store_result and output_stored_result signals as this inputs are active low.

The ALU resides on three breadboards. From top to bottom:

  • ALU chips
  • Zero checking and flag storage
  • Result storage and output
_images/alu_bb.png

Language

The language of the computer is broadly defined with the following terms:

Name Description
Assembly / Assembly code Collective name for operations in sequence to form a program. The input to the assembler.
Token A set of characters separated from other characters by spaces at the beginning and end. E.g the assembly line LOAD [#123] B has 3 tokens: LOAD [#123] and B.
Operation A specific, simple step that the computer can perform. E.g. add the value in a given register to the accumulator, or copy a value in one register to another.
Operation code / Op code A unique code used to identify an operation, e.g. AND, JUMP, or ADD.
Operation Argument A value passed to an operation to specify it’s behaviour. E.g. passing B to the ADD operation to specify that the value in register B should be added to the accumulator register.
Instruction A fully specified operation, e.g. a copy from B to C or setting the A register to a value. Effectively a line of assembly.
Instruction byte A byte that uniquely identifies an instruction.
Machine code Collective name for the bytes in program memory that form the instructions and constants of a program. The assembler generates machine code.
Machine code byte A byte that makes up machine code. Could be an instruction or an constant value.
Microcode The pattern of bits that determine the control signals to operate the computer to complete an instruction. The Control Unit contains microcode.
Microcode Step A single transfer of data via the bus or action of a module occurring on a rising clock edge. The smallest, most specific level of control. An instruction is completed by doing a number of microcode steps.

See the language table (or download it) for a complete listing of all the machine code and operations with their arguments. The table is sortable by clicking on the headers (but it’s a little slow).

Assembly

Assembly code provides a way to specify instructions for the computer to perform in a more human readable from than machine code

Assembly operations and arguments to specify their behaviour can be saved in an assembly file and passed to an assembler to convert them to machine code that the computer can then execute.

These are the operations and other assembly constructs that can be used to create a valid assembly file.

Arithmetic Operations

ADD

The ADD operation adds the value held in the specified module (or a constant) to the accumulator. The ALU flags generated by this operation are stored.

The possible usages are:

  • ADD A
  • ADD B
  • ADD C
  • ADD CONST
ADDC
SUB
SUBB
LSHIFT
LSHIFTC
INCR
DECR

Data Operations

COPY

The COPY operation copies the value from a source module to a destination module. This overwrites the current value of the destination register. It requires a single machine code byte in program memory.

It is used by specifying the source module as the first argument and the destination module as the second.

The possible usages are:

  • COPY ACC A
  • COPY ACC B
  • COPY ACC C
  • COPY ACC SP
  • COPY A ACC
  • COPY A B
  • COPY A C
  • COPY A SP
  • COPY B ACC
  • COPY B A
  • COPY B C
  • COPY B SP
  • COPY C ACC
  • COPY C A
  • COPY C B
  • COPY C SP
  • COPY PC ACC
  • COPY PC A
  • COPY PC B
  • COPY PC C
  • COPY PC SP
  • COPY SP ACC
  • COPY SP A
  • COPY SP B
  • COPY SP C
LOAD

The LOAD operation loads a value from data memory into a module.

It is used by specifying the position in memory as the first argument and the destination module as the second. The position in memory can be a module or a constant and is encased in square parentheses.

The possible usages are:

  • LOAD [ACC] ACC
  • LOAD [ACC] A
  • LOAD [ACC] B
  • LOAD [ACC] C
  • LOAD [A] ACC
  • LOAD [A] A
  • LOAD [A] B
  • LOAD [A] C
  • LOAD [B] ACC
  • LOAD [B] A
  • LOAD [B] B
  • LOAD [B] C
  • LOAD [C] ACC
  • LOAD [C] A
  • LOAD [C] B
  • LOAD [C] C
  • LOAD [PC] ACC
  • LOAD [PC] A
  • LOAD [PC] B
  • LOAD [PC] C
  • LOAD [SP] ACC
  • LOAD [SP] A
  • LOAD [SP] B
  • LOAD [SP] C
  • LOAD [CONST] ACC
  • LOAD [CONST] A
  • LOAD [CONST] B
  • LOAD [CONST] C
STORE
PROGLOAD
PROGSTORE
PUSH
POP
SET

The SET operation will set a module in the computer to a given constant value.

It is used by specifying a module as the first argument, then the value to set it to as a constant.

It requires two machine code bytes in program memory. Consider the SETZERO operation if the constant is zero.

The possible usages are:

  • SET ACC CONST
  • SET A CONST
  • SET B CONST
  • SET C CONST
  • SET SP CONST
SETZERO

Program Control Operations

NOOP
JUMP

The JUMP operation will set the program counter to a value.

The possible usages are:

  • JUMP ACC
  • JUMP A
  • JUMP B
  • JUMP C
  • JUMP SP
  • JUMP CONST
  • JUMP [ACC]
  • JUMP [A]
  • JUMP [B]
  • JUMP [C]
  • JUMP [SP]
  • JUMP [PC]
  • JUMP [CONST]
JUMP_IF_LT_ACC
JUMP_IF_LTE_ACC
JUMP_IF_EQ_ACC
JUMP_IF_GTEQ_ACC
JUMP_IF_GT_ACC
JUMP_IF_EQ_ZERO
JUMP_IF_POSITIVE_FLAG
JUMP_IF_NEGATIVE_FLAG
JUMP_IF_OVERFLOW_FLAG

The JUMP_IF_OVERFLOW_FLAG operation will set the program counter to the value of a given constant if the overflow flag is high.

The possible usages are:

  • JUMP_IF_OVERFLOW_FLAG CONST
JUMP_IF_NOT_OVERFLOW_FLAG
JUMP_IF_UNDERFLOW_FLAG
JUMP_IF_NOT_UNDERFLOW_FLAG
JUMP_IF_ZERO_FLAG
JUMP_IF_NOT_ZERO_FLAG
CALL
RETURN
HALT

Logical Operations

NOT
AND
NAND
OR
NOR
XOR
NXOR

Constants

Constants are values that the assembler will convert to machine code bytes for operations that require data in the machine code. For example, a jump to an explicit index in program memory, or setting a register to an explicit value.

Labels

A label binds to the line of assembly that follows it. Once assembly is complete the label’s value is the index in program memory of the instruction byte that followed the label definition. E.g. If an assembly file looked like this:

    LOAD [#123] A
    ADD A

@label
    SET B #42

The value of @label would be 3. The instruction byte corresponding to SET B #42 is at program memory index 3. LOAD [#123] A takes 2 bytes, ADD A one, and SET B #42 is the byte after that.

Labels are typically used by jump operations.

A label is a token that starts with the @ character followed by any letter or an underscore, then any alphanumeric or an underscore. E.g.:

  • @label
  • @label_1
  • @_other_label

Labels must be unique.

A label is defined by putting it on a line by itself.

Variables

Variables are named aliases for indexes into data memory. They can be predeclared by putting them by themselves on a line or declared as they are used by using them as an argument.

The index for a given variable is determined by the assembler. As it parses assembly lines from the start of the file to the end, addresses are assigned to variables as they are encountered in the file. E.g. for the following assembly:

$variable1
COPY A ACC
LOAD [$variable2] A

variable1 is predeclared, variable2 is declared as it’s used. Once assembled, variable1 is an alias for 0, variable2 is an alias for 2.

A variable is a token that starts with the $ character followed by any letter or an underscore, then any alphanumeric or an underscore. E.g.:

  • $variable
  • $variable1
  • $_other_variable
Numbers

Numbers are integer values. In most cases they within the range -127 to 255 (inclusive). This range comes from the minimum and maximum values that 8 bits, or 8 bits with 2’s compliment encoding can hold.

A number is a token that starts with the # character and is followed by any valid Python integer definition. E.g.

  • #123 (decimal)
  • #-5 (decimal)
  • #0b00010010 (binary)
  • #-0b0101 (binary)
  • #0xA2 (hex)
  • #0o107 (octal)

Comments

Comments are parts of the assembly file ignored by the assembler.

A comment is anything after and including // on a line until the end of the line.

Machine Code

The computer is directly controlled by machine code. It is stored in program memory. The machine code is stored as 8 bit bytes.

Machine code consists of instruction bytes and constant bytes. Instruction bytes encode a particular instruction (e.g. copying the value in register A to register B), constant bytes encode a value that an operation may require (e.g. setting the A register to 42).

There are 256 possible instruction bytes, most of these are occupied by the instruction set. They are organised using the grouping described below. This is very much inspired by how James Bates organised his machine code.

See the language table (or download it) for a complete listing of all the machine code and operations with their arguments. The table is sortable by clicking on the headers (but it’s a little slow).

Instruction Groups

The first two bytes of an instruction byte always represent the instruction group. There are 4 groups:

Bit value Group Name
00...... Copy
01...... Load
10...... Store
11...... ALU

Sources and destinations

For copy, load and store instructions, the third to fifth, and sixth to eighth bytes are the source and destination respectively.

The table below only shows the source codes for brevity. The destination bits are the last 3 bits, e.g.: .....000 and have the same name and meaning.

Bit value Name Meaning
..000... ACC The accumulator register.
..001... A The A register.
..010... B The B register.
..011... C The C register.
..100... SP The stack pointer.
..101... PC The program counter
..110... SP+/- The stack pointer, preceded or followed by incrementing or decrementing it.
..111... CONST A constant value.

ALU Operations

There are 16 ALU operations and they are identified with bits 3-6 of the instruction byte when the operation group is ALU. The operations and codes are:

Bit value Name Meaning
110000.. ZERO The ALU will output zero.
110001.. INCR The argument supplied will be incremented by 1.
110010.. DECR The argument supplied will be decremented by 1.
110011.. ADD The argument will be added to the accumulator.
110100.. ADDC The argument will be added to the accumulator and one will be added if the last add resulted in a carry.
110101.. SUB The argument will be subtracted from the accumulator.
110110.. SUBB The argument will be subtracted from the accumulator and one will be subtracted if the last subtraction resulted in a borrow.
110111.. AND The argument will be ANDed with the accumulator.
111000.. NAND The argument will be NANDed with the accumulator.
111001.. OR The argument will be ORed with the accumulator.
111010.. NOR The argument will be NORed with the accumulator.
111011.. XOR The argument will be XORed with the accumulator.
111100.. NXOR The argument will be NXORed with the accumulator.
111101.. NOT The argument will have all it’s bits inverted
111110.. LSHIFT All the bits in the argument will move one place to the left (toward the most significant bit)
111111.. LSHIFTC All the bits in the argument will move one place to the left (toward the most significant bit). If the last shift resulted in a carry then the least significant bit is set to 1.

ALU Arguments

ALU operations work on an argument. This is specified with bits 7 and 8 of the instruction byte when the operation group is ALU. The arguments and codes are:

Bit value Argument
11....00 ACC
11....01 A
11....10 B
11....11 C

Instruction byte gaps

Not all source and destination combinations are valid or make sense. For example, copying the value in Register A to Register A has no purpose. In these cases, those instructions are re purposed for other instructions.

These “instruction byte gaps” are:

Instruction byte(s) Explanation Used by
  • 00000000
  • 00001001
  • 00010010
  • 00011011
  • 00100100
  • 00101101
  • 00110110
  • 00111111
Copying a register to itself. JUMP_IF_XXX_FLAG
00110... Copy from SP+/-. Ambiguous. JUMP_IF_LT_ACC
00...110 Copy to SP+/-. Ambiguous. JUMP_IF_LTE_ACC
00...111 Copy to a constant. Constants cannot be written to. JUMP_IF_EQ_ACC
01...100 Loading into SP. SP has a dedicated register, instead a load to a register then copy. JUMP_IF_GTE_ACC
01...110 Loading into SP+/-. SP+/- cannot be written to. CALL
01...111 Loading into a constant. Constants cannot be written to. PROGRAM_LOAD
10110... Storing SP+/-. Ambiguous. JUMP_IF_GT_ACC
10100... Storing SP. SP has a dedicated register, instead copy to a register and store. JUMP_IF_EQ_ZERO
10111... Storing a constant value. Instead Set a register and store. PROGRAM_STORE

Fetch

To execute an instruction, the instruction byte must be loaded from program memory into the instruction register.

This is handled by the first two steps of every instruction which:

  • Load the program counter into the memory address register.
  • Load the instruction register with the data from program memory at increment the program counter ready for the next instruction.

Micro Code

Microcode is the encoding of what specific steps the computer should take to complete an instruction.

It is effectively a large combinational logic circuit where the inputs are the instruction byte, microcode step and flag bits and the outputs are the control signals to drive the modules. EEPROM chips are programmed and used to implement this combinational logic circuit. The same address is fed into 4 chips, differently programmed to get the 30 output bits.

Every instruction is comprised of up to six micro code steps. There are eight available but the first two are reserved for fetching the instruction byte itself. The steps are executed sequentially to perform the instruction. The microcode step counter controls the order of the sequence. The flags bits allow conditional operation based on a given instruction byte and step. An instruction can “surrender” it’s remaining steps and move onto the next instruction if it has completed all it’s necessary steps.

Address bits

The address bits for the EEPROM and their meanings are:

Bits Meaning
xxxxxxxx....... Instruction byte
........xxxx... Flags
............xxx Microcode step

Output bits

The output bits for the EEPROMs and their meanings (which will typically occur on the next rising data clock edge) are:

Bits Name Description
1....... ........ ........ ........ ACC_IN Enable input for the accumulator register.
.1...... ........ ........ ........ ACC_OUT Enable output for the accumulator register.
..1..... ........ ........ ........ A_IN Enable input for the A register.
...1.... ........ ........ ........ A_OUT Enable output for the A register.
....1... ........ ........ ........ B_IN Enable input for the B register.
.....1.. ........ ........ ........ B_OUT Enable output for the B register.
......1. ........ ........ ........ C_IN Enable input for the C register.
.......1 ........ ........ ........ C_OUT Enable output for the C register.
........ 1....... ........ ........ ALU_STORE_RESULT Store the current output of the ALU.
........ .1...... ........ ........ ALU_STORE_FLAGS Store the current flags of the ALU..
........ ..1..... ........ ........ ALU_OUT Enable output for the ALU.
........ ...1.... ........ ........ ALU_A_IS_BUS Set the A input of the ALU to be the bus (otherwise it is the content of the accumulator register).
........ ....1... ........ ........ ALU_S0 Set the S0 function select input on the ALU.
........ .....1.. ........ ........ ALU_S1 Set the S1 function select input on the ALU.
........ ......1. ........ ........ ALU_S2 Set the S2 function select input on the ALU.
........ .......1 ........ ........ ALU_S3 Set the S3 function select input on the ALU.
........ ........ 1....... ........ ALU_M Set the M input on the ALU.
........ ........ .1...... ........ ALU_C_IN Set the carry in input on the ALU.
........ ........ ..1..... ........ MAR_IN Enable input for the memory address register.
........ ........ ...1.... ........ RAM_IN Enable input for the RAM.
........ ........ ....1... ........ RAM_OUT Enable output for the RAM.
........ ........ .....1.. ........ RAM_SEL_PROG_MEM Retrieve data from program memory rather than data memory.
........ ........ ......1. ........ SP_IN Enable input for the stack pointer.
........ ........ .......1 ........ SP_OUT Enable output for the stack pointer.
........ ........ ........ 1....... PC_IN Enable input for the program counter.
........ ........ ........ .1...... PC_OUT Enable output for the program counter.
........ ........ ........ ..1..... PC_COUNT Increment the value stored in the program counter.
........ ........ ........ ...1.... IR_IN Enable input for the instruction register.
........ ........ ........ ....1... CU_STEP_RESET Reset the step counter in the control unit.
........ ........ ........ .....1.. CLOCK_HALT Halt the computer.

Hardware

HCT vs. LS

Fanout

Materials and Tools

Edge Detection

Debouncing

EEPROM Programmer

Safe Clock Enable

Makes sure that when reset is released, only the next rising clock edge is passed on. If only an AND gate was used, a rising edge would pass through if the clock was already high, and (an inverted) reset signal went low.

Open Collector Outputs

Software

Assembler

The assembler module is responsible for taking lines of assembly code and processing it to generate equivalent machine code.

It is passed a list of strings which are the lines of the assembly file. Each line is then processed and the information about the line stored in a dictionary.

During processing each line is checked to see if it’s a constant definition, if not, it’s treated as an assembly line and parsed into machine code bytes.

The line is passed to each operation to attempt to generate the machine code bytes. The operations can expect certain tokens on the line to be constants and are identified as such and returned to the assembler. The constants are then validated and identified and the machine code bytes added to the dictionary of information about the line of assembly.

With all the lines processed, machine code bytes generated and constants identified, the assembler checks for overall validity and structure The assembler then performs global operations that need to take the entire assembly code into account:

With these checks, assignments and resolutions complete, the final assembly line dictionaries are returned.

Command Line Tools

ebc-assemble

Assemble eight bit computer assembly files to machine code.

usage: ebc-assemble [-h] [-o OUTPUT_FILEPATH] [-s VARIABLE_START_OFFSET]
                    [-f {logisim,cpp}]
                    asm_filepath
Positional Arguments
asm_filepath Path to the assembly file to assemble.
Named Arguments
-o, --output_filepath
 Filepath to write the machine code to. E.g. “../machine_code.mc”. Including ./ for a file in the current directory is optional.
-s, --variable_start_offset
 

Index in data memory to start assigning automatically assigned variables at.

Default: 0

-f, --output_format
 

Possible choices: logisim, cpp

Format to write the machine code in.

Default: “logisim”

ebc-gen-roms

Generate ROMs that contain the microcode.

usage: ebc-gen-roms [-h] [-o OUTPUT_DIR] [-p ROM_PREFIX] [-f {logisim,cpp}]
Named Arguments
-o, --output_dir
 

Directory to write the ROMs into.

Default: “.”

-p, --rom_prefix
 

Prefix for the ROM files.

Default: “rom”

-f, --output_format
 

Possible choices: logisim, cpp

Format to write the ROMs in.

Default: “logisim”

eight_bit_computer package

Subpackages

eight_bit_computer.operations package
Submodules
eight_bit_computer.operations.add module

ADD Operation

eight_bit_computer.operations.add.generate_microcode_templates()[source]

Generate microcode for all the ADD instructions.

Returns:DataTemplates for all the ADD instructions.
Return type:list(DataTemplate)
eight_bit_computer.operations.add.parse_line(line)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a ADD assembly line, return an empty list instead.

Parameters:line (str) – Assembly line to be parsed.
Returns:List of machine code byte template dictionaries or an empty list.
Return type:list(dict)
eight_bit_computer.operations.copy_op module

The COPY operation.

Copies a value from one module into another.

eight_bit_computer.operations.copy_op.generate_signatures()[source]

Generate the definitions of all possible arguments passable.

Returns:All possible arguments. See get_arg_def_template() for more information.
Return type:list(list(dict))
eight_bit_computer.operations.copy_op.generate_microcode_templates()[source]

Generate microcode for all the COPY operations.

Returns:DataTemplates for all the COPY microcode.
Return type:list(DataTemplate)
eight_bit_computer.operations.copy_op.generate_operation_templates(signature)[source]

Create the DataTemplates to define a copy with the given args.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular copy operation to generate templates for.
Returns:Datatemplates that define this copy.
Return type:list(DataTemplate)
eight_bit_computer.operations.copy_op.generate_instruction_byte_bitdefs(signature)[source]

Generate bitdefs to specify the instruction byte for these args.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular copy operation to generate the instruction byte bitdefs for.
Returns:Bitdefs that make up the instruction_byte
Return type:list(str)
eight_bit_computer.operations.copy_op.parse_line(line)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a COPY assembly line, return an empty list instead.

Parameters:line (str) – Assembly line to be parsed.
Returns:List of instruction byte template dictionaries or an empty list.
Return type:list(dict)
eight_bit_computer.operations.fetch module

The fetch steps added to the start of all operations.

eight_bit_computer.operations.fetch.generate_microcode_templates()[source]

Generate datatemplates for all the fetch steps.

Returns:Datatemplates that represent the fetch steps.
Return type:list(DataTemplate)
eight_bit_computer.operations.fetch.fetch_step_0()[source]

Create template for the first fetch step.

Returns:The first fetch step.
Return type:DataTemplate
eight_bit_computer.operations.fetch.fetch_step_1()[source]

Create template for the second fetch step.

Returns:The second fetch step.
Return type:DataTemplate
eight_bit_computer.operations.jump module

JUMP Operation

eight_bit_computer.operations.jump.generate_microcode_templates()[source]

Generate microcode for all the JUMP instructions.

Returns:DataTemplates for all the JUMP instructions.
Return type:list(DataTemplate)
eight_bit_computer.operations.jump.generate_signatures()[source]

Generate all the argument signatures for the jump operation.

Returns:All possible signatures, See get_arg_def_template() for more information on an argument definition dictionary.
Return type:list(list(dict))
eight_bit_computer.operations.jump.generate_operation_templates(signature)[source]

Create the DataTemplates to define a JUMP with the given signature.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular JUMP operation to generate templates for.
Returns:Datatemplates that define this JUMP.
Return type:list(DataTemplate)
eight_bit_computer.operations.jump.generate_instruction_byte_bitdefs(signature)[source]

Generate bitdefs to specify the instruction byte for this signature.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular JUMP operation to generate the instruction byte bitdefs for.
Returns:Bitdefs that make up the instruction_byte
Return type:list(str)
eight_bit_computer.operations.jump.generate_control_steps(signature)[source]

Generate control steps for this signature.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular JUMP operation to generate the control steps for.
Returns:List of list of bitdefs that specify the control steps.
Return type:list(list(str))
eight_bit_computer.operations.jump.parse_line(line)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a LOAD assembly line, return an empty list instead.

Parameters:line (str) – Assembly line to be parsed.
Returns:List of machine code byte template dictionaries or an empty list.
Return type:list(dict)
eight_bit_computer.operations.jump_if_flag_base module
eight_bit_computer.operations.jump_if_flag_base.generate_microcode_templates(src_dest, true_flag_bitdef, false_flag_bitdef)[source]

Generate microcode for all the JUMP_IF_XXX_FLAG instructions.

Parameters:
  • src_dest (str) – Name of the module used for both the source and destination.
  • true_flag_bitdef (str) – Bitdef that represents the state of the flags if the condition is true, i.e. the operation should jump.
  • false_flag_bitdef (str) – Bitdef that represents the state of the flags if the condition is false, i.e. the operation should not jump and just execute the next instruction instead.
Returns:

DataTemplates for all the JUMP_IF_XXX_FLAG instructions.

Return type:

list(DataTemplate)

eight_bit_computer.operations.jump_if_flag_base.generate_instruction_byte_bitdefs(src_dest)[source]

Generate bitdefs to specify the instruction byte

Parameters:src_dest (str) – Name of the module used for both the source and destination.
Returns:Bitdefs that make up the instruction_byte
Return type:list(str)
eight_bit_computer.operations.jump_if_flag_base.generate_true_control_steps()[source]

Generate control steps to carry out the jump.

Returns:List of list of bitdefs that specify the control steps.
Return type:list(list(str))
eight_bit_computer.operations.jump_if_flag_base.parse_line(line, src_dest, name)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a JUMP_IF_XXX_FLAG assembly line, return an empty list instead.

Parameters:
  • line (str) – Assembly line to be parsed.
  • src_dest (str) – Name of the module used for both the source and destination.
  • name (str) – Name of the Operation.
Returns:

List of machine code byte template dictionaries or an empty list.

Return type:

list(dict)

eight_bit_computer.operations.jump_if_flag_base.generate_signatures()[source]

Generate all the argument signatures for the JUMP_IF_XXX_FLAG operation.

Returns:All possible signatures, See get_arg_def_template() for more information on an argument definition dictionary.
Return type:list(list(dict))
eight_bit_computer.operations.jump_if_overflow_flag module

JUMP_IF_OVERFLOW_FLAG operation

eight_bit_computer.operations.jump_if_overflow_flag.generate_microcode_templates()[source]

Generate microcode for all the JUMP_IF_OVERFLOW_FLAG instructions.

Returns:DataTemplates for all the JUMP_IF_OVERFLOW_FLAG instructions.
Return type:list(DataTemplate)
eight_bit_computer.operations.jump_if_overflow_flag.parse_line(line)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a JUMP_IF_OVERFLOW_FLAG assembly line, return an empty list instead.

Parameters:line (str) – Assembly line to be parsed.
Returns:List of machine code byte template dictionaries or an empty list.
Return type:list(dict)
eight_bit_computer.operations.load module

The LOAD operation.

Loads a value from data memory into a module.

eight_bit_computer.operations.load.generate_microcode_templates()[source]

Generate microcode for all the LOAD instructions.

Returns:DataTemplates for all the LOAD instructions.
Return type:list(DataTemplate)
eight_bit_computer.operations.load.generate_signatures()[source]

Generate the definitions of all possible arguments passable.

Returns:All possible arguments. See get_arg_def_template() for more information.
Return type:list(list(dict))
eight_bit_computer.operations.load.generate_operation_templates(signature)[source]

Create the DataTemplates to define a load with the given args.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular load operation to generate templates for.
Returns:Datatemplates that define this load.
Return type:list(DataTemplate)
eight_bit_computer.operations.load.generate_instruction_byte_bitdefs(signature)[source]

Generate bitdefs to specify the instruction byte for these args.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular LOAD operation to generate the instruction byte bitdefs for.
Returns:Bitdefs that make up the instruction_byte
Return type:list(str)
eight_bit_computer.operations.load.generate_control_steps(signature)[source]

Generate control steps for these args.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular load operation to generate the control steps for.
Returns:List of list of bitdefs that specify the control steps.
Return type:list(list(str))
eight_bit_computer.operations.load.parse_line(line)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a LOAD assembly line, return an empty list instead.

Parameters:line (str) – Assembly line to be parsed.
Returns:List of machine code byte template dictionaries or an empty list.
Return type:list(dict)
eight_bit_computer.operations.operation_template module

Template for operation module

eight_bit_computer.operations.operation_template.generate_microcode_templates()[source]

Generate microcode for all the OP_TEMPLATE instructions.

Returns:DataTemplates for all the OP_TEMPLATE instructions.
Return type:list(DataTemplate)
eight_bit_computer.operations.operation_template.generate_signatures()[source]

Generate all the argument signatures for the OP_TEMPLATE operation.

Returns:All possible signatures, See get_arg_def_template() for more information on an argument definition dictionary.
Return type:list(list(dict))
eight_bit_computer.operations.operation_template.generate_operation_templates(signature)[source]

Create the DataTemplates to define a OP_TEMPLATE with the given signature.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular OP_TEMPLATE operation to generate templates for.
Returns:DataTemplates that define this OP_TEMPLATE.
Return type:list(DataTemplate)
eight_bit_computer.operations.operation_template.generate_instruction_byte_bitdefs(signature)[source]

Generate bitdefs to specify the instruction byte for this signature.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular OP_TEMPLATE operation to generate the instruction byte bitdefs for.
Returns:Bitdefs that make up the instruction_byte
Return type:list(str)
eight_bit_computer.operations.operation_template.generate_control_steps(signature)[source]

Generate control steps for this signature.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular OP_TEMPLATE operation to generate the control steps for.
Returns:List of list of bitdefs that specify the control steps.
Return type:list(list(str))
eight_bit_computer.operations.operation_template.parse_line(line)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a OP_TEMPLATE assembly line, return an empty list instead.

Parameters:line (str) – Assembly line to be parsed.
Returns:List of machine code byte template dictionaries or an empty list.
Return type:list(dict)
eight_bit_computer.operations.set_op module

The set operation.

Sets a module to a certain value.

eight_bit_computer.operations.set_op.generate_signatures()[source]

Generate the definitions of all possible arguments passable.

Returns:All possible arguments. See get_arg_def_template() for more information.
Return type:list(list(dict))
eight_bit_computer.operations.set_op.generate_microcode_templates()[source]

Generate datatemplates for all the SET operations.

Returns:All the datatemplates that make up the SET operation.
Return type:list(DataTemplate)
eight_bit_computer.operations.set_op.generate_instruction_byte_bitdefs(signature)[source]

Generate bitdefs to specify the instruction byte for these args.

Parameters:signature (list(dict)) – List of argument definitions that specify which particular set operation to generate the instruction byte bitdefs for.
Returns:Bitdefs that make up the instruction byte.
Return type:list(str)
eight_bit_computer.operations.set_op.parse_line(line)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a SET assembly line, return an empty list instead.

Parameters:line (str) – Assembly line to be parsed.
Returns:List of instruction byte template dictionaries or an empty list.
Return type:list(dict)
Raises:OperationParsingError – If the line was identifiably a SET operation but incorrectly specified.
eight_bit_computer.operations.simple_alu_op_base module

Base for simple ALU operations

eight_bit_computer.operations.simple_alu_op_base.generate_microcode_templates(alu_op, control_flags)[source]

Generate microcode for all the ADD instructions.

Parameters:
  • alu_op (str) – The ALU operation to perform - one of the ALU_OPERATIONS.
  • control_flags (list(str)) – List of ALU control flags for this ALU operation. One of the ALU_CONTROL_FLAGS.
Returns:

DataTemplates for all the ADD instructions.

Return type:

list(DataTemplate)

eight_bit_computer.operations.simple_alu_op_base.generate_signatures()[source]

Generate all the argument signatures for the ADD operation.

Returns:All possible signatures, See get_arg_def_template() for more information on an argument definition dictionary.
Return type:list(list(dict))
eight_bit_computer.operations.simple_alu_op_base.generate_operation_templates(signature, alu_op, control_flags)[source]

Create the DataTemplates to define a ADD with the given signature.

Parameters:
  • signature (list(dict)) – List of argument definitions that specify which particular ADD operation to generate templates for.
  • alu_op (str) – The ALU operation to perform - one of the ALU_OPERATIONS.
  • control_flags (list(str)) – List of ALU control flags for this ALU operation. One of the ALU_CONTROL_FLAGS.
Returns:

DataTemplates that define this ADD.

Return type:

list(DataTemplate)

eight_bit_computer.operations.simple_alu_op_base.generate_instruction_byte_bitdefs(signature, alu_op)[source]

Generate bitdefs to specify the instruction byte for this signature.

Parameters:
  • signature (list(dict)) – List of argument definitions that specify which particular ADD operation to generate the instruction byte bitdefs for.
  • alu_op (str) – The ALU operation to perform - one of the ALU_OPERATIONS.
Returns:

Bitdefs that make up the instruction_byte

Return type:

list(str)

eight_bit_computer.operations.simple_alu_op_base.generate_control_steps(signature, control_flags)[source]

Generate control steps for this signature.

Parameters:
  • signature (list(dict)) – List of argument definitions that specify which particular ADD operation to generate the control steps for.
  • control_flags (list(str)) – List of ALU control flags for this ALU operation. One of the ALU_CONTROL_FLAGS.
Returns:

List of list of bitdefs that specify the control steps.

Return type:

list(list(str))

eight_bit_computer.operations.simple_alu_op_base.parse_line(line, name, alu_op)[source]

Parse a line of assembly code to create machine code byte templates.

If a line is not identifiably a ADD assembly line, return an empty list instead.

Parameters:
  • line (str) – Assembly line to be parsed.
  • name (str) – Name of the Operation.
  • alu_op (str) – The ALU operation to perform - one of the ALU_OPERATIONS.
Returns:

List of machine code byte template dictionaries or an empty list.

Return type:

list(dict)

Module contents

Operations in the assembly language

eight_bit_computer.operations.get_all_operations()[source]

Get a list of all the operations in the assembly language

Deferring the import to the function so that importing the operations module doesn’t mean automatically importing all the operations.

Returns:All the modules that represent operations in the assembly language
Return type:list(module)

Submodules

eight_bit_computer.assembler module

Process assembly code and output machine code.

eight_bit_computer.assembler.process_assembly_lines(lines, variable_start_offset=0)[source]

Parse, assemble and generate machine code.

Parameters:
  • lines (list(str)) – The lines that made up the assembly file to be assembled.
  • variable_start_offset (int) (optional) – How far to offset the first variable in data memory from 0.
Returns:

The assembly file converted to an equivalent list of dictionaries with information about what each line was resolved to.

Return type:

list(dict)

Raises:

AssemblyError – If there was an error assembling the machine code.

eight_bit_computer.assembler.process_line(line)[source]

Process a single line of assembly.

Parameters:line (str) – The line of assembly to process. This line has already been cleaned (excess whitespace and comments removed).
Returns:A dictionary of information about this line. See the get_assembly_line_template() documentation for more information about what is in the dictionary.
Return type:dict
eight_bit_computer.assembler.clean_line(line)[source]

Clean a line of assembly ready for further processing.

Removes leading and trailing whitespace, comments, and excess whitespace between tokens.

Parameters:line (str) – The line to clean.
Returns:The cleaned line.
Return type:str
eight_bit_computer.assembler.remove_comments(line)[source]

Remove comments from a line.

A comment is anything on the line after and including an occurrence of //.

Parameters:line (str) – line to remove comments from.
Returns:The line with comments removed.
Return type:str
eight_bit_computer.assembler.remove_excess_whitespace(line)[source]

Remove excess whitespace from a line.

Parameters:line (str) – line to remove excess whitespace from.
Returns:The line with excess whitespace removed.
Return type:str
eight_bit_computer.assembler.machine_code_bytes_from_line(line)[source]

Get machine code bytes that describe this line.

Uses all the defined instructions and defers the work of parsing to them. See get_machine_code_byte_template() for information on machine code dictionaries from instructions.

Expects the passed in line to be a valid line of machine code. That is, the passed in line should be translatable to valid machine code.

Parameters:

line (str) – Line to parse.

Returns:

Machine code byte information dictionaries.

Return type:

list(dict)

Raises:
  • LineProcessingError – Failure to extract machine code or matching
  • multiple operations.
eight_bit_computer.assembler.validate_and_identify_constants(machine_code_bytes)[source]

Validate and identify constants from assembly code.

Assumed constants are returned from the instruction parsers. This function then validates them to make sure they are correct and determines what kind of constant they are.

See get_mc_byte_template() for information on machine code dictionaries from instructions.

This function modifies the passed in machine code templates list in place.

Parameters:machine_code_bytes (list(dict)) – The machine code byte dicts as returned by an instruction line parser.
Raises:LineProcessingError – Invalid constants were specified.
eight_bit_computer.assembler.assign_machine_code_byte_indexes(assembly_lines)[source]

Assign indexes to the machine code bytes.

This modifies the passed in list of assembly lines, adding data to it.

Parameters:
  • assembly_lines (list(dict)) – Lines of assembly to add label
  • to. (information) –
eight_bit_computer.assembler.assign_labels(assembly_lines)[source]

Assign labels to the lines for later reference

This modifies the passed in list of assembly lines, adding data to it.

Parameters:
  • assembly_lines (list(dict)) – Lines of assembly to add label
  • to. (information) –
eight_bit_computer.assembler.resolve_labels(assembly_lines)[source]

Resolve labels to indexes in the machine code bytes.

This modifies the passed in list of assembly line dictionaries.

Parameters:assembly_lines (list(dict)) – List of assembly lines to resolve label references in.
eight_bit_computer.assembler.create_label_map(assembly_lines)[source]

Create a map of labels to machine code byte indexes.

Parameters:assembly_lines (list(dict)) – List of assembly lines to create a label map for.
Returns:str): Dictionary of label names to machine code indexes.
Return type:dict(str
eight_bit_computer.assembler.resolve_numbers(assembly_lines)[source]

Resolve number constants to machine code byte values.

This modifies the passed in list of assembly line dictionaries.

Parameters:assembly_lines (list(dict)) – List of assembly lines to resolve numbers for.
eight_bit_computer.assembler.resolve_variables(assembly_lines, variable_start_offset)[source]

Resolve variable constants to indexes in data memory.

This modifies the passed in list of assembly line dictionaries.

Parameters:
  • assembly_lines (list(dict)) – List of assembly lines to resolve variables in.
  • variable_start_offset (int) – An offset into data memory for where to start storing the variables.
eight_bit_computer.assembler.create_variable_map(assembly_lines, variable_start_offset)[source]

Create a map of variables to indexes in data memory.

Parameters:
  • assembly_lines (list(dict)) – List of assembly lines to create a variable map for.
  • variable_start_offset (int) – An offset into data memory for where to start storing the variables.
Returns:

str): Dictionary of variable names to machine code indexes.

Return type:

dict(str

eight_bit_computer.assembly_summary module

Extract information from a list of assembly line info dictionaries.

eight_bit_computer.assembly_summary.generate_assembly_summary(asm_line_infos)[source]

Produce a summary that combines assembly and machine code.

The summary will be like this:

 1 $variable0              |
 2 @label1                 |
 3     LOAD [$variable1] A |  0 00 00000000 - @label1 255 FF 11111111
                           |  1 01 00000001 -           1 01 00000001 $variable1
 4                         |
 5 @label2                 |
 6     LOAD [$variable2] A |  2 02 00000010 - @label2 255 FF 11111111
                           |  3 03 00000011 -           2 02 00000010 $variable2
 7     JUMP @label1        |  4 04 00000100 -         255 FF 11111111
                           |  5 05 00000101 -           0 00 00000000 @label1
 8                         |
 9     STORE A [#123]      |  6 06 00000110 -         255 FF 11111111
                           |  7 07 00000111 -         123 7B 01111011 #123
10 @label3                 |
11     LOAD [$variable3] B |  8 08 00001000 - @label3 255 FF 11111111
                           |  9 09 00001001 -           3 03 00000011 $variable3
12     LOAD [$variable0] C | 10 0A 00001010 -         255 FF 11111111
                           | 11 0B 00001011 -           0 00 00000000 $variable0
13 $variable4              |
14 // comment
Parameters:asm_line_infos (list(dict)) – List of dictionaries of information about the parsed assembly.
Returns:Printable summary.
Return type:str
eight_bit_computer.assembly_summary.generate_assembly_summary_lines(asm_line_infos)[source]

Generate list of lines for an assembly summary

Parameters:asm_line_infos (list(dict)) – List of dictionaries of information about the parsed assembly.
Returns:List of lines for the summary.
Return type:list(str)
eight_bit_computer.assembly_summary.get_assembly_summary_data(asm_line_infos)[source]

Process assembly data to make formatting easier for the summary.

Parameters:asm_line_infos (list(dict)) – List of line info dictionaries as returned by process_assembly_lines() .
Returns:List of entries for the assembly summary print out
Return type:list
eight_bit_computer.assembly_summary.get_widest_column_values(assembly_summary_data)[source]

Find widest values in the columns of the output.

Required for the eventual printed table to line up correctly.

Parameters:assembly_summary_data (list(dict)) – List of dictionaries (as returned by get_assembly_summary_data()) with all the summary information data.
Returns:Mapping of columns for widest values.
Return type:dict
eight_bit_computer.assembly_validity module

Validity checks on the processed assembly lines

eight_bit_computer.assembly_validity.check_structure_validity(asm_line_infos, variable_start_offset)[source]

Check the processed assembly lines for consistency/correctness.

Parameters:asm_line_infos (list(dict)) – List of dictionaries (conforming to get_assembly_line_template()) with information about all the lines in the assembly file.
eight_bit_computer.assembly_validity.check_multiple_label_defs(asm_line_infos)[source]

Check if the same label been defined more than once.

Parameters:asm_line_infos (list(dict)) – List of dictionaries (conforming to get_assembly_line_template()) with information about all the lines in the assembly file.
Raises:AssemblyError – If the same label been defined more than once.
eight_bit_computer.assembly_validity.check_multiple_label_assignment(asm_line_infos)[source]

Check if a single line been assigned more than one label.

Parameters:asm_line_infos (list(dict)) – List of dictionaries (conforming to get_assembly_line_template()) with information about all the lines in the assembly file.
Raises:AssemblyError – If a single line been assigned more than one label.
eight_bit_computer.assembly_validity.check_undefined_label_ref(asm_line_infos)[source]

Check if an operation is using a label that hasn’t been defined.

Parameters:asm_line_infos (list(dict)) – List of dictionaries (conforming to get_assembly_line_template()) with information about all the lines in the assembly file.
Raises:AssemblyError – If an operation is using a label that hasn’t been defined.
eight_bit_computer.assembly_validity.check_multiple_variable_def(asm_line_infos)[source]

Has the same variable been defined multiple times.

Parameters:asm_line_infos (list(dict)) – List of dictionaries (conforming to get_assembly_line_template()) with information about all the lines in the assembly file.
Raises:AssemblyError – If a variable has been defined more than once.
eight_bit_computer.assembly_validity.check_num_variables(asm_line_infos, variable_start_offset)[source]

Check there are more variables defined than will fit in data mem.

There are 255 bytes of data memory available and the start offset may eat into this.

Parameters:
  • asm_line_infos (list(dict)) – List of dictionaries (conforming to get_assembly_line_template()) with information about all the lines in the assembly file.
  • variable_start_offset (int) – How far in memory to offset when defining the first variable.
Raises:

AssemblyError – If there are more variables defined than will fit in data memory.

eight_bit_computer.assembly_validity.check_num_instruction_bytes(assembly_lines)[source]

Check there aren’t too many instruction_bytes.

Parameters:asm_line_infos (list(dict)) – List of dictionaries (conforming to get_assembly_line_template()) with information about all the lines in the assembly file.
Raises:AssemblyError – If there are more instruction bytes than will fit in program memory.
eight_bit_computer.bitdef module

The bitdef and associated functions.

A bitdef is a string made up of .s, 0s, and 1s.

  • . means that the bit at this position could be a 0 or a 1.
  • 0 means that the bit at this position is a 0.
  • 1 means that the bit at this position is a 1.

When indexing into a bitdef, indexes start at 0 and begin at the right hand side or least significant bit of the value. E.g.:

Index:  76543210
Bitdef: 010.1..1
eight_bit_computer.bitdef.same_length(bitdefs)[source]

Check if the passed in bitdefs are all the same length.

Parameters:list (bitdefs) – Bitdefs to check length of.
Returns:True if all the bitdefs are the same length, False otherwise
Return type:bool
eight_bit_computer.bitdef.length(bitdef)[source]

Calculate length of a bitdef.

Parameters:bitdef (str) – The bitdef to find the length of.
Returns:The length of the bitdef.
Return type:int
eight_bit_computer.bitdef.have_overlapping_bits(bitdefs)[source]

Check if the bitdefs have any bits set in the same position.

Example with overlap (bits at index 2 and 6 overlap):

  • 0...101.
  • 11...1..

Example with no overlap:

  • 11010...
  • ......11
Parameters:bitdefs (list(str)) – Bitdefs to check for overlaps.
Returns:Whether or not there were overlaps.
Return type:bool
eight_bit_computer.bitdef.merge(bitdefs)[source]

Merge the bitdefs to a single bitdef.

Bitdefs must

  • All be the same length.
  • Not have any bits defined in the same position.
Parameters:

bitdefs (list(str)) – Bitdefs to merge.

Returns:

The merged bitdef.

Return type:

str

Raises:
  • ValueError – If the bitdefs are not all the same length or have
  • overlapping bits.
eight_bit_computer.bitdef.collapse(bitdef)[source]

Collapse undefined bits into real bits to make new bitdefs.

The undefined bits are expanded in order, from left to right, with 0 first, then 1.

For example, 10.0. becomes:

  • 10000
  • 10001
  • 10100
  • 10101
Parameters:bitdef (str) – The bitdef to collapse.
Returns:The list of bitdefs the original bitdef has collapsed to.
Return type:list(str)
eight_bit_computer.bitdef.fill(bitdef, value)[source]

Fill undefined bits with a value.

For example 1..0100.1 becomes 111010011 when filled with 1s.

Parameters:
  • bitdef (str) – The bitdef to fill.
  • value (str) – The value to fill with, “0” or “1”.
Returns:

The filled bitdef.

Return type:

str

eight_bit_computer.bitdef.extract_bits(bitdef, end, start)[source]

Extract a region from the bitdef.

Indexes for start and end start at zero from the right or least significant bit.

For example, if the bitdef was 00101011 and the extraction end was 4 and start was 1 the result would be 0101:

Extracted bits:      xxxx
Index:            76543210
Bitdef:           00101011
Result:              0101
Parameters:
  • bitdef (str) – The bitdef to extract bits from.
  • end (int) – Index of the leftmost bit of the portion to extract.
  • start (int) – Index of the rightmost bit of the portion to extract.
Returns:

The extracted portion of the bitdef.

Return type:

str

Raises:

ValueError – If:

  • Extraction region is larger than bitdef.
  • Extraction end index is before extraction start index.
  • Extraction start index is less than 0.
eight_bit_computer.bitdef.remove_whitespace(input_string)[source]

Remove the whitespace from a string.

Parameters:input_string (str) – The string to remove whitespace from.
Returns:The string with the whitespace removed.
Return type:str
eight_bit_computer.bitdef.reverse_index(index, length)[source]

Reverse the passed in index as if the index direction was flipped.

Taking the string “hello” as an example the regular indexes for each letter are:

01234
hello

Reversing the indexes yields:

43210
hello

This allows easily indexing into a bitdef on bitdef indexing terms.

Parameters:
  • index (int) – The index position to reverse.
  • length (int) – The length of the array being indexed into.
Returns:

The reversed index.

Return type:

int

eight_bit_computer.cli module
eight_bit_computer.cli.assemble()[source]

Entry point for the command line assemble script.

eight_bit_computer.cli.get_assemble_parser()[source]

Generate arg parser for the ebc_assemble command line script.

Returns:The argument parser.
Return type:argparse.ArgumentParser
eight_bit_computer.cli.positive_int(value)[source]

Validate a string is an int greater than or equal to zero.

Used for the type argument in an argparse.ArgumentParser.add_argument call.

Parameters:value (str) – Value to be tested.
Returns:Value as an integer if it was >= 0.
Return type:int
Raises:argparse.ArgumentTypeError – If the value was not greater than or equal to zero.
eight_bit_computer.cli.gen_roms()[source]

Entry point for the command line rom generation script.

eight_bit_computer.cli.get_gen_roms_parser()[source]

Generate arg parser for the gen_roms command line script.

Returns:The argument parser.
Return type:argparse.ArgumentParser
eight_bit_computer.data_structures module

Data structures use to pass information between functions.

class eight_bit_computer.data_structures.DataTemplate(address_range, data)

Bases: tuple

Some data and a range of addresses to store that data in

address_range

The range of addresses to store the data in. 0 and 1 are absolute values, X is either a 0 or 1 and the expectation is that the data will expand out to the parts of the address marked with an X. and example could be “0010XX001”.

Type:str
data

The data to be stored at the given addresses.

Type:str
address_range

Alias for field number 0

data

Alias for field number 1

class eight_bit_computer.data_structures.RomData(address, data)

Bases: tuple

Some data and an address to store it in

address

The address to store the data in.

Type:str
data

The data to be stored at the given address.

Type:int
address

Alias for field number 0

data

Alias for field number 1

eight_bit_computer.data_structures.get_summary_entry_template()[source]

Get a template to describe each line in an assembly summary

Keys have the following meanings:

  • has_assembly: Does this line of the summary have assembly code.
  • assembly: Information about the assembly in this summary line.
  • assembly/info: The assembly line information dictionary (as returned by get_assembly_line_template()) and filled in by the assembler.
  • has_mc_byte: Does this line of the summary have a machine code byte.
  • mc_byte: Information about the machine code byte on this line.
  • mc_byte/info: Machine code byte information dictionary (as returned by get_machine_code_byte_template() and filled by the assembly process).
  • mc_byte/has_label: Whether of not this machine code byte has an associated label.
  • mc_byte/label: The label of this machine code byte.
Returns:Summary entry template.
Return type:dict
eight_bit_computer.data_structures.get_assembly_line_template()[source]

Get a template for the assembly line information bundle.

Template for a dictionary that contains information about this line of assembly code. The keys have the following meanings:

  • line_no: The line in the assembly file that this line was on.
  • raw: The line as it was in the assembly file.
  • clean: The cleaned up line, ready for parsing.
  • defines_label: Whether or not this line is a label definition.
  • defined_label: The label that this line defined.
  • has_label_assigned: Whether or not this line has a label assigned to it.
  • assigned_label: The label that has been assigned to the first line of the machine code generated for this line.
  • defines_variable: Whether or not this line is a variable definition.
  • defined_variable: The variable that this line defines.
  • has_machine_code: Whether or not this line results in machine code. E.g. a comment has no machine code.
  • mc_bytes: List of machine code byte templates (with constant expansion information) for this assembly line.
Returns:Assembly line description template.
Return type:dict
eight_bit_computer.data_structures.get_arg_def_template()[source]

Get a definition template for an assembly operation argument.

This is a set of information that describes an argument used in a line of assembly.

The keys have the following meaning:

  • value_type: What kind of argument this is. constant or module_name.
  • is_memory_location: Whether this argument is referring to a location in memory.
  • value: The permitted value of the argument if it’s a module.

These dictionaries will be grouped in a list of lists that describe the possible arguments for an assembly operation. E.g. if the possible arguments for an assembly operation were:

  • ACC A
  • B C
  • A [#123]

The data structure would be as follows:

[
    [
        {
            "value_type": "module_name",
            "is_memory_location": False,
            "value": "ACC",
        },
        {
            "value_type": "module_name",
            "is_memory_location": False,
            "value": "A",
        },
    ],
    [
        {
            "value_type": "module_name",
            "is_memory_location": False,
            "value": "B",
        },
        {
            "value_type": "module_name",
            "is_memory_location": True,
            "value": "C",
        },
    ],
    [
        {
            "value_type": "module_name",
            "is_memory_location": False,
            "value": "A",
        },
        {
            "value_type": "constant",
            "is_memory_location": True,
            "value": "",
        },
    ],
]
Returns:Machine code byte description template.
Return type:dict
eight_bit_computer.data_structures.get_machine_code_byte_template()[source]

Get the template used to describe a machine code byte.

This is a set of information that describes the byte (of which there could be many) of machine code that an operation (e.g. LOAD [$variable] A) results in.

The keys have the following meaning:

  • bitstring: A byte bitstring of the final byte that will make up the machine code.
  • byte_type: The type of machine code byte. Will be instruction or constant.
  • constant_type: The type of the constant. Could be a label, variable or number.
  • constant: The constant that this byte will need to become. The resolution of the constant to a real machine code byte is done by the assembler.
  • number_value: The value of the constant as an int if it’s a number.
  • index: The index of this byte in program data.
Returns:Machine code byte description template.
Return type:dict
eight_bit_computer.exceptions module

Custom exceptions used in this project.

exception eight_bit_computer.exceptions.EightBitComputerError[source]

Bases: exceptions.Exception

Base class for exceptions in the computer

exception eight_bit_computer.exceptions.OperationParsingError[source]

Bases: eight_bit_computer.exceptions.EightBitComputerError

Raised when parsing an operation fails.

E.g. An incorrect argument is used with the LOAD operation.

exception eight_bit_computer.exceptions.LineProcessingError[source]

Bases: eight_bit_computer.exceptions.EightBitComputerError

Raised when processing a line fails.

E.g. The line was not a constant declaration and no operations matched.

exception eight_bit_computer.exceptions.AssemblyError[source]

Bases: eight_bit_computer.exceptions.EightBitComputerError

Raised when the assembly could not be converted to machine code.

eight_bit_computer.export module

Functionality to convert data other package friendly formats.

eight_bit_computer.export.bitstrings_to_cpp(bitstrings)[source]
eight_bit_computer.export.bitstrings_to_logisim(bitstrings)[source]

Convert bitstrigs to a logising RAM/ROM file format.

Used to convert ROMs and machine code.

Parameters:bitstrings (list(str)) – List of bitstrings to convert to a logisim friendly format.
Returns:String ready to be written to a file.
Return type:str
eight_bit_computer.export.chunker(seq, chunk_size)[source]

Take a larger sequence and split it into smaller chunks.

E.g.:

chunker([0,1,2,3,4,5], 4) -> [0,1,2,3], [4,5]
Parameters:
  • seq (list) – List of things to chunk up
  • chunk_size (int) – How big each chunk should be.
Returns:

Generator that yields each chunk.

Return type:

generator

eight_bit_computer.language_defs module

Defnitions for the machine code and microcode.

eight_bit_computer.language_defs.instruction_byte_from_bitdefs(bitdefs)[source]

Extract an instruction byte from the bitdefs that make it up.

If more than one bitdef is passed it will be merged with the others prior to extraction.

Parameters:list (bitdefs) – List of bitdefs to potentially merge and extract
Returns:Bitstring of the instruction byte
Return type:str
eight_bit_computer.main module

Top level interface for the module

eight_bit_computer.main.assemble(input_filepath, output_filepath=None, variable_start_offset=0, output_format='logisim')[source]

Read an assembly file and write out equivalent machine code.

Parameters:
  • input_filepath (str) – The location of the assembly file.
  • output_filepath (str) (optional) – The location to write out the machine code. If nothing is passed, the output path will be the input path with the extension changed to mc.
  • variable_start_offset (int) (optional) – How far to offset the first variable in data memory from 0.
  • output_format (str) (optional) – How to format the output. logisim or cpp.
eight_bit_computer.main.filepath_to_lines(input_filepath)[source]

Take a filepath and get all the lines of the file.

The lines returned have the newline stripped.

Parameters:input_filepath (str) – Path to the file of disk to read.
Returns:Lines of the file.
Return type:list(str)
eight_bit_computer.main.get_mc_filepath(asm_path)[source]

Get the filepath for the machine code.

This is the assembly filepath with .asm replaced with .mc

Parameters:asm_path (str) – Path to the assembly file.
Returns:Path to the machine code file.
Return type:str
eight_bit_computer.main.extract_machine_code(assembly_lines)[source]

Extract machine code from assembly line dictionaries.

Parameters:assembly_lines (list(dict)) – List of assembly line info dictionaries to extract machine code from. See get_assembly_line_template() for details on what those dictionaries contain.
Returns:List of bit strings for the machine code.
Return type:list(str)
eight_bit_computer.main.gen_roms(output_dir='.', rom_prefix='rom', output_format='logisim')[source]

Write files containing microcode for drive the roms.

Parameters:
  • output_dir (str) (optional) – The directory to write the roms into.
  • rom_prefix (str) (optional) – The prefix for the rom files.
  • output_format (str) (optional) – How to foramt the output. logisim or cpp.
eight_bit_computer.number_utils module

Functions for working with, checking and converting numbers.

All numbers are stored within the computer as the positive equivalent. They may be interpreted as negative.

eight_bit_computer.number_utils.number_to_bitstring(number, bit_width=8)[source]

Convert a number to an equivalent bitstring of the given width.

Raises:ValueError – If number doesn’t fit in the bit width.
eight_bit_computer.number_utils.number_is_within_bit_limit(number, bit_width=8)[source]

Check if a number can be stored in the number of bits given.

Negative numbers are stored in 2’s compliment binary.

Parameters:
  • number (int) – The number to check.
  • bit_width (int, optional) – The number of bits available.
Returns:

True if within limits, False if not.

Return type:

bool

eight_bit_computer.number_utils.get_positive_equivalent(number)[source]

Read the 2’s compliment equivalent of this number as positive.

With a 3 bit number, the positive equivalent of -2 is 5. E.g.:

-4 4 100
-3 5 101
-2 6 110
-1 7 111
 0 0 000
 1 1 001
 2 2 010
 3 3 011
Parameters:number (int) – The number to convert to a positive quivalent
Returns:The positive equivalent of the number.
Return type:int
eight_bit_computer.number_utils.bitstring_to_number(bitstring)[source]

Convert a bitstring to a number.

E.g. 10110101 gives 181.

Parameters:bitstring (str) – String of 1s and 0s.
Returns:The equivalent integer.
Return type:int
eight_bit_computer.number_utils.bitstring_to_hex_string(bitstring, zero_pad_width=2)[source]

Convert a bitstring to a hex number.

Parameters:
  • bitstring (str) – String of 1s and 0s.
  • zero_pad_width (int) (optional) – How many zeroes to pad the returned hex value with.
eight_bit_computer.operation_utils module

Common functions for operations.

eight_bit_computer.operation_utils.assemble_instruction(instruction_bitdefs, flags_bitdefs, control_steps)[source]

Create templates for all steps to form a complete instruction.

Parameters:
  • instruction_bitdefs (list(str)) – List of the bitdefs that make up the instruction byte.
  • flags_bitdefs – list(str): List of the bitdefs that make up the flags for this instruction.
  • control_steps – list(list(str): List of list of bitdefs that make up the control signals for each step.
Returns:

All the steps for this instruction.

Return type:

list(DataTemplate)

Raises:

ValueError – If too many steps were provided.

eight_bit_computer.operation_utils.add_quotes_to_strings(strings)[source]

Add double quotes strings in a list then join with commas.

Parameters:strings (list(str)) – List of strings to add parentheses to.
Returns:The strings with quotes added and joined with commas.
Return type:str
eight_bit_computer.operation_utils.match_and_parse_line(line, opcode, signatures=None)[source]

Examine assembly code to see if it is valid and parse the arguments.

This is a common function used by most of the assembly operations.

Parameters:
  • line (str) – The line of assembly code.
  • opcode (str) – The opcode this line is being tested to match.
  • signatures (list(list(dict)), optional) – Data structure that defines the different combinations of arguments. See get_arg_def_template() for more details.
Returns:

Whether or not the line matched, and if it did, the parsed arguments.

Return type:

(bool, list(dict))

Raises:
  • OperationParsingError – If multiple op_args defs matched. Or
  • if no op_args defs matched if the opcode matched (i.e. the
  • arguments weren’t valid for that assembly operation).
eight_bit_computer.operation_utils.generate_possible_signatures_list(signatures)[source]

Create a readable list of all possible signatures.

Parameters:signatures (list(list(dict))) – Data structure that defines the different combinations of arguments. See get_arg_def_template() for more details.
Returns:All possible argument combinations.
Return type:list(str)
eight_bit_computer.operation_utils.match_and_parse_args(line_args, signature)[source]

Parse assembly operation args if they match the definition.

Take arguments supplied for the assembly operation and see if they match this arguments definition.

Parameters:
  • line_args – (list(str)): The arguments supplied for this assembly operation.
  • signature (list(dict)) – Definition of a set of arguments. See get_arg_def_template() for more details.
Returns:

Whether or not the arguments matched, and if they did, the parsed values.

Return type:

(bool, list(dict))

Raises:

OperationParsingError – If a single argument managed to match different kinds of argument definitions.

eight_bit_computer.rom module

Create and export roms for the computer

eight_bit_computer.rom.get_rom()[source]

Get complete representation of the rom.

Returns:All the defined microcode.
Return type:list(RomData)
eight_bit_computer.rom.collect_language_datatemplates()[source]

Get all the datatemplates from all the defined operations.

Returns:
All the data templates from the defined
operations
Return type:list(DataTemplate)
eight_bit_computer.rom.collapse_datatemplates_to_romdatas(datatemplates)[source]

Collapse any addresses in datatemplates to real values.

If an address does need collapsing the original data is copied out to all the collapsed addresses.

Parameters:list (datatemplates) – A list of templates to collapse.
Returns:The expanded datatemplates
Return type:list(RomData)
eight_bit_computer.rom.populate_empty_addresses(romdatas, all_addresses, default_data)[source]

Form a complete set of rom data by filling any undefined addresses.

Parameters:
  • list (romdatas) – The romdatas defined by the instructions.
  • all_addresses (list(str)) – List of bitdefs representing every address in the rom
  • default_data (str) – The value to set for any address that isn’t in romdatas.
Returns:

List of RomDatas representing a completely full

rom

Return type:

list(RomData)

eight_bit_computer.rom.romdatas_have_duplicate_addresses(romdatas)[source]

Check if any of the romdatas have duplicate addresses.

Parameters:list (romdatas) – List of romdatas to check.
Returns:Whether or not there were any duplicated addresses.
Return type:Bool
eight_bit_computer.rom.slice_rom(rom)[source]

Slice a rom into chunks 8 bits wide.

This is to prepare the data to write into the roms. To take a single RomData as an example, if it looked like this (spaces added for clarity):

RomData(
    address="0000000 0000 000",
    data="10101010 111111111 00000000 11001100"
)

We would end up with:

{
    0: RomData(
        address="0000000 0000 000",
        data="11001100"
    ),
    1: RomData(
        address="0000000 0000 000",
        data="00000000"
    ),
    2: RomData(
        address="0000000 0000 000",
        data="11111111"
    ),
    3: RomData(
        address="0000000 0000 000",
        data="10101010"
    )
}
Parameters:rom (list(RomData)) – The complete ROM
Returns:list(RomData)) Dictionary of ROM slices
Return type:dict(int
eight_bit_computer.rom.get_num_bytes(bitstring)[source]

Get the number of bytes needed to store this bitdef.

Parameters:bitstring (str) – Bitstring representing the bits to store.
Returns:The number of bytes needed to store the bitstring.
Return type:int
eight_bit_computer.rom.get_romdata_slice(romdatas, end, start)[source]

Get a slice of the data in the romdatas.

Parameters:
  • romdatas (list(RomData)) – The romdatas to get a slice from
  • end (int) – The index for the end of the slice. Starts at zero at the rightmost (least significant) bit.
  • start (int) – The index for the start of the slice. Starts at zero at the rightmost (least significant) bit.
Returns:

The sliced list of romdatas

Return type:

list(RomData)

eight_bit_computer.token_utils module

Functionality for working with string tokens on assembly lines

eight_bit_computer.token_utils.is_label(test_string)[source]

Test if a string is a valid label.

Parameters:test_string (str) – The string to test
Returns:True if the string is a valid label, false otherwise.
Return type:bool
eight_bit_computer.token_utils.is_variable(test_string)[source]

Test if a string is a valid variable.

Parameters:test_string (str) – The string to test
Returns:True if the string is a valid variable, false otherwise.
Return type:bool
eight_bit_computer.token_utils.is_constant(test_string)[source]
eight_bit_computer.token_utils.is_number(test_string)[source]

Test if a string is a valid number.

Parameters:test_string (str) – The string to test
Returns:True if the string is a valid number, false otherwise.
Return type:bool
eight_bit_computer.token_utils.number_constant_value(number_constant)[source]

Get the value that a number constant represents.

Parameters:number_constant (str) – The constant to extract the value from.
Returns:The value of the constant.
Return type:int
eight_bit_computer.token_utils.is_memory_index(argument)[source]

Determine whether this argument is a memory index.

Memory indexes can be module names or constants with a [ at the start and a ] at the end. e.g.:

  • [A]
  • [#42]
  • [$variable]
Parameters:argument (str) – The argument being used for the assembly operation.
Returns:True if the argument is a memory index, false if not.
Return type:bool
eight_bit_computer.token_utils.represent_as_memory_index(argument)[source]

Format the argument so it appears as a memory index.

See is_memory_index() for details on what a memory index is.

Parameters:argument (str) – The argument to represent as a memory index.
Returns:The formatted argument.
Return type:str
eight_bit_computer.token_utils.extract_memory_position(argument)[source]

Extract a memory position from a memory index argument.

See is_memory_index() for details of what a memory index is.

Parameters:argument (str) – The argument to extract a memory position from.
Returns:The location in memory being referenced.
Return type:str
eight_bit_computer.token_utils.get_tokens_from_line(line)[source]

Given a line split it into tokens and return them.

Tokens are runs of characters separated by spaces. If there are no tokens return an empty list.

Parameters:line (str) – line to convert to tokens
Returns:The tokens
Return type:list(str)

Module contents

Development Tools

Logisim

Fritzing

Falstad Circuit Simulator

Credits

This project would not have been possible without the generosity of the following people:

Ben Eater

James Bates