This document contains descriptions of ALUS and ALUD functions.
For flag type (ADDCF
, SUBBF
, etc.) functions, we put it at ALUD-H naturally.
Nonetheless, due to some hardware trick,
there are some constraints for encoding some functions:
B
/ZF_B
/SETCY
must have same encoding,ZF_B
shoud encoded at ALUD-H.OR
shoud encoded at ALUD-H.
ALUS is a single-operand ALU, which have 3 inputs and 1 output.
It contains an 8-bit operand input, denoted as A
, a 1-bit operand C
and a 4-bit function selection input S
.
It has an 8-bit output Q
.
The number in first column are the upper nibble, the number in fisrt row are the lower nibble.
encode | 0 | 1 | 2 | 3 |
---|---|---|---|---|
0 | ADJF | IVADDR | CAA | SFR |
1 | RR | RL | RRC | RLC |
2 | INC | DEC | BADDR | BIDX |
3 | SETCY | SELHIRRQN | ISRRETI | SWAP |
Swap A[6]
and A[3]
.
7 | 6 | 5-4 | 3 | 2-0 |
---|---|---|---|---|
A[7] | A[3] | A[5:4] | A[6] | A[2:0] |
Usage:
See function SETPSWF to know how this example work.
# ADDC example
RF(T0,WE), ALU(ADDCF), BUS(ALU) # T0 + WR, store flag to T0, CY at A[7], OV at A[6], AC at A[3]
RF(T0), ALU(ADJF), BUS(ALU), WR(WE) # now OV at A[3], AC at A[6]
RF(PSW, WE), ALU(SETPSWF), BUS(ALU)
See function DA to know how this example work.
# DA example
RF(PSW), ALU(ADJF), BUS(ALU), WR(WE) # now AC at A[3], CY still at A[7].
RF(A, WE), ALU(DA), BUS(ALU)
Get interrupt vector address according to IRQ number.
7-0 |
---|
((A & 3) << 3) + 3 |
Each bit in A
and C
performs logical AND operation.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
C&A[7] | C&A[6] | C&A[5] | C&A[4] | C&A[3] | C&A[2] | C&A[1] | C&A[0] |
Obtain the SFR number in RF according to the SFR address.
7-5 | 4 | 3-0 |
---|---|---|
0 | SFR hit | SFR number in RF |
For example, if RF[1] is register B, and we know that B's SFR address is 0xF0. When input A
is 0xF0, the Q[4]
(SFR hit) is 1, and Q[3:0]
is 1. If A
is an address without SFR mapped, the Q[4]
(SFR hit) is 0, Q[3:0]
can be any arbitrary number.
Rotate shift right A
.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
A[0] | A[7] | A[6] | A[5] | A[4] | A[3] | A[2] | A[1] |
Rotate shift left A
.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
A[6] | A[5] | A[4] | A[3] | A[2] | A[1] | A[0] | A[7] |
Rotate shift right A
with C
.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
C | A[7] | A[6] | A[5] | A[4] | A[3] | A[2] | A[1] |
Rotate shift left A
with C
.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
A[6] | A[5] | A[4] | A[3] | A[2] | A[1] | A[0] | C |
Q = A + 1
.
7-0 |
---|
A + 1 |
Q = A - 1
.
7-0 |
---|
A - 1 |
Get bytes's direct address according bit address.
7-0 |
---|
A < 0x80 ? A >> 3 : A & 0xF8 |
8051 have bit-addressable ram region.
In short, for addresses less than 0x80, the direct address is 0x20 + ( A
>> 3), and for addresses that greater than or equal to 0x80, the direct addresses is A
& 0xF8.
Get the target bit index in the target byte from the byte address.
7-4 | 3-0 |
---|---|
A & 0x7 | A & 0x7 |
For 8051, it's always lower 3-bit of the bit address. it's usually work with BADDR. To facilitate the implementation of INSB and EXTB functions in ALUD, the target index is in both low nibble and high nibble.
set A[7]
to C
.
7 | 6-0 |
---|---|
C | A[6:0] |
It's usually used to set PSW's CY flag. It's work for instruction that only affected CY flag(SETB C, DA A, CPL C, etc.)
Select the highest priority interrupt(not the interrupt number) from the input A
.
In short, you must using function GENIRRQN to get the IRQ Number, IRQ flag and the IP flag, then using SELHIRRQN
to get highest IRQ number and IP
flag.
7 | 6-3 | 2-0 |
---|---|---|
IP | IRQN |
#example
RF(IP), ALU(A), WR(WE) # WR <- IP
RF(T0,WE), BUS(IRR) # T0 <- IRQ
RF(T0,WE), ALU(GENIRRQN) # T0 <- generate IRQ number, IP flag, interrupt valid flag.
RF(T0,WE), ALU(SELHIRRQN) # T0 <- get highest interrupt request and IP flag.
RF(T0), ALU(GENIRRQN), WR(WE), JLT(0x1, STAGE_FETCH)
RF(ISR, WE), ALU(ISRSET), IRR(CLR)
RF(ISR), JGT(0x7F, STAGE_FETCH)
Clear the interrupt service flag in ISR, used in RETI
instruction.
See Architecture Design
in /README.md to get more detail.
7 | 6 | 5 | 4-0 |
---|---|---|---|
A[7] | A[6] == 1 ? 0 : A[6] | A[6] == 0 ? 0 : A[5] | A[4:0] |
Swap the nibble within the A
.
7-4 | 3-0 |
---|---|
A[3:0] | A[7:4] |
ALUD is a double operands ALU, it have four part inputs, two 4-bit operand A
B
, and a function select input S
, a 1-bit operand C
.
It have two 4-bit output LQ
and HQ
.
╔══════════╗
┌ ╢A0 ║
│ ╢. ║
A ┥ ╢. D0╠ ┐
└ ╢. A .╠ │
┌ ╢. T .╠ ├ LQ
│ ╢. 2 .╠ ┘
B ┥ ╢. 8 .╠ ┐
└ ╢. C .╠ │
┌ ╢. 6 .╠ ├ HQ
│ ╢. 4 D7╠ ┘
S ┥ ╢. ║
└ ╢. ║
C ─ ╢A12 ║
╚══════════╝
Obviously, a single chip can't encoding function that contain two 8-bit inputs with and an 8-bit output, but we can combine two chip togther. For function like AND
OR
, they don't need info from low part, lower nibble and high nibble can calculate indvidually and then combine the output to one 8-bit output. For one chip, a function only using 4-bit output, and we can using another 4-bit to encoding other function, for example, we can encode AND
and OR
in the same S
input:
╔══════════╗
A ─╢ AT28 ║
B ─╢ C64 ╟─ LQ: A or B
S ─╢ ╟─ HQ: A and B
C ─╢ ║
╚══════════╝
So forth, combine two chip we can get:
╔══════════╗
AL ─╢ AT28 ║
BL ─╢ C64 LQ╟─────┯━━━━━ LQ
S ─╢ HQ╟─────┼─┐
C0 ─╢ ║ │ │
╚══════════╝ │ │
│ │
╔══════════╗ │ │
AH ─╢ AT28 ║ │ │
BH ─╢ C64 LQ╟─────┘ │
S ─╢ HQ╟───────┷━━━ HQ
C1 ─╢ ║
╚══════════╝
But for function like ADD
, SUB
, the need carry signal from low part, we need some output to perform carry out output. In my design, HQ was used as carry out output(although carry out need only 1 bit), so one S
can only encode one function for these case:
╔══════════╗
┌ ╢A0 ║
│ ╢. ║
AL ┥ ╢. D0╟ ┐
└ ╢. A .╟ │
┌ ╢. T .╟ ├ AL + BL
│ ╢. 2 .╟ ┘
BL ┥ ╢. 8 .╟
└ ╢. C .╟
┌ ╢. 6 .╟ CY
│ ╢. 4 D7╟────────┴─────┐
S ┥ ╢. ║ │
└ ╢. ║ │
C ─ ╢A12 ║ │
╚══════════╝ │
│
╔══════════╗ │
┌ ╢A0 ║ │
│ ╢. ║ │
AH ┥ ╢. D0╟ ┐ │
└ ╢. A .╟ │ │
┌ ╢. T .╟ ├ AH + BH │
│ ╢. 2 .╟ ┘ │
BH ┥ ╢. 8 .╟ │
└ ╢. C .╟ │
┌ ╢. 6 .╟ │
│ ╢. 4 D7╟ ─ CY │
S ┥ ╢. ║ │
└ ╢. ║ │
C┌──╢A12 ║ │
│ ╚══════════╝ │
└────────────────────────────┘
For convenient, we treat high part's QL
and low part's QL
to one 8-bit QL
output, and QH
so on. We will explain it separately if necessary.
The number in first column is the upper nibble in S
, the number in fisrt row is the lower nibble in S
.
QL:
encode | 0 | 1 | 2 | 3 |
---|---|---|---|---|
0 | XOR | DA | ADDC | SUBB |
1 | A | Ri | INSB | XCHD |
2 | GENIRRQN | SETPSWF | ADDR11REPLACE | SETOVCLRCY |
3 | B | Rn | SETPF | INCC |
QH:
encode | 0 | 1 | 2 | 3 |
---|---|---|---|---|
0 | CPLB | DAF | ADDCF | SUBBF |
1 | PF | OR | INSBF | EXTB |
2 | ISRSET | ZF | NA | |
3 | ZF_B | AND | (NONE) | INCCF |
Remember, QL is consist of low part chip's low nibble and high part chip's low nibble.
QL
equal to A
logic xor B
.
7-0 |
---|
A ^ B |
See instruction DA A
to get detail.
7-0 |
---|
DA(A, B) |
We treat B[3]
as AC
, B[7]
ac CY
, according to instruction set manual, it's essentially to perform two step conditional addition using to A
. First additionneed to using AC
flag, but it's in low part chip, that's why CY
must at B[3]
to B[0]
rather than in original position PSW[6]
(see ADJF to know how could we transform PSW
to B
that used by this function). Second is in high part, so it's need output a carry signal, AC
flag don't need to change position but need using carry signal from low part, so it's must work together with function DAF.
QL
= A
+ B
+ C
.
7-0 |
---|
A + B + C |
Need output carry signal from low part chip to high part chip, see ADDCF.
QL
= A
- B
- C
.
7-0 |
---|
A - B - C |
Need output borrow signal from low part chip to high part chip, see ADDCF.
QL
= A
.
7-0 |
---|
A |
Q = (A & 0x18) | (B & 0x1)
.
7-5 | 4-3 | 2-1 | 0 |
---|---|---|---|
0 | A[4:3] | 0 | B[0] |
It's used to generate register bank address when using indirect address. Under normal circumstances, A = IR
, B = PSW
.
#example
RF(PSW), ALU(A), WR(WE)
RF(IR), ALU(Ri), SR(WE) # load to SR as ram address
BUS(RAM) # do something with @Ri addr value
Let T = A
, Then let T[B[2:0]] = C
, then Q = T
.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
B[2:0] == 7 ? C : A[7] | B[2:0] == 6 ? C : A[6] | B[2:0] == 5 ? C : A[5] | B[2:0] == 4 ? C : A[4] | B[2:0] == 3 ? C : A[3] | B[2:0] == 2 ? C : A[2] | B[2:0] == 1 ? C : A[1] | B[2:0] == 0 ? C : A[0] |
Because the high part output must using C
when B[2:0] >= 4, then we have function INSBF to do this stuff.
Q = {A[7:4],B[3:0]}
.
Concat the high nibble of A
and the low nibble of B
.
7-4 | 3-0 |
---|---|
A[7:4] | B[3:0] |
# example: swap low nibble of T0 and low nibble of T1
RF(T0), ALU(A), WR(WE)
RF(T2,WE), ALU(B) # backup, T2 <- T0
RF(T1), ALU(A), WR(WE)
RF(T0,WE), ALU(XCHD) # {T0[7:4], T1[3:0]}
RF(T2), ALU(A), WR(WE)
RF(T1,WE), ALU(XCHD)
Select the highest priority interrupt number in the high nibble and low nibble respectively.
See example in SELHIRRQN.
7 | 6 | 5-4 | 3 | 2 | 1-0 |
---|---|---|---|---|---|
IV | IP | IRQn | IV | IP | IRQn |
H | H | H | L | L | L |
The A
must be IRQ and B
must be thevalue of IP
.
IV: interrupt valid flag. If there is any IRQ in this nibble, this flag is 1, otherwise it is 0.
IP: IP flag. If the corresponding bit of the high priority interrupt in the IP
register is 1, then this flag is 1, otherwise it is 0.
IRQn: the highest priority interrupt number.
Replace A[7]
to B[7]
, A[6]
to B[6]
, A[2]
to B[3]
.
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
B[7] | B[6] | A[5] | A[4] | A[3] | B[3] | A[1] | A[0] |
It's usually used to set PSW flag when execute ADDC
, SUBB
, ADD
instruction. See ADJF to know how to use this function.
A[2:0]
= B[3:1]
.
7-3 | 2-0 |
---|---|
A[7:3] | B[3:1] |
This function was used to AJMP
and ACALL
, let's explain how it work. In ISA, the abs address is a 11bit immed:
encoding | byte0 | byte1 |
---|---|---|
value | A10-A8 xxxxx | A7-A0 |
And when excute AJMP
and ACALL
we have a step PC[10:0]= A[10:0]
, note PC[15:8]
as PCH
, PC[7:0]
as PCL
, then we get PCL = A[7:0] = byte1
it's simply move byte1
to PCL
. For PCH
, we have PCH[2:0] = A[10:8]
, meaing we want PCH[2:0]
= byte0[7:5]
. That's seem can't work by this function, but if excute SWAP to byte0
, we have _byte0[3:0] = byte0[7:4]
, it's meaing _byte0[3:1] = byte0[7:5]
, and now, we can excute ADDR11REPLACE
.
# example
RF(IR), ALU(SWAP), WR(WE) # move ADDR11[10:8] to WR[3:1]
RF(PCH,WE), ALU(ADDR11REPLACE)
Q = (A & 0x18) | (B & 0x7)
.
7-5 | 4-3 | 2-0 |
---|---|---|
0 | B[4:3] | A[2:0] |
Similar to function Ri
, it's used to generate register bank address when using Rn address. Under normal circumstances, A = IR
, B = PSW
.
#example
RF(PSW), ALU(A), WR(WE)
RF(IR), ALU(Rn), SR(WE) # load to SR as ram address
BUS(RAM) # do something with Rn value
replace A[0]
to C
.
7-1 | 0 |
---|---|
A[7:1] | C |
Use to set parity flag in PSW
.
Q
= A
+ C
.
7-0 |
---|
A + C |
Obviously, it's need generate carry output, see INCCF.
using A
as bit index, invert(complement) the bit in B
.
Q = B
Q[A[2:0]] = ~Q[A[2:0]]
|7-0| |:-:|:-:|:-:|:-:| |CPLB(A, B)|
The A
must be the result of BIDX.
If there a carry from low nibble, AC
is 1, if there a carry from high nibble, CY
is 1.
7 | 6-4 | 3 | 2-0 |
---|---|---|---|
CY | X | AC | X |
If there a carry from low nibble, AC
is 1, if there a carry from high nibble, CY
is 1. if the result of ADDC
is overflow, OV
is 1.
7 | 6 | 5-4 | 3 | 2-0 |
---|---|---|---|---|
CY | OV | X | AC | X |
If there a borrow from low nibble, AC
is 1, if there a borrow from high nibble, CY
is 1. if the result of SUBB
is overflow, OV
is 1.
7 | 6 | 5-4 | 3 | 2-0 |
---|---|---|---|---|
CY | OV | X | AC | X |
if A[3:0]
contains an odd number of 1s, then PFL
is 1, if A
contains an odd number of 1s, then PF
is 1.
7 | 6-4 | 3 | 2-0 |
---|---|---|---|
PF | X | PFL | X |
QL
equal to A
logic or B
.
7-0 |
---|
A | B |
Cooperate with INSB function, it's simply transmit signal C
from low part chip to high part chip.
7-4 | 3 | 2-0 |
---|---|---|
X | C | X |
Q[7] = B[A[2:0]]
.
7 | 6-4 | 3 | 2-0 |
---|---|---|---|
B[A[2:0]] | X | A[2:0] < 4 ? B[A[2:0]] : 0 | X |
Let see how it work.
Usually, the A is the result of BIDX, so A[2:0]
and A[6:4]
is the same value, they are both the bit index.
In low part, A[2:0] < 4
meaing the bit you want get is in B[3:0]
, we get the bit from it, but the final output of bit is in Q[7]
, so we need send the bit from low part chip to high part chip, which what you see at Q[3]
.
In high part, A[2:0] < 4
meaing the bit that you wanna get is in low part, so we set the Q[7]
to the value of C
. But if A[2:0] >= 4
, we take bit from B[7:4]
as output.
Mark the corresponding interrupt servicing flag in the ISR
register.
In short, if ISR
can accept current interrupt, the Q[7]
is 0(be careful!) , otherwise the Q[7]
is 1. Current IRQ number are stored in ISR[2:0]
.
It should work with ISRRETI.
7-0 |
---|
ISRSET(A, B) |
notice
A
must be ISR
. and B
must be the result of SELHIRRQN
(see example in SELHIRRQN).
detail
Let's see how it work: The bit where the interrupt service flag in the ISR is arbitrarily selected by us, but according to my Hardware encoding
in /README.md
:
ISR[6] == 1
means an interrupt with priority 1 is being servced.ISR[5] == 1
means an interrupt with priority 0 is being servced.ISR[7]
can be customized.
Now assume A = ISR
, B
is the result of SELHIRRQN
, we first list the condition that can't accept current interrupt.
A[6] == 1
, means that the priority of the interrupt being serviced is 1. You cannot accept any other interrupts because 1 is the highest priority.A[6] == 0 && A[5] == 1 && B[7] == 0
, means that only interrupt with priority 0 be serviced, but IRQ's priority is 0 too, we shouldn'taccept it.
Then, we use Q[7]
to indicate whether we can accept the interrupt.
if A[3:0] == 0
, then ZFL = 1
. If A
is 0, then ZF
is 1.
7 | 6-4 | 3 | 2-0 |
---|---|---|---|
ZF | X | ZFL | X |
Q
equal to logic not A
.
7-0 |
---|
~A |
Same as ZF, but using B as operand. If B[3:0] == 0
, then ZFL = 1
. If B
is 0, then ZF
is 1.
7 | 6-4 | 3 | 2-0 |
---|---|---|---|
ZF | X | ZFL | X |
QL
equal to A
logic and B
.
7-0 |
---|
A & B |
If there a carry from low nibble, AC
is 1, if there a carry from high nibble, CY
is 1.
7 | 6 | 5-4 | 3 | 2-0 |
---|---|---|---|---|
CY | X | X | AC | X |