RSS

Ośmiobitowa pamięć wirtualna

Liczba odsłon: 206

Mechaniz­my pa­mię­ci wir­tu­al­nej ko­ja­rzą się wy­łącz­nie z no­wo­czes­ny­mi, 32-bi­to­wy­mi mi­kro­pro­ce­so­ra­mi. W rze­czy­wi­sto­ści już nie­któ­re póź­ne kon­struk­cje 16-bi­to­we (na przy­kład Intel 80286 i Zilog Z8000) wpro­wa­dza­ły pew­ne moż­li­woś­ci wir­tu­ali­zac­ji pa­mię­ci i ofe­ro­wa­ły co naj­mniej dwa try­by pra­cy (zwyk­ły i uprzy­wi­le­jo­wa­ny). Te moż­li­woś­ci są cał­ko­wi­cie wy­star­cza­ją­ce do stwo­rze­nia sys­te­mu opera­cyj­ne­go ściś­le nad­zo­ru­ją­ce­go pra­cę apli­kac­ji (sys­te­mem ta­kim był choć­by 16-bi­to­wy Micro­soft OS/2 stwo­rzo­ny dla mi­kro­pro­ce­so­ra 80286).

Co jed­nak wie­lu oso­bom mo­że wy­dać się sza­leń­stwem, w więk­szość tych funk­cji moż­na wy­po­sa­żyć na­wet naj­prost­sze oś­mio­bi­to­we mi­kro­pro­ce­so­ry, zna­ne ze sta­rych do­mo­wych kom­pu­te­rów. Za ob­słu­gę pa­mię­ci wir­tu­al­nej od­po­wia­da bo­wiem osob­ny blok funkcjo­nal­ny zwa­ny MMU. We wszyst­kich obec­nie pro­du­ko­wa­nych pro­ce­so­rach prze­zna­czo­nych na ry­nek kon­su­menc­ki MMU sta­no­wi zin­te­gro­wa­ną część skła­do­wą, tyl­ko w star­szych kon­struk­cjach (choć­by w przy­pad­ku Z8000) po­ja­wia­jąc się w po­sta­ci od­ręb­ne­go ukła­du sca­lo­ne­go. Niezbyt skom­pli­ko­wa­ne­go ukła­du sca­lo­ne­go, na­le­ży do­dać, a za­pro­jek­to­wa­nie ta­kie­go ukła­du le­ży w za­kre­sie moż­li­woś­ci wie­lu elek­tro­ni­ków.

Poniższy ar­ty­kuł po­ka­zu­je, jak każ­dy, na­wet oś­mio­bi­to­wy mi­kro­pro­ce­sor moż­na wy­po­sa­żyć w me­cha­nizm za­pew­nia­ją­cy wir­tu­ali­zac­ję pa­mię­ci opera­cyj­nej oraz jej stro­ni­co­wa­nie tak, by nad­zo­ru­ją­cy pra­cę mi­kro­kom­pu­te­ra sys­tem opera­cyj­ny mógł do­wol­nie zmie­niać kon­fi­gu­rac­ję pa­mię­ci i roz­dzie­lać prze­strze­nie adre­so­we po­szcze­gól­nych pro­ce­sów, chro­niąc je od sie­bie na­wza­jem.

Rozważania nad moż­li­woś­cią wy­po­sa­że­nia mi­kro­pro­ce­so­ra oś­mio­bi­to­we­go w układ za­rzą­dza­nia pa­mię­cią na­le­ży roz­po­cząć od przy­pom­nie­nia so­bie za­sad dzia­ła­nia me­cha­niz­mów pa­mię­ci wir­tu­al­nej. Wirtualiza­cja pa­mię­ci po­le­ga na roz­róż­nie­niu adre­sów ko­mó­rek pa­mię­ci ge­ne­ro­wa­nych przez pro­gram od ich adre­sów prze­ka­zy­wa­nych ukła­dom sca­lo­nym RAM. Mówiąc bar­dziej obra­zo­wo, pro­gram ge­ne­ru­je ja­kiś adres (wir­tu­al­ny), mo­duł MMU za­mie­nia go w zu­peł­nie in­ny adres na pod­sta­wie prze­ka­za­nych mu przez sys­tem opera­cyj­ny in­for­mac­ji o uło­że­niu pa­mię­ci i prze­ka­zu­je ten adres (już nie wir­tu­al­ny, a fi­zycz­ny) ukła­dom pa­mię­ci. Zazwyczaj ca­ła sztu­ka od­by­wa się we wnęt­rzu mi­kro­pro­ce­so­ra i na je­go ma­gi­stra­li adre­so­wej po­ja­wia się od ra­zu adres fi­zycz­ny, nic nie stoi jed­nak na prze­szko­dzie, by po­ja­wił się na niej jesz­cze adres wir­tu­al­ny, prze­ra­bia­ny na fi­zycz­ny do­pie­ro w osob­nym ukła­dzie po­śred­ni­czą­cym w trans­mis­ji po­mię­dzy pro­ce­so­rem a pa­mię­cią.

Na po­trze­by te­go ar­ty­ku­łu przyj­mij­my, że ma­my do czy­nie­nia z do­wol­nym oś­mio­bi­to­wym mi­kro­pro­ce­so­rem po­sia­da­ją­cym 16-bi­to­wą ma­gi­stra­lę adre­so­wą (a więc zdol­nym za­adre­so­wać do 64 KiB pa­mię­ci), osob­ne li­nie R oraz W syg­na­li­zu­ją­ce chęć za­pi­su lub od­czy­tu da­nych oraz li­nię MEM/IO wy­bie­ra­ją­cą ob­szar adre­so­wy, któ­re­go do­ty­czyć ma ope­rac­ja trans­mis­ji (pa­mięć opera­cyj­na lub prze­strzeń adre­so­wa urzą­dzeń wejś­cia-wyj­ścia).

Mikroprocesor

Aby w ogó­le moż­li­we by­ło tłu­ma­cze­nie adre­sów wir­tu­al­nych na adre­sy fi­zycz­ne o in­nej war­toś­ci, na­le­ży po­dzie­lić pa­mięć opera­cyj­ną na jed­nost­ki or­ga­ni­za­cji. Firma Intel pro­jek­tu­jąc układ 80286 wy­bra­ła elas­tycz­ny me­cha­nizm seg­men­tac­ji, w któ­rym pro­gra­mis­ta okre­śla po­czą­tek w pa­mię­ci fi­zycz­nej i roz­miar w baj­tach każ­de­go uży­wa­ne­go ob­sza­ru z osob­na. Po la­tach oka­za­ło się, że teore­tycz­nie mniej elas­tycz­ny, lecz za to prost­szy me­cha­nizm stro­ni­co­wa­nia cie­szy się więk­szą po­pu­lar­noś­cią i więk­szość ukła­dów 32-bi­to­wych (na­wet włas­ny układ 80386 fir­my Intel) mo­że ko­rzy­stać ze stro­ni­co­wa­nia pod­czas gdy nie­wie­le mo­de­li ob­słu­gu­je ja­kąś for­mę seg­men­tac­ji. My ma­my za­miar za­pro­jek­to­wać coś no­wo­czes­ne­go, pro­po­nu­ję za­tem po­dzie­lić pa­mięć o po­jem­noś­ci mak­sy­mal­nej 256 KiB na stro­ny po 4 KiB każ­da (ta­ki roz­miar stron pa­mię­ci, a do­kład­niej ra­mek pa­mię­ci, jest nie­for­mal­nym stan­dar­dem w świe­cie mik­ro­elek­tro­ni­ki).

Widzisz już chy­ba pierw­szą cie­ka­wo­stkę: ma­my za­miar pod­łą­czyć do sta­re­go ukła­du ob­słu­gu­ją­ce­go 64 KiB pa­mię­ci czte­ro­krot­nie wię­cej, niż jest on w sta­nie za­adre­so­wać. Normalnie sta­no­wi­ło­by to prob­lem, jed­nak wir­tu­al­ne za­rzą­dza­nie pa­mię­cią po­zwo­li ko­rzy­stać z ca­łej po­jem­noś­ci pa­mię­ci — choć oczy­wiś­cie na­raz wi­docz­ne bę­dzie co naj­wy­żej 64 KiB. Przełą­cza­nie frag­men­tów pa­mię­ci tak, aby 8-bi­to­wy pro­ce­sor mógł ob­słu­żyć 128 KiB lub wię­cej pa­mię­ci nie jest zresz­tą no­wym po­mys­łem: ta­kie sztucz­ki sto­so­wa­no w wie­lu mik­ro­kom­pu­te­rach do­mo­wych, spo­śród któ­rych wy­star­czy wy­mie­nić Atari 130XE lub mo­de­le CPC 6128 i PCW 8512 fir­my Amstrad. Przełą­cza­nie blo­ków by­ło też chęt­nie sto­so­wa­nym spo­so­bem peł­ne­go wy­ko­rzys­ta­nia pa­mię­ci RAM o po­jem­noś­ci 64 KiB: wy­łą­cza­jąc ROM i włą­cza­jąc w za­mian blo­ki RAM moż­na by­ło uży­wać ca­łe­go ob­sza­ru RAM na­raz. Dosyć jed­nak tych dyg­res­ji.

Efektem jest na­stę­pu­ją­cy po­dział adre­sów pa­mię­ci:

Widać wy­raź­nie, że sko­ro pro­ce­sor mo­że wy­ge­ne­ro­wać w ra­mach adre­su wir­tu­al­ne­go je­dy­nie 16 moż­li­wych nu­me­rów stron pa­mię­ci od 0 do 15, układ MMU mu­si za­wie­rać 16 re­jest­rów o po­jem­noś­ci 6 bi­tów każ­dy, by za­mie­niać adre­sy stron pa­mię­ci wir­tu­al­nej na adre­sy ra­mek pa­mię­ci fi­zycz­nej. Wyposa­ży­my go jesz­cze w mo­duł inter­fej­su umoż­li­wia­ją­cy wy­ko­na­nie na roz­kaz mi­kro­pro­ce­so­ra jed­nej tyl­ko ope­rac­ji: pod­sta­wie­nia pod stro­ni­cę pa­mię­ci wir­tu­al­nej o nu­me­rze x (0…15) ram­ki pa­mię­ci fi­zycz­nej o nu­me­rze y (0…63). Całość bę­dzie wy­glą­da­ła na­stę­pu­ją­co:

System pamięci wirtualnej

Podłączamy te­raz do MMU 128 KiB pa­mię­ci RAM i ty­leż pa­mię­ci ROM (gdzieś w koń­cu mu­si zna­leźć się sys­tem opera­cyj­ny lub przy­naj­mniej pro­gram ła­du­ją­cy go z pa­mię­ci ma­so­wej) i sta­je­my przed za­da­niem okre­śle­nia, pod ja­ki­mi adre­sa­mi wir­tu­al­ny­mi po­win­ny się znaj­do­wać ja­kie ele­men­ty sys­te­mu opera­cyj­ne­go i apli­kac­ji. Mógłby to być na­stę­pu­ją­cy po­dział:

Podział pamięci fizycznej na ramki
Podział 256 KiB pa­mię­ci fi­zycz­nej (ROM oraz RAM) na ram­ki po 4 KiB każ­da oraz adre­sa­cja fi­zycz­na blo­ków pa­mię­ci
Podział przestrzeni adresowej
Podział 64 KiB wir­tu­al­nej prze­strze­ni adre­so­wej mi­kro­pro­ce­so­ra na stro­ny po 4 KiB każ­da

Zerową stro­nę pa­mię­ci w wir­tu­al­nej prze­strze­ni adre­so­wej każ­de­go pro­ce­su za­re­zer­wo­wa­łem na po­trze­by sys­te­mu opera­cyj­ne­go, gdyż więk­szość mi­kro­pro­ce­so­rów trzy­ma w pierw­szych kil­ku­dzie­się­ciu baj­tach pa­mię­ci istot­ne dla sys­te­mu opera­cyj­ne­go in­for­mac­je, na przy­kład wek­to­ry ob­słu­gi przer­wań lub da­ne kon­fi­gu­ra­cyj­ne. Dwanaście ko­lej­nych stron prze­zna­czo­nych jest do do­wol­ne­go wy­ko­rzys­ta­nia przez apli­kac­je, zaś trzy ostat­nie prze­ka­za­łem sys­te­mo­wi ope­ra­cyj­ne­mu na je­go kod.

Trzy stro­ny (ra­zem 12 KiB) mo­gą wy­da­wać się o wie­le zbyt ma­łym ob­sza­rem bio­rąc pod uwa­gę, że nasz pro­jekt mi­kro­kom­pu­te­ra za­wie­ra 128 KiB (a więc 32 ram­ki) pa­mię­ci ROM prze­cho­wu­ją­cej wszyst­kie pro­ce­du­ry sys­te­mo­we. Niech jed­nak tyl­ko pierw­sza z trzech za­re­zer­wo­wa­nych stron za­wie­ra non-stop tę sa­mą ram­kę ROM, a po­zo­sta­łe są prze­łą­cza­ne w za­leż­noś­ci od po­trzeb. Jest po­trzeb­na pro­ce­du­ra znaj­du­ją­ca się w ram­ce pią­tej ROM? Niech stro­na E pa­mię­ci wir­tu­al­nej wska­zu­je na ram­kę 5 ROM, a pod przy­na­leż­ny­mi tej stro­nie adre­sa­mi ko­mó­rek pa­mię­ci $E000…$F000 po­ja­wi się kod z wy­cin­ka ROM. Kolejne od­wo­ła­nie do ;sys­te­mu opera­cyj­ne­go wy­ma­ga ob­szer­niej­szej funk­cji za­pi­sa­nej w ram­kach 7 i 8 ROM? Dwie ope­rac­je za­pi­su do re­jest­rów MMU wy­star­czą, by w stro­ni­cach E i F (adre­sy od $E000 do $FFFF) po­ja­wi­ły się w mgnie­niu oka da­ne z po­trzeb­nych ra­mek ROM! Wszystko oczy­wiś­cie bez żad­ne­go ko­pio­wa­nia: po pros­tu MMU w mo­men­cie od­wo­ła­nia się do okreś­lo­nych ko­mó­rek pa­mię­ci wir­tu­al­nej prze­kie­ro­wu­je ope­ra­cję w okreś­lo­ny w je­go re­jest­rach ob­szar pa­mię­ci fi­zycz­nej.

Zmiana adresacji wirtualnej
Przeprogra­mo­wa­nie ukła­du MMU po­zwa­la mi­kro­pro­ce­so­ro­wi pod ty­mi sa­my­mi adre­sa­mi wir­tu­al­ny­mi „wi­dzieć” róż­ne ram­ki pa­mię­ci i wy­ko­rzys­ty­wać je za­mien­nie. Dzięki te­mu w ra­mach tyl­ko 12 KiB prze­strze­ni adre­so­wej moż­na wy­wo­ły­wać pod­pro­gra­my za­pi­sa­ne w 128 KiB pa­mię­ci ROM.

Ten sam me­cha­nizm pod­sta­wia­nia moż­na wy­ko­rzys­tać w ce­lu uru­cha­mia­nia jed­no­cześ­nie kil­ku pro­gra­mów i prze­łą­cza­nia się mię­dzy ni­mi. Załóżmy, że sys­tem opera­cyj­ny za­pi­sa­ny w pa­mię­ci ROM umoż­li­wia uru­cho­mie­nie kil­ku pro­gra­mów (z któ­rych ak­tyw­ny jest tyl­ko je­den) i prze­łą­cza­nie się po­mię­dzy ni­mi za po­mo­cą od­po­wied­niej kom­bi­nac­ji kla­wi­szy. Naciśnię­cie tej kom­bi­nac­ji po­wo­du­je skok do ko­du sys­te­mu opera­cyj­ne­go w ra­mach ob­słu­gi przer­wa­nia kla­wia­tu­ry, roz­poz­na­nie na­ciś­nię­tej kom­bi­nac­ji i włą­cze­nie w ob­szar adre­so­wy stro­nic E i F funk­cji ob­słu­gi prze­łą­cza­nia ak­tyw­nej apli­kac­ji. Funkcja ta za­pa­mię­tu­je gdzieś w pa­mię­ci opera­cyj­nej in­for­mac­ję o tym, któ­rej stro­nie pa­mię­ci wir­tu­al­nej z za­kre­su od 1 do C (przy­na­leż­ne­go apli­kac­jom) od­po­wia­da któ­ra ram­ka pa­mię­ci fi­zycz­nej (aby móc przy­wró­cić po­tem stan apli­kac­ji), po czym za­pi­su­je w re­jest­rach MMU od­czy­ta­ne z pa­mię­ci ta­kie sa­me in­for­mac­je o pro­ce­sie, do któ­re­go ma na­stą­pić prze­łą­cze­nie dzia­ła­nia. Następ­nie wy­star­czy tyl­ko przy­wró­cić za­war­toś­ci re­jest­rów i wskaź­ni­ka sto­su (rów­nież za­pi­sa­ne w pa­mię­ci obok ta­be­li trans­lac­ji stron na ram­ki) i wró­cić z po­zio­mu sys­te­mu opera­cyj­ne­go do apli­kac­ji. Przed przer­wa­niem wy­ko­ny­wa­ła się apli­kac­ja A wy­ko­rzys­tu­jąc w do­wol­nym za­kre­sie przy­słu­gu­ją­cych jej 12 stron pa­mię­ci wir­tu­al­nej, po po­wro­cie z przer­wa­nia dzia­ła już apli­kac­ja B ob­słu­gu­ją­ca pa­mięć w do­wol­ny in­ny spo­sób i – co naj­cie­kaw­sze – nie ma­ją­ca fi­zycz­ne­go do­stę­pu do pa­mię­ci apli­kac­ji A. Ramki tej pa­mię­ci nie są bo­wiem włą­czo­ne w ob­szar adre­so­wy pro­ce­so­ra i do­pó­ki nie zmie­ni się za­pi­sów w re­jest­rach MMU, nie ma spo­so­bu, by się do nich do­stać i od­czy­tać lub zmo­dy­fi­ko­wać ob­ce da­ne.

Można pójść jesz­cze da­lej i za­cząć oszczę­dzać pa­mięć za po­mo­cą kre­a­tyw­ne­go wy­ko­rzys­ta­nia stro­ni­co­wa­nia. Wyobraź so­bie pro­gram wy­ma­ga­ją­cy 5 ra­mek pa­mię­ci na swój kod (20 KiB), 1 ram­ki na stos (4 KiB) i 6 ra­mek na da­ne (24 KiB). Razem zaj­mu­je on 12 ra­mek (48 KiB), czy­li wszyst­ko, co ma do dys­po­zyc­ji. Jeżeli te­raz uru­cho­misz dwie ko­pie te­go sa­me­go pro­gra­mu, zaj­mą one w su­mie 24 ram­ki, a więc 96 KiB pa­mię­ci — pra­wie ca­łą pa­mięć RAM kom­pu­te­ra.

Tak jed­nak być nie mu­si: prze­cież w obu przy­pad­kach kod pro­gra­mu jest ten sam, zmie­nia­ją się je­dy­nie da­ne. Sprytny sys­tem opera­cyj­ny mo­że wy­ko­rzys­tać ten fakt, by kod pro­gra­mu za­pi­sać w pa­mię­ci tyl­ko jed­no­krot­nie w 5 ram­kach, a na­stęp­nie te sa­me 5 ra­mek włą­czać w wir­tu­al­ną prze­strzeń adre­so­wą każ­dej kopii te­go pro­gra­mu. W efek­cie raz uru­cho­mio­ny pro­gram zaj­mie tak sa­mo jak po­przed­nio 48 KiB pa­mię­ci, jed­nak dru­ga (i każ­da ko­lej­na) ko­pia bę­dzie wy­ma­ga­ła je­dy­nie do­dat­ko­wych 28 KiB pa­mię­ci na stos i da­ne. W 128 KiB pa­mię­ci zmiesz­czą się w ta­kim przy­pad­ku trzy ko­pie pro­gra­mu i jesz­cze zo­sta­nie nie­co wol­nej pa­mię­ci.

Takie pros­te włą­cza­nie stron pa­mię­ci jed­ne­go pro­ce­su w wir­tu­al­ną prze­strzeń adre­so­wą in­ne­go pro­ce­su mo­że dać jesz­cze jed­ną ko­rzyść: współ­dzie­le­nie da­nych. Jeżeli je­den pro­ces za­pi­sze coś w ram­ce pa­mię­ci do­stęp­nej dla in­nych pro­ce­sów, po prze­łą­cze­niu ak­tyw­ne­go pro­ce­su przez użyt­kow­ni­ka dru­ga ko­pia pro­gra­mu (lub in­ny pro­gram) mo­że od­czy­tać za­pi­sa­ne tam wcześ­niej da­ne i wy­ko­rzys­tać je lub zmo­dy­fi­ko­wać. W ten spo­sób two­rzy się me­cha­niz­my wy­mia­ny da­nych ta­kie jak po­to­ki (ang. streams, pipes) lub współ­dzie­lo­ne blo­ki pa­mię­ci (ang. shared me­mo­ry area).

Coraz więk­szym prob­le­mem sta­je się jed­nak za­bez­pie­cze­nie pa­mię­ci przed nie­au­to­ry­zo­wa­nym do­stę­pem lub mo­dy­fi­ka­cją. Co praw­da w na­szym przy­kła­dzie apli­kac­je nie mo­gą usz­ko­dzić ko­du sys­te­mu opera­cyj­ne­go (jest on prze­cho­wy­wa­ny w pa­mię­ci ROM), mo­gą jed­nak znisz­czyć swój włas­ny kod (co w przy­pad­ku współ­dzie­le­nia go mię­dzy wie­lo­ma ko­pia­mi apli­kac­ji spo­wo­du­je awa­rię wszyst­kich z nich) lub da­ne prze­ka­za­ne im wy­łącz­nie do od­czy­tu. Wrażliwa na ata­ki bę­dzie też stro­na pa­mię­ci o nu­me­rze 0, któ­rą za­re­zer­wo­wa­łem na po­trze­by sys­te­mu opera­cyj­ne­go. Trzeba coś z tym zro­bić.

Zacznijmy od moż­li­woś­ci wska­za­nia, czy stro­na pa­mię­ci ma być do­stęp­na do od­czy­tu i do za­pi­su. W tym ce­lu do każ­de­go z szes­na­stu re­jest­rów MMU opi­su­ją­cych szes­naś­cie do­stęp­nych w prze­strze­ni adre­so­wej stron do­daj­my dwa bi­ty. Jeden z nich bę­dzie okreś­lał, czy stro­nę moż­na czy­tać, a dru­gi — czy moż­na za­pi­sy­wać. W sta­nie 00 stro­na bę­dzie cał­ko­wi­cie nie­dos­tęp­na (co po­zwo­li przy­zna­wać apli­kac­jom tyl­ko ty­le stron, ile na­praw­dę po­trze­bu­jąc, resz­tę za­zna­cza­jąc ja­ko nie­dos­tęp­ne), w sta­nie 01 bę­dzie ją moż­na czy­tać, w sta­nie 10 tyl­ko za­pi­sy­wać (ma­ło to przy­dat­ne), a w sta­nie 11 bę­dzie ona w peł­ni do­stęp­na do za­pi­su i do od­czy­tu.

Dzięki te­mu stro­ny pa­mię­ci za­wie­ra­ją­ce kod pro­gra­mu bę­dą ozna­cza­ne try­bem do­stę­pu 11 tyl­ko w cza­sie ła­do­wa­nia pro­gra­mu do pa­mię­ci. Zaraz po ukoń­cze­niu te­go za­da­nia bę­dzie moż­na ozna­czyć je ja­ko 01, przez co pro­ce­sor bę­dzie mógł je od­czy­ty­wać, jed­nak pró­by za­pi­su bę­dą igno­ro­wa­ne lub zgła­sza­ne przer­wa­niem. Próba od­czy­ta­nia stro­ny w ogó­le nie­dos­tęp­nej spo­wo­du­je zwró­ce­nie ja­kiejś bez­piecz­nej war­toś­ci (naj­le­piej 00h lub ko­du in­struk­cji ty­pu NOP, nic nie ro­bią­cej) i rów­nież zgło­sze­nie pro­ce­so­ro­wi przer­wa­nia. Od te­go mo­men­tu pro­gra­my nie mo­gą usz­ko­dzić włas­ne­go ko­du i mo­gą chro­nić udos­tęp­nia­ne in­nym pro­ce­som da­ne przed mo­dy­fi­ka­cją (o ile so­bie te­go ży­czą, oczy­wiś­cie), jed­nak na­dal stro­ny pa­mię­ci na­le­żą­ce do sys­te­mu opera­cyj­ne­go do­stęp­ne są do za­pi­su dla wszyst­kich.

Kolejnym kro­kiem po­win­no być za­tem wpro­wa­dze­nie dru­gie­go try­bu pra­cy o wyż­szych upraw­nie­niach, tak zwa­ne­go try­bu uprzy­wi­le­jo­wa­ne­go (na­zy­wa­ne­go też try­bem jąd­ra). Do re­jest­rów MMU do­da­je­my jesz­cze dwa bi­ty peł­nią­ce iden­tycz­ną ro­lę jak te do­da­ne przed chwi­lą, jed­nak w od­nie­sie­niu do try­bu uprzy­wi­le­jo­wa­ne­go; po­przed­nie dwa bę­dą się ty­czy­ły zwyk­łe­go try­bu pra­cy (na­zy­wa­ne­go try­bem apli­kac­ji lub try­bem użyt­kow­ni­ka). Teraz sys­tem opera­cyj­ny mo­że usta­wić dla try­bu użyt­kow­ni­ku pra­wo do­stę­pu do stro­ny sys­te­mo­wej o war­toś­ci 01, co unie­moż­li­wi apli­kac­ji zmo­dy­fi­ko­wa­nie da­nych, po­zo­sta­wia­jąc dla try­bu jąd­ra pra­wo do­stę­pu o war­toś­ci 11, do­pusz­cza­ją­ce wpro­wa­dza­nie zmian. Jak jed­nak roz­róż­nić te try­by?

Nowo­czes­ne mi­kro­pro­ce­so­ry po­sia­da­ją wbu­do­wa­ne ukła­dy roz­dzie­la­ją­ce oba try­by i na­wet po­tra­fią blo­ko­wać wy­ko­ny­wa­nie z po­zio­mu try­bu użyt­kow­ni­ka roz­ka­zów po­ten­cjal­nie de­sta­bi­li­zu­ją­cych sys­tem opera­cyj­ny (na przy­kład zmie­nia­ją­cych kon­fi­gu­rac­ję pa­mię­ci). Mikropro­ce­so­ry 8-bi­to­we nie ob­słu­gu­ją oczy­wiś­cie ta­kie­go po­dzia­łu, mu­si­my go za­tem nie­co nie­udol­nie uda­wać. Niech w MMU znaj­dzie się jesz­cze je­den re­jestr, jed­no­bi­to­wy tym ra­zem, usta­wia­ny w stan zero w cza­sie, gdy pro­ce­sor ma wy­ko­ny­wać kod sys­te­mu opera­cyj­ne­go i w stan je­den w cza­sie, gdy wy­ko­ny­wa­ny jest kod apli­kac­ji. W za­leż­noś­ci od sta­nu te­go bi­tu MMU ana­li­zu­je pierw­szą lub dru­gą pa­rę bi­tów okre­śla­ją­cych pra­wa do­stę­pu do stro­ny pa­mię­ci: je­że­li pro­ce­sor znaj­du­je się w try­bie uprzy­wi­le­jo­wa­nym, mo­że otrzy­mać szer­sze pra­wa do­stę­pu do stro­ny pa­mię­ci niż w try­bie apli­kac­ji.

Realizacja praw dostępu do pamięci
MMU znaj­du­je się w try­bie uprzy­wi­le­jo­wa­nym (USER=0), dla­te­go pra­wa do­stę­pu do stro­ny pa­mię­ci po­bie­ra­ne są z bi­tów SW (ang. super­visor write) i SR (ang. super­visor read) des­kryp­to­ra stro­ny. Zapis jest do­zwo­lo­ny (SW=1), za­tem ope­rac­ja jest prze­pro­wa­dza­na.
Realizacja praw dostępu do pamięci
MMU znaj­du­je się w try­bie zwyk­łym (USER=1), dla­te­go pra­wa do­stę­pu do stro­ny pa­mię­ci po­bie­ra­ne są z bi­tów UW (ang. user write) i UR (ang. user read). Zapis jest za­bro­nio­ny (UW=0), za­tem ope­rac­ja jest anu­lo­wa­na a stan linii W zmie­nia­ny.

Nic jed­nak nie prze­łą­czy auto­ma­tycz­nie try­bu pra­cy pro­ce­so­ra z jed­ne­go w dru­gi i dla­te­go sam sys­tem mu­si po roz­po­czę­ciu dzia­ła­nia przez jed­ną z je­go funk­cji prze­łą­czać MMU w tryb nad­zo­ro­wa­ny i vice ver­sa, przy po­wro­cie do ko­du apli­kac­ji po­now­nie wpi­sy­wać je­dyn­kę w re­jestr try­bu ogra­ni­cza­jąc upraw­nie­nia do­stę­pu do pa­mię­ci. Będzie to dzia­łać cał­kiem spraw­nie z jed­nym wy­jąt­kiem: po­nie­waż ope­rac­ja prze­łą­cze­nia try­bu pra­cy mu­si być wy­ko­na­na przez sys­tem oczy­wiś­cie jesz­cze w mo­men­cie pra­cy w try­bie użyt­kow­ni­ka, po­ten­cjal­nie apli­kac­ja mo­że zro­bić to sa­mo na­da­jąc so­bie bar­dzo wy­so­kie upraw­nie­nia. Taką ce­nę pła­ci­my za roz­sze­rza­nie moż­li­woś­ci pros­te­go mi­kro­pro­ce­so­ra.

Teraz dy­spo­nu­je­my już pro­jek­tem na­praw­dę dzia­ła­ją­ce­go oś­mio­bi­to­we­go sys­te­mu mik­ro­pro­ce­so­ro­we­go z me­cha­niz­mem pa­mię­ci wir­tu­al­nej rea­li­zo­wa­nym przez stro­ni­co­wa­nie. Krótko mó­wiąc — ZX Spec­trum z moż­li­woś­cia­mi Pentium! Możemy za­tem za­sza­leć jesz­cze bar­dziej: do­daj­my jesz­cze je­den bit w każ­dym z re­jest­rów opi­su­ją­cych stro­ny pa­mię­ci (te re­jest­ry na­zy­wa­ją się fa­cho­wo des­kryp­to­ra­mi stron, a blok 16 re­jest­rów opi­su­ją­cych ca­łą prze­strzeń adre­so­wą to tab­li­ca de­skryp­to­rów stron). Ten je­den bit o naz­wie PRESENT bę­dzie usta­wio­ny na war­tość je­den je­że­li stro­na bę­dzie obec­na w pa­mię­ci lub nie­dos­tęp­na, zaś na war­tość zero je­że­li stro­na zo­sta­nie usu­nię­ta do pli­ku wy­mia­ny. W mo­men­cie prze­łą­cza­nia ak­tyw­ne­go za­da­nia sys­tem opera­cyj­ny mo­że zwol­nić nie­co pa­mię­ci RAM prze­no­sząc dez­akty­wo­wa­ne stro­ny pa­mię­ci do pli­ku wy­mia­ny i ka­su­jąc bit PRESENT w ich des­kryp­to­rach; z kolei des­kryp­to­ry stron pa­mię­ci ak­ty­wo­wa­ne­go za­da­nia mu­szą zo­stać prze­gląd­nię­te i je­że­li któ­raś z nich bę­dzie nie­obec­na, mu­si zo­stać wczy­ta­na do pa­mię­ci za­nim zacz­nie być wy­ko­ny­wa­ny kod ak­ty­wo­wa­ne­go pro­gra­mu. W efek­cie mi­mo po­sia­da­nia w pro­jek­to­wa­nym przez nas kom­pu­te­rze je­dy­nie 128 KiB pa­mię­ci RAM, moż­na uży­wać wie­lu pro­gra­mów zaj­mu­ją­cych łącz­nie set­ki ki­lo­baj­tów pa­mię­ci na da­ne — nie­ak­tyw­ne za­da­nia bę­dą prze­no­szo­ne na dys­kiet­kę, a zwol­nio­na przez to pa­mięć sta­nie się do­stęp­na dla włą­cza­ne­go pro­gra­mu w cią­gu kil­ku se­kund po­trzeb­nych na do­ko­na­nie za­pi­su i od­czy­tu stron pa­mię­ci.

Nie mo­że­my nie­ste­ty w peł­ni zau­to­ma­ty­zo­wać pro­ce­su wy­mia­ny stron mię­dzy pa­mię­cią fi­zycz­ną i pli­kiem wy­mia­ny tak, jak jest to zro­bio­ne w no­wo­czes­nych mik­ro­pro­ce­so­rach 32-bi­to­wych. Spowodo­wa­ne jest to fak­tem, że gdy­by na­wet MMU mog­ło za­rea­go­wać przer­wa­niem na pró­bę od­wo­ła­nia się do ko­mór­ki pa­mię­ci le­żą­cej w stro­nie nie­obec­nej w pa­mię­ci fi­zycz­nej, oś­mio­bi­to­wy pro­ce­sor ob­słu­żył­by je do­pie­ro po za­koń­cze­niu wy­ko­ny­wa­nia roz­ka­zu od­wo­łu­ją­ce­go się do tej ko­mór­ki! Tymcza­sem moż­li­wość za­ła­do­wa­nia stro­ny do pa­mię­ci fi­zycz­nej w ra­mach przer­wa­nia i kon­ty­nuo­wa­nia reali­zac­ji przer­wa­ne­go bra­kiem da­nych roz­ka­zu wy­ma­ga, by prze­rwa­nie wstrzy­my­wa­ło wy­ko­na­nie roz­ka­zu, a po je­go ob­słu­że­niu pro­ce­sor był w sta­nie po­wtór­nie wy­ko­nać ten sam roz­kaz, tym ra­zem dy­spo­nu­jąc już po­trzeb­ny­mi stro­na­mi pa­mię­ci. Proceso­ry od po­cząt­ku pro­jek­to­wa­ne z myś­lą o me­cha­niz­mie pa­mię­ci wir­tu­al­nej ma­ją ta­ką moż­li­wość, pod­czas gdy sta­re mi­kro­pro­ce­so­ry oś­mio­bi­to­we ob­słu­gu­ją przer­wa­nia tyl­ko w przer­wie mię­dzy cyk­la­mi roz­ka­zo­wy­mi i nie do­pusz­cza­ją moż­li­woś­ci, że po­trzeb­ne­go blo­ku pa­mię­ci „chwi­lo­wo nie ma”.

Z te­go sa­me­go po­wo­du nie­moż­li­we jest zre­a­li­zo­wa­nie ob­słu­gi me­cha­niz­mu page on de­mand, po­le­ga­ją­ce­go na wczy­ty­wa­niu za­war­toś­ci stron pa­mię­ci z dys­ku do­pie­ro w mo­men­cie pierw­sze­go od­wo­ła­nia się pro­ce­so­ra do ko­mó­rek da­nej stro­ny. Gdyby za­tem na­wet nasz mi­nia­tu­ro­wy sys­tem opera­cyj­ny zo­stał wy­po­sa­żo­ny w ob­słu­gę pli­ków od­wzo­ro­wy­wa­nych w pa­mię­ci, pli­ki te mu­sia­ły­by być ła­do­wa­ne w ca­ło­ści już w mo­men­cie de­fi­nio­wa­nia od­wzo­ro­wa­nia, co z dzi­siej­sze­go punk­tu wi­dze­nia jest mar­no­wa­niem cza­su i pa­mię­ci (po­za przy­pad­ka­mi, gdy pro­gram od­wo­łu­je się do ca­łej za­war­toś­ci uży­wa­ne­go pli­ku). Odwzorowy­wa­nie pli­ków w pa­mię­ci wy­ma­ga­ło­by też roz­sze­rze­nia funkcjo­nal­noś­ci MMU: do­dat­ko­wy bit de­skryp­to­rów stron pa­mię­ci (na przy­kład o naz­wie MODIFIED lub DIRTY) prze­cho­wy­wał­by in­for­mac­ję, czy da­na stro­na pa­mię­ci zo­sta­ła zmo­dy­fi­ko­wa­na i w związ­ku z tym czy jej za­war­tość po­win­na być za­pi­sa­na na dys­ku. Układ MMU sam by usta­wiał ten bit na war­tość je­den w mo­men­cie prze­chwy­ce­nia na ma­gi­stra­li ope­rac­ji za­pi­su da­nych w kon­kret­nej stro­ni­cy. Warto do­dać, że ta­ki me­cha­nizm uspraw­nił­by też wy­mia­nę stron z pli­kiem wy­mia­ny na dys­ku, gdyż sys­tem mógł­by wy­kry­wać stro­ny da­nych któ­re nie uleg­ły mo­dy­fi­ka­cji od mo­men­tu akty­wo­wa­nia pro­gra­mu i w mo­men­cie prze­łą­cza­nia za­dań nie pow­ta­rzać ope­rac­ji za­pi­sy­wa­nia ich w pli­ku wy­mia­ny.

Aktualizacja bitu DIRTY
W mo­men­cie prze­chwy­ce­nia do­zwo­lo­nej ope­rac­ji za­pi­su do pa­mię­ci układ MMU auto­ma­tycz­nie zmie­nia stan bi­tu DIRTY des­kryp­to­ra stro­ny, któ­rej ope­rac­ja do­ty­czy­ła, na 1.

A te­raz za­sza­lej­my już cał­kiem i do­daj­my do na­sze­go pro­jek­tu… dru­gi mi­kro­pro­ce­sor! Układ MMU mu­si zo­stać wy­po­sa­żo­ny w dwie od­ręb­ne ma­gi­stra­le lo­kal­ne (czy­li po­łą­czo­ne ma­gi­stra­le adre­so­wa, da­nych i ste­ru­ją­ca), po jed­nej dla każ­de­go pro­ce­so­ra, oraz w me­cha­nizm ar­bi­tra­żu wstrzy­mu­ją­cy je­den pro­ce­sor w mo­men­cie, gdy dru­gi wy­ko­nu­je ope­rac­je na pa­mię­ci lub ko­mu­ni­ku­je się z urzą­dze­nia­mi. Przydać się mo­że rów­nież re­jestr za­wie­ra­ją­cy nu­mer pro­ce­so­ra chwi­lo­wo mo­no­po­li­zu­ją­ce­go ca­ły kom­pu­ter: dzię­ki nie­mu sys­tem opera­cyj­ny mo­że od­ciąć wszyst­kie pro­ce­so­ry po­za jed­nym od do­stę­pu do pa­mię­ci (bę­dą wstrzy­my­wa­ne do mo­men­tu zwol­nie­nia ma­gi­stra­li), wy­ko­nać sek­wen­cję ope­rac­ji o cha­rak­te­rze ato­mo­wym (nie­po­dziel­nym) i zwol­nić blo­ka­dę, uni­ka­jąc w ten spo­sób nie­po­żą­da­ne­go współ­za­wod­nic­twa z in­ny­mi pro­ce­so­ra­mi.

W ta­kim ukła­dzie wie­lo­pro­ce­so­ro­wym wy­ko­rzys­ta­nie pa­mię­ci sta­je się jesz­cze bar­dziej eko­no­micz­ne. Układ }MMU mu­si co praw­da dys­po­no­wać po jed­nym ze­sta­wie de­skryp­to­rów stron na każ­dy pod­łą­czo­ny pro­ce­sor, jed­nak oba mi­kro­pro­ce­so­ry mo­gą pra­co­wać rów­no­leg­le i wy­ko­ny­wać cał­ko­wi­cie nie­za­leż­nie ten sam lub zu­peł­nie in­ne pro­gra­my. Oba mo­gą też w swo­ich prze­strze­niach adre­so­wych uży­wać róż­nych lub tych sa­mych stron pa­mię­ci. Nawet sys­tem opera­cyj­ny mo­że na obu znaj­do­wać się w róż­nym sta­nie kon­fi­gu­rac­ji pa­mię­ci, na przy­kład na pro­ce­so­rze pierw­szym w dwóch ostat­nich stro­nach pa­mię­ci o adre­sach E i F mo­gą znaj­do­wać się ram­ki 3 i 5 pa­mię­ci ROM, zaś dru­gi pro­ce­sor w tych sa­mych stro­nach pa­mię­ci wir­tu­al­nej mo­że „wi­dzieć” ram­ki 7 i 10 pa­mię­ci ROM, wy­ko­nu­jąc zu­peł­nie in­ną funk­cję sys­te­mu opera­cyj­ne­go.

System wieloprocesorowy
System wie­lo­pro­ce­so­ro­wy. Każdy z mi­kro­pro­ce­so­rów mo­że być chwi­lo­wo wstrzy­ma­ny syg­na­łem WAIT (na przy­kład w mo­men­cie, gdy dru­gi do­ko­nu­je ope­rac­ji na pa­mię­ci). Rejestr EXCLUSIVE mo­że za­wie­rać nu­mer pro­ce­so­ra chwi­lo­wo rosz­czą­ce­go so­bie pra­wo do wy­łącz­ne­go do­stę­pu do pa­mię­ci i urzą­dzeń. Co naj­waż­niej­sze, każ­dy pro­ce­sor dys­po­nu­je włas­ną ta­be­lą de­skryp­to­rów stron i mo­że od­wo­ły­wać się do tych sa­mych lub róż­nych ra­mek z róż­ny­mi upraw­nie­nia­mi, re­a­li­zu­jąc róż­ne pro­ce­sy. Dla każ­de­go pro­ce­so­ra mu­si też być utrzy­my­wa­ny osob­ny re­jestr sta­nu, bo­wiem je­den pro­ce­sor mo­że znaj­do­wać się w try­bie uprzy­wi­le­jo­wa­nym, a dru­gi w try­bie użyt­kow­ni­ka.

Osobną kwe­stią, sła­bo już zwią­za­ną z te­ma­ty­ką te­go ar­ty­ku­łu, jest ob­słu­ga przer­wań w sys­te­mie wie­lo­pro­ce­so­ro­wym. Jeżeli za­ło­ży­my, że jest to sys­tem sy­met­rycz­nie wie­lo­pro­ce­so­ro­wy (SMP), czy­li ta­ki, w któ­rym wszyst­kie mi­kro­pro­ce­so­ry są rów­no­waż­ne i nie ma­ją z gó­ry przy­dzie­lo­ne­go za­kre­su wy­ko­ny­wa­nych za­dań, każ­dy z ukła­dów mo­że ode­brać prze­rwa­nie, jed­nak tyl­ko je­den z nich po­wi­nien je ob­słu­żyć. Wymaga to do­da­nia oprócz ukła­du MMU rów­nież pro­gra­mo­wal­ne­go kon­tro­le­ra przer­wań (PIC), któ­ry we współ­pra­cy z sys­te­mem opera­cyj­nym bę­dzie roz­dzie­lał przer­wa­nia po­cho­dzą­ce od róż­nych urzą­dzeń mię­dzy pro­ce­so­ry sta­ra­jąc się rów­no­mier­nie ob­cią­żać je tym obo­wiąz­kiem.

Pod­su­mo­wa­nie

Tak oto do­tar­liś­my do koń­ca pro­ce­su pro­jek­to­wa­nia, uzys­ku­jąc mi­kro­kom­pu­ter z kil­ko­ma oś­mio­bi­to­wy­mi pro­ce­so­ra­mi ko­rzy­sta­ją­cy­mi ze wspól­nej pa­mię­ci opera­cyj­nej (sta­łej i o do­stę­pie swo­bod­nym) za po­śred­nic­twem me­cha­niz­mu pa­mię­ci wir­tu­al­nej ob­słu­gu­ją­ce­go stro­ni­co­wa­nie, wy­mia­ta­nie stron do pli­ku wy­mia­ny, wy­kry­wa­nie mo­dy­fi­ka­cji da­nych w pa­mię­ci i ogra­ni­cza­nie praw do­stę­pu do pa­mię­ci w za­leż­noś­ci od try­bu pra­cy pro­ce­so­ra (zwyk­łe­go lub uprzy­wi­le­jo­wa­ne­go).

Możesz się spy­tać: po co to wszyst­ko? Nikt o zdro­wych zmys­łach nie bę­dzie prze­cież dzi­siaj bu­do­wał kom­pu­te­ra z tak pry­mi­tyw­ny­mi pro­ce­so­ra­mi, na­wet, je­że­li mia­ły­by to być mik­ro­kon­tro­le­ry ste­ru­ją­ce pro­ce­sem pro­duk­cyj­nym. Celem te­go ar­ty­ku­łu nie jest jed­nak za­pre­zen­to­wa­nie go­to­we­go roz­wią­za­nia sprzę­to­we­go na ba­zie ar­chaicz­ne­go sprzę­tu, a po­ka­za­nie ideiza­sad dzia­ła­nia me­cha­niz­mu pa­mię­ci wir­tu­al­nej na kon­kret­nym przy­kła­dzie. Pokazanie, że tak na­praw­dę me­cha­nizm ten opar­ty jest o kil­ka bar­dzo pros­tych ukła­dów sprzę­to­wych po­słu­gu­ją­cych się głów­nie bo­ga­tym ze­sta­wem re­jest­rów pro­gra­mo­wa­nych bez­po­śred­nio przez sys­tem opera­cyj­ny, od któ­re­go spry­tu za­le­ży, czy wy­ko­rzy­sta funk­cje ofe­ro­wa­ne przez ten pros­ty sprzęt i czy zro­bi to w naj­szer­szym moż­li­wym za­kre­sie.

Nowo­czes­ne mi­kro­pro­ce­so­ry ca­łą po­trzeb­ną elek­tro­ni­kę ma­ją zin­te­gro­wa­ną w swo­im wnęt­rzu. Deskryptory stron opi­su­ją­ce ol­brzy­mie, wie­lo­gi­ga­baj­to­we wir­tu­al­ne prze­strze­nie adre­so­we prze­cho­wy­wa­ne są nie w kil­ku re­jest­rach, a w pa­mię­ci fi­zycz­nej (zaj­mu­ją jej częs­to wie­le me­ga­baj­tów) i bu­fo­ro­wa­ne są dla szyb­sze­go do­stę­pu w tab­li­cy trans­lac­ji we­wnątrz pro­ce­so­ra (TLB). Moż­li­wość wzna­wia­nia wy­ko­ny­wa­nia roz­ka­zu nie­moż­li­we­go do wy­ko­na­nia z po­wo­du bra­ku po­trzeb­nej stro­ni­cy pa­mię­ci po­zwa­la na ła­do­wa­nie bra­ku­ją­cych stron z dys­ku w mo­men­cie od­wo­ła­nia się do nich, a nie „na za­pas” w mo­men­cie akty­wo­wa­nia za­da­nia. Z kolei du­ża szyb­kość pra­cy po­zwa­la my­śleć nie o pros­tym prze­łą­cza­niu za­dań za po­mo­cą kom­bi­nac­ji kla­wi­szy, a o re­gu­lar­nym prze­łą­cza­niu ich w ce­lu stwo­rze­nia ilu­zji jed­no­czes­nej reali­zac­ji (wie­lo­za­da­nio­wość przez wy­własz­cze­nie, ang. pre­emptible mul­ti­task­ing). W pre­zen­to­wa­nym pro­jek­cie świa­do­mie zre­zyg­no­wa­łem z ta­kiej funk­cji, gdyż oś­mio­bi­to­we pro­ce­so­ry pra­cu­ją­ce z częs­to­tli­woś­cią kil­ku me­ga­her­ców wię­cej cza­su spę­dza­ły­by na prze­łą­cza­niu pro­ce­sów niż na wy­ko­ny­wa­niu ich ko­du.

Najważniej­sze, abyś za­pa­mię­tał, że pa­mięć wir­tu­al­na to pros­ty układ elek­tro­nicz­ny nie ma­ją­cy wie­le wspól­ne­go z dys­kiem twar­dym i „uda­wa­niem” pa­mię­ci za je­go po­mo­cą. Obsługę pa­mię­ci wir­tu­al­nej, a więc wią­za­nia adre­sów uży­wa­nych przez pro­gram z in­ny­mi adre­sa­mi właś­ci­wych blo­ków więk­szej pa­mię­ci fi­zycz­nej moż­na do­dać na­wet do naj­prost­szych mi­kro­pro­ce­so­rów, pra­wie ma­gicz­nie zwięk­sza­jąc ich moż­li­woś­ci o funk­cje do­stęp­ne wy­da­wa­ło­by się tyl­ko w 32-bi­to­wych i 64-bi­to­wych „mon­strach” znaj­du­ją­cych się dzi­siaj w każ­dym kom­pu­te­rze do­mo­wym kla­sy PC. Właśnie ta­kim pros­tym ukła­dom za­wdzię­cza­my sta­bil­ną pra­cę no­wo­czes­nych sys­te­mów opera­cyj­nych, moż­li­wość współ­dzia­ła­nia róż­nych apli­kac­ji ze so­bą i wy­mie­nia­nia da­nych po­mię­dzy ni­mi oraz łat­we łą­cze­nie róż­nych mo­du­łów bi­blio­tecz­nych w je­den du­ży pro­gram.


Procesory miałem co prawda 10 lat temu, ale nawet bardzo dobry wykładowca AGH dr Wiśniewski nie wyłożył trybu wirtualnego tak w prosty i zrozumiały sposób dla mnie ja Ty to zrobiłeś. :-)
Bardzo dobry artykuł. Czasem przychodzi mi na myśl, że ośmiobitowce mogłyby być znacznie mocniejsze, gdyby projektanci i programiści dysponowali obecnym doświadczeniem.
Mimo to rozwijanie ośmiobitowców nie miało już większego sensu ;) Jak widać z obecnych doświadczeń, nawet architektura 32-bitowa nie wystarcza do niektórych zastosowań. A szczególnie, gdy oprogramowanie jest nadęte i niezoptymalizowane.
"Nikt o zdrowych zmysłach nie będzie przecież dzisiaj budował komputera z tak prymitywnymi procesorami" – oj, znam paru takich, co są gotowi się porwać. :)