Haluaisikohan joku valaista mua muutamassa tietokoneen laitteistotason toimintaan liittyvässä kysymyksessä...
Ensinnäkin, mistään lukemastani materiaalista ei käy selville, että kuinka tietokone kykeneee tunnistamaan, että käsittelyssä oleva muistisana sisältää juuri käskyn, eikä esim. kokonaisluvun tai liukuluvun? Nuo kaikki em. ovat kuiten muistissa esitetty binäärilukuina (tai todellisuudessa sähköisinä signaaleina) ja ainoastaan tuon binäärimuotoisen koodauksen tulkinta ratkaisee sen, että mitä tuo käsiteltävänä oleva data on.
Toiseksi haluaisin tietää, että vieläkö tämän päivän tietokoneissa CPU sisältää MARin eli muistiosoiterekisterin sekä MBR eli muistipuskurirekisterin? Tämä lukemani materiaali on vuodelta -87, joten vaikka pääpiirteissä asiat ovat samoin, niin jotkut asiat ovat voineet muuttua. Entä sisältääkö keskusyksiköt edelleen erillisiä IOPeitä eli Input/Output-prosessoreja ja jos sisältää, niin missä nämä tarkkaan ottaen sijaitsevat?
Mistähän CPU:sta olet lukenut? Esimerkiksi Wikipediassa on aloittelijalle kohtuullisen kattava artikkeli x86-arkkitehtuurista.
Tietokone ei mitenkään arvaile, mitä muistissa on. Tietty rekisteri (x86-arkkitehtuurissa IP, Instruction Pointer) osoittaa muistissa seuraavaan suoritettavaan kohtaan, ja siellä on kerta kaikkiaan pakko olla kelvollisia käskyjä. Jos käsky ei ole kelvollinen, aiheutuu keskeytys (eli prosessori laittaa tietoja pinoon ja hyppää ennalta määrättyyn toiseen osoitteeseen). Double fault tarkoittaa, että myös tässä toimenpiteessä tapahtuu virhe; triple fault tarkoittaa, että myös double faultin käsittelyssä tapahtuu virhe, ja silloin kone muistaakseni reboottaa.
Kun kone käynnistyy, BIOS lataa boottilevyn ensimmäisen sektorin RAMiin ja siirtää IP:n osoittamaan tuon sektorin alkuun. Siitä eteenpäin prosessori lukee kyseisestä kohdasta tietoja käskyinä ja kasvattaa IP:tä aina käskyn pituuden verran. (x86-käskyt ovat vaihtelevan mittaisia tiettyjen sääntöjen mukaan.) Lisäksi on tietenkin hyppykäskyjä ja eräitä muita käskyjä, jotka vaikuttavat IP:n arvoon muulla tavalla.
Rekisterikysymykseesi vastaan vain, että nykyprosessoreissa on kymmenittäin rekistereitä, joihin ohjelmoijalla ei ole mitään pääsyä kuitenkaan, eikä niillä kannata välttämättä vaivata päätään, jos ei ole erityisesti kiinnostunut prosessorin sisäisestä tekniikasta. Pikaisen vilkaisun perusteella mainitsemasi rekisterit – tai ainakin vastaavat toiminnot – kuuluvat juuri tähän luokkaan.
Näköjään muodostin omassa päässäni ongelma, jota ei todellisuudessa edes ole. Piti vain tajuta se, että kun ohjelma ladataan RAMiin, niin silloinhan kaikki käskyt ovat väkisinkin peräkkäisissä muistipakoissa. Tosin ohjelmoitaessa assembler-tasolla on kyllä oltava tarkkana siitä, että mihin tallettaa mitäkin, ettei vahingossa mene tallentamaan esim. jotain dataa jonkun ohjelman käskyn päälle...
No ei niiden sinänsä ole mikään pakko olla peräkkäisissä paikoissa, hyppykäskyn jälkeen voi ihan hyvin tulla jotain muuta ja sitten taas koodi jatkua siellä mihin hypätään.
Hmm, taisi mennä pilkun viilaamiseksi ;D
Triton kirjoitti:
Tosin ohjelmoitaessa assembler-tasolla on kyllä oltava tarkkana siitä, että mihin tallettaa mitäkin, ettei vahingossa mene tallentamaan esim. jotain dataa jonkun ohjelman käskyn päälle...
Ei nykypäivänä tuollaisia vahinkoja helposti pääse syntymään. Rupeaa muutenkin tuo assembly ohjelmointi olemaan jo kohtuullisen siedettävää touhua, kun käytettävissä on rutiininomaisia töitä helpottavia HLA ja macro assemblereita.
Itse olen harrastemielessä hiukan opiskellut mainframe koneiden ohjelmoinnin alkeita ajamalla MVS:ää Hercules emulaattorilla ja käyttämällä erinomaista z390 ASM pakettia OS/390 assembler ohjelmoinnin alkeiden opiskeluun.
Esim. macrojen avulla ohjelmoitaessa OS/390 "Hello world" assembler ohjelma näyttäisi yksinkertaisesti tältä:
HELLO SUBENTRY WTO 'HELLO WORLD' SUBEXIT END
Valmiiden makrojen käyttäminen on huijaamista :D
Edit.
Tuli vielä muutama lisäkysymys aiheeseen liittyen.
Eli miten nykypäivänä, kun on moniytimisiä prosessoreja voidaan viitata assembler-kielessä, että mitä prosessoria tarkkaan ottaen käytetään. Käsitykseni mukaan jokaisella ytimellä on omat (saman nimiset) rekisterinsä, joten jollain tavalla niihin täytyy pystyä viittamaan.
Toinen kysymykseni liittyy RAMin toimintaan täysin fyysisellä tasolla. Eli minkä kokosiksi palasiksi RAM-muisti on jaettu eli lähinnä kiinnostaa tietää se, että onko jokaiseen muistin bittiin ihan fyysinen "väylä" ja näin ollen pystyttäisiin erikseen muokkaamaan yksittäisen bitin tilaa vai täytyykö kerralla muokata esim. koko tavun eli 8 bitin tilaa?
Ohjelmoijan kannalta on ihan sama, onko koneessa monta ydintä vai monta erillistä prosessoria. Niitä ei käytetä samasta koodista. Kuten itsekin totesit, niillä on omat rekisterinsä, siis myös oma IP, jolloin ne suorittavat koodia aivan toisistaan riippumatta. Kun kone käynnistyy, koodi ajetaan yhdellä ytimellä, ja käyttöjärjestelmä alustaa sitten loput ytimet tai prosessorit ohjelmallisesti, kuten muutkin laitteet alustetaan.
Metabolix kirjoitti:
Kun kone käynnistyy, koodi ajetaan yhdellä ytimellä, ja käyttöjärjestelmä alustaa sitten loput ytimet tai prosessorit ohjelmallisesti, kuten muutkin laitteet alustetaan.
Jos asia kiinnostaa käyttöjärjestelmätasolla, niin tutustumisen arvoinen linkki voisi olla: Notes on the Plan 9tm 3rd edition Kernel Source
Triton kirjoitti:
Ensinnäkin, mistään lukemastani materiaalista ei käy selville, että kuinka tietokone kykeneee tunnistamaan, että käsittelyssä oleva muistisana sisältää juuri käskyn, eikä esim. kokonaisluvun tai liukuluvun?
Yleensä se ei mistään tunnista sellaista, vaan kone esimerkiksi suorittaa kokonaislukujen yhteenlaskukäskyn, joka käsittelee operandeja kokonaislukuina, tai liukulukujen yhteenlaskukäskyn, joka käsittelee niitä liukulukuina. Joissakin tilanteissa operandi on sellainen, ettei se ole esim. mikään kelvollinen liukuluku, ja tästä mahdollisesti tulee keskeytys tms., tavallisemmin kuitenkin vain tulos on NaN (not a number, epäluku).
Periaatteessa voisi olla toisinkin. Ja jos olen oikein ymmärtänyt ja oikein muistan, niin aikoinaan Helsingin yliopiston käytössä olleessa Burroughs-keskuskoneessa oli tyypitetty data, eli data sisälsi aivan konetasolla jonkin tiedon sen tyypistä. Yleensä on kuitenkin ajateltu, että tyypitystä ei kannata tehdä siellä vaan korkeamman tason kielissä.
Myös korkean tason kielissä saattaa olla mahdollisuus tulkita sama data eri tavoin eli esimerkiksi tallentaa jonnekin osoitin (pointer) ja sitten käyttääkin sitä (raakadataa, bittijonoa) kokonaislukuna. Esimerkiksi niinkin öpuhtaassaö, alkujaan opetuskäyttöön tehdyssä korkean tason kielessä kuin Pascalissa on sellaiseen väline, ns. variant record. (Syynä lähinnä se, että sitä tarvittiin, jotta Pascal-kääntäjä voidaan kirjoittaa Pascalilla.)
Yucca kirjoitti:
Periaatteessa voisi olla toisinkin. Ja jos olen oikein ymmärtänyt ja oikein muistan, niin aikoinaan Helsingin yliopiston käytössä olleessa Burroughs-keskuskoneessa oli tyypitetty data, eli data sisälsi aivan konetasolla jonkin tiedon sen tyypistä. Yleensä on kuitenkin ajateltu, että tyypitystä ei kannata tehdä siellä vaan korkeamman tason kielissä.
No kyllähän esim. Harvard-arkitetuuriin perustuvissa prosessoreissa (joita on esim laajasti käytössä oelvat 8-bit PIC-mikrokontrollerit) on erilliset muistit datalle ja ohjelmakoodille. Ohjelmamuistissa olevaa tavaraa prosessori pystyy ainoastaan suorittamaan (joissain malleissa sitä pystyy välillisesti lukemaan ja kirjoittamaan kuten oheislaitteita) ja käyttömuistissa olevaa dataa taas ei pysty suorittamaan.
Toisaalta myöskin esim. nykyisissä x86 -prossuissa, joissa on yhteinen muistiavaruus kaikelle, on ihan rautatason tuki DEPille, eli että dataksi määriteltyä muistia ei suoriteta koodina.
Ei olisi lainkaan huonompi idea käyttää esim. kahta bittiä ilmaisemaan muistipaikassa olevan datan tyypin (esim. 00 = int, 01 = float, 10 = bool, 11 = käsky). Toki kaikki tällaiset ns. apubitit ovat aina varsinaisen datan tilasta pois, mutta itse pidän ajatuksesta, että tyypitys tehtäisiin jo ihan rautatasolla.
Tässä tietokoneen toiminnan opiskelussa on ollut hieman ongelmallista se, ettei oikein pysty aina tietämään, että mihin asioihin kannattaisi tarkemmin paneutua ja mitkä asiat ovat olennaisia assembler-ohjelmoinnin opiskelun kannalta. Itseäni ei kiinnosta, mitkään valmiit käyttöjärjestelmien keskeytykset juuri tällä hetkellä, sillä haluan päästä vielä matalemmalle tasolle, kuitenkin niin, että pysytään symboliisessä konekielessä eikä mennä varsinaiselle konekielen tasolle. Onko muuten ohjelmoijan mahdollista käyttää I/O- tai muita oheislaitteita ilman BIOSin keskeytyksiä eli toisin sanoen voiko assemblerillä itse toteuttaa BIOSin tarjoamat keskeytykset? BIOS on kuitenkin ohjelmoitu assemblerillä, joten sen takia en näkisi asiassa ongelmaa, ellei sitten muut asiat vaikuta. Liikun siis tällä hetkellä nyt sellaisella tasolla, ettei edes käyttöjärjestelmää ole asennettu. Ja ei, en ole tekemässä omaa käyttöjärjestelmää, vaan lähinnä haluan tutustua siihen, miten käyttöjärjestelmä tai ylipäätään ohjelmat toimivat rautatasolla.
Triton kirjoitti:
Ei olisi lainkaan huonompi idea käyttää esim. kahta bittiä ilmaisemaan muistipaikassa olevan datan tyypin (esim. 00 = int, 01 = float, 10 = bool, 11 = käsky). Toki kaikki tällaiset ns. apubitit ovat aina varsinaisen datan tilasta pois, mutta itse pidän ajatuksesta, että tyypitys tehtäisiin jo ihan rautatasolla.
Itse taas en näe vähäisintäkään järkeä tuhlata bittejä sellaisen informaation säilömiseen, mistä ei missään tilanteessa ole mitään hyötyä.
Ennemmin kannattaisi käyttää ne bitit vaikka virheenkorjaukseen.
Triton kirjoitti:
Onko muuten ohjelmoijan mahdollista käyttää I/O- tai muita oheislaitteita ilman BIOSin keskeytyksiä eli toisin sanoen voiko assemblerillä itse toteuttaa BIOSin tarjoamat keskeytykset?
On toki. Biosin ideahan on vain tarjota ohjelmoijalle yhtenäinen rajapinta laitteistoon riippumatta laitteistosta. Eli jos haluat koodata ohjelman joka toimii omalla koneellasi muttei todennäköisesti naapurin Kallella eikä naapurikylän Pertillä, niin toki voit käskyttää suoraan laitteistoa silloin kun ohjelmasi ei toimi käyttöjärjestelmän sisällä.
Ongelmaa voi toki tuottaa se, että mistä kaikesta raudasta on saatavilla dokumentaatiota ja kuinka helppoa sen käyttö on. Itse en esimerkiksi lähtisi kirjoittamaan kymmentätuhatta riviä assemblerkoodia pystyäkseni käyttämään USB-liitännäistä näppäimistöä, jos vaihtoehtona on käyttää bioskutsua.
Triton kirjoitti:
Itse pidän ajatuksesta, että tyypitys tehtäisiin jo ihan rautatasolla.
Mitä hyötyä tästä olisi?
Triton kirjoitti:
Onko muuten ohjelmoijan mahdollista käyttää I/O- tai muita oheislaitteita ilman BIOSin keskeytyksiä eli toisin sanoen voiko assemblerillä itse toteuttaa BIOSin tarjoamat keskeytykset?
Mikään nykyaikainen käyttöjärjestelmä ei käytä BIOSin keskeytyksiä, koska ne toimivat reaalitilassa eivätkä siis toimi lainkaan 32-bittisessä tai 64-bittisessä tilassa. Tämähän alkoi olla ongelma jo parikymmentä vuotta sitten, kun koneisiin tuli yli mega muistia; ks. DOS extender. Laitteistokeskeytykset ja PIC eivät taas liity BIOSiin mitenkään.
x86-arkkitehtuurissa on käsky LIDT, jolla välitetään prosessorille taulukko (Interrupt descriptor table), jossa ovat keskeytyksiä käsittelevien funktioiden osoitteet.
Kyllähän näiden apubittien avulla olisi näin ollen mahdollista estää jotkin virheet rautatasolla ajonaikaisesti, kun pystyttäisiin tarkistamaan ennen operaation suorittamista, että onko operandien tyypit kelvollisia operaation suorittamisen kannalta. Niin no en sitten tiedä, mikä todellisuudessa on kannattavaa, ehkäpä nuo tyypin tarkistusoperaatiot ovat sen verran nopeita, että ne onnistuvat tehokkaasti ihan ohjelmatasollakin...
Edit. Missä nuo IDT:ssä sijaitsevat keskeytykset ovat sitten toteutettu, jos ei Biosissa?
Miten niin "missä toteutettu"? Omassa koodissa tietenkin. Sitä mahdollisuuttahan juuri äsken kysyit.
Tietotyyppien tarkistaminen on turha hidaste, kun yleensä tyypit tiedetään jo valmiiksi. Javallakin koodanneena varmaan ymmärrät tämän.
Triton kirjoitti:
Kyllähän näiden apubittien avulla olisi näin ollen mahdollista estää jotkin virheet rautatasolla ajonaikaisesti, kun pystyttäisiin tarkistamaan ennen operaation suorittamista, että onko operandien tyypit kelvollisia operaation suorittamisen kannalta.
Jos tuollainen tarkistus halutaan, niin kääntäjä pitää valmiiksi huolen siitä, että ajettavassa koodissa ei ole tuollaisia tilanteita. Olisi jokseenkin pöljää varautua tilanteeseen jota ei tapahdu koskaan, paitsi silloin kuin data esim. korruptoituu muistissa. Datan korruptoitumiseenkin auttaisi paremmin tuo mainitsemani virheenkorjaus.
Jos nyt mietitään muistiylivuotoa tms, niin tuo ehdottamasi rautatarkistus nappaisi kiinni vain tietyn tyyppiset tilanteet. Todennäköisesti int ylikirjoittaisi intin useammin kuin esim. floatin, ja ensimmäisessä tapauksessa rautapohjainen tyypintarkistus ei auttaisi mitään - kuitenkin haitta olisi ihan yhtä suuri. Puskuriylivuotojen tunnistamiseen on paljon tehokkaampia ja järkevämpiä tapoja.
Metabolix kirjoitti:
Miten niin "missä toteutettu"? Omassa koodissa tietenkin. Sitä mahdollisuuttahan juuri äsken kysyit.
Tietotyyppien tarkistaminen on turha hidaste, kun yleensä tyypit tiedetään jo valmiiksi. Javallakin koodanneena varmaan ymmärrät tämän.
Ai hitto, ymmärsin tuon vastauksesi hieman eri tavalla ensimmäisellä kerralla ja siitä tuo outo jatkokysymys.
Ei tullutkaan sitten mieleen, että nuo tyypithän (jos tarkistetaan) nimenomaan käännösvaiheessa eikä ajonaikana. No sen siitä saa, kun ei ole aikasemmin oikeasti sisäistänyt, mitä käännösaika ja ajonaika todella tarkoittavat :D Joo, eli siis ei todellakaan ole fiksua tuhlata bittejä moiseen... Täytyy kyllä todeta, että sitä todella saa kokea täällä välillä itsensä todella tyhmäksi :D
Eikö siis BIOS nykypäivänä todella tee juuri muuta kuin lataa käyttöjärjestelmän muistiin? Siinä tapauksessa akronyymi BIOS alkaa olla varsin harhaanjohtava.
Käsittääkseni BIOS ei varsinaisesti lataa käyttöjärjestelmääkään muistiin, vaan ainoastaan sen boot sectorin, joka sitten suoritetaan ja joka huolehtii varsinaisen käyttöjärjestelmän lataamisesta.
BIOS on kuitenkin sikäli nykyäänkin tarpeen, kun kuitenkin nykykoneetkin tehdään edelleen tukemaan DOSia. Toisaalta ehkä ajan mittaa UEFI tai vastaava korvaa BIOSin.
Tästä voisi olla ehkä vähän apua: http://en.wikipedia.org/wiki/X86
Aihe on jo aika vanha, joten et voi enää vastata siihen.