This is a CPU witch is capable of executing a lot of stuff in one clock cycle, and this CPU can shift left up to 7 times and shift right up to 7 times witch means that it is possible to multiply and divide in one clock cycle if you program a table in the program memory. it has a 32 bit instruction width and a 8 bit address. it also has
The ALU has the following operations:
This Was made by miles
This is an half adder circuit, it takes two one bit binary digits and adds them to produce a sum bit and a carry bit, however this circuit is incapable of adding more than one bit binary digit hence it is known as the binary half adder
A circuit that adds two 3-bit numbers using a half-adder and a full-adder.
A circuit that takes two decimal numbers A and B as input and then splits in into their corresponding three bits using a splitter and then calculates their summation using XOR, AND and OR gates. This generates 4 output lines for 4 bits of the summation, and a reversed splitter is finally used to join the output lines to produce a 4-bit output and displayed using a Hex-Display.
This is a ripple-carry adder.
A 16-bit computer/maybe console inspired thing, the Femto-4. This will be the main branch and backups will be forks from it. This project was started around November 2020.
Do fork the project and write your own code for it! If you want more information on how to do so read the Developer Guide in the assembler.
Note: The Flappy Bird high score and the Snake high score are mine. If you want to save your own scores permanently you will have to fork the project.
General Architecture: The Femto-4 is a 16-bit, Von Neumann architecture computer with variable length instructions that are comprised of multiple 16-bit words. It has many features associated with CISCs, such as variable length instructions, and multicycle indirect loads, however operates like a RISC, with each instruction taking exactly 1 clock cycle. This was done to give the Femto-4 power whilst keeping its construction simple. First the OP code of the instruction is read, and then depending on the OP code, additional pieces of data may be read for the operands. This allows execution to become incorrectly offset, which can lead to the execution of garbage if the PC is jumped to an incorrect address. This is usually fine, since the OP code space is so empty that the data will likely be passed one at a time until the next valid instruction. Instructions are read from main memory, making this architecture a Von Neumann architecture as opposed to a Harvard architecture. The MAR always specifies the address being read to or written from, whilst the MDR always holds the data being written. Data from the data out bus can be written to most special registers during the instruction. OP codes and operands are all 16-bits. The large OP code size was chosen due to the high number of ALU instructions. There are approximately 500 interpretable OP codes that the computer can handle.
Memory Mapping: The 16-bit address space of the Femto-4 is memory mapped, with all data being stored somewhere in the address space. The last 48kx16b of memory (all addresses starting with 0b01, 0b10, or 0b11) are dedicated to the cart memory. This is where the interchangeable program would be stored, allowing programs to be easily changed by changing carts. The carts have 32 16kx16b EEPROM/RAM chips, which can be switched between during execution by writing to address 0x00cc. This gives each cart 512kx16b of memory to play with. In theory, additional memory can be added in a cart by creating a similar system on the inside of the cart, which would allow it to swap between even more EEPROM/RAM chips. The initial 16kx16b are therefore mapped to everything else, including a fixed WRAM chip that cannot be switched out, the bootloader, the PPU data, general registers, the stack, inputs, outputs, and a few special registers, such as the protect, mode, and flag registers.
Fast Execution: Execution at the fastest clock speed (one pulse every 100ms, or 10Hz, which is defined as the clock changing state every 50ms, or at a rate of 20Hz) is terribly slow, and would make reasonable graphics effectively impossible. Due to this, the Femto-4 includes several execution modes that allow the computer to run much faster. There are two registers involved in this, address 0x00ca, the mode register, and address 0x00cb, the protect register. When the two least significant bits of the mode register are low, the computer runs normally, executing 1 instruction per clock pulse. When bit 0 is set high, the computer enters fast execution on the rising edge, where it executes multiple instructions per clock pulse. This is achieved by looping an inverter into itself, producing a loop that will pulse indefinitely until the looping line is stopped by some external factor. Stopping the loop is critical since leaving the loop running will stop CircuitVerse's execution, due to it going over the stack limit of the execution. Fast execution is always paused by a 0x0000 and 0x0001 OP Code. Bit 2 enables falling edge fast execution, which can be done with rising edge fast execution producing dual edge fast execution. Setting the third bit of the mode register high will enable protection. This will ensure that computer only executes as many instructions as the value in the protect register. This protects execution by ensuring that the loop will always pause before the cycle limit is reached. Since some operations are far more complex than other operations, the maximum number of instructions per clock pulse is variable, and testing should always be conducted to ensure that the limit is not reached. Due to this, for games that need regular graphics updates, it is recommended that protection is not used, and instead the pauses are fully code controlled. On the other end of the mode register are the graphics mode. The highest two bits give the graphics update mode, 0b00 for falling edge only (normal speed), 0b01 for dual edge (double speed), 0b10 for every other clock pulse (half speed), and 0b11 for code controlled, where the 0x0001OP Code is required to update the graphics. The third most significant bit is the graphics disable bit. Setting it high stops updating the graphics, reducing lag by prevent the graphics fast execution loop from running. The mode and protection values are only updated on the rising edge of the clock pulse, and therefore there should always be pauses before and after any execution mode or protection change. By default, the Femto-4 executes with a protection value of 16, to allow the carts to run smoothly, however, depending on the instructions being used, that number can be raised to 64.
Graphics (16x16): The Femto-4 is capable of driving a 16x16 15-bit direct colour screen. It has space for 32 sprites which are rectangles with an assigned colour. All the sprites are drawn to the screen whenever a graphics update occurs, depending on the graphics mode. When using dual-edge fast execution, the falling edge should only be used to execute game code, since writing graphics data as the screen is being drawn may mess up the graphics. These 32 sprites have their data stored in the PPU RAM in the following format: The first 16 bits are the corners of the rectangle, with each coordinate being 4 bits. The coordinates are ordered x coordinate 1 (4), x coordinate 2 (4), y coordinate 1 (4), y coordinate 2 (4). The second coordinates are offset up by 1, to allow the full screen to be drawn to, such that the dimensions of the rectangle are (x2 - x1) + 1 and (y2 - y1) + 1. The next 16 bits are the sprites colour, with the first 15 bits being used for 15-bit direct colour, and the last bit being used to enable or disable drawing the sprite. The last bit is important to ensure that blank sprites are not drawn to the screen. Since the screen is not wiped every time it is refreshed, the background must be a sprite to ensure that the screen is fully wiped before the rest of the sprites are drawn on. Control of this allows carts to draw a single frame over multiple updates, allowing the 32-sprite limit to be bypassed (see how Snake works). The sprites are drawn in memory order, with the sprite with the largest address always being drawn last and therefore on top, of all other sprites. This is achieved by using the exact same system as fast execution, which reads off all the sprite data and draws them to the screen in a single clock pulse. This can loop more times safely than the main CPU since it has less dependencies which dramatically decreases the simulation's stack usage.
Graphics (32x32): The Femto-4 can also drive a 32x32 screen, with sprites able to be drawn through 3 different modes. The 32x32 screen PPU treats the addresses as one combined 32-bit value, with the value with the smaller address going first. The first 3 bits of the 32 bits define the mode. Only the values 1, 2, 3, correspond to actual sprites, whilst the rest are not drawn to the screen. Mode 1 splits the remaining 29-bit space as the following: unused (1), x coordinate (5), y coordinate (5), red (6), green (6), blue (6). Mode 2 splits the 29-bit space in the following way: x coordinate 1 (5), x coordinate 2 (5), y coordinate 1 (5), y coordinate 2 (5), red (3), green (3), blue (3). Mode 3 splits the 29-bit space in the following way: unused (3), x coordinate (5), y coordinate (5), red (5), green (5), blue (5), alpha/transparency (1). As with the 16x16 screen, Mode 2's second coordinates are offset by 1 resulting in rectangles having the dimensions of (x1 - x2) + 1 and (y1 - y2) + 1. Mode 3 is designed to allow the colours used in the 16x16 screen to be the same, making converting code between the two versions easier. The update mechanism is the same as 16x16 screen.
ALU: The basic ALU was inspired by the ALU-74LS181. It was designed to flexibly change between various operations by changing an additional piece of data which is bundled in the OP code. This allows a single ALU to handle all the required processes, such as the basic binary logic operations, shift left, adding, and subtracting, reducing the number of circuits required, as well as the logic required to decide which instruction to use. The Femto-4 also can multiply, divide, shift right, shift left/right by a specified number of bits, and perform operations designed to work with the computer's graphics data.
Conditional Jumps: The Femto-4 can perform immediate and direct jumps depending on the flags, a specified bit of the accumulator, and the clock. The flag jumps allow for comparisons to be made. There are three flags, the carry, the most significant bit in the accumulator, and if the accumulator value is 0, the equals flag. By performing A-B, we can compare A and B by looking at the flags. If the equals flag is true, then A=B, since A-B = 0. If the most significant bit is 0, then the number is positive or 0 (by two's complement) and therefore A>=B. The comparison is not entirely correct for numbers in two's complement (a large positive number and a large negative number when subtracted can yield a positive number), but for small values it works well. Whilst we cannot directly check A<=B using A-B in this design, we can simply flip the subtraction to B-A to do so. The accumulator bit testing is mainly used to check for controller inputs. Since each button in the controller is mapped to one bit, bit testing that bit effectively allows us to check if a button has been pressed. A similar test could be performed using an AND instruction, and checking if the result is equal to 0 or not. Bit testing is most useful for testing an input from both controllers, since it can cut out an additional instruction. The jump on clock is there to ensure that we can jump execution on the right clock pulse, which ensures that graphics can be updated on the edge of execution.
Timing: The computer is timed using several standard delay chips. The pulse length running in to the computer is about 10k units long. Therefore, different parts an instruction are separated by 20k unit delays. Further control of timings inside these periods is achieved through 1k "On Delays", which have a 1k delay turning on, but a 0k delay turning off, ensuring that pulses do not bleed into the next pulse. These pulses can tell registers to write and what source to write from, enable the read and write lines, update the ALU, and update the stack Each instruction is separate by 600k of delay in fast execution. For more information on how delay works see here: https://circuitverse.org/users/4699/projects/circuitverse-delay-introduction.
Keyboard Mapping: The Femto-4's keyboard controller mapping was created using a specialised chip. This chip used the fast execution loop to take 15 inputs from a keyboard and map the inputs to button presses on the controllers. Since the buttons are updated several times in a clock pulse, the keyboard controller cannot handle held buttons. The keyboard mapping is designed to work with both controllers, allowing two player games to be feasible on the computer.
Assembly: The Femto-4 has an assembler that converts assembly written in a .txt into hex values in a .txt that can be copied and loaded into the EEPROM banks for storage. The assembler can handle symbol assignment, as well as assigning addresses in the code symbols to make handling jumps easier. For full details on the Femto-4's assembly language view the assembly developer guide.
Phemton: Phemton is the Femto-4's high level language, with a compiler to compile it's code into Femto-4 assembly. Phemton handles variable memory assignment, basic array assignment, if, elif, else statments, while loops, for loops, and functions. Phemton Lite is the only compiler complete, and lacks an optimiser. Phemton Lite has the concept of local scope only when compiling. All uniquely identified variables are given a global address. This reduces the runtime load since the computer does not need to decide where the variables need to go during run time. Future planned additions include generated code optimisations and optimisers, Phemton Full, which has dynamic memory assignment, and Phemton Plus, which adds additional types for floats and longs. For more details view Phemton's developer guide.
Other Notes: The memory wrappers allow external chips to interact with the main data control system, in this case used for RNG, controllers, the keyboard, and driving the text output. This makes it easy to additional chips to the computer. All assembly and Phemton code can be found in the project for the Femto-4's assembler and compiler respectively. The save data cart must be located outside of the Femto-4 circuit to ensure that its contents are automatically saved. The GitHub links are more likely to be up to date than the Replit links, however the Replit links allow the code to be ran in the browser.
For more information, please read the developer guide found in the Femto-4's Assembler, or just post a comment and ask me.
This is a secret to everybody, unless you found it.
A 20bit processor
Yet to be done:
-Ram address decode logic (Internal, External)
-Instruction decode logic
00000-9ffff: App rom
a0000-ff86e: GP ram
ff86f-ff999: Video ram
ffa00-ffeff: The stack
Uses empty stack convention
Uses ascending stack convention
Parameters should be pushed onto the stack before calling the function
Status register goes as following (MSB first): OW, Z, N, E, IntterruptProgress (4bit), ResetProgress (4bit), InterruptAvailable, ResetAvailable, 0b000000
Every mention of "address" refers to the 6-nibble value with address configuration concatenated before the actual memory reference/data
ra1/ra2/ra3 refers to a register or address. The number is to distinguish between different arguments for instructions.
Stack Pointer: 0
Instruction step: <INTERNAL>
-Address configuration is concatenated after reg id. Immeadiates and stack pointer indexes are not supported. e.g. 71 is data at B register, 32 is using SUM register as a xxxxx to use $?xxxxx on it.
-fpA through fpD &fpS are floating point registers. Bit functionality is as follows (MSB first): S, EXP (6bit), MAN (13 bit). To calculate the value: (1 + (1-(1/MAN))) * 2^(EXP - 63). -fpA-fpD & fps are incompatible with iadd/isub/idiv/imul/iinc/idec. fpadd/fpfpsub/fpmul/fpdiv/fpinc/fpdec work only on fp registers. They add the actual values, not bits.
-bor/band/bxor do operations on bits, not fpti values, still store binary result in S register.
Memory page 0: fff00
Memory page 1: fff01
Memory page 2: fff02
Memory page 3: fff03
-When using on registers, 0x8 as added onto the config
Immeadiate: $#xxxxx (0xxxxx) (Uses data xxxxx)
Address: [email protected] (1xxxxx) (Uses data at address xxxxx)
Pointer: $?xxxxx (2xxxxx) (Uses data at address xxxxx and uses [email protected]***** on it)
Stack pointer index: $-xxxxx(%sp) (3xxxxx) (Subtracts xxxxx from stack pointer and uses $?***** on it)
mov ra1, ra2:
Stores ra1 into ra2 address (000(ra1)(ra2))
fpti ra1, ra2:
Transfers data at ra1 to ra2 (not bits, value. kinda like "(int) float" in c) (ra1 is encoded in floating point. requires ra1 to be using a fpr or fp address) (001(ra1)(ra2))
itfp (r, fpR), ($Xxxxxx, fpR):
Transfers data at ra1 to ra2 (not bits, value. kinda like "(float) int" in c) (ra1 is encoded in floating point. requires ra2 to be using a fpr or fp address) (002(ra1)(ra2))
bor (ra1,ra2), (ra1, ra2, ra3):
Ors ra1 and ra2 registers, stores result in S register. Updates OW, Z, and N flags in status register (003(r1ID)(r2ID))
Ors ra1 and ra2 registers, stores result in ra3. Updates OW, Z, and N flags in status register (004(r1ID)(r2ID)(r3))
band (ra1, ra2), (ra1, ra2, ra3):
Ands ra1 and ra2, stores result in S register. Updates OW, Z, and N flags in status register (005(r1ID)(r2ID))
Ands ra1 and ra2, stores result in ra3 register. Updates OW, Z and N flags in status register (006(r1ID)$Xxxxxx)
bxor (,r1,r2), (,r1,$Xxxxxx), ($Xxxxxx,r2), ($Xxxxxx,$Yyyyyy):
Xors ra1 and ra2, stores result in S register. Updates OW, Z, and N flags in status register (007(r1ID)(r2ID))
Xors ra1 and ra2, stores result in ra3 register. Updates OW, Z and N flags in status register (008(r1ID)$Xxxxxx)
iadd (,r1,r2), (,r1,$Xxxxxx), ($Xxxxxx,r2), ($Xxxxxx,$Yyyyyy):
Adds r1 and r2 registers, stores result in S register. Updates OW, Z, and N flags in status register (013(r1ID)(r2ID))
Adds r1 register and $xxxxx address, stores result in S register. Updates OW, Z and N flags in status register (014(r1ID)$Xxxxxx)
Adds $xxxxx address and r2 register, stores result in S register. Updates OW, Z and N flags in status register (015$Xxxxxx(r2ID))
Adds $xxxxx and $yyyyy addresses, stores result in S register. Updates OW, Z and N flags in status register (016$XxxxxxYyyyyy)
isub (,r1,r2), (,r1,$Xxxxxx), ($Xxxxxx,r2), ($Xxxxxx,$Yyyyyy):
Subtracts r2 from r1, stores result in S register. Updates OW, Z and N flags in status register (017(r1ID)(r2ID))
Subtracts $xxxxx from r1, stores result in S register. Updates OW, Z and N flags in status register (018(r1ID)$Xxxxxx)
Subtracts r2 from $xxxxx, stores result in S register. Updates OW, Z and N flags in status register (019$Xxxxxx(r2ID))
Subtracts $yyyyy from $xxxxx, stores result in S register. Updates OW, Z and N flags in status register (01a$XxxxxxYyyyyy)
imul (,r1,r2), (,r1,$Xxxxxx), ($Xxxxxx,r2), ($Xxxxxx,$Yyyyyy):
Multiplyes r1 and r2 registers, stores result in S register. Updates OW, Z and N flags in status register (01b(r1ID)(r2ID))
Multiplyes r1 register and $xxxxx address, stores result in S register. Updates OW, Z and N flags in status register (01c(r1ID)$Xxxxxx)
Multiplyes $xxxxx address and r2 register, stores result in S register. Updates OW, Z and N flags in status register (01d$Xxxxxx(r2ID))
Multiplyes $xxxxx and $yyyyy addresses, stores result in S register. Updates OW, Z and N flags in status register (01e$XxxxxxYyyyyy)
idiv (,r1,r2), (,r1,$Xxxxxx), ($Xxxxxx,r2), ($Xxxxxx,$Yyyyyy):
Divides r1 by r2, stores result in S register. Updates E, Z and N flags in status register (01f(r1ID)(r2ID))
Divides r1 by $xxxxx, stores result in S register. Updates E, Z and N flags in status register (020(r1ID)$Xxxxxx)
Divides $xxxxx by r2, stores result in S register. Updates E, Z and N flags in status register (021$Xxxxxx(r2ID))
Divides $xxxxx by $yyyyy, stores result in S register. Updates E, Z and N flags in status register (022$XxxxxxYyyyyy)
iinc $Xxxxxx, r:
Increments address xxxxx (023$Xxxxxx)
Increments register r (024(rID))
idec $Xxxxxx, r:
Decrements address XXXXX (025Xxxxxx)
Decrements register r (026(rID))
Adds r1 and r2 registers, stores result in fpS register. Updates OW, Z, and N flags in status register (027(r1ID)(r2ID))
Subtracts r2 from r1, stores result in fpS register. Updates OW, Z and N flags in status register (028(r1ID)(r2ID))
Multiplyes r1 and r2 registers, stores result in fpS register. Updates OW, Z and N flags in status register (029(r1ID)(r2ID))
Divides r1 by r2, stores result in fpS register. Updates E, Z and N flags in status register (02a(r1ID)(r2ID))
Increments register r (02b(rID))
Decrements register r (02c(rID))
phb $Xxxxxx, r:
Increments stack register and sets $-00000(%sp) to $xxxxx (02d)
Increments stack register and sets $-00000(%sp) to r register (02e)
plb $Xxxxxx, r:
Decrements stack register and sets $xxxxx register to $-00000(%sp) (02f)
Decrements stack register and sets r register to $-00000(%sp) (030)
jmp $Xxxxxx, r:
Jumps to $xxxxx address(031Xxxxxx)
Jumps to r register (032(rID))
jow $Xxxxxx, r:
Jumps to $xxxxx address if OW bit is set (033Xxxxxx)
Jumps to r register if OW bit is set (034(rID))
jnow $Xxxxxx, r:
Jumps to $xxxxx address if OW bit is clear (035Xxxxxx)
Jumps to r register if OW is clear (036(rID))
jz $Xxxxxx, r:
Jumps to $xxxxx address if Z bit is set (037Xxxxxx)
Jumps to r register if Z bit is set (038(rID))
jnz $Xxxxxx, r:
Jumps to $xxxxx address if Z bit is clear (039Xxxxxx)
Jumps to r register if Z bit is clear(03a(rID))
jn $Xxxxxx, r:
Jumps to $xxxxx address if N bit is set (03bXxxxxx)
Jumps to r register if N bit is set (03c(rID))
jnn $Xxxxxx, r:
Jumps to $xxxxx address if N bit is clear (03dXxxxxx)
Jumps to r register if N bit is clear(03e(rID))
je $Xxxxxx, r:
Jumps to $xxxxx address if E bit is set (03fXxxxxx)
Jumps to r register if E bit is set(040(rID))
jne $Xxxxxx, r:
Jumps to $xxxxx address if E bit is clear (041Xxxxxx)
Jumps to r register if E bit is clear(042(rID))
A multiplication circuit designed to multiply two 8-bit numbers, creating a 16-bit output. The values are unsigned, and input into two locations in a currently unconventional way. The output is stored in the flip-flop array to the right. The button at the top starts or resets the program.