Close
0%
0%

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, the Nintendo NES 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.


TECHNICAL DESCRIPTION

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.

PERFORMANCE

I hope to reach 6.25 MHz as clock frequency. This is actually a must, because this is 1/4 of the VGA video clock frequency.

For the 6502, the cpu will run many instructions in about the same number of clock cycles as a real 6502, but some 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 output is directly generated by the CPU, so this will use a lot of CPU power. (As an option, a stand-alone video system could be used).

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

PROJECT LOGS

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

9. Update

10. Started KiCad layout

11. Video system

12. Colors

generated microcode 230725.txt

Current microcode for 6502 and Z80

plain - 256.34 kB - 07/25/2023 at 20:36

Download

Adobe Portable Document Format - 3.12 MB - 06/18/2023 at 19:05

Preview
Download

Isetta 20230610.pdf

First version of schematic.

Adobe Portable Document Format - 2.17 MB - 06/11/2023 at 13:31

Preview
Download

microcode 230421_new.txt

First version of 6502 microcode generator.

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

Download

  • Colors

    roelh08/24/2023 at 18:34 1 comment

    This computer will have 64 colors (2bits red, 2 bits green, 2 bits blue).

    I was curious what the colors would look like, so I made a kind of three-dimensional plot with dimensions red, green and blue. It became a cube, built from 64 small bricks. If we look at the cube with the black brick pointing to us, we see this:

    So, starting from the black brick going to the right, we see increase in the red component. To the left, we see increase in blue component, and from black going down we see increase in green component. At the circumference we see the bright rainbow colors, and the remaining colors are darker versions of the colors.

    At the opposite side of the cube (unvisible in the above picture), we have a point where the amount of all primary colors is maximal. That is the color white. If we look at the other side of the cube we see this:

    At the circumference we see the same bright rainbow colors again. The remaining bricks are the lighter versions of the rainbow colors.

    But we did not see everything ! At the inside of the cube, there are 8 bricks that we didn't see yet. These are light-grey, dark-grey, and a grayish version of the primary colors:

    So here we see the eight hidden bricks, shown from both sides.

    The following table shows these colors, numbered from 0 to 63, and also with their hexadecimal web color. The first column has the grayish colors, and black, white and the two greys are at the bottom:

    So this table will be a help if I ever come so far to program colored stuff for my contraption.

  • Video system

    roelh08/22/2023 at 18:51 0 comments

    The Isetta video system will now be described.

    GENERAL

    The generated signal is a VGA signal with a maximum resolution of 640 x 480 pixels.

    The timing is: 
      25.175 master clock
      approx. 40nS per pixel (for 640 pixels/line)
      approx. 32uS scanline
    A whole scanline has a width of 800 pixels (only 640 pixels used)
    A whole frame has a height of 525 scanlines (480 scanlines used).
    Exact values can be found here.

    The isetta can work with maximum or reduced horizontal resolutions:

    • 40nS per pixel (for 640 pixels per line)
    • 80nS per pixel (for 320 pixels per line)
    • 160nS per pixel (for 160 pixels per line)

    In the vertical direction, scanlines can be repeated (to keep the pixels almost square, also when a lower horizontal resolution is used), giving:

    •  480 lines, or 
    • 240 lines (480 divided by 2), or
    • 120 lines (480 divided by 4)

    There are 64 colors (but the color can not be freely chosen for every pixel).

    HARDWARE PRINCIPLE

    The Isetta processor runs with a cycle of 160nS (6.25 MHz). The video output is directly generated by the processor, so during every instruction, one or more pixels must be generated:

    • 1 pixel/instruction (for 160 pixels per line)
    • 2 pixels/instruction (for 320 pixels per line)
    • 4 pixels/instruction (for 680 pixels per line)

    Isetta contains a hardware timer that triggers an interrupt at the beginning of every scanline. At the beginning of the visual part of the screen, this interrupt transfers control to the microcode that has the video instructions to give screen output. It can also initiate the generation of the frame sync pulse (the line sync pulse is generated by the hardware timer). If the interrupt is outside the visible area, the interrupt code will only increment the line counter, and return to the interrupted program almost immediately.

    A video instruction will get a single byte from the RAM, and gets color information, pixel information, and sometimes control information (flags) from that byte.

    The microinstruction fetches this byte from the indicated RAM location.
    The RAM is normally addressed with the (PC++) mode (Of course this requires that the PC that was used in the Z80 or 6502 program was saved when the interrupt is entered).

    The color information is 6 bits. The instruction can store the color in register T (foreground color) or in register A (background color).

    Information about a pixel is only 1 bit. If that bit is 1, the color of the pixel comes from register A. If the bit is 0, the color comes from register T.

    We now come to the basic byte formats that are used for the video system.

    • Color definition:

    Byte format: -cccccc-

    The 6 color bits 'cccccc' can be transferred to the T (Foreground, FG) or A (Background, BG) register.

    • 320 pixels/line :

    Byte format: a------b

    Two pixel bits 'ab' select the two pixels to be used in 320 pixels/line mode. These pixels are placed in the 4-bit 'pixel register' as 'bbaa'. The pixel register is loaded at every instruction, so the information is only available in the next cycle.

    • 160 pixels/line :

    This format is the same as the 320 pixels/line format, but in this case the bits a and b simply have the same value.

    • 640 pixels/line :

    Byte format for 4 pixels: --pppp-- 
    Four pixel bits 'pppp' select four pixels to be used in the 640 pixels/line mode. These pixels are transferred to the DPH register, and the output of this register is connected to a multiplexer that selects the correct pixel to be displayed. The 'pppp' bits are in bit5, bit4, bit3 and bit2 of the byte. The pixel register is connected to other inputs of the same multiplexer.

    Byte format for 2 pixels:  ab-----1

    This 2-pixel byte must be loaded with a SHL (shift left) instruction, while shifting a '1' into bit0. That will put the bits in the 4-bit pixel register as '11ab'. The '11' section defines background color (in register A).

    • Select pixel register or DPH register:

    One of the bits in the microinstruction ( ctl_reg1...

    Read more »

  • Started KiCad layout

    roelh08/06/2023 at 10:49 2 comments

    This is the first project where I use KiCad (version 7.0.2).

    Schematic entry was quite smooth, but with the pcb layout I experienced some problems. It was difficult to arrange the components on a 0.1" grid. And since I do everything on a 0.1" or 0.05" grid, it is annoying that KiCad displays things like cursor position only in millimeters. [ Edit. You can set this to inches, see comments ].

    Routing is not very difficult, but I miss certain functions. But it is possible that I do not yet know all commands for routing.

    But don't get me wrong, I think the KiCad developers did a great job !

    Here is the current state (you can click on the picture to get more detail) :

    The lower part of the pcb is routed. For the rats nest I use several colors:

    • red, VCC
    • blue, GND
    • light blue, databus
    • green, address bus, micro-address bus, internal ALU signals
    • magenta, microcode bus
    • yellow, operand bus
    • orange, result bus
    • white, several control signals

    This is far from finished, especially I/O components must still be added.

  • Update

    roelh07/21/2023 at 21:15 2 comments

    In the past months i've been working on the hardware design and the microcode.

    The number of required IC's got too high in my opinion, so some hardware was removed, every removal costing some effort to implement the removed instruction in microcode. I removed the following items:

    • The 74ac151 that calculated the V flag. Now, the two bit-7 adder inputs and the bit-7 adder output, needed to calculate the V flag, are connected to an input port (that has some unused inputs), and the value is saved after every addition/subtraction (or 6502 BIT instruction). When the V flag is tested, it is first calculated (by using the 3 bits to compose an opcode, and executing that opcode).
    • The zero-calculation. This was composed of three 3-input NOR gates (74F27) and a 3-input AND gate (74ac11). The other gates in the 74ac11 could not be used somewhere else, so this saves 2 IC's. After a ALU operation, the result byte is now saved to a (memory-based) register called reg_z. When the Z flag is tested (for a BEQ or BNE instruction), an 0xFF value is added to this register (same as decrementing), and when a carry occurs, the value was non-zero. There also was a physical flag Z and a upd_z microcode bit that indicated that this flag had to be updated. Both are not needed any more, and the upd_z frees a microcode bit that will be used to double the amount of available (memory-resident) registers. But it costs one extra cycle at every instruction that updates the Z flag, and one extra cycle when the Z flag is tested.
    • The shift-right multiplexers (2 x 74hc157) were removed. Shift-right is now done with a table in RAM (outside normal 64K section). After reset, some microcode constructs this table.
    • There was a special 8-bit buffer to put a byte of microcode on the databus. It was intended for special microinstructions that could place this byte in RAM (at an auto-incrementing pc++ position). This would be convenient for moving boot-code to RAM directly after reset. But this can also be done without this buffer, costing one extra microinstruction per transferred byte, and some microcode to organize this.
    • It was intended that the video section would have it's own RAM, such that video can be generated while the cpu is doing it's own thing. This will now be an option (additional pcb, called 'performance option' ), and the on-board video is bit-banged by the cpu ( 'economy version' ). It saves around 8 or 9 IC's.

    Of course I've been thinking about the video generation. The on-board economy version will have a 6-bit color value in both the A and T register. A multiplexer will select which color is connected to the output. There can be a few video modes, and the video mode can be different for each line. The basic video modes are:

    • 160 pixels/line. In every 160nS cycle, the A register will be filled with a new 6-bit color, providing 160 pixels per line, of 64 colors each. (it is just a special case of the 320 pixel/line mode).
    • 320 pixels/line. In every 160nS cycle, the A register will get a new 6-bit color. The remaining two bits determine the color of the two 80nS pixels that will be displayed in this cycle. Each pixel is either the foreground color from the A register, or the background color from the T register.
    • 80 column text mode. In an 'odd' 160nS cycle, 7 bits will be read, 4 bits will be 40nS pixels in the odd cycle, and 3 bits are 40nS pixels in the next ('even') 160nS cycle. The 4th pixel in this next cycle will be background color (blank pixel between characters). Again, each pixel is either the foreground color from the A register, or the background color from the T register. Since only 7 bits of the byte are used, there is one spare bit that indicates that a new foreground and background color will be loaded in the next two 160nS cycles (while displaying a space character).

    During the verical blanking time, the cpu will be executing instructions. At the end of every line, it will get an interrupt, that counts the lines,...

    Read more »

  • Another interesting BASIC to test the 8080 instructions

    roelh05/23/2023 at 18:52 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...
    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.

    MICROCODE UPPER CONTROL BYTE

    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.

    ALU CONTROL

    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:

    START OF NEW INSTRUCTION

    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. 

    SEQUENCE

    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.

    PIPELINE

    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.

    CONDITIONAL EXECUTION

    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.

    PAGES

    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.

    ALU FUNCTION

    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

    FLAGS

    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.

    CONDITIONAL EXECUTION

    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 »

View all 12 project logs

Enjoy this project?

Share

Discussions

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: 

http://www.enscope.nl/simas_nac/

Did you read the logs ? If you also look at the schematic,  ( https://hackaday.io/project/161251-1-square-inch-ttl-cpu/log/154913-schematic-of-the-cpu ) 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 https://csc-knu.github.io/sys-prog/books/Andrew%20S.%20Tanenbaum%20-%20Structured%20Computer%20Organization.pdf

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!

Con

  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