Isetta TTL computer

Retro computer built from TTL, with 6502 and Z80 instruction set. Includes video system.

Similar projects worth following
For this project I decided to adhere to a wellknown instruction set. I started with the 6502 but with very little extra hardware added it should also be able to execute Z80 instructions.
The video should have at least 320 x 200 pixel graphics with several colors, and a 80-column text mode.

The idea for this design was born at the end of januari (2023). 

While many homebuilt CPU's have their own, unique instruction set, I decided that for this project the CPU would use an existing instruction set. Also, I wanted to build the computer from parts of the TTL family. That are integrated circuits, that can perform elementary logic functions. They were first used in the 60's of the previous age. I use a modern version of these circuits, that use less power.

So which instruction set to use ? The CPU inside your laptop or cellphone is much, much too complex to build. But it is possible to use an older CPU from the time when microprocessors just became available. There were two processors that can be regarded as the Beetle and the Deux Cheveaux of the beginning of the homecomputer age.

Those processors are the MOS Technology 6502 and the Zilog Z80

The 6502 is famous because it was the heart of the Apple 1, the Apple ] [, the Commodore 64 and many others. The Z80 is well known because it was used in the TRS-80 and the ZX Spectrum.

So now, I had to decide whether to use the instruction set of the 6502 or that of the Z80. But then I thought... why not build a CPU that can do both ?

I did spend a few months searching for a good design that would give good performance while not using too many parts. I think the basic design is stable now (end of march). I can now start with the details of the design.


Here comes the system diagram of the CPU. All arrows and buses represent 8 wires, that means that the information flows through the processor in units of 8 bits (a Byte).

I might as well start with explaining some of the parts.

  • PCH/PCL (program counter high byte and low byte)
  • A (Accumulator)
  • T (Temporary register)
  • DPH/DPL (Data pointer high byte and low byte)
  • IR (Instruction register)
  • CGH Constant generator, generates only 0 (mainly for zpage addressing)
  • CGL Constant generator for small integer values
  • MBANK Bank selection system

You will miss the 6502 registers S (stack pointer) and the index registers X and Y. And you will miss the long list of registers that are inside a Z80 CPU. Those registers are in memory. A special memory section (outside the normal 64K range) is used for the registers.

The control section will use microcode to run the whole show. The microcode has 8 pages, 1 page is needed for the 6502 and probably 4 will be needed for the Z80, and the software can switch between 6502 and Z80.


I hope to reach 6.25 MHz as clock frequency. 

For the 6502, the cpu will run many instructions in about the same number of clock cycles as a real 6502, but several instructions will take significantly more cycles. But since the clock frequency is higher, it will run programs faster than the original 2 MHz 6502.

For the Z80, most instructions will use less cycles, and a few will use more. Programs will run faster than on an original 4 MHz Z80. 

The video system will be loosely coupled to the CPU. The CPU is not needed to refresh the screen.

There will be some restrictions, decimal mode might not be implemented.


1. Data flow in the CPU

2. The ALU of Isetta

3. Requirements for the control section

4. CPU control section

5. Control section details

6. First 6502 microcode

7. Javascript emulator runs Apple 1 BASIC

8. Another interesting BASIC to test the 8080 instructions

microcode 230421_new.txt

First version of 6502 microcode generator.

plain - 39.04 kB - 04/21/2023 at 20:55


  • Another interesting BASIC to test the 8080 instructions

    roelh5 days ago 1 comment

    Since the Isetta supports instructions of the Z80 processor, it also supports the famous 8080 processor, because the Z80 has the 8080 instruction set as a basis. The 8080 is interesting because it is the grandfather of almost all INTEL CPU's for personal computers of the past 40 years.

    One of the very first personal computers was the Altair 8800 from the company MITS. It was built around the 8080 processor. Bill Gates, Paul Allen and Monte Davidoff created a BASIC interpreter for this computer, the Altair BASIC. It was called a 4KByte BASIC. But the actual program was smaller than 4K, because 4K was all memory that the Altair had ! There were only 790 bytes free for user programs ! Altair BASIC was the very first product of Microsoft !

    So I used this Altair BASIC to test the 8080 instructions on the Javascript emulator of the Isetta processor. I found an Annotated disassembly and a binary version. And I really needed the disassembly, to understand a little of the program.

    Subtract and compare

    After the first tests, I found that for the 8080/Z80, the carry flag behaviour for the subtract and compare instructions is inverted w.r.t. the behaviour in the 6502. I created a new microinstruction that complements the carry flag for the 8080/Z80. In several situations, it can be executed at the same time as another microinstruction.

    Parity flag

    Altair Basic has a FCompare at 0A4C that compares two floating point numbers. It returns
    one of the following values:
    • 0x00 for zero
    • 0x01 for greater than zero
    • 0xFF for less than zero

    That's very logical. Now you can simply test the sign flag or zero flag to jump according to the result.

    But in two instances, at 0B63 and 0C03, it is followed by a JP PO (jump on parity odd) instruction,

    that will jump if the result is greater than zero (as 0x01 is the only one of these three values that has an odd number of '1' bits in the byte)

    On the 8080, (almost ?) all arithmetic and logic instructions that act on the accumulator will 
    set the P flag according to the parity.

    But on the Z80, the parity flag behaviour is different. Logic instructions will set the flag according to the parity, but the arithmetic instructions will set the flag when there is an overflow. And indeed, it has been reported on the Internet that the Altair Basic will not work on a replica Altair that has a Z80 processor instead of the 8080 in the original Altair.

    So, although it is widely believed that the Z80 can run all programs intended for the 8080, this

    is not true because the behaviour of the P/V flag is different.

    And on the Isetta it is also a problem because parity is not implemented at all.

    The parity flag is tested with JP PO on only two occasions, at 0B66 and 0C04. Both instances are preceded by a call to FCompare at 0A4C. I replaced the calls to FCompare by a new subroutine, that does the following:

    • Call the FCompare function at 0AC4
    • Return if result is non-zero
    • Save Accumulator
    • Load Accumulator with 0xFF, and OR A to set the sign flag
    • Restore Accumulator and return

    Both JP PO instructions are now replaced by JP P (jump if positive). The parity flag is not needed any more (at least not for this BASIC).

    A nasty bug

    Strange thing were going on. The display of numbers displayed the first digit as a letter. And with another test program (TRS-80 Basic), identifiers were not recognized. After a few days, I found out that the carry flag was not cleared after an XOR A,A instruction. The microcode seemed ok, it said the carry was written (and at the same time a load operation was done). But for the carry-complement (for subtract and compare) I changed the behaviour such that writing the carry while doing a load resulted in a complement-carry ! Sometimes it is difficult to program your own contraptions.

    Other changes to the BASIC

    The code at the following positions was changed for character output and keyboard input:

    • The code at 0377 that handles character output
    • The code...
    Read more »

  • Javascript emulator runs Apple 1 BASIC

    roelh05/09/2023 at 18:43 0 comments

    The emulator is ready (in Javascript), and the microcode that makes Isetta a 6502 was also completed. The emulator mimics the operation of the hardware, so it needs the same microcode.

    What could be better to test it, than the original BASIC of the Apple 1 ?

    There were a few mistakes in the microcode, the BIT instruction did not set all flags correctly, and the LDX/LDY-immediate did not set the flags. And originally I thought I could do without the overflow flag, but the Apple BASIC uses it, so I changed my mind and also implemented this.

    Here is the proof that it is working:

    After the basic is loaded in the memory of the emulator, a few patches are done to enable keyboard-input and textarea-output. It is amazing that you must jump through several hoops to convert javascript-keystrokes in 'normal' ASCII. 

  • First 6502 microcode

    roelh04/22/2023 at 10:16 0 comments

    A first version of the microcode was made. The microcode is generated with a Javascript program. The script can be downloaded from the file section. An effort was made to make the microcode as clear as possible, the output will be a list of microcode instructions with a good explanation, for all 151 opcodes of the original 6502. It lookes like this:

    ---- 0xC8 INY reg_y ----
    001900 3A84B3 3A84B3 to_t <- inc(reg_y),upd_nz
    001902 008301 008301 next
    001904 11B403 11B403 reg_y <- acc_t
    ---- 0x69 ADC imm ----
    000D20 D1AB80 D1AB80 to_dpl <- (pc++),irq_to_f
    000D22 5D46F0 008301 F:[to_a <- adc(acc_a, dpl),upd_nzc] T:[next]
    000D24 000401 5D46F0 F:[interrupt] T:[to_a <- adc(acc_a, dpl),upd_nzc]
    000D26 110400 110400 int2
    ---- 0x65 ADC zp ----
    000CA0 D1AB80 D1AB80 to_dpl <- (pc++),irq_to_f
    000CA2 5DC5F0 008301 F:[to_a <- adc(acc_a, (0|dpl)),upd_nzc] T:[next]
    000CA4 000401 5DC5F0 F:[interrupt] T:[to_a <- adc(acc_a, (0|dpl)),upd_nzc]
    000CA6 110400 110400 int2
    ---- 0x75 ADC zpx ----
    000EA0 D18B80 D18B80 to_t <- (pc++),irq_to_f
    000EA2 1DA402 1DA402 to_dpl <- add(acc_t, reg_x)
    ---- end of ea calculation ---
    000EA4 5DC5F0 008301 F:[to_a <- adc(acc_a, (0|dpl)),upd_nzc] T:[next]
    000EA6 000401 5DC5F0 F:[interrupt] T:[to_a <- adc(acc_a, (0|dpl)),upd_nzc]
    000EA8 110400 110400 int2

    You can easily generate the full microcode:

    • Go to: this W3Schools page.
    • Clear the text in the left window and paste the microcode-generator code there.
    • Press Run
    • The microcode will appear within a few seconds.

    Some remarks about the notation:

    • The first hex number on a line is the address in the microcode ROM
    • The next two hex numbers are the microcode. These numbers are the same, except when conditional execution is used
    • When there is no interrupt, only the T:[ ... ] sections are executed.
    • When there is an interrupt, the F:[ ... ] sections are executed.
    • 'next' is an indication that the next opcode will be loaded. Due to the pipeline effect, one extra instruction will be executed after 'next'.
    • Depending on its role in the instruction, register A will be shown as 'to_a' or 'acc_a'. Same for register T.

    It is easy to count the number of cycles for each opcode. Just count the lines up to the first instruction that contains 'next', then add one cycle for the instruction after 'next'. So the shown 'ADC zp' opcode takes 3 cycles.

    The operation of some instructions will probably still be unclear to you. In that case, check the microcode script that contains a lot of comments about the used instructions.

    In the microcode generator, there is a central role for the function 'ins6( a, b, c, d, e, f )'. The name 'ins6' stands for 'instruction with 6 components'. The six components are:

    • a. Condition. An instruction to move a condition to the F flag, like c_to_f, z_to_f, or irq_to_f.
    • b. Destination. Data destination, like to_t, to_a, to_dpl.
    • c. ALU operation, like ld, adc, and, rol.
    • d. Source 1, this can only be acc_a or acc_t. For single-operand instructions, source 1 is unused. For writing to memory it decides if A or T gets written.
    • e. Source 2, this will in most cases be a memory location, like 'dir' (zpage location), 'ext' (full 64k range address in dph|dpl), 'pc_pp' (  (pc++) ), or a hardware register (dph, dpl, pch, pcl), or a register location in memory (like reg_s, reg_x), or a small constant lit0, lit2, lit8, lit32 for the values 0, 2, 8, 32. For writing to memory, source 2 is the destination address.
    • f. Update flags. It is indicated as upd_nz, upd_c, upd_nzc. Any combination of N, Z, C flags is allowed.

    The ins6 simply combines the control-wire values of its arguments, so it would not matter in which order the components are placed. But the comment-printing function 'pr_print' depends on the order of this components.

    It is tempting to continue with writing the Z80 micro-instructions, but I will now first make a simulator to check if everything works as intended.

  • Control section details

    roelh04/15/2023 at 21:15 0 comments

    In this log I talk about the 24 control signals that come from the microcode. The names of all signals that come from the microcode register will start with CTL.

    The microcode will be organized in 3 bytes, the upper, middle, and lower control byte. The upper microcode has the longest description.


    The first diagram shows the signals that select a condition and handles flags:

    Flag update

    Some instructions update flags, others don't. The signals CTL_UPD_C, CTL_UPD_Z and CTL_UPD_N indicate if the flags C, Z or N must be updated with a new value coming from the ALU_COUT, the ZERO detector at the output of the ALU, or bit 7 of the ALU output (R7, Result-bus bit7). If a flag is not updated, it will keep the same value.

    There is no provision to read or write the flag bits as a full byte, like that is normally used in many 6502 designs. The microcode will have to read or write the flags one-by-one in order to push or pull the flags (or do EX AF,AF' for Z80).

    Condition select

    The signals CTL_C0, CTL_C1 and CTL_C2 select one of eight conditions. It is used for two different things:

    • As a selector of the CONDITION carry-in signal of the ALU (adder or shift-right)
    • As a selector for the FLAG_F for conditional execution. This second function is activated when CTL_WR_F is active (high). If CTL_WR_F is low, FLAG_F keeps the same value.

    Early condition evaluation

    There is one more important thing to say about the condition selection. Suppose that we want tot test for a condition, and execute a conditional micro-instruction based on that, this would be 3 micro-instructions (3 cycles):

    1. Select the relevant condition, like C, Z or N, (putting this condition in FLAG_F)
    2. Fetch the next micro-instruction based on the FLAG_F value
    3. Execute the micro-instruction that was selected with FLAG_F

    Number 1 and 3 are really needed, but in number 2 we are just waiting for the next micro-instruction coming out of the pipeline, and if we can not do something useful in that cycle, it is a 'lost cycle'. But we can do something about that. By connecting the signals CTL_C0, CTL_C1, CTL_C2 and CTL_WR_F not to the microcode register, but directly to the microcode ROM output, we can evaluate the condition one cycle earlier (at the end of the fetch phase). We now get the following two-cycle sequence:

    1. Select the condition. The selection has actually been done in the previous cycle, so in this cycle the new micro-instruction, based on the FLAG_F value, will be fetched.
    2. Execute the micro-instruction that was selected with FLAG_F

    So we just gained one cycle for micro-instructions that use conditional execution.


    The ALU control section must provide the control signals for the ALU. In the ALU log it is shown that the following control signals are needed:

    • 4 bits DCBA controls for the upper logic unit
    • 4 bits DCBA controls for the lower logic unit
    • 2 bits Carry-input signals for added (ADD_CIN) and right-shift (SHR_CIN)
    • 1 bit selection of shift-right unit CTL_SHR

    So the ALU can be controlled with 11 signals. But we don't want to spend so many of the available micro-instruction bits for these. Fortunately, it is not difficult to bring this number of signals down to a reasonable amount.

    Generating the DCBA control signals

    The DCBA controls for the upper logic unit have bit A being '1' only in the case of a DEC instruction (and for complementing a value, but we don't use that). So bit A can be removed from the control wires and be regarded as a special case. 

    The DCBA controls for the lower logic unit only use the patterns 0000, 1010 and 0101. Of these, the 0101 is only used in a single case (SUB, Subtract), so if we handle that as a special case we only need a single control signal CTL_ALU0 for choosing between 0000 and 1010.

    To handle the special cases, we have a decoder (U5) that decodes the CTL_ALU1, CTL_ALU2 and CTL_ALU3 lines into 8 separate cases. The output of the decoder...

    Read more »

  • CPU control section

    roelh04/14/2023 at 20:45 0 comments

    The following diagram shows the principle of the control section:


    As said before, the processor is controlled by microcode. At the start of a new 6502 or Z80 instruction, the opcode of that instruction will be put in the 8-bit instruction register and the counter will be reset to zero. The counter has 4 bits, so for each opcode in the instruction register there can be a sequence of maximal 16 micro-instructions. 


    The Microcode ROM will deliver the 24-bit micro-instruction and store it in the microcode register at the end of the cycle. At the end of the cycle, the counter is incremented to prepare for fetching the next micro-instruction. In the next cycle, the micro-instruction is decoded (in the decoding block) and the micro-instruction is executed.

    For each opcode, there is a unique sequence of micro-instructions that will be executed. At the end of this sequence there is a special micro-instruction called LD_IR that loads the new opcode in the instruction register, resets the counter, and sets the 3-bit page.


    Note that when the micro-instruction is executed, the following micro-instruction is already being looked up in the microcode ROM. This means, that when the decoding section decides that a new opcode must be put in the instruction register, the next micro-instruction is already read from the microcode ROM. So the LD_IR is not the last instruction in the sequence:  The micro-instruction that follows LD_IR will also be executed. This is the pipeline effect.


    The control unit is capable of conditional execution of micro-instructions. At each location in the microcode ROM, there are actually two instructions stored. Which of these is executed, is determined by a flag called F. If, for a certain instruction, we do not want it to be conditional, we simply store two identical instructions, so in that case it doesn't matter which one is executed.


    If we would only have 6502 instructions, the page register would not be needed because there are only 256 possible opcodes (of which many are unused). But a Z80 would need several pages, because in its basic set of 256 opcodes there are some that are followed by another opcode byte. It is expected that the Z80 can be handled in 4 pages (A basic page, index-IX-IY page, shift-and-bit page, and the 0xED page for the special Z80 instructions). So of the 8 available pages, 1 will be needed for the 6502 and 4 for the Z80. The page register can be changed by the LD_IR instruction.

  • Requirements for the control section

    roelh04/06/2023 at 20:21 0 comments

    Micro-instructions...  which instructions do we need ? Before the control section can be designed, we need a list of everything that we want to control with a microinstruction.

    We start with a short list of pseudo-code that will have to be supported, it tells how the information will flow and what the ALU does:

    // Load Instruction Register with next opcode. It would be nice 
    // if it can also be loaded from another source. This micro-instruction must
    // also specify which microcode page to use for the next opcode. Each microcode 
    // page has room for 256 opcodes of max. 16 microinstructions each.
        IR <- (PC++)
    // Instructions with a single operand.
    // op1 can be LOAD, INC, DEC, SHR
        dst_reg <- op1 (data_source)
    // Instructions with two operands. Can use A or T register as first operand.
    // op2 can be ADD, SUB, ADC, SBC, AND, OR, XOR
        dst_reg <- op2 (A, data_source)
        dst_reg <- op2 (T, data_source)
    // Store instructions. Only A or T can be stored.
        mem(address) <- A
        mem(address) <- T

    The dst_reg can be A, T, DPH, DPL, PCH.  When PCH is written, DPH will be written to PCL

    The data_source can be:

    • a register (DPH, DPL, PCH, PCL),
    • a small constant (CGL) 
    • a value read from a memory address.

    A memory address can be:

    • a register stored in memory, addressed by the CGL constant as low byte and a zero as high byte
    • a zero page address (for 6502), addressed by the DPL register, with high byte zero
    • a full 64k address, provided by DPH and DPL
    • the program address, addressed by PCH and PCL. Automatic increment of PCH/PCL after it is used.

    The register that is stored in memory sits in a special 64K memory area called SYS. It is addressed by the CGL constant (4 bits). The lower 3 bits specify 8 registers. If the 4th bit is set, it specifies 8 other registers, but in this case there is an extra address bit that is provided by the Z80-exx flipflop. The result is that the upper 8 registers can be swapped for other registers by toggling this flipflop. It implements the alternative register set of the Z80.


    The control section has to tell the ALU what it should do:

    Two-operand instructions like ADD, SUB, AND, OR, EOR

    One-operand instructions like LD, INC, DEC, ASL, LSR, ROL, ROR


    But the microinstruction has to specify more:

    • which processor flags ( N, Z, C ) to update
    • if the internal carry flag (TC, Temporary Carry) must be updated (see below)
    • which value to use for the carry-input of the ALU and for the SHR block ( 0, 1, C, TC )
    • which condition to use for conditional execution (see below)

    The Temporary Carry is a flag that is used for address calculations, in situations where the programmer-visible C flag may not be changed.


    The control unit is capable of conditional execution of microinstructions. At each location in the ROM that stores the microinstruction, there are actually two instructions stored. Which of these is executed, is determined by a flag called F. If, for a certain instruction, we do not want it to be conditional, we simply store two identical instructions, so it doesn't matter which one is executed.

    This flag F can be modified by the microcode. For example, in a BCS (branch on carry set) instruction, the flag F will first get the same value as the C flag. The following microinstructions will now put the jump-address in the program counter when the F flag is '1'.
    Another use is, that the Interrupt-signal can be copied to the F flag at the beginning of the microcode for an instruction. At the end of the microcode sequence, the F flag will then be tested, and if there was an interrupt, the instruction register (IR) will be loaded with the opcode of the instruction that handles the interrupt. (That will probably be an opcode in an other page.)
    The F flag can be set to one of the following conditions:...

    Read more »

  • The ALU of Isetta

    roelh04/01/2023 at 14:35 1 comment

    Which functions do we need in the ALU and how must that be arranged? Well, we need the usual ones:


    • ADD  (Add two bytes)
    • SUB  (Subtract one byte from the other)
    • INC   (Increment: Add 1 to a byte)
    • DEC  (Decrement: Subtract 1 from a byte)


    • bitwise AND
    • bitwise OR
    • bitwise XOR (called EOR for the 6502)


    • Shift the bits in the byte one bit to the left (For ROL and ASL on the 6502)
    • Shift the bits in the byte one bit to the right (For ROR and LSR on the 6502)


    • PASS Pass data from databus unmodified.

    It must be possible to pass a byte from the databus to the output of the ALU, because otherwise it would not be possible to simply load a value in one of the registers (it is needed for LOAD functions).

    The ADDER

    The ADD function is easy to realize. There is a chip that can add two 4-bit values, it is called the 74AC283. If we use two of them, we can add two bytes that are present on the A and B input of the adder. The chip has a 'carry' input that can be connected to the Carry flag to provide ADC (Add with carry).

    We need a way to connect the A-input of the adder to zero, this causes the B-input (databus) to be added to zero, passing the databus value to the output, providing the PASS function. If we set the carry-input of the adder to logic 1, this function will become INC.

    If we connect the A-input of the adder to the value 0xFF (all 8 bits '1'), the adder will add the value 255 to the B-input (databus). But this is a value greater than 8 bits ! The 8th bit will be dropped and the result is that the databus value is decremented, so we now have DEC.

    And we need a subtract function, SUB. This can be calculated by bitwise complementing (replacing 0 by 1, and 1 by 0) the B-input, and then ADD (while providing '1' on the carry input). The 'carry' input can be connected to the Carry flag to provide SBC (Subtract with carry).

    Left shift can be done by adding a value to itself. The microcode will handle that, so the 6502 or Z80 functions to shift left will work as usual. The right shift needs a dedicated chip.

    A simple way to do LOGIC functions

    For providing logic functions, the easiest way to do that seems to be to build something like the following circuit: 

    This not only provides the logic functions, but also ADD. It is very easy to understand, the left side of the diagram calculates the ADD, AND, OR and XOR functions, and at the right side the ALU Opcode selects which of the four results will be used (with a 4-input multiplexer). It will work perfectly.

    How many parts will be needed ?

    Since this is an 8-bit ALU, we need 8 gates of each type. There are 4 gates in a chip, so calculating the three logic functions cost 6 chips. We need eight 1-bit multiplexers, there are two in a chip, so that cost 4 chips, and there are the two adder chips, for a total of 12 for the circuit above.

    But there are more chips needed. For subtract, the B input must be complemented, that can be done with two 74HC86 XOR gate chips. And for INC or DEC we must be able to put a value of 0 or 0xFF on the A input. This can be done with two multiplexers 74HC157, that can connect the A-input either to the input bus or to the fixed value 0 or 0xFF. That is 4 chips for these special functions, bringing the total to 16 chips for the ALU.

    Calculating Logic functions with less chips

    In the previous section we used a multiplexer. It has two inputs X and Y that select one of the inputs A, B, C or D and put the selected signal on its output Q:

    The truth table of this device is:

    X   Y   Q
    0   0   A
    0   1   B
    1   0   C
    1   1   D

    If we now regard X and Y as inputs and A, B, C and D as constants, we actually have a programmable logic gate, where the value on the four ABCD inputs determines which 1-bit function it performs on the values X and Y:

    0000  Always 0
    1000  AND
    1110  OR
    0110  XOR
    1100  pass value X
    1010  pass value Y
    0011  pass value X complemented
    Read more »

  • Data flow in the CPU

    roelh03/31/2023 at 20:28 0 comments

    The following diagrams will clarify how the information flows in this CPU.

    All registers and busses that are shown here are 8 bits wide.


    This diagram shows how the opcode of an instruction is fetched from memory. The program counter, consisting of an 8-bit high part (PCH) and an 8-bit low part (PCL), addresses a location in memory where the opcode can be found. This opcode is read from memory and transferred to the instruction register (IR). The instruction register is connected to the control unit, that is responsible for the execution of this instruction. It will do this by executing micro-instructions that are in its own private read-only memory (not shown here). The program counter (PCH/PCL) contains a counter that will increment after the opcode is read.


    If the instruction has an operand, the operand will be addressed by PCH/PCL. It will be read from that address in memory and will first be transferred to the databus and the ALU. The ALU is in this case configured to ignore the contents of the operand bus, and transfer the contents of the databus to its output without modifying it. The byte then is transferred through the result bus and delivered to one of the indicated registers A, T, DPH, DPL or PCH.


    This example shows how an operand in zero page is added to register A, with the result flowing into register T.  It supposes that the opcode of the instruction has been read, and that an operand (the zpage address) has been read and has been put into register DPL.

    We see here that the lower 8 bits of the address come from DPL while the upper 8 bits are zero. The contents of that memory location is read from memory and transferred to the input of the ALU. The other input of the ALU is equal to the contents of the A register. The ALU will be configured to ADD both inputs, and the result of the ALU is transferred to the T register.


    This diagram shows how one of the internal registers (DPL in this case) can be incremented. The ALU is configured to transfer the databus to the result bus (ignoring the operand bus), but the ADDER inside the ALU has its carry-input set to 1, so the ALU will increment the value that is presented on the input.


    Here is shown how  the A register is stored in memory, at a memory location that is addressed by registers DPH and DPL. 

View all 8 project logs

Enjoy this project?



Con Cunningham wrote 04/21/2023 at 21:11 point

Just  been reading your square inch TTL CPU - great work. Did you publish your microcode, or is that proprietary ? Curious how you managed such a rich ISA with so few control signals!

  Are you sure? yes | no

roelh wrote 04/22/2023 at 18:08 point

Hi Con, the microcode can be seen when you scroll down on the simulator page:

Did you read the logs ? If you also look at the schematic,  ( ) you should be able to understand how it works.

  Are you sure? yes | no

Con Cunningham wrote 04/21/2023 at 20:51 point

I honestly don't know - perhaps when I get to wire-wrap some of it, and it starts to operate I might do so. Yes indeed, I use the 74181 as a 16 bit address incrementer, and of course, for all of the other 6502 arithmetic and logical ops.  Keep up the good work.

  Are you sure? yes | no

Con Cunningham wrote 04/20/2023 at 21:03 point

An ambitious project. I know this because I have been working on something similar (When work allows) for some months. I am on my 3rd iteration. This time I have based my design roughly on Tanenbaum's "Structured Computer Organisation" , see

I am using 74LS TTL, and have opted to make life easy by using 4 x 74181 as a 16 but ALU. I hope to implement all of the 6502 ISA, with the exception of Decimal Mode.

Have you begun to use a simulator, such as Logisim or Digital yet?

I wish you the very best of luck with your project, keep the updates coming!


  Are you sure? yes | no

roelh wrote 04/21/2023 at 20:03 point

Thanks Con !  I'm curious about your project. Will you publish it on Hackaday ? I suppose you will use the 4 x 74181 as address incrementer ? Yes I will soon start simulating.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates