Hirdetés

Aktív témák

  • t3m1nat0r

    csendes tag

    Sokak szerint a processzorok működésének megértéséhez magas mérnöki előtanulmányok szükségesek.
    Hogy ez mekkora tévedés, az itt ki fog hamarosan derülni..

  • t3m1nat0r

    csendes tag

    Talán érdemes lenne egy egyszerű processzorral kezdeni. Legyen ez a 6502, ami a commodore 64-est is vezérelte, na meg a terminátort.

    "In the science fiction movie The Terminator (1984), starring Arnold Schwarzenegger, the audience at one point is treated to a view through the T-800 Model-101 robot character's eye/camera display with some 6502 assembly/machine code program fragments scrolling down the screen. "

    http://en.wikipedia.org/wiki/MOS_Technology_6502

    http://www.llx.com/~nparker/a2/opcodes.html

    http://www.llx.com/~nparker/a2/opcodes.html
    Ha az ember megnézi ennek a processzornak az utasítás készletét, akkor első pillanatban megrémül, hogy milyen sok van ezekből. Ha kicsit átnézzük ezeket, akkor kiderül, hogy a legtöbb utasítás ugyan annak az alaputasításnak a módosulatai.

    http://www.obelisk.demon.co.uk/6502/instructions.html

    Igy már 11 csopotra lehet bontani ezeket. Egy kezdőnek még mindig ijesztő lehet, de tovább lehet egyszerűsíteni a képet.

    Az első csoport valamilyen adatot visz egyik "hely"-ről a másikra. A hely fogalmát itt még majd definiálni kell. Itt ez a fogalom nem utcákat vagy bármi más megszokott dolgot jelent.

    Ezek:
    Load/Store Operations
    Register Transfers
    Stack Operations

    töltés/tárolás
    register tartalom áthelyezése
    verem operációk

    A második csoport "biteken" "bitekkel" végez műveletet. A bitek fogalmát is definiálni kell majd.

    Logical
    Arithmetic
    Increments & Decrements
    Shifts
    Status Flag Changes

    lokigai bit műveletek
    aritmetikai műveletek
    regiszter érték nővelés, csökkentés 1-el
    eltolások és forgatások
    státusz beállitások

    A harmadik csopot a program elagazasait kezeli, irányítja. Ezek valójában az első csoporthoz tartoznak, mert az a fő feladatuk, hogy az ugrasi cimet a Program Counter-be helyezik.

    http://www.obelisk.demon.co.uk/6502/registers.html

    Jumps & Calls
    Branches
    System Functions

    Ugrások és szubrutin hívások
    elágazások
    rendszer funkciók

  • ABnormal

    őstag

    jó ezekről olvasni,1993-ban írtam ilyen nyelven az utolsó programomat :D

  • t3m1nat0r

    csendes tag

    Definiálni kellene, hogy mit jelent a hely és bit fogalma a gépben.
    Ehhez tudni kell, hogy hogyan tárolja az információt a gép.

    Ez a memória feladata.
    http://en.kioskea.net/contents/pc/ram.php3
    A megadott linken rácsban elhelyezett kondenzátorokat lehet látni.
    Itt egy 6x6-os rács van a képen. Ez egy 6 byte-os memóriának felel, ahol egy byte-ból most csak 6 bit látszik.Egy byte valójában 8 bites, vagyis 8 kondenzátornak felel meg.

    A c64-ben 65536x8 ilyen kondenzátor van. A c64 memóriája 64 kbyte. Ennek a fogalomnak fizikailag ez a kondenzátor tömb felel meg.
    Ilyen egyszerű a kezdet, és később sem lesz sokkal bonyolultabb.

    A fizikai megvalósítástól függően nem kizárólag kondenzátor tárolhat egy bitet. Lehet tranzisztorokkal visszacsatolást létrehozni, amik szintén képesek egy bit tárolására.

    http://class.ee.iastate.edu/cpre305/labs/lab07.html

    A programozás szempontjából a különböző fizikai megoldások nem lényegesek. Annyi azért még leírok, hogy a műveleteket nem követlenül a memóriában végzi a processzor, hanem beolvassa a memória tartalmát egy regiszterbe. Ezek a regiszterek gyors működésű tranzisztorokból állnak legtöbbször. A modern processzorok már nem is egyből a regiszterekbe olvasnak, hanem elöszőr közbeiktatott több lépcsős gyorsító memóriákba. De nem célom a modern cpu-k leírása.

    Minden byte-ot /8 kondenzátort/ a memóriába egy címvezetékkel lehet elérni. Ez lehet a vízszintes vonal. Ez a c64-nél 65536 vezzetéket jelent. Ezt binárisan kódolva 16 biten lehet ábrázolni. 2 a 16.-on az pont ennyi. Ezt a 16 bites számot nevezzük címnek. Ez az adott 8 bites kondenzátor tömb memória címe.
    Ahhoz, hogy fizikailag ez működjön, át kell alakítani ezt a 16 bitet 65536 bitté, hiszen a memóriát ennyi vezetéken lehet elérni. Ez a memória címző egység dolga, amivel szintén nem kell programozás szinten nekünk foglalkozni.
    A memória fele haladó 16 bites vezetékköteg a címbusz, a kondenzátoroktól visszajövő az adatbusz.

    Innentől ismert, hogy mi az a bit, hogy lesz belőle byte, továbbá mit jelent ennek a helye és címe.

  • t3m1nat0r

    csendes tag

    Nem kerülhetem el azt, hogy a számábrázolásokról írjak. Ezek nélkül nem lesz tiszta, miért fér el 16 biten a 65536-1 decimális szám.

    A kettes számrenszer igazából egy választott ábrázolás a gépekben, hiszen bármilyen szimbólumrendszert hozzá lehetne rendelni egy adott byte-hoz, ami 8 kondenzátor értéke.

    De a kettes számrendszer felel meg a legjobban a célnak. Miért?
    Mert a kondenzátor vagy fel van töltve, vagy nem. A kettes számrendszerben pedig egy számot 0 és 1 számokkal írunk le, ami egyértelműen megfeleltethető a kondenzátor állapotainak.

    Tizes számrendszerben, mint köztudott, 10 számjeggyel /szimbólummal/ írható le bármilyen szám. Elszámolunk 9-ig, utánnak "túlcsordulás" keletkezik. Ezt egy újabb helyiértékkel jelöljük, majd folytatjuk a számolást megint 0-tól 9-ig.
    A kettes számrendszerben annyi a változás, hogy mindig 0-tól 1-ig számolunk, majd "túlcsordulás" jön. Az első bináris szám, ahol ez megtörténik a bináris 10,ami a decimális 2-es. Ugye 0, 1, 10/túlcsordulás/, ami decimálisan 0,1,2.

    Lehet ez már óvodás szintnek tűnik, de nem mérnököknek írok, hanem azoknak, akiknek halvány elképzelésük sincs, mi megy végbe egy ilyen gépben.

    Egy egyszerű számológéppel meg lehet nézni, hogy az 10000000000000000 bináris szám, az 65536. Azért írtan hogy -1, mert ez már 17 számjegyű. 16 bittel tehát a legmagasabb címezhető memória a bináris 1111111111111111, azaz a 65535.

    Ezt a sok egyest nagyon kényelmetlen kezelni, könnyű eggyel kevesebbet vagy többet írni belőle. Hamar meg is unták a programozók, és bevezettek egy egyszerűbb ábrázolási módot. Ami elsőre talán bonyolultabb.

    Ez a hexadecimális, tizenhatos számrendszerbeli ábrázolás.
    Mivel nincs a 10 11 12 13 14 15 számjegyekre külön jel, így használni őket pedig veszélyes lenne, mert nem lenne egyértelmű a leírás, ezért ezeket betűkkel helyettesítették. Ezek lettek az A,B,C,D,E,F betűk.
    Tehát úgy számolunk 16-os számokkal, hogy 1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15,16,17,18,19,1A,1B,.....

    Két hexadecimális számmal leírható egy byte. Nem csoda, hiszen egy fél byte 4 bit. 2 a 4.-en pedig 16. Egyszerű.

  • t3m1nat0r

    csendes tag

    Most kezd érdekes lenni a probléma.
    Az már sejthető, hogy a programkód és az adatok a memóriában vannak.

    De mit jelent az, hogy fut a program?
    Ehhez először is a regisztereket kell megismerni.

    Program Counter
    Stack Pointer
    Accumulator
    Index Register X
    Index Register Y
    Processor Status

    Az elsőnél már meg is állhatunk. Ez az a 16 bites címregiszter, ami arra a memória byte-ra mutat, ami éppen "végrehajtódik".
    Ez annyit jelent, hogy a processzor kiküldi a memória fele ezt a címet /rákapcsolja a regisztert a címbuszra/ , az pedig beállítja az adatvezetékeket az adott című memória byte kondenzátorainak megfelelő értékre.
    Ezeket a folyamatokat időziteni kell, mivel az elektronok nem végtelen sebességgel haladnak.

    A behozott byte lesz az utasítás. Ez az adott processzortól függően megmondja, hogy mi lesz a következő lépés.
    Lehet, hogy az utasítás utáni byte-ot be kell hozni, és hozzáadni az akkuhoz /Accumulator/, vagy az utasítás után egy cím található, ahova el kell ugrani.
    Ha elvégezte a műveletet, akkor a Program Counter értékét növeli automatikusan a processzor, és elhozza a következő byte-ot, a következő utasítást.

    Már fut is a program.

    Látszik, hogy igazából a memóriában bármilyen byte-kombináció lehet. Akár képi információ vagy valamilyen szöveg. A gépet ezt nem érdekli, neki az a feladata, hogy ezt utasítások sorozataként értelmezze, mivel a Program Counter értékét valaki erre a memória területre állította.
    Ha a program eltéved /hibás, vagy a memória sérül, stb/ akkor "megfagy" a program, vagy jön a "kékhalál".
    A commodore egyszerűen csak újraindult. Nem bonyolította túl a témát.

    Mostmár az is érthető, hogy hogyan ágazik el egy program.
    A Program Counter értékét néhány utasítás képes módosítani. Ezek a Jumps & Calls és a Branches utasítások.
    A jump és a "jump to a subrutine2 mindig elugranak, míg a Branches tipusúk csak akkor, ha a megadott feltétel teljesül. A szubrutin hívás még annyiban különleges, hogy a veremben eltárolja a visszatérési értéket, amit majd a "return from subrutine" újra elő fog venni. A magasabb szintű nyelvek ezt használják a procedurák és függvényhívásokhoz. Nem pont ezt, csak ha c64-en fug a progi.

    Eddig igazából semmi más nem törént, csak byte-ok mozogtak a memóriából a processzor valamelyik regiszterébe. Ez a lényege a gép működésének. Magasabb szinten a memória nem címekkel érhető el, hanem változók neveivel. De ezek mindig ilyen memória-címeket rejtenek.

  • t3m1nat0r

    csendes tag

    A maradék regiszterek

    Stack Pointer
    Accumulator
    Index Register X
    Index Register Y
    Processor Status

    A Stack Pointer-ről már volt szó a szubrutin hívásnál. Ez egy ideiglenes tárolásra kijelölt memória területre
    mutat. Ide lehet olyan adatokat "lenyomni", amiket csak kevés ideig kell megőrízni.
    A magasszintű nyelvek lokális változói is ide kerülnek.
    Ehhez tartoznak a POP és PUSH műveletek, amikke a veremre rakhatunk adatot, vagy leszedhetjük onnan.

    PHA Push accumulator on stack
    PHP Push processor status on stack
    PLA Pull accumulator from stack N,Z
    PLP Pull processor status from stack

    Az Accumulator az a regiszter, ahol műveleteket végez a processzor az adatokon. A modernebb cpu-kon már több ilyen funkciójú regiszter is található, és így jobban optimalizálható a kód, kevesebbszer kell a memóriához fordulnia.
    Az index regiszterek a címzést segítik.

    A Processor Status vagy flag register bitjei különböző állapotait mutatják a processzornak. Ezeket bitműveleteket lehet végezni, és a Branches utasításokhoz is ezek adják meg a feltételeket. Ezek általában valamilyen művelet elvégzése után állnak be az adott állapotba. Mondjuk a "CMP Compare accumulator", ami összehasonlít két számot kivonás segítségével, két egyenlő értéket hasonlított össze. Ez a "Zero Flag" bitet fogja beállítani, ami majd a következő "BEQ Branch if zero flag set" vagy "BNE Branch if zero flag clear" utasítást fogja vezérelni.

  • t3m1nat0r

    csendes tag

    A logikai és az aritmetikai műveletek az Accumulator értékét manipulálják.
    Itt még nem volt szorzás és osztás, bizony meg kellett írni azt is. Mennyivel egyszerűbb, és unalmasabb a mai processzorok programozása.

    A logikai műveletek bitekre vonatkoznak. Egyszerűen leírva annyi a feladatuk, hogy biteket állítsanak 1-re vagy 0-ra. Ez nem ugy oldották meg, hogy mindig megadom a módosítani kívánt bit azonosító-számát, hanem maszkokkal lehet a biteket manipulálni. Ha 1-esre akarom az akku 7.bitjét állítani, akkor az "ORA Logical Inclusive OR" utasításnak egy 0x80 hexadecimális paramétert adok. A 8 az binárisan 1000 a 0 az 0000, tehát a 7.bit egyes lesz, a többi marad a régi állapotában. A ugyanezt az "EOR Exclusive OR"-al végzem el, akkor a 7. bit az ellentétes állapotába áll be.
    Igazából nincs szándékomban leírni az összes utasítás működését, csak a gép alapvető funkcióinak a bemutatása volt a célom.

    Mint kiderült, nem kell mérnöki végzettség ahhoz, hogy az ember megértse a processzor működésének alapjait.
    Ahogy a programozásához sem diploma kell, hanem gép, idő és kódtáblázat.

    Persze aki úgy képzeli, hogy a próbálkozás egyenlő azzal, hogy összevissza nyomkodja a gombokat, az feleslegesen végzett el annyi egyetemet.

  • t3m1nat0r

    csendes tag

    Miért beszélek én 7. bitről, mikor az a 8.?

    Nem elírás, hanem ez egy megszokott számozási módszer a számítástechnikában. Akik programoznak, azok ismerik, hogy az első tömb elem mindig a [0]. Ennek praktikus oka van, a tömbnek van egy báziscíme, amihez hozzáadódik az index.

    A bitek ugyan így 0-tól kezdődnek.

  • t3m1nat0r

    csendes tag

    Az elején a bitekkel végzett műveletekhez soroltam az aritmetikai műveleteket. Ez nem a szokásos besorolás, de igaz.
    A regiszterekbe és a memóriában mindig bitek vannak.

    Az hogy mi most egy decimális számként gondolunk rá, vagy egy pixel szinértékeként, már csak értelmezésbeli külömbség. Attól ott még egy kondenzátor tömb van fizikailag, ami biteket tárol.

    Szín úgy lesz ebből a bitcsoportból, hogy áthelyezzük a videómemóriába. Ebből egy dekódoló áramkör olyan jelet fog előállítani , ami a monitoron szines pixelként fog megjelenni.
    Ha ezt a bitcsopotot a hangkártyára küldöm, akkor hang lesz belőle. Ha ráállítom az utasításszámláló regisztert, akkor programkód.

    Egy memóriaterület azért fog lebegőpontos számot jelképezni, mert olyan műveleteket végzek el rajta, amelyek ezt lebegőpontos számként kezelik.

  • t3m1nat0r

    csendes tag

    válasz t3m1nat0r #4 üzenetére

    Ahhoz, hogy fizikailag ez működjön, át kell alakítani ezt a 16 bitet 65536 bitté, hiszen a memóriát ennyi vezetéken lehet elérni.

    Igazából ennek elég egyszerű a megoldása. A bináris fákhoz hasonlóan felosztható a memória is. A legfelső bit ha 0, akkor a baloldali memória aktív, ha 1 akkor a jobb. A következő bit ezt a két részt osztja további darabokra.

  • t3m1nat0r

    csendes tag

    válasz t3m1nat0r #2 üzenetére

    A c64-ben már a 6510 processzor volt, ami tartalmaz néhány kiegészítést.
    De ez alapjában ugyan az a processzor.

  • sonar

    addikt

    Nem rossz, de szerintem egy logout blogban nagyon olvasottsága lenne ;)

    A tudást mástól kapjuk, a siker a mi tehetségünk - Remember: Your life – Your choices!

  • ABnormal

    őstag

    nincs kedved egy pár példaprogrammal szemléletesebbé tenni az utasításokat? nem kell bonyolult,csak néhány alap...

    LDA #$FF
    STA $1000

    ilyesmikre gondoltam...

  • t3rm1nat0r

    csendes tag

    válasz ABnormal #16 üzenetére

    Na , visszatértem

    Mielőtt az asm-ről beszélnék, előbb a hardvert kellene megismerni közelebbről.

    Szó volt a Program Counter-ről, ami egy számláló, ahogy a neve is mutatja.
    Ezek egy olyan elemekből állnak, amik visszacsatolással megjegyzik az állapotukat.
    Ezeknek ismét több megoldásuk lehetséges, a vezérlésük történhet élvezérléssel, lehet rajtuk reset bemenet.

    De az átláthatóság miatt vegyük a legegyszerűbb megoldást:
    http://webpages.charter.net/dawill/tmoranwms/Circuits_2008/Type_D_Flip-Flop.gif

    Ez két visszacsatolt részből áll, a master és a slave flip-flop-ból. Ezek lehetnek nand vagy nor kapukból felépítve, ismét csak részletkérdés. A fontos, az a visszacsatolt hurok, ez a két csatolt kapunál mindig ellentétes állapotú.
    A két flip-flop ellentétes jelre áll be. Ez a CLK bemenet, ami vezérli a számlálót. A slave rész ezt invertálva kapja.

    Ez még igazából nem jó számláláshoz, ahhoz össze kell kötni a D bemenetet az egyik Q kimenettel. Ekkor mindig ellentétes állapotába áll be a bit.
    Ha több ilyen bitet összerakok, és a CLK bemenetre az előző Q kimenetét kötöm, akkor meg is kaptam a számlálót.

    Hogy ne csak a levegőbe beszéljek, ezt működés közben is be fogom mutatni.

    [ Szerkesztve ]

  • t3rm1nat0r

    csendes tag

    A továbbiakban ahhoz hogy valamiféle értelmes szimulációt lehessen bemutatni, tudni kellene, hogy épül fel egy nand kapu.

    Ehhez a cmos megoldást választottam. De itt is sok más fizikai kivitelezés létezik.

    http://en.wikipedia.org/wiki/File:CMOS_NAND.svg
    http://en.wikipedia.org/wiki/CMOS

    Ez egy kevert megoldás, n és p tipusú tranzisztorokból áll. Mindig csak az egyik tipus aktív, amiatt áram csak nagyon rövid ideig folyik. Ezeket használják a kis fogyasztású eszközökben.

    Ha A vagy B bemenetet Vss-re kapcsolom /-/, akkor az Out Vdd szintre /+/ áll be, mert valamelyik felső tranzisztor lesz aktív.
    A minkét bemenet Vdd-t kap, akkor a két alsó tranzisztor Vss-re kapcsolja a kimenetet. /out/
    Ennek van egy igazság táblája
    A B out
    + + -
    - + +
    + - +
    - - +

    Ez megfelel a nand állapotainak, ha + az az 1, és a - a 0 állapot,
    Ezeket az igazságtáblákat nem kell bemagolni. Néhány egyszerű szabályra épül az egész.

    Az AND csak akkor ad igaz /1/ kimenetet, ha mindkét bemenet igaz. Ez az első eset.
    A B out
    11 1
    01 0
    10 0
    00 0

    Az OR csak akkor ad 0-át, ha mindkét bemenet 0. Ha VAGY az egyik VAGY a másik 1, akkor a kimenet is 1.
    A B out
    11 1
    01 1
    10 1
    00 0

    A XOR csak akkor lesz 1, ha ellentétes a két bemenet állapota.
    A B out
    11 0
    01 1
    10 1
    00 0

    Az N betű a NAND-nál annyit jelent, hogy invertálni kell a kapott értéket.
    Ez az 1-est 0-ára változtatja, és fordítva.

    A NAND kapuk használatának mint látszik, ismét praktikus oka van, ez az egyik legegyszerűbben megvalósítható logikai kapu.

    AND kaput úgy kapunk, hogy egy invertert kapcsolunk utánna. Ha nem akarunk túl optimalizált áramkört, akkor kiindulhatunk a tervezésnél tisztán NAND vagy NOR kaputömbből.
    Ekkor az invertert úgy kapom, hogy az egy NAND kapu egyik bemenetét felhúzom.
    Ezt a módszert fogom alkalmazni.

  • t3rm1nat0r

    csendes tag

    válasz t3rm1nat0r #18 üzenetére

    A linkbe került egy smile-vezérlő karakter, de jó a link.

  • t3rm1nat0r

    csendes tag

    válasz t3m1nat0r #9 üzenetére

    A logikai műveleteknél ezeket az alaputasításokat már említettem. Ott azt mondtam, hogy maszkokkal lehet a biteket manipulálni. Ez annyit jelent, hogy egy 8 bites regiszterhez 8 bitet adunk meg, amiken egyenként végrehajtódik az, amit az imént leírtam.
    Esszerint 8 darab A és B bemeneti érték van. A bitek bármilyen állapotban lehetnek.
    /terhesek azért nem xD /

    10101100 A
    11001101 B
    10001100 out = A AND B

    10101100 A
    11001101 B
    11101101 out A OR B

    10101100 A
    11001101 B
    01100001 out A XOR B /vagy EOR/

    Igy látszik a logikája a műveleteknek, ha decimálisan vagy hexában írom fel, akkor már csak gyakorlott szem érti, mi miért történik.

    0xac A
    0xcd B
    0x8c out = A AND B

    Vannak még a forgatások és léptetések. Itt csak annyi történik, hogy jobbra vagy balra eltolódik bitenként a regiszter
    10101100 A
    01011000 out ROTATE LEFT /a rövidítés processzor függő/

    10101100 A
    01010110 out RIGHT
    11010110 out RIGHT a legfelső bit ismétlődik. Mivel INT tipusnál az tárolja az előjelet, ezért így a szám negatív marad.

    Az A értéke decimálisan 172, jobbra forgatás után 86. Ez a fele, a forgatást osztás helyett lehet használni, ha 2 hatványával osztunk.
    Régebbi hardveres megoldásoknál ezért találkozunk lépten nyomon olyan számokkal, hogy 2 4 8 16 32 64 128 256 512 1024 ... mert ez az osztást /és ha balra forgatok, akkor szorzást/ sokkal egyszerűbben valósítható meg hardverileg, és még gyorsabb is a működése, mint majd kiderül.

    Be fogok mutatni egy teljes összeadó arámkört működés közben, ahol látszani fog, miért eszik olyan sok időt egy hagyományos processzor, és miért számol például egy geforce sokkal gyorsabban.

    [ Szerkesztve ]

  • t3rm1nat0r

    csendes tag

    Mivel egy c program lesz ami majd emulálja a hardver működését, ezért kicsit a programozás rejtelmeiben is elmerülök. Ezért került ide ez a topik.

    Ha megnézzük, hogy egy D flip-flop mennyi kapuból áll, és egy kapu mennyi tranzisztorból, elsőre elég elrettentőnek látszik a feladat.
    Itt jön a képbe a programozás igazi lényege. Mert nem az a lényeg, hogy az összes c nyelvjárás szintaktikai külömbségeit fejből fujja az ember. Azt egy ovodás is kiguglizza.

    A feladat felbontása kisebb részfeladatokra. Ez az, amit /még/ nem tudnak a gépek.

    Mivel a c nyelv egy logikai kapu működését egyszerűen utánnozza, ezért ez lesz az alapszint. Erre épül fel majd minden. Mivel látványos programot akarok, ezért a tranzisztorok állapotát is meg lehet majd jeleníteni a kapu állapotának ismeretében.
    Igazából egy processzort nem kapuszinten terveznek, hanem nagyobb egységekből rakják össze. Ez programozásnál is egy bevált módszer. Ha már tesztelt, és biztosan jól működő részegységekből építkezünk, akkor az új rendszer gyorsabban áll össze, és kisebb a hibalehetőség.

    A nemrég belinkelt Dflipflop-nak ennyi a kódja.Ez egyszerűen megmonja, hogy egy sorXsor-os NAND tömbben melyik kapu melyik két másikhoz kapcsolódjon.

    void define_dflip(int t,int l1)
    {
    int tr[]={
    t+0, t+1, t+2, t+3, t+4,
    t+sor+0, t+sor+1, t+sor+2, t+sor+3, t+sor+4,0};//5-9

    define_links(tr[0],link_5V,l1,0); //inverter
    define_links(tr[5],link_5V,tr[4],1);

    define_links(tr[1],l1,tr[5],0); define_links(tr[2],tr[7],tr[1],1);
    define_links(tr[6],l1,tr[4],1); define_links(tr[7],tr[2],tr[6],0);

    define_links(tr[3],tr[0],tr[2],1); define_links(tr[4],tr[9],tr[3],0);
    define_links(tr[8],tr[0],tr[7],1); define_links(tr[9],tr[4],tr[8],1);
    }

    Innentől egy 8 bites számlálónak már csak ennyi a kódja.Ez megadja, hogy két soronként legyen lefele egy-egy Dflipflop, és mindig az előző kimenetére csatlakozzon.

    //counter
    int counter_bazis=2,counter_out_offset=4;
    int tr=counter_bazis;
    define_dflip(tr,0); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;

    Definiálni kellene a define_links() függvényt. Mondjuk legyen ilyen.

    void define_links(int t,int l1,int l2,int default_out)
    {
    nand[t].link[0]=l1;
    nand[t].link[1]=l2;
    nand[t].out=default_out;
    }

    Ebből már sejthető, hogy az alapstruktúrának valami hasonlónak kell lennie.

    struct struct_nand
    {
    int in1,in2,out;
    int link[2];
    int xykoord[3][2];
    }nand[2000];

    Lesz egy kapunak két bemenete és egy kimenete. Hogy honnan szedje az értéket a bemenet, ezt adja meg a link[]. Ezt definiáltam az előbb.

    A kapuk szimulációja sem sokkal bonyolultabb.

    for(j=0;j<sor*sor;j++)
    if((j%sor))//elso sor nem kell
    {
    int link1=nand[j].link[0];
    int link2=nand[j].link[1];

    if(link1==link_5V) nand[j].in1=1;//fix input
    if(link2==link_5V) nand[j].in2=1;
    if(link1==link_0V) nand[j].in1=0;
    if(link2==link_0V) nand[j].in2=0;

    if(link1>=0) nand[j].in1=nand[link1].out;//out -> in1,2
    if(link2>=0) nand[j].in2=nand[link2].out;
    }
    for(j=0;j<sor*sor;j++)
    if((j%sor))//elso sor nem kell
    {
    nand[j].out=!(nand[j].in1 & nand[j].in2);//NAND muvelet
    }

    A kapu lehet az 5V-ra vagy a földre kötve, de kaphatja a bemenetét egy másik kapuról. Ha ezt beállitottuk, akkor már csak a NAND művelet elvégzése van hátra.

    Igazából ez már így működik, de senki nem kiváncsi egyesek meg nullák ugrálására a képernyőn.
    Na ez a rész már kissé bonyolultabb lesz, de nem vészes.

    [ Szerkesztve ]

  • t3rm1nat0r

    csendes tag

    Elég a mellébeszélésből, lássuk a lényeget.

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>


    #include <X11/Xlib.h>
    #include <assert.h>
    #include <unistd.h>


    Display *disp;
    Window win;
    GC gc;


    void pixel(int x,int y,int color)
    {
    XSetForeground(disp,gc,color);
    XDrawPoint(disp, win, gc, x,y);
    }
    void draw_line(int x1,int y1,int x2,int y2,int color)
    {
    XSetForeground(disp,gc,color);
    XDrawLine(disp, win, gc, x1,y1,x2,y2);
    }
    void initialize()
    {
    disp = XOpenDisplay((0));
    win = XCreateSimpleWindow(disp, DefaultRootWindow(disp), 0,0, 1200, 850, 0,0,0);

    XSelectInput(disp, win, StructureNotifyMask);
    XMapWindow(disp, win);

    gc = XCreateGC(disp, win, 0, (0));
    XSetForeground(disp,gc,0);

    while(1)
    {
    XEvent event;
    XNextEvent(disp, &event);
    if (event.type == MapNotify)break;
    }
    }






    //#define random_nand

    struct struct_nand
    {
    int in1,in2,out;
    int link[2];
    int xykoord[3][2];
    }nand[2000];



    int szinek[]={0x777700,0xffff00};
    int xykoord[10][2];

    const int link_0V =-3;
    const int link_5V =-2;
    const int link_none=-1;



    const int sor=32;
    const int ketsor=32*2;






    void draw_tranz(int x,int y,int l1,int l2,int l3,int n)
    {
    draw_line(x,y,x,y+8,0xff0000);
    draw_line(x+2,y,x+2,y+8,0xff0000);

    xykoord[n][0]=x-4;//in
    xykoord[n][1]=y+4;
    draw_line(x-4,y+4,x-1,y+4,szinek[l1]);

    draw_line(x+3,y+2,x+6,y+2,szinek[l2]);
    draw_line(x+3,y+6,x+6,y+6,szinek[l3]);

    draw_line(x+6,y-2,x+6,y+2,szinek[l2]);
    draw_line(x+6,y+9,x+6,y+6,szinek[l3]);

    if(n==1)
    {
    xykoord[4][0]=x+6;//out
    xykoord[4][1]=y+9;
    }
    if(n==0)
    {
    xykoord[5][0]=x+6;//0.tr out
    xykoord[5][1]=y+9;
    }
    }
    void draw_nand(int j)
    {
    if(nand[j].link[0]==link_none)
    if(nand[j].link[1]==link_none) return;

    int x=j%sor;
    int y=j/sor;
    x*=30;
    y*=40;

    draw_tranz(x+12,y+12,nand[j].in1,1,!nand[j].in1,0);//1 bal
    draw_tranz(x+24,y+12,nand[j].in2,1,!nand[j].in2,1);//3 db jobb lefele
    draw_tranz(x+24,y+24,nand[j].in1, !nand[j].in1,0,2);
    draw_tranz(x+24,y+36,nand[j].in2, !nand[j].in2,0,3);

    int q=0,w=2;//in
    draw_line(xykoord[q][0],xykoord[q][1],xykoord[q][0],xykoord[w][1],szinek[nand[j].in1]);
    draw_line(xykoord[w][0],xykoord[w][1],xykoord[q][0],xykoord[w][1],szinek[nand[j].in1]);

    q=1;w=3;
    draw_line(xykoord[q][0],xykoord[q][1],xykoord[q][0],xykoord[w][1],szinek[nand[j].in2]);
    draw_line(xykoord[w][0],xykoord[w][1],xykoord[q][0],xykoord[w][1],szinek[nand[j].in2]);

    w=4;
    draw_line(xykoord[w][0],xykoord[w][1],xykoord[w][0]+3,xykoord[w][1],szinek[nand[j].out]);//out
    nand[j].xykoord[0][0]=xykoord[w][0]+3;
    nand[j].xykoord[0][1]=xykoord[w][1];

    w=0;
    draw_line(xykoord[w][0],xykoord[w][1],xykoord[w][0]-3,xykoord[w][1],szinek[nand[j].in1]);//in1
    draw_line(xykoord[w][0]+12,xykoord[w][1]+15,xykoord[w][0]-3,xykoord[w][1]+15,szinek[nand[j].in2]);//int2
    nand[j].xykoord[1][0]=xykoord[w][0]-3;
    nand[j].xykoord[1][1]=xykoord[w][1];
    nand[j].xykoord[2][0]=xykoord[w][0]-3;
    nand[j].xykoord[2][1]=xykoord[w][1]+15;

    w=5;
    draw_line(xykoord[w][0],xykoord[w][1],xykoord[w][0]+12,xykoord[w][1],szinek[!nand[j].in1]);//in1
    }
    void draw_connect(int j,int link,int s,int dx,int col)
    {
    if(link<0) return;

    int x=nand[j].xykoord[s][0];
    int y=nand[j].xykoord[s][1];
    int x3=nand[link].xykoord[0][0];
    int y3=nand[link].xykoord[0][1];
    int x2,y2;

    x2=x;
    y2=y+dx;
    draw_line(x,y,x2,y2,col);x=x2;y=y2;

    x2=x3;
    y2=y;
    draw_line(x,y,x2,y2,col);x=x2;y=y2;

    x2=x3+dx;
    y2=y;
    draw_line(x,y,x2,y2,col);x=x2;y=y2;

    x2=x3+dx;
    y2=y3;
    draw_line(x,y,x2,y2,col);x=x2;y=y2;

    x2=x3;
    y2=y3;
    draw_line(x,y,x2,y2,col);x=x2;y=y2;
    }
    void draw_layer()
    {
    int j;

    for(j=0;j<1000;j+=40) draw_line(0,j+9,1000,j+9,0xffff00);//tap vezetekek
    for(j=0;j<1000;j+=40) draw_line(0,j+6,1000,j+6,0x777700);


    for(j=0;j<sor*sor;j++) draw_nand(j);

    for(j=0;j<sor*sor;j++)
    {
    int dx=2+(j%8)*2;
    draw_connect(j,nand[j].link[0],1,dx,szinek[nand[j].in1]);
    draw_connect(j,nand[j].link[1],2,dx,szinek[nand[j].in2]);
    }
    }






    void define_links(int t,int l1,int l2,int default_out)
    {
    nand[t].link[0]=l1;
    nand[t].link[1]=l2;
    nand[t].out=default_out;
    }

    void define_adder(int t,int l1,int l2,int l3)
    {
    int tr[]={
    t+0, t+1, t+2, t+3, t+4,
    t+sor+0, t+sor+1, t+sor+2, t+sor+3, 0};//5-8

    define_links(tr[0],l1,l2,0); define_links(tr[1],l1,tr[0],0);
    define_links(tr[2],tr[5],tr[1],0); define_links(tr[3],tr[2],l3,0);
    define_links(tr[4],tr[3],tr[2],0);

    define_links(tr[5],tr[0],l2,0); define_links(tr[6],tr[3],l3,0);
    define_links(tr[7],tr[4],tr[6],0); define_links(tr[8],tr[0],tr[3],0);
    }

    void define_dflip(int t,int l1)
    {
    int tr[]={
    t+0, t+1, t+2, t+3, t+4,
    t+sor+0, t+sor+1, t+sor+2, t+sor+3, t+sor+4,0};//5-9


    define_links(tr[0],link_5V,l1,0); //inverter
    define_links(tr[5],link_5V,tr[4],1);

    define_links(tr[1],l1,tr[5],0); define_links(tr[2],tr[7],tr[1],1);
    define_links(tr[6],l1,tr[4],1); define_links(tr[7],tr[2],tr[6],0);

    define_links(tr[3],tr[0],tr[2],1); define_links(tr[4],tr[9],tr[3],0);
    define_links(tr[8],tr[0],tr[7],1); define_links(tr[9],tr[4],tr[8],1);

    }
    void define_decoder(int tr3,int tr4,int mask)
    {
    int dx[]={sor,sor,sor,sor};//invertalva egy ketsorral lejjebb

    if(mask&1) dx[0]=0;
    if(mask&2) dx[1]=0;
    if(mask&4) dx[2]=0;
    if(mask&8) dx[3]=0;

    define_links(tr3 ,tr4+dx[0] ,tr4+ketsor+dx[1],0);
    define_links(tr3+1,tr3,link_5V,0); //inverter

    define_links(tr3+2,tr4+ketsor*2+dx[2],tr4+ketsor*3+dx[3],0);
    define_links(tr3+3,tr3+2,link_5V,0);//inverter

    define_links(tr3+4,tr3+1,tr3+3,0);
    define_links(tr3+5,tr3+4,link_5V,0);//inverter
    }
    void nand_gates()
    {
    int i,j,y;

    nand[0].out=1;
    nand[sor].out=1;





    for(j=0;j<sor*sor;j++)
    {
    nand[j].in1=1;
    nand[j].in2=1;

    nand[j].link[0]=link_none;
    nand[j].link[1]=link_none;
    #ifdef random_nand
    nand[j].link[0]=j + (rand()%4) + (rand()%4)*sor;
    nand[j].link[1]=j + (rand()%4) + (rand()%4)*sor;
    #endif
    }





    //counter
    int counter_bazis=2,counter_out_offset=4;
    int tr=counter_bazis;
    define_dflip(tr,0); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;
    define_dflip(tr,tr+counter_out_offset-ketsor); tr+=ketsor;


    //address decoder
    int tr3=20,tr4=counter_bazis + counter_out_offset, addr=0;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;
    define_decoder(tr3,tr4,addr++); tr3+=sor;


    //adder
    int adder_bazis=9,adder_out_offset=34;
    int tr1=adder_bazis,tr_out1=counter_bazis+counter_out_offset,tr_out_carry=link_0V;
    define_adder(tr1,link_0V,tr_out1,tr_out_carry); tr1+=ketsor; tr_out1+=ketsor;

    tr_out_carry=adder_bazis + adder_out_offset + 1;
    define_adder(tr1, link_5V, tr_out1, tr_out_carry); tr1+=ketsor; tr_out1+=ketsor; tr_out_carry+=ketsor;
    define_adder(tr1, link_0V, tr_out1, tr_out_carry); tr1+=ketsor; tr_out1+=ketsor; tr_out_carry+=ketsor;
    define_adder(tr1, link_5V, tr_out1, tr_out_carry); tr1+=ketsor; tr_out1+=ketsor; tr_out_carry+=ketsor;
    define_adder(tr1, link_0V, tr_out1, tr_out_carry); tr1+=ketsor; tr_out1+=ketsor; tr_out_carry+=ketsor;
    define_adder(tr1, link_0V, tr_out1, tr_out_carry); tr1+=ketsor; tr_out1+=ketsor; tr_out_carry+=ketsor;
    define_adder(tr1, link_0V, tr_out1, tr_out_carry); tr1+=ketsor; tr_out1+=ketsor; tr_out_carry+=ketsor;
    define_adder(tr1, link_0V, tr_out1, tr_out_carry); tr1+=ketsor; tr_out1+=ketsor; tr_out_carry+=ketsor;






    while(1)
    {
    //printf("%d.. %d %d \n",k,nand[0].out,nand[sor].out);



    draw_layer();

    for(i=0;i<100;i++)
    {
    //draw_layer();//ido kell mig minden beall, kirajzolhato a koztes allapot

    for(j=0;j<sor*sor;j++)
    if((j%sor))//elso sor nem kell
    {
    int link1=nand[j].link[0];
    int link2=nand[j].link[1];

    if(link1==link_5V) nand[j].in1=1;//fix input
    if(link2==link_5V) nand[j].in2=1;
    if(link1==link_0V) nand[j].in1=0;
    if(link2==link_0V) nand[j].in2=0;

    if(link1>=0) nand[j].in1=nand[link1].out;//out -> in1,2
    if(link2>=0) nand[j].in2=nand[link2].out;
    }
    for(j=0;j<sor*sor;j++)
    if((j%sor))//elso sor nem kell
    {
    nand[j].out=!(nand[j].in1 & nand[j].in2);//NAND muvelet
    }

    }

    int s=1,a=0;
    for(y=0;y<16;y+=2)
    {
    // for(x=0;x<6;x++) printf("%d ",nand[x+sor*y].out);

    int e=nand[counter_bazis + counter_out_offset + sor*y].out;//counter kimenet
    if(e) a+=s;
    s<<=1;

    printf("%d ",e);
    }
    printf(" = %d \n",a);

    a=0;s=1;
    for(y=0;y<16;y+=2)
    {
    int e=nand[adder_bazis + sor*y + adder_out_offset].out;//adder kimenet

    if(e) a+=s;
    s<<=1;

    printf("%d ",e);
    }
    printf(" = %d \n",a);




    nand[0].out^=1;

    printf("\n");
    // getchar();
    }
    }





    int main()
    {
    initialize();
    nand_gates();
    XFlush(disp);
    getchar();
    return 0;
    }

  • t3rm1nat0r

    csendes tag

    válasz t3rm1nat0r #23 üzenetére

    A program már tartalmazza a cím dekódolót és az összeadót is. Ezek így néznek ki.

  • t3rm1nat0r

    csendes tag

    válasz t3rm1nat0r #24 üzenetére

    Talán kezdem a decoderrel.

    Mint kivehető a képből, négy bemeneti vezetékből 16 kimenetet állít elő. Helyesebben a 16 közül mindig csak egy aktív. A 4 vezeték egy 4 bites decimális számként van értelmezve. A 16 vezeték közül mindig az aktív, amelyik sorszáma megfelel a deciális szám értékének.
    Az első lépés, hogy a 4 vezeték invertálódik. Ez azért kell mert mint mostmár tudjuk, az AND kapu akkor fog aktíválódni, ha minden bemenete 1. Itt négy bemenetű AND kapuk vannak a képen. Ezért nem kell megtanulni a táblázatot, elég csak a szabályt alkalmazni. 1000 bemenetű AND kapura is az érvényes.
    Tehát a 10 érteknél a vezetéken 1010 érték lesz. A két nullás vezetéknek az inverzét kell venni, így kapjuk az 1111 értéket, ami már aktíválni fogja az AND kaput.

    void define_decoder(int tr3,int tr4,int mask)
    {
    int dx[]={sor,sor,sor,sor};//invertalva egy ketsorral lejjebb

    if(mask&1) dx[0]=0;
    if(mask&2) dx[1]=0;
    if(mask&4) dx[2]=0;
    if(mask&8) dx[3]=0;

    define_links(tr3 ,tr4+dx[0] ,tr4+ketsor+dx[1],0);
    define_links(tr3+1,tr3,link_5V,0); //inverter

    define_links(tr3+2,tr4+ketsor*2+dx[2],tr4+ketsor*3+dx[3],0);
    define_links(tr3+3,tr3+2,link_5V,0);//inverter

    define_links(tr3+4,tr3+1,tr3+3,0);
    define_links(tr3+5,tr3+4,link_5V,0);//inverter
    }

    /A programban nincs szükség invertálásra, mert a decoder a counter-t dekódolja, ami Dflipfloppokból áll. Azokról pedig közvetlenül levehető a kimenet inverze, hiszen a visszacsatolt rész pont így működik./

    [ Szerkesztve ]

Aktív témák