O AX8 ou Axle é uma CPU CISC little-endian de 8-bits com um bus de 16-bits endereçável. Instruções são 8-bits com decodificação ortogonal: Os bits 7:5 ditam a unidade funcional, enquanto bits 4:0 mais baixos configuram a operação. Algumas instruções são seguidas por um endereço de 16-bits ou um valor de 8-bits, fazendo-os serem 3-bytes ou 2-bytes no total.
Todos os programas se iniciam pelo endereço contido em 0xfff1:0xfff0,
ao reset/powerup da CPU, dois bytes são lidos nesse endereço e registrados no
Program Counter. Por conveniência, o endereço 0x0100 pode ser utilizado
como Stack Pointer.
| Registrador | Tamanho | Descrição |
|---|---|---|
| A | 8-bit | Acumulador -> Operador primário para todas as operações |
| IX | 8-bit | Index register X -> Operador secundário |
| IY | 8-bit | Index register Y -> Operador secundário |
| PC | 16-bit | Program Counter (Contador do Programa). |
| FR | 4-bits | Flag Register (V, C, N, Z). |
* Não há ponteiro de stack, controle de stack deve ser feita pelo usuário.
| Flag | Bit | Definição |
|---|---|---|
Z |
0 |
result == 0x00 |
N |
1 |
result[7] & 1 |
C |
2 |
carry-out bit 7 |
V |
3 |
(A[7] != Y[7]) && (B[7] != Y[7]) |
* Mesmo que essa seja a definição normal, é possível que as flags V e C sejam modificadas por outras operações.
* As flags N e Z sempre são modificadas por qualquer instrução que passe pela ALU.
| Mnemônico | V C N Z |
Nome |
|---|---|---|
ADC |
* * * * |
ADdition with Carry |
AND |
. . * * |
logical AND |
BCC |
. . . . |
Branch if Carry Clear |
BCS |
. . . . |
Branch if Carry Set |
BEQ |
. . . . |
Branch if EQual |
BMI |
. . . . |
Branch if MInus |
BNQ |
. . . . |
Branch if Not eQual |
BPL |
. . . . |
Branch if PLus |
BRA |
. . . . |
BRAnch |
BVC |
. . . . |
Branch if oVerflow Clear |
BVS |
. . . . |
Branch if oVerflow Set |
CLC |
. 0 . . |
CLear Carry |
CLV |
0 . . . |
CLear oVerflow |
CMP |
* * * * |
CoMPare |
DEC |
. . * * |
DECrement |
HLT |
. . . . |
HaLT |
INC |
. . * * |
INCrement |
JMP |
. . . . |
JuMP |
LD |
. . * * |
LoaD |
LDI |
. . * * |
LoaD Immediate |
LDX |
. . * * |
LoaD iX |
LDY |
. . * * |
LoaD iY |
MUL |
* 0 * * |
MULtiply |
NOP |
. . . . |
No OPeration |
NOT |
. . * * |
logical NOT |
OR |
. . * * |
logical OR |
ROL |
. * * * |
logical ROtate Left |
ROR |
. * * * |
logical ROtate Right |
SEC |
. 1 . . |
SEt Carry |
SEV |
1 . . . |
SEt oVerflow |
ST |
. . * * |
STore |
STX |
. . * * |
STore iX |
STY |
. . * * |
STore iY |
SBC |
* * * * |
SuBtract with Carry |
SHL |
. * * * |
logical SHift Left |
SHR |
. * 0 * |
logical SHift Right |
XOR |
. . * * |
logical eXclusive OR |
TAX |
. . * * |
Transfer A to iX |
TAY |
. . * * |
Transfer A to iY |
TXA |
. . * * |
Transfer iX to A |
TXY |
. . * * |
Transfer iX yo iY |
TYA |
. . * * |
Transfer iY to A |
TYX |
. . * * |
Transfer IY to iX |
Instrução:
Bit 7 6 5 4 3 2 1 0
Name G G G S S C C C
[7:5] G = Group -> Seleciona a unidade funcional
[4:3] S = Subgroup -> Subgrupo funcional
[2:0] C = Execution -> Unidade de execução
Modos:
Implied: (1-byte) | MNEMONIC
byte 0: instrução
Indirect: (1-byte) | MNEMONIC [IY]
byte 0: instrução
Immediate: (2-bytes) | MNEMONIC #imm8
byte 0: instrução
byte 1: imm8[7:0] (sem sinal: 0 a 255)
Relative: (2-bytes) | MNEMONIC rel8
byte 0: instrução
byte 1: rel8[7:0] (com sinal: -128 a 127)
Absolute: (3-bytes) | MNEMONIC addr
byte 0: instrução
byte 1: addr[7:0]
byte 2: addr[11:8]
| Bits | Grupo | Descrição |
|---|---|---|
000 |
System | Operações do sistema |
001 |
Memory | Transferência de Memória e Registradores |
010 |
Arithmetic | Aritmética e Lógica |
011 |
Unary | Aritmética Unária |
100 |
Jump | Manipulação do Program Counter |
Os modos são ditos pelos bits 4:0 do opcode. Sua encodificação
é feita da seguinte forma1: Os bits 4:3 determinam se o modo é a
alternação de submodo, e os bits 2:0 ditam qual submodo deve ser configurado
e executado. Qualquer modo não encodificado aqui deve gerar a instrução: HALT
Grupo 000: System
Encoding:
NOP:
4 3 2 1 0
0 0 0 0 0
Flag Manipulation:
4 3 2 1 0
0 0 1 F B
- F = Flag -> 0=C, 1=V
- B = Bit -> 0=Clear, 1=Set
HALT:
4 3 2 1 0
0 1 x x x -> HALT
1 0 x x x -> HALT
1 1 x x 0 -> HALT
1 1 1 1 1 -> Official HALT
| Mode | Mnemônico | Operação |
|---|---|---|
00000 |
NOP |
Consome ciclo |
00100 |
CLC |
C <- 0 |
00101 |
SEC |
C <- 1 |
00110 |
CLV |
V <- 0 |
00111 |
SEV |
V <- 1 |
01xxx |
XXX |
Halt CPU |
10xxx |
XXX |
Halt CPU |
11xx0 |
XXX |
Halt CPU |
11111 |
HLT |
Halt CPU |
Grupo 001: Memory
Enconding:
Memory:
4 3 2 1 0
0 M D R R
- M = Mode -> 0=Absolute, 1=Indirect
- D = Direction -> 0=Read, 1=Write
- RR = Register -> 00=A, 01=IX, 10=IY, 11=HALT
Transfer:
4 3 2 1 0
1 S S D D
- SS = Source -> 00=A, 01=IX, 10=IY, 11=HALT
- DD = Destination -> 00=A, 01=IX, 10=IY, 11=HALT
| Mode | Mnemônico | Operação |
|---|---|---|
00000 |
LD addr |
A <- mem[addr] |
00001 |
LDX addr |
IX <- mem[addr] |
00010 |
LDY addr |
IY <- mem[addr] |
00011 |
XXX |
Halt CPU |
00100 |
ST addr |
mem[addr] <- A |
00101 |
STX addr |
mem[addr] <- IX |
00110 |
STY addr |
mem[addr] <- IY |
00111 |
XXX |
Halt CPU |
01000 |
LD [IY] |
A <- mem[IY] |
01001 |
LDX [IY] |
IX <- mem[IY] |
01010 |
LDY [IY] |
IY <- mem[IY] |
01011 |
XXX |
Halt CPU |
01100 |
ST [IY] |
mem[IY] <- A |
01101 |
STX [IY] |
mem[IY] <- IX |
01110 |
STY [IY] |
mem[IY] <- IY |
01111 |
XXX |
Halt CPU |
10000 |
NOP |
Sem operação |
10001 |
TAX |
IX <- A |
10010 |
TAY |
IY <- A |
10011 |
XXX |
Halt CPU |
10100 |
TXA |
A <- IX |
10101 |
NOP |
Sem operação |
10110 |
TXY |
IY <- IX |
10111 |
XXX |
Halt CPU |
11000 |
TYA |
A <- IY |
11001 |
TYX |
IX <- IY |
11010 |
NOP |
Sem operação |
11011 |
XXX |
Halt CPU |
111xx |
XXX |
Halt CPU |
Grupo 010: Arithmetic
Encoding:
4 3 2 1 0
M M O O O
- MM = Mode -> 00=Implied, 01=Indirect, 10=Immediate, 11=Absolute
- OOO = Operation -> 000=ADC, 001=SBC, 010=MUL, 011=AND,
100=OR, 101=XOR, 110=CMP, 111=HLT|LDI
LoaD Immediate:
4 3 2 1 0
1 0 1 1 1
- Load A with Immediate value
| Mode | Mnemônico | Operação |
|---|---|---|
00000 |
ADC IX |
A <- A + IX + C |
00001 |
SBC IX |
A <- A - IX - (1 - C) |
00010 |
MUL IX |
A <- A[3:0] * IX[3:0] |
00011 |
AND IX |
A <- A & IX |
00100 |
OR IX |
A <- A | IX |
00101 |
XOR IX |
A <- A ^ IX |
00110 |
CMP IX |
A - IX, sem writeback |
00111 |
XXX |
Halt CPU |
01000 |
ADC [IY] |
A <- A + mem[IY] + C |
01001 |
SBC [IY] |
A <- A - mem[IY] - (1 - C) |
01010 |
MUL [IY] |
A <- A[3:0] * mem[IY][3:0] |
01011 |
AND [IY] |
A <- A & mem[IY] |
01100 |
OR [IY] |
A <- A | mem[IY] |
01101 |
XOR [IY] |
A <- A ^ mem[IY] |
01110 |
CMP [IY] |
A - mem[IY], sem writeback |
01111 |
XXX |
Halt CPU |
10000 |
ADC imm8 |
A <- A + imm8 + C |
10001 |
SBC imm8 |
A <- A - imm8 - (1 - C) |
10010 |
MUL imm8 |
A <- A[3:0] * imm8[3:0] |
10011 |
AND imm8 |
A <- A & imm8 |
10100 |
OR imm8 |
A <- A | imm8 |
10101 |
XOR imm8 |
A <- A ^ imm8 |
10110 |
CMP imm8 |
A - imm8, sem writeback |
10111 |
LDI imm8 |
A <- imm8 |
11000 |
ADC addr |
A <- A + mem[addr] + C |
11001 |
SBC addr |
A <- A - mem[addr] - (1 - C) |
11010 |
MUL addr |
A <- A[3:0] * mem[addr][3:0] |
11011 |
AND addr |
A <- A & mem[addr] |
11100 |
OR addr |
A <- A | mem[addr] |
11101 |
XOR addr |
A <- A ^ mem[addr] |
11110 |
CMP addr |
A - mem[addr], sem writeback |
11111 |
XXX |
Halt CPU |
Grupo 011: Unary
Enconding:
Shift/Bit:
4 3 2 1 0
0 0 O O O
- OOO = Operation -> 000=SHL, 001=SHR, 010|011|100=HALT,
101=NOT, 110=ROL, 111=ROR
Increment/Decrement:
4 3 2 1 0
0 1 D R R
- D = Decrement -> 0=INC, 1=DEC
- RR = Register -> 00=A, 01=IX, 10=IY, 11=HALT
HALT:
4 3 2 1 0
1 x x x x -> HALT
| Mode | Mnemônico | Operação |
|---|---|---|
00000 |
SHL |
A <- A << 1; C <- [7..0] <- 0 |
00001 |
SHR |
A <- A >> 1; 0 -> [7..0] -> C |
00010 |
XXX |
Halt CPU |
00011 |
XXX |
Halt CPU |
00100 |
XXX |
Halt CPU |
00101 |
NOT |
A <- ~A |
00110 |
ROL |
A <- A <] 1; C <- [7..0] <- C |
00111 |
ROR |
A <- A [> 1; C -> [7..0] -> C |
01000 |
INC |
A <- A + 1 |
01001 |
INC IX |
IX <- IX + 1 |
01010 |
INC IY |
IY <- IY + 1 |
01011 |
XXX |
Halt CPU |
01100 |
DEC |
A <- A - 1 |
01101 |
DEC IX |
IX <- IX - 1 |
01110 |
DEC IY |
IY <- IY - 1 |
01111 |
XXX |
Halt CPU |
1xxxx |
XXX |
Halt CPU |
Grupo 100: Jump
Encoding
Conditional Branch:
4 3 2 1 0
0 0 T F F
- T = Test -> 0=Test if Clear, 1=Test if Set
- FF = Flag -> 00=Z, 01=C, 10=N, 11=V
Unconditional Control Flow:
4 3 2 1 0
1 0 0 O O
- OO = Operation -> 00=BRA, 01=JMP, 10|11=Reserved/NO-OP
HALT:
4 3 2 1 0
0 1 x x x -> HALT
1 1 x x x -> HALT
| Mode | Mnemônico | Operação |
|---|---|---|
00000 |
BNQ rel8 |
if Z=0: PC = PC + 2 + rel8 |
00001 |
BPL rel8 |
if N=0: PC = PC + 2 + rel8 |
00010 |
BCC rel8 |
if C=0: PC = PC + 2 + rel8 |
00011 |
BVC rel8 |
if V=0: PC = PC + 2 + rel8 |
00100 |
BEQ rel8 |
if Z=1: PC = PC + 2 + rel8 |
00101 |
BMI rel8 |
if N=1: PC = PC + 2 + rel8 |
00110 |
BCS rel8 |
if C=1: PC = PC + 2 + rel8 |
00111 |
BVS rel8 |
if V=1: PC = PC + 2 + rel8 |
01xxx |
XXX |
Halt CPU |
10000 |
BRA rel8 |
PC = PC + 2 + rel8 |
10001 |
JMP addr |
PC = addr |
10010 |
NOP |
Reservado: Sem operação |
10011 |
NOP |
Reservado: Sem operação |
101xx |
XXX |
Halt CPU |
11xxx |
XXX |
Halt CPU |
Notas:
XXXsignificaindefinido, logo sempre ocorrerar umHLT- rel8 sempre é um inteiro com sinal, que vai de
-128à127. - imm8 sempre é um inteiro sem sinal, que vai de
0à255.
A ULA é a unidade lógica de aritmética principal do AX8, nela é possível computar duas entradas A e B, e retornar o resultado Y, junto com as flags apropriadas. A ULA é limitada a computação de números inteiros de 8-bits, sendo sua saída também limitada a 8-bits.
| OP | Nome | Operação | Carry | Overflow |
|---|---|---|---|---|
0000 |
ADC |
Y <- A + B + C |
carry-out | sobrecarga de sinal |
0001 |
SBC |
Y <- A - B - (1 - C) |
borrow invertido | sobrecarga de sinal |
0010 |
MUL |
Y <- A[3:0] * B[3:0] |
zero | Y > 0x0F |
0011 |
AND |
Y <- A & B |
--- | --- |
0100 |
OR |
Y <- A | B |
--- | --- |
0101 |
XOR |
Y <- A ^ B |
--- | --- |
0110 |
ROL |
Y <- A <] 1 |
A[7] antes do roll | --- |
0111 |
ROR |
Y <- A [> 1 |
A[0] antes do roll | --- |
1000 |
MOV |
Y <- B |
--- | --- |
1xx1 |
HLT |
Sem operação |
--- | --- |
Será somente utilizado portas lógicas: AND, OR, NOT e XOR. A implementação deve ser feita e documentada em blocos de lógica isoladas.
Notas relevantes sobre cada instrução:
ADC: Por causa da natureza da instrução, é recomendado utilizar 'CLC' para limpar o CarrySBC: A operação pode ser implementada como:A + (~B) + C, já que, tecnicamente, isto é equivalente a:A - B - (~C). Por causa desta natureza, o resultado sempre está uma unidade maior, portanto, antes de qualquer operação, é recomendado usar oSECpara ativar o Carry.SHLeSHR: Não tem opcodes próprios na ALU, as operações são feitas comoROLeROR, mas com a flag carry em0NOT: Pode ser implementado como:A XOR 0xFFCMP: O resultado da operação pode ser interpretado como:ZC = A <=> BSTYeLDY: O modo indireto dessas instruções são válidos, mas raramente úteis na práticaINCeDEC: Ambos são lidos comoADCpela ULA, mas a diferença é queINCsempre teráBcomo0x00eCincomo1, e queDECsempre teráBcomo0xFFeCincomo0.LDI: Tecnicamente, deveria estar no grupo Memory, mas devido a limitações é necessário que fique me Arithmetic
Notas sobre as flags:
-
V: O resultado da sobrecarga de sinal emADCeSBCvem do sexto carry (c6) e o carry resultante (cout), entãoV = c6 xor coutRelação Z C N A < B00Y[7]A = B110A > B01Y[7]
- nesdev.org
- mass:werk
- 6502.org
- CRAFTING A CPU TO RUN PROGRAMS
- HOW TRANSISTORS RUN CODE?
- Building an 8-bit breadboard computer!
- Wikipédia - Arithmetic Logic Unit
- Wikipédia - Adder Eletronics
- Wikipédia - Adder Subtractor
- Wikipédia - Instruction Cycle
- Wikipédia - Addressing Mode
- Wikipédia - Orthogonal Instruction Set
- decoding.txt
- sequencer.txt
- datapath.txt
Footnotes
-
Em fato, essa encodificação não é obrigatória, mas uma forma de facilitar a decodificação em hardware. ↩