From Hackstrich

ECLair is a long-term project to build an ECL minicomputer. All code/formal documentation/utilities can be found in the ECLair GitHub Repository.

This page is now deprecated and will not be updated. Current information on this project can be found at

Project Status

  • 2018-04-26: Implemented ability to choose a register to drive the XY bus using IR[7:6], the same as we've been able to do for loading values into registers for ages. Will simplify a bunch of things and only added one extra decoder. High byte shift left instructions are now implemented and working using this as well. Next will probably work on the wrcse instruction, as a break from shifts before returning to implement shift right.
  • 2018-04-25: Shift low byte of registers left now works and has tests. Started working on shift high byte of registers left, it's still a work in progress.
  • 2018-04-24: Shift 16-bit register left now works for registers B-D as well, and has tests. RPT has also been expanded to 12 bits, so in hardware we'll build it with two 6-bit chips. This isn't needed for the shifts, but will add flexibility for future microcode. Next up is implementing 8-bit register shift lefts.
  • 2018-04-22: Shift 16-bit register left now fully works with any number of bits! Next step is to implement the tests for registers B-D, then implement byte-wide left shifts. Should also decide if RPT will be 6 bits or 12 bits wide, since the current 8 bits doesn't make sense in real life (the chips are 6 bits wide).
  • 2018-04-21: Shift left now works when shifting by 1 bit, next task will be to implement (simple) conditional jumps in microcode based on RPT, so that multiple shifts can be done in one instruction.
  • 2018-04-20: Started working on shl16.a, first bits of the microcode are implemented (for a single left shift) including a simple test case, but the CPU runs away running the test, need to troubleshoot this more.
  • 2018-04-06: Implemented RPT register in simulator, not yet tested but passes all existing regression tests.
  • 2018-04-05: Added work done on 2018-04-03 to the functional simulator. Also added RPT register for things the microcode has to do more than once (like the upcoming shift operations), haven't implemented it in anything yet, but started thinking about it and will probably implement it as two chained MC10E136 6-bit presettable up/down counters.
  • 2018-04-03: Eliminated the separate XY Nibble microcode bit and associated logic, integrated intvect as xy_src=8. All tests still passing. Functional simulator still needs to be updated for this change.
  • 2018-04-02: The big XY mux is finally gone! Have been bumping up against limits in this since 2014-06-03, all sorted out now. Moved the Memory Read bit in microcode up to make room to expand the X/Y Register Source field from 3 bits to 4, which will allow 15 different X/Y bus sources, more than enough. Each source now has a latch w/ async reset (MC10E143) to connect it to the bus, and an 8-bit binary decoder (MC10H161) drives their reset pins to allow one at a time to drive the bus. Still passes tests at up to 33MHz (30ns clock) which is great. Next step is to eliminate the XY Nibble microcode bit (which was a temporary hack put in place until I could sort out the XY mux) and use an extra binary decoder to add an extra bit to the XY Source field instead, expanding it to the final 16 bits rather than the current 8. This will allow intvect to be added as an XY driver, as well as anything else needed in the future (probably at least DP).
  • 2018-04-01: Implemented high-byte loads for the 8-bit registers, tests for these all now exist and pass. Started thinking about implementing shifts, looked at barrel shifters and they're too much logic for now, I can always add one as an extension later. Next step is going to be to figure out the slight mess of the XY bus, currently it uses a big mux but it looks like the best way is going to be for each signal that can drive XY to go through a MC10E143 latch, and use the async reset to zero it out when it's not driving the bus, then wired-OR the outputs all together. Wired-OR makes the VIL noise margin worse, but per the formula in the DEC WRL Research Report 90/1 (0.03*ln(n) where n is the number of gates) this shouldn't be a problem at the number of gates I'd be looking at for the XY bus.
  • 2018-03-31: Thought a lot about whether 64k per page table block is really enough space, considered going to 9bit bytes and 18bit alu/registers, decided against it after a lot of consideration as I think it would make interop/testing/etc. much harder. Going to stick with 64k per PTB for now, and can work around it later if need be (giving processes multiple pages using code overlays and such).
  • 2018-03-28: Fixed a significant bug in the functional simulator test functionality, was testing at the wrong moment and also skipping tests at HALT time. Both fixed now, and sub8.ab passes all tests (was failing ~60% before).
  • 2018-03-27: Test Console in the functional simulator now mostly functional, a couple loose ends to tie up (it doesn't appear automatically when a test is loaded, and you have to manually show it before loading the ROM).
  • 2018-03-26: Started working on a separate Test Console window that lists all the tests and their status.
  • 2018-03-25: Implemented the rest of basic test functionality, simple test now run successfully.
  • 2018-03-24: Finished implementing paging in the functional simulator. Started implementing functionality to run the same test cases the Verilog simulator does, all the functionality to read ASCII-binary test files and build a list of test steps is done, as is most of the test runner, just need to get it integrated now.
  • 2018-03-23: Finished migrating the memory display to a table and implementing highlighting of where pc currently points. Started adding a page table display as well.
  • 2018-03-22: Added a diagram of how paging is set up, started working to move the memory display from a text box into a proper table so we can do highlighting and such, not done this yet but getting close.
  • 2018-03-21: Implemented basic STATUS/FLAGS support in the functional simulator and made numerous bug fixes so now the jmp and jmpz tests now run successfully, as does the mode test. Next up is implementing paging, which first requires I re-learn how paging actually works (and create some better documentation so people other than me, or me in the future, can someday understand).
  • 2018-03-20: Refactored the way control words are decoded, much simpler/more readable code now. Next steps will be to complete support for the rest of the microcode including paging, and then implement the microcode disassembler properly (rather than hardcoding it all like it is now).
  • 2018-03-19: Improved the code for the functional simulator some, made it more "Swift-like". Also used it to make a slight optimization to ldi16.?, it was doing an extra memory read that wasn't required, so eliminated that and all tests still pass.
  • 2018-03-18: More work on the functional simulator, now executes the ldi16.a test from start to finish without issues. Still doesn't support paging, any of the FLAGS, and a lot of other microcode features.
  • 2018-03-17: Worked on the simulator a bunch more, it now gets 3 microcode steps in before dying, which is progress!
  • 2017-07: Started moving the basic Swift simulator into XCode and building a GUI for it.
  • 2016-12: Started working on a high-level simulator to use in working out the final instruction set before finalizing hardware. Learning Swift as I do it, so progress is not super fast.
  • 2015-07-03: Realized there's no reason to have a Z register at all, X and Y need it since they share a common input bus (XY), but every time we load Z we immediately then use the result from Z to load elsewhere. Eliminated it and all tests still pass.
  • 2015-07-01: Wrote a thing that generates digraphs out of the microcode. Did a bunch of prep work for doing more interrupt work. Now have instructions to disable/enable interrupts and can individually set/reset FLAGS bits (instead of having to do all at once like before). Realized I need a read path for FLAGS so that the interrupt microcode can push/pop them, but out of inputs on the XY muxes, so will have to figure that out.
  • 2015-06-30: Finished converting all of the unit tests to seasm. Implemented the basic hardware so that interrupts are able to force a jump to microcode interrupt vectors rather than IR when a fetch/execute next happens. Also moved ECLair into a test harness which can poke interrupts, which will make writing a unit test possible. Next step is to write the microcode to actually handle this.
  • 2015-06-28: Started working at moving the unit tests over to seasm rather than raw binary, most of the 8bit-operand tests are moved over, the "assembler" doesn't support 16-bit operands yet so that will be the next step to getting all the tests moved over.
  • 2014-06-03: call/ret are finally done and have passing unit tests! Started work on interrupt logic, no inputs left on the X/Y mux to get the interrupt vectors in, added a 4-bit mux on the A port to select either immediate bits or highest active interrupt vector. Next step is to start wiring up the actual interrupt flag gates and clearing logic.
  • 2014-06-01: Added realistic propagation delays to alu4 and finished the RAM delays, tests still all pass at 25MHz and 33MHz now. Leaving the clock at 25MHz for hte time being, as that's the actual goal (though 33MHz would be nice). Wrote a super simple assembler (if you can call it that) which pulls instructions/locations from microcode.txt and lets you write text instead of ones and zeroes. Should rewrite the tests to use this at some point, will make troubleshooting them a lot easier. Still avoiding working on CALL/RETURN >_>
  • 2014-05-31: sub8.ab now works and passes all its unit tests (and added some more unit tests for it, to be safe/confident). Next step is finishing CALL/RETURN. Started working on this, realized that since CALL takes 2 immediate bytes currently, RETURN returns into the middle of it. CALL should probably push the return address, not its own address. Added realistic propagation delays for the clag4, added simplified worst-case propagation delays to the alu4 and a bunch of tests start failing. Backed those ones out for now, will finish implementing CALL/RETURN then go back and finish the delay/timing work.
  • 2014-05-30: pop16.pc now working/passing its unit tests, sub8.ab still being worked on.
  • 2014-05-18: Worked on pop16.pc microcode and sub8.ab microcode. Neither working yet.
  • 2014-05-06: Figured out register flow requirements for pop16.pc, documented. Next step is to implement the test and microcode for it.
  • 2013-12-29: Implemented include functionality in mcgen.rb so that instruction like call which are really just push16.pc then jmp can be written like that, rather than having to copy/paste the contents of those other instructions.
  • 2013-12-26: Implemented CALL and extended mcgen.rb to generate a mapping file from control store address to instruction, which can be loaded into gtkwave. Next steps are to implement POP and RET.
  • 2013-05-05: Refactored all microcode instructions and their unit tests so that PC now equals the instruction currently being executed, and not the memory location after that. Implemented push16.pc and its unit test. Next step is to implement CALL.
  • 2013-05-04: Reworked PUSH into push8.imm and push8.? (for future implementation of other non-immediate push8s). Implemented push16.imm and push16.? including their unit test and register transfer diagram. Started working on push16.pc, but ran into an issue where PC is incremented within FETCH, so if we push it as part of push16.pc then we're actually pushing PC+1. Need to figure out if that's okay, or if we need to rework where inc_pc is triggered in the fetch process.
  • 2013-02-24: Finished implementing PUSH, including the unit test for it. All working now, and fixed a lot of bugs related to RAM while doing so, as this is the first use of RAM in the system. Next step is to go back to working on CALL/RETURN now that PUSH is working.
  • 2013-02-20: Started working on the unit test for PUSH, found that I hadn't ever implemented the SP register. Did so, and troubleshot a bunch around that. Test still not passing, more troubleshooting required.
  • 2013-02-19: Added proposed microcode for PUSH, which is a prerequisite for CALL/RETURN. Next step is to write a useful unit test for it.
  • 2013-02-17: Figured out/diagrammed initial interrupt logic. Next step is to implement CALL/RETURN, which will be needed to integrate it into the system.
  • 2013-01-16: Added supervisor/user mode support and a test for it. All working. Still need to write a branch-if-CO unit test.
  • 2013-01-08: Added conditional branch support and a test for it. Currently only have a test for the branch-if-Z path, not the branch-if-CO path. Next step is to add that branch-if-CO unit test, then probably work on fault/trap/interrupt support.
  • 2013-01-06: Added support for ALU status bits Z (last operation result was zero) and OC (overflow/carry out) and tests for them. Also added add8.ab. Next step is adding conditional branch support.
  • 2013-01-05: Wrote a new unit test system, and implemented tests for all current instructions. Next step is probably to look at implementation of ALU status bits.
  • 2012-12-26: Implemented ldi8.flags so that paging can be turned on. Fixed a couple bugs in the system, now paging works! Also renamed PID to PTB (page table block) as one PID could potentially use more than one page table entry. Still need to figure out how to automatically test this.
  • 2012-12-25: Worked out details on the paging system, added it to the schematic, implemented it. Can now to load the current PID into the PID register, then wrpte to load the first entry in the page table. Can't yet switch paging on, as status/flags isn't implemented yet. Next step is figuring out how to integrate paging mechanism into the automated tests, then implement status/flags so paging can actually be turned on.
  • 2012-12-23: Worked out the right way to fix the sequencing issue/dependency between microcode signals, now the level-sensitive signals (bits 0-23) latch first, then the edge-sensitive signals (bits 24-63) latch, then the edge-sensitive signals are reset back to 0 so they don't mis-trigger when the level-sensitive signals change again on the next cycle. This means all edge-sensitive signals need to be active-high, but that hasn't been an issue so far.
  • 2012-12-22: Hacked on the sequencing issue more, no real progress.
  • 2012-12-21: Reordered microcode bits into 24 bits for edge-sensitive signals and 40 for level-sensitive signals. Redesigned the microcode sequencer to use a latch and an always-populated next_addr bit field rather than a counter, which solves many problems around jumping and runt cycles. Still don't have sequencing of the two classes of microcode bits worked out, it's proving a much harder problem than I'd expected.
  • 2012-12-20: Implemented realistic propagation delays for most parts (main EPROM and ALU not done yet), and fixed a bunch of bugs related to previous lack of delays. Have a hacked-in delay for cs_jump right now, next step is to split the microcode into 24 "edge-sensitive signal bits" and 40 "level-sensitive signal bits", and clock the former slightly after the latter. This will avoid needing two separate microcode instructions to set up level-sensitive signals then trigger the edge-sensitive ones. Will mean a complete reorganization order-wise of the microcode though.
  • 2012-12-19: Finished implementing 8/16-bit width selection and got the bugs worked out. Now have 8-bit and 16-bit immediate loads, and 8-bit don't trash the other 8 bits of the destination register. Still need to get 8-bit high-byte load implemented. Next step though is probably to rethink the hold_last function in the microcode assembler, should probably make it more intelligent to only hold level sensitive signals and leave the edge-sensitive ones out.
  • 2012-12-18: Half-implemented 8/16-bit width selection using a microcode bit. Doesn't work yet.
  • 2012-12-17: Implemented a data path from IR[7:6] to the register latch signals, so that all register-related operations can be simplified in microcode. Moved the ldi* instructions to use this new path, everything is tested and working. Next step is probably to get the 8/16-bit split working, so that 8-bit operations don't erase the other byte of the word.
  • 2012-12-16: MDR-XY data path implemented, Load Immediate 8-bit values to A and B instructions written, add 16-bit A=A+B written, all working.
  • 2012-12-15: Found bug in the datasheet of the MC10H181, fixed it and now the ALU checks out completely. Started integrating it into the CPU. Next step is to finish the MDR-XY data path so that Load Immediate instruction can be implemented, then Add can be implemented/tested.
  • 2012-12-14: Verilog transcription of ALU almost complete, but malfunctioning in the carry logic of the second bit. Will be using MC10H181 w/ MC10H179 CLA generator.
  • 2012-12-09: Started working on ALU design/verilog transcription.
  • 2012-12-08: Added decode support for ROM/RAM/device memory, got everything working so that now an instruction is fetched from ROM at startup and is jumped to in microcode. Started work on the X/Y registers, and started work on a microcode generator so I can stop typing in individual bits.
  • 2012-12-05: Got the part that's been diagrammed into Verilog and working. Now fetches instructions (from a constant right now, not RAM yet) and does jumps to their location in microcode.
  • 2012-12-04: Started the logical diagram and the microcode layout.
  • 2012-12-01/02: Spent the weekend reading Bit-Slice Microprocessor Design.
  • 2012-11-24: Basic ideas/architecture starting to get put together.

Architecture Overview

  • Many blinkie lights and switches on the front panel
  • MECL-based (10KH/10KE levels/speeds)
  • 25MHz main clock (hopefully)
  • 8-bit data width
  • 24-bit physical address, 16-bit virtual address
  • DMA support (for front panel, maybe for storage)
  • Microcoded, running control store in SRAM for speed
    • Unless I can find equally-fast EPROMs
    • Copied to SRAM from EPROM before the system releases reset on powerup
    • Could implement a writable control store using this too
  • 8 interrupt lines (heartbeat, RTC, serial 0, serial 1, storage, 3 for future use)
  • Big-endian


Name Width (bits) Function Accessibility
PC 16 Program Counter User
SP 16 Stack Pointer User
PTB 6 Page Table Block User
IR 8 Instruction Register Microcode Only
MAR 16 Memory Address Register Microcode Only
MDR 16 Memory Data Register Microcode Only
FLAGS/STATUS 16 System flags and status bits (8 bits for R/W flags, 8 for R/O status) Special
A 16 General Purpose User
B 16 General Purpose User
C 16 General Purpose User
D 16 General Purpose User
RPT 12 Repeat Counter Microcode Only, Write-Only
X 16 ALU Input Microcode Only
Y 16 ALU Input Microcode Only

Memory Map

  • 24bit / 16MB address space
  • 14MB for RAM, 1MB for ROM, 1MB for memory-mapped devices
  • 0x000000 - 0x0FFFFF - ROM
  • 0x100000 - 0xEFFFFF - RAM
  • 0xF00000 - 0xFFFFFF - Devices

Page Table

  • 64 process slots available in page table
  • Each process gets access to 64 pages
  • Each page is 1K long
  • Bit 15 (MSB) in page table entry is 1 if page is present
  • Bit 14 in page table entry is 1 if page is writable
  • Address Routing
    • Virtual address - 16 bits
      • Bits 15-10 - Page Select - passed to page table address bits 11-6
      • Bits 9-0 - Address - passed to ADDR LSB as-is
    • Physical address - 24 bits
      • Bits 23-10 - Page - passed from page table data bits 13-0
      • Bits 9-0 - Address - passed from virtual address bits 9-0

ECLair Paging.png


High Byte - Flags

  • Bit 9 - PE - Paging Enabled
  • Bit 8 - M - Mode (User/Supervisor)
  • Bit 7 - IE - Interrupts Enabled

Low Byte - Status

  • Bit 1 - CO - Carry/Overflow
  • Bit 0 - Z - Last Result is Zero

Microcode Layout

Bit # Width Function Details
Edge Sensitive Signals
0 1 Write Page Table Entry
1 1 MDR Load Latches data or Z (determined by bit 34) into byte of MDR (determined by bit 24)
3 1 MAR Load MAR latch load (source specified by bit 33)
4 1 IR Load IR latch load from data bus
5 1 PC Increment Increment PC by 1
6 1 PC Load PC counter load from Z
7-9 3 Register Load from Z 000 - None
001 - A
010 - B
011 - C
100 - D
101 - SP
111 - Value from IR[6..7] (only A-D)
10 1 X Load
11 1 Y Load
13 1 PTB Load
14 1 RPT Execute
15 1 Load Status from ALU
16 1 Clear Interrupt Flags from Z 1 bit in Z clears that interrupt flag
17 1 Memory Write
18 1 Load Flag PE Loads PE flag from Z[0]
19 1 Load Flag M Loads M flag from Z[1]
20 1 Load Flag IE Loads IE flag from Z[2]
Level Sensitive Signals
24 1 MDR Byte Select low = low byte for 8bit ops, high = high byte for 8bit ops
25-32 8 Next CS Address 0 = Use IR
33 1 MAR Source low = Z, high = PC
34 1 MDR Source low = Z, high = data bus
35 1 ALU Mode 0 = Arithmetic, 1 = Logic
36-39 4 ALU Operation 1111 - Z=X
40-43 3 X/Y Register Source 0000 - Immediate Value (LSB from bit 24)
0001 - A
0010 - B
0011 - C
0100 - D
0101 - SP
0110 - MAR
0111 - MDR
1000 - IntVect
1111 - Select register from IR[6..7] (only A-D)
44 1 Carry In Carry input for the ALU. Used to carry in a 1 to let the ALU subtract.
45 1 Operation Width 0 = 8-bit, 1 = 16-bit
46-48 3 Branch Condition 000 - Unconditional
001 - Z
002 - OC
49-50 2 X/Y Immediate LSBs Used if 40-43 = 000
51 1 RPT Mode low = Load from MDR (all 12 bits if op_16bit, just low 8 with rest forced to zero if !op_16bit)
high = Decrement
52 1 Register Byte Select low = low byte for 8bit ops, high = high byte for 8bit ops
53 1 Memory Read
54-57 4 Next CS Address Low Bits if RPT=0 0000 = No branch
0001-1111 = Use these 4 low bits instead of the lowest 4 bits of Next CS Address if RPT=0