What and why...
This page is somewhere to document all the stages of the development of a discrete-logic processor.
I'll discuss the basics of a microprocessor; simulate it as I go along in Logisim (Logisim); and develop the hardware using the free version of Eagle CAD (Eagle CAD).
Eventually there will also have to be some sort of assembler for it -
probably in Python, since I need the practice. As things get developed
they'll be available for download; I encourage you to experiment,
modify, extend... it's the best way to learn. And since everything is
done using free tools, until you actually want to build one, it costs
nothing.
I'm a Linux user by choice, but all the tools mentioned are available for Windows, should you prefer it.
The Basics
This
is probably obvious, but it's perhaps worth a brief overview of what a
microprocessor is. It's remarkably simple, in concept: a processor is a
device which takes a sequence of instructions and data and manipulates
the data according to the instructions. At its simplest, it takes a
value from some sort of memory and puts it somewhere else in memory. A
slightly more complex operation does some logical or arithmetical
process on the value before it saves it back to the memory. Finally,
there is some mechanism to allow the processor to perform jumps - to let
it make loops.
To operate the processor, a number of things have to happen in the right order.
- the processor has to get the next instruction from memory.
- it has to decode this instruction to decide what to do next.
- it has to execute the decoded instruction - this may require further access of the memory, either reading, writing, or both
- it then gets the next instruction and repeats.
Strictly
speaking, any process can be executed with the simplest of processor.
However, there are certain things which can make it simpler to write a
program, if it's in any way complex. With this in mind, let's have a
look at what this processor ought to be able to do.
Memory size
For
simplicity, we'll keep this as an eight-bit processor. That is, the
access width for internal registers and for external memory is eight
bits wide. We'll make the memory itself a maximum size of 64k bytes -
which needs sixteen bits of address to specify any particular memory
location. This is the same size as most of the popular processors in the
1970s and 1980s; our processor should have similar abilities to those.
Programmer's model
I
propose a simple model which uses an accumulator A as the source or
destination for the majority of instructions. Two internal registers - X
and Y may be used either for short term storage (e.g. loop counters) or
together as a sixteen bit pointer to memory. A sixteen bit stack
pointer allows local storage and the delivery and return of parameters
to subroutines.
A number of memory access modes are available:
- immediate - where the second byte of the instruction is an eight-bit data byte.
- absolute - the second and third bytes of the instruction are the address of the data byte being referred to.
- indexed - a little more complex; the second and third bytes of the instruction point to an address in memory. At that address and the following one is a pointer to the actual memory location with the data required.
- XY - similar to the above, but the XY register pair points to the memory location.
- stack - the stack pointer register pair is again similar, but with an added twist: after a write instruction to the stack, the pointer is automatically decremented. Before a read instruction, the pointer is automatically incremented.
Logic and arithmetic
To do anything useful, the processor needs to be able to do a minimum of arithmetic and logical instructions. While it is possible to make all the possible options using nothing more than NAND instructions it's less than simple - better to have all that likely to be needed to hand, so here's a short list of the functions I'd like to include:- adc - add with carry. It can be argued that add without carry is more useful, since you don't need to clear the carry before an addition, but it's a lot more simple for multi-byte operations if the carry is include. So adc it is...
- sbc - for the same reasons.
- inc - just add one...
- dec - or take one away...
- and - logical and.
- or - logical or.
- xor - logical exclusive or.
- shr - shift the bits in the target right by one, loading the carry into the top bit and shifting the lowest bit into the carry. This is useful for arithmetic division as well as for serial/parallel conversion.
- shl - as above but shifting left; the carry goes into bit zero and is loaded from bit seven. Useful for multiplication.
Jumps and calls and things
The other thing a processor needs
to do is jump around. If you can't change the flow through a program
depending on the results of previous processing, you can't really do
much at all.Here's what I'd like to include:
- jmp - jump to an absolute address.
- jc - jump if the carry flag is set.
- jnc - jump if the carry flag is clear.
- jz - jump if the zero flag is set.
- jnz - jump if the zero flag is clear.
- jsr - jump to a subroutine, pushing the current program counter on the stack before the jump occurs.
- ret - jump back from a subroutine, using the previously stored address.
No comments:
Post a Comment