74JK4080 CPU

- amatérský 4-bitový procesor sestavený z obvodů TTL 74xx



Instrukční soubor 74JK4080 CPU

Přestože třeba 4-bitový mikroprocesor Intel 4004 měl instrukce 8-bitové (operační kód se přenášel nadvakrát) a není to technicky žádný problém, zvolil jsem pro svůj procesor 74JK4080 CPU instrukční soubor pouze 4-bitový. Pochopitelně především z důvodu jednoduchosti stavby a také kvůli omezenému adresovému rozsahu paměti. Kdyby každá instrukce zabírala v paměti 2 nibble, tak by té paměti při rozsahu 256 adres opravdu moc k dispozici nebylo. Tím je dáno, že počet instrukcí může být maximálně 16. To není moc, ale stačí to. Přesto se mi podařilo aplikovat instrukcí mnohem více díky následujícím fíglům. V podstatě se dá říci, že 12 instrukcí je opravdu 4-bitových, ale zbylé 4 instrukce jsou 8-bitové. Přesto se principiálně zpracovávají jako 4- bitové a instrukční registr je skutečně 4-bitový. To je guláš, co? Fígle, jak je to možné, byly popsány již u příslušných bloků procesoru, ale neškodí si to zopakovat.


fígl č.1: práce s ALU, instrukce LOP, AOP a SWAP

Pro práci s ALU používám pouze dvě základní instrukce. LOP pro logickou operaci a AOP pro aritmetickou operaci. Jenže použitá ALU 74181 provádí až 32 operací. 16 logických a 16 aritmetických. A jelikož můžeme rozlišit ty aritmetické ještě na případ kdy příznak C = 0 a C = 1, je teoreticky k dispozici neuvěřitelný počet 48 operací! Pochopitelně ne všechny mají nějaký praktický význam. Stačí se podívat do funkční tabulky 74181... Ale i tak jsem chtěl mít možnost využít všechny tyto operace. Proto je povinnou součástí instrukcí LOP a AOP ještě parametr, kterým se zvolí požadovaný typ operace. V případě AOP je ještě nutné před touto instrukcí (v případě potřeby) příslušně nastavit příznaky pomocí SEF (Set Flags) , resp. CLF (Clear Flags). Procesor 74JK4080 CPU tedy nemá přímo instrukce typu ADD, INC, OR, NOT, SUBB, apod. ale namísto toho se používají LOP a AOP s příslušným parametrem. Rozdíl mezi LOP a AOP je jinak pouze v nastavení vstupu Mode u 74181.

Například instrukci pro inkrementaci akumulátoru (INC A) nahradíme pomocí AOP 0Fh, ale ještě předtím nastavíme příznak C pomocí SEF. Aritmetická operace 0Fh totiž u 74181 znamená F=A+C. AOP dále zařídí nahrání výsledku zpět do Acc a také uloží aktuální příznaky do Flag registru.

Dalším příkladem je třeba instrukce XOR data, která provede exkluzívní logický součet mezi akumulátorem a konstantou data. Konstantu data je nejprve nutné nahrát do Bcc pomocí LBD data a následně se provede XOR instrukcí LOP 09h. LOP pochopitelně také zařídí nahrání výsledku zpět do Acc a také uloží aktuální příznaky do Flag registru.

Tento postup je sice pro programátora poměrně nepohodlný, ale je to asi jediný způsob jak možnosti 74181 plně využít při omezeném počtu instrukcí. Holt je potřeba mít neustále po ruce funkční tabulku 74181. Jelikož je hardwarově zajištěno, že vstup dat do ALU i výstup dat z ALU je invertován, je nutné používat funkční tabulku 74181 s active LOW operands! Pochopitelně je teoreticky možné vytvořit překladač (assembler) pro PC, který bude mít pseudoinstrukce odpovídající těm zavedeným (AND, OR, XOR, NOT, ADD, ADDC, INC, apod) a v podstatě jen tyto instrukce přeloží jako LOP/AOP s příslušným parametrem a podle potřeby předtím nastaví/smaže příznaky. Kdo takový assembler napíše? :-)

Speciální případem je instrukce SWAP sloužící k vzájemnému prohození obsahů registrů Acc a Bcc. Instrukce je v podstatě modifikací operace LOP, ale jako parametr pro nastavení kódu operace (F=B) slouží přímo operační kód instrukce, který tak musí být Ah. Díky tomu instrukce SWAP nevyžaduje žádný další parametr.

Abych si programování zjednodušil, vytvořil jsem si následující tabulku asi nejvíce využitelných operací 74181 s klasickými názvy. Všechny tyto operace jsem prakticky vyzkoušel a ověřil a vše funguje dle očekávání:


operace ALU 74181


Ještě, než jsem to vydal, tak jsem si ten assembler vytvořil sám. Jednoduše jsem si udělal vlastní tabulku instrukcí pro můj oblíbený assembler TASM. Takže mám překladač vlastního assembleru. Funguje perfekt, ale jednu vadu má: adresy se musejí zadávat ručně, protože TASM neumí pracovat s nibble. To je nepříjemné hlavně při skocích, nelze používat klasická návěští. Ale i tak je to ohromné zjednodušení práce.


fígl č.2: skoky, instrukce JMP, CALL a RET

Podobný princip, tedy další parametr, který přímo ovlivňuje chování instrukce, jsem použil i u skoků JMP a CALL. Opět používám pouze tyto dvě základní instrukce, kterými ale realizuji mnohem větší škálu funkcí, konkrétně podmíněných a nepodmíněných skoků. Za instrukcemi JMP i CALL je nutné uvést tzv. masku příznaků a teprve poté adresu skoku. Ke skoku dojde jen v případě, že alespoň jeden nastavený bit z masky se shoduje s příslušným bitem v registru příznaků. Příznaky jsou v registru příznaků (Fcc, čili vlajkonoš:-)) uspořádány takto: D3=C, D2=NC, D1=NZ, D0=Z. Nejvíce významný bit je tedy příznak C (Carry), nejméně významný bit je příznak Z (Zero). Prostřední dva bity jsou komplementární příznaky NC a NZ.

Pro nepodmíněný skok je vhodné použít masku ve tvaru 1111 (0Fh). V tomto případě je podmínka splněna vždy a jedná se tak o de facto nepodmíněný skok. Stejné funkce dosáhne i maska ve tvaru 1100 (C+NC), resp. 0011 (NZ+Z), protože vždy jeden z těchto bitů bude nastaven. Naopak maska ve tvaru 0000 způsobí pouze přechod na následující adresu. Ke skoku nedojde nikdy a chová se stejně jako NOP. Skok typu JP Z (jump if zero) pak vypadá takto: JMP 0001. Skok typu JP NC (jump if not carry) se zapíše jako JMP 0100. Lze vytvářet i kombinované podmínky jako JP NC+Z = JMP 0101, JP NC+NZ = JMP 0110, či JP C+Z = JMP 1001. Zde je nutné si uvědomit, že operátor PLUS je ve skutečnosti logický OR! Totéž platí i pro CALL. Teoretický počet skoků je tedy 16 pro JMP a 16 pro CALL, ale opět lze část z nich považovat za nesmyslné, takže praktický počet je výrazně menší.

Specialitou je instrukce RET, která provádí vlastně nepodmíněný skok na adresu vyzvednutou z registru návratové adresy, kterou tam uložila instrukce CALL. Instrukce RET má operační kód 1111 (Fh) a tento kód je zároveň použit jako maska příznaků, která zajistí nepodmíněný skok. Instrukce RET se tedy obejde bez dalšího parametru.

Suma sumárum je čistě teoretický počet instrukcí 74JK4080 CPU neuvěřitelných 92! Ale zdaleka ne všechny jsou prakticky využitelné. Asi jako MOV A,A u CPU 8080...


seznam a popis instrukcí 74JK4080 CPU

V následující tabulce jsou všechny základní instrukce procesoru 74JK4080 CPU. Níže následuje popis každé jednotlivé instrukce, její operační kód, přehled povinných parametrů a také rozklad instrukce na mikroinstrukce z řídící ROM procesoru. Díky 4-bitovému čítači mikroinstrukcí v 74JK4080 CPU je možno každou instrukci rozložit na maximálně 16 mikroinstrukčních kroků. Tohoto maxima jsem nakonec nemusel dosahovat a nejdelší instrukcí je CALL s 13 kroky. Pochopitelně je jinak délka zpracování každé instrukce variabilní a je vždy provedeno jen tolik kroků, kolik daná instrukce opravdu potřebuje, protože mikroinstrukce LIR způsobí mimo jiné i vynulování čítače mikroinstrukcí. Význam mikroinstrukcí byl popsán na předešlé stránce.


NOPLADLBDLAI
LBISTALOPAOP
SEFCLFSWAPIN
OUTJMPCALLRET


NOP - No Operation. Žádná operace. Nezdá se to, ale je to jedna z nejdůležitějších instrukcí mikroprocesorů:-) Proto i v tak omezeném instrukčním souboru, jaký 74JK4080 CPU bezpochyby má, má instrukce NOP své místo. Instrukce pouze inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LIR

Operační kód: 00h
Parametry: žádné


LAD - Load Accumulator Directly. Přímé nahrání konstanty do akumulátoru. Instrukce převezme data bezprostředně následující za operačním kódem instrukce a vloží je do Acc registru. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LA
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 01h xxxx
Parametry: data (1x nibble)


LBD - Load "Beccumulator" Directly. Přímé nahrání konstanty do "bekumulátoru". Instrukce převezme data bezprostředně následující za operačním kódem instrukce a vloží je do Bcc registru. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LB
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 02h xxxx
Parametry: data (1x nibble)


LAI - Load Accumulator Indirectly. Nahrání dat do akumulátoru z určené adresy. Instrukce adresuje paměť a adresovaná data vloží do Acc registru. Adresa dat se nachází za operačním kódem instrukce v klasické podobě Low nibble a High nibble. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LMAR1
PLONK + INCA
AB + OA
RD + LMAR2
PLONK + OA
RD + LA
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 03h xxxx xxxx
Parametry: addr_low + addr_high (2x nibble)


LBI - Load "Beccumulator" Indirectly. Nahrání dat do "bekumulátoru" z určené adresy. Instrukce adresuje paměť a adresovaná data vloží do Bcc registru. Adresa dat se nachází za operačním kódem instrukce v klasické podobě Low nibble a High nibble. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LMAR1
PLONK + INCA
AB + OA
RD + LMAR2
PLONK + OA
RD + LB
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 04h xxxx xxxx
Parametry: addr_low + addr_high (2x nibble)


STA - Store Accumulator Indirectly. Nahrání dat z akumulátoru na určenou adresu paměti. Instrukce adresuje paměť a do adresované buňky vloží data z Acc registru. Adresa dat se nachází za operačním kódem instrukce v klasické podobě Low nibble a High nibble. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LMAR1
PLONK + INCA
AB + OA
RD + LMAR2
PLONK + OA
EA + WR
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 05h xxxx xxxx
Parametry: addr_low + addr_high (2x nibble)


LOP - Logic Operation. Logická operace v ALU. Instrukce provede v ALU logickou operaci s operandy, které musí být předem nahrány v registrech Acc a Bcc. Výsledek operace je následně zapsán do Acc a nové příznaky jsou zapsány do Fcc. Kód operace (1x nibble) se nachází bezprostředně za operačním kódem instrukce. Kódy odpovídají funkční tabulce ALU 74181 při Mode = 1 a active low operands. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LOP
PLONK + LR
PLONK + LF
ER + LA
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 06h xxxx
Parametry: operation_code (1x nibble)


AOP - Arithmetic Operation. Aritmetická operace v ALU. Instrukce provede v ALU aritmetickou operaci s operandy, které musí být předem nahrány v registrech Acc a Bcc. Instrukce bere v úvahu i aktuální stav příznaku C! Proto je v případě potřeby nutné před použitím AOP nastavit (SEF), či naopak vynulovat (CLF) příznaky. Nebo je ponechat beze změny. Výsledek operace je následně zapsán do Acc a nové příznaky jsou zapsány do Fcc. Kód operace (1x nibble) se nachází bezprostředně za operačním kódem instrukce. Kódy odpovídají funkční tabulce ALU 74181 při Mode = 0 a active low operands. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LOP
M + LR
M + LF
ER + LA
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 07h xxxx
Parametry: operation_code (1x nibble)


SEF - Set Flags. Nastavení příznaků. Instrukce nastaví příznaky C a Z v Fcc na 1. Zároveň se komplementární příznaky NC a NZ nastaví na 0 - zajištěno hardwarově. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

F1 + LF
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 08h
Parametry: žádný


CLF - Clear Flags. Nulování příznaků. Instrukce nuluje příznaky C a Z v Fcc na 0. Zároveň se komplementární příznaky NC a NZ nastaví na 1 - zajištěno hardwarově. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

F0 + LF
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 09h
Parametry: žádný


SWAP - SWAP Acc and Bcc registers. Prohození obsahu registrů Acc a Bcc. Instrukce vzájemně prohodí obsahy Acc a Bcc prostřednictvím ALU. Operační kód instrukce je zároveň kódem operace pro ALU 74181 (F=B) a jedná se tak o speciální modifikaci instrukce LOP. Příznaky nejsou dotčeny. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

RD + LOP
PLONK + LR
EA + LB
ER + LA
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 0Ah
Parametry: žádný


IN - Input data. Nahrání dat do akumulátoru z portu určené adresy. Instrukce adresuje vstupní port a data vloží do Acc registru. Adresa portu se nachází za operačním kódem instrukce jako 1x nibble. Na horní nibble adresy je zároveň vystaven dosavadní obsah Acc, podobně jako u Z-80 CPU. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 1) je následující:

PLONK + INCA
AB + OA
RD + LMAR1
EA + LMAR2
PLONK + OA
RD + LA
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 0Bh xxxx
Parametry: addr (1x nibble)


OUT - Output data. Nahrání dat z akumulátoru do portu určené adresy. Instrukce adresuje výstupní port a zapíše do něj data z Acc registru. Adresa portu se nachází za operačním kódem instrukce jako 1x nibble. Na horní nibble adresy je zároveň vystaven obsah Acc, podobně jako u Z-80 CPU. Následně inkrementuje programový čítač a vyzvedne z paměti další instrukci pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 1) je následující:

PLONK + INCA
AB + OA
RD + LMAR1
EA + LMAR2
PLONK + OA
EA + WR
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 0Ch xxxx
Parametry: addr (1x nibble)


JMP - Jump. Skok (podmíněný i nepodmíněný). Instrukce provede skok na zadanou adresu. Ještě předtím je ale do registru Flag Mask nahrána maska příznaků (typ skoku). V případě, že alespoň jeden bit z masky a z Flag registru se shodují, je skok proveden. Jinak dojde pouze k inkrementaci čítače adres. Bezprostředně za operačním kódem instrukce se musí nacházet kód typu skoku (maska příznaků) a dále následuje adresa skoku v klasické podobě Low nibble a High nibble. Instrukce tedy celkem zabírá 4 nibble.

Význam bitů ve Flag registru a v Masce příznaků je tento: C, NC, NZ, Z. Nejvíce významný bit je tedy příznak C (Carry), nejméně významný bit je příznak Z (Zero). Prostřední dva bity jsou komplementární příznaky NC a NZ. Pro nepodmíněný skok je vhodné použít masku ve tvaru 1111 (0Fh). V tomto případě je podmínka splněna vždy a jedná se tak o de facto nepodmíněný skok. Stejné funkce dosáhne i maska ve tvaru 1100 (C+NC), resp. 0011 (NZ+Z), protože vždy jeden z těchto bitů bude nastaven. Naopak maska ve tvaru 0000 způsobí pouze přechod na následující adresu a chová se stejně jako NOP. Následně je vyzvednuta z nové adresy v paměti další instrukce pro zpracovnání. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LFM
PLONK + INCA
AB + OA
RD + LJA1
PLONK + INCA
AB + OA
RD + LJA2
AB + JPE
AB + OA
RD + LIR

Operační kód: 0Dh xxxx xxxx xxxx
Parametry: flag_mask + addr_low + addr_high (3x nibble)


CALL - Call Subroutine. Skok do podprogramu (podmíněný i nepodmíněný). Instrukce provede skok do podprogramu na zadanou adresu. Instrukce se chová v podstatě identicky jako předchozí JMP, ale navíc uchovává návratovou adresu pro korektní návrat z podprogramu instrukcí RET. Jinak platí totéž co u JMP. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

PLONK + INCA
AB + OA
RD + LFM
PLONK + INCA
AB + OA
RD + LJA1
PLONK + INCA
AB + OA
RD + LJA2
PLONK + LST
AB + JPE
AB + OA
RD + LIR

Operační kód: 0Eh xxxx xxxx xxxx
Parametry: flag_mask + addr_low + addr_high (3x nibble)


RET - Return from Subroutine. Návrat z podprogramu. Instrukce provede návrat z podprogramu volaného instrukcí CALL. Instrukce vyzvedne ze zásobníku návratovou adresu, inkrementuje ji a vyzvedne z nové adresy v paměti další instrukci pro zpracovnání. Instrukční kód zároveň slouží jako předvolba pro Flag Mask register, aby opravdu došlo k nahrání obsahu Stacku do programového čítače. RET je v podstatě jen speciální modifikací instrukce JMP. Rozklad na mikroinstrukce (při IO/M = 0) je následující:

RD + LFM
PLONK + JPE
PLONK + INCA
AB + OA
RD + LIR

Operační kód: 0Fh
Parametry: žádný


Indexové adresování

Pozornému čtenáři nemohlo uniknout, že instrukční soubor neumožňuje tzv. indexové adresování. Neumožňuje to totiž ani hardware procesoru 74JK4080 CPU. Není tam žádný použitelný indexregistr, který by byl obdobou IX, či HL ze známých procesorů. Uměl bych ho vytvořit, ale musel bych rozšířit kapacitu sequenceru a použít osmibitové instrukce, aby jich mohlo být více. Zkrátka musel bych podstatně zesložitnit svůj návrh a nejspíše bych to pak nikdy nepostavil...

Jenže indexové adresování prostě potřeba JE. Jak jinak napsat program třeba pro test celé paměti RAM? A jak jinak napsat operační systém, který umožní vkládání a prohlížení zapsaného programu v RAM? Takže jsem 74JK4080 CPU prostě musel naučit adresovat indexem. Jak? Díky podprogramům. Třeba operační systém CABOS počítače CLAUDIA-1 si po startu nejprve na konec RAM vloží následující kód a tím vytvoří následující podprogram:

03h ;instrukce LAI
00h ;dolní nibble adresy 080h
08h ;horní nibble adresy 080h
0Fh ;instrukce RET

Zavoláním podprogramu se do Acc načtou data z adresy 080h (u Claudie počátek RAM). Vtip je v tom, že tuto adresu lze libovolně měnit (přepisovat, inkrementovat, dekrementovat) instrukcí STA, která sice vyžaduje pevnou adresu, ale tu my známe:-) Tím si v RAM vytvoříme obdobu indexregistru. Při potřebě data na adresu z indexregistru naopak uložit, stačí přepsat první hodnotu 03h (LAI) na 05h (STA) a opět zavolat podprogram. Obojí je v CABOSu využito a obojí funguje k naprosté spokojenosti. Obdobně by bylo možné vytvářet i indexové skoky. Takto vytvořený indexregistr sice vyžaduje nepatrně složitější softwarovou obsluhu a obsazuje pár niblí v uživatelské RAM, ale za to výrazné HW zjednodušení procesoru to snad stojí.

Motto: Člověk může bejt v životě blbej. Ale musí si umět poradit... :-)


RET



Odladěno na MS IE 8.0 a Firefoxu při rozlišení min. 1024 x 768 pixelů
www.NOSTALCOMP.cz    2010  -  2017