Esitysmuodot (little endian -muodossa tavut vaihdettu):
+73 | Big Endian | Little Endian |
---|---|---|
Etumerkki | 00000000 01001011 | 01001011 00000000 |
2:n kompl. | 00000000 01001011 | 01001011 00000000 |
1:n kompl. | 00000000 01001011 | 01001011 00000000 |
+32767 | 10000000 01001010 | 01001010 10000000 |
-23 | Big Endian | Little Endian |
---|---|---|
Etumerkki | 10000000 00010111 | 00010111 10000000 |
2:n kompl. | 11111111 11101001 | 11101001 11111111 |
1:n kompl. | 11111111 11101000 | 11101000 11111111 |
+32767 | 01111111 11101000 | 11101000 01111111 |
Yleisesti ottaen oikeasta vastauksesta on saanut kolme pistettä. Yksittäisestä mokasta ei välttämättä ole rokotettu, jos muuten näkyy, että periaate on kuitenkin hanskassa. Jos kuitenkin on enemmälti vikaa, on rokotettu yksi tai kaksi (tai ääritapauksessa kolme) pistettä virheiden määrän mukaan. Endianismiepäselvyyksistä on rokotettu yksi piste.
Yhden pisteen on yleisesti ottaen saanut kunhan on osannut konvertoida binäärimuotoon jollain tapaa (oikein) noita lukuja.
Muutetaan luku binääriksi ja jaetaan IEEE-standardin mukaisiin osiin:
Merkki | Eksponentti | Mantissa |
---|---|---|
0 | 10000010 | 0010000 00000000 00000000 |
(-1)Merkki * 1.Mantissa * 2Eksponentti-127 ->
(-1)0 * 1.0012 * 23 =
1.12510*8 = 910
Yksi piste on irronnut jos IEEE-liukuluvun rakenne on ainakin ollut selvillä, kaksi pistettä virheellisestä, mutta oikealla idealla varustetusta laskusta ja kolme pistettä oikeasta ratkaisusta.
Yleinen merkkijonojen tallennustapa on merkkitaulukko, jossa jokainen alkio (usein tavu, esim. unicoden tapauksessa tosin 16 bittiä) sisältää yhden merkkikoodin jonkin tietyn koodaustavan mukaan (esim. ASCII, ISO-Latin-1, Unicode). Lisäksi merkkijono pitää jotenkin erottaa muusta muistissa olevasta materiaalista, eli merkkijono pitää esim. lopettaa erityisellä lopetusmerkillä (esim. C:ssä ja sukulaisissa 0-merkki) tai ilmaista merkkijonon pituus taulukon ensimmäisessä alkiossa olevalla luvulla.
Esimerkkijono voitaisiin tallettaa esim. seuraavasti (C-tyylinen lopetusmerkki, yhteen osoitteeseen talletetaan yksi kirjain):
1A... | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B |
'v' | 'e' | 'r' | 'y' | ' ' | 's' | 'i' | 'm' | 'p' | 'l' | 'e' | 00 |
1A... | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B |
0B | 'v' | 'e' | 'r' | 'y' | ' ' | 's' | 'i' | 'm' | 'p' | 'l' | 'e' |
Pisteytys menee pääpiirteittäin niin, että yhden pisteen on saanut koodauksen maininnasta, taulukkomuotoisessa tietorakenteessa olevasta esityksestä toisen, ja kolmeen pisteeseen on lisäksi vaadittu pituuden merkitseminen jollain tavalla.
Kyllä voi: liukuluvut voitaisiin koodata esimerkiksi IEEE-standardin mukaisesti tavallisiin kokonaislukumuuttujiin. Niiden käsittely liukulukuina toki vaatii erilliset aliohjelmat, jotka purkavat koodatut luvut eri osiinsa, suorittavat laskutoimitukset ja koodaavat tuloksen taas IEEE-standardimuotoon.
Tietystikään ei tarvitse tyytyä käyttämään yhtä muuttujaa, vaan voitaisiin ajatella käytettävän esimerkiksi erillisiä muuttujia mantissalle ja eksponentille. (Huomattakoon kuitenkin, että vaikka desimaalilukuja voisikin säilöä siten, että jaottelu tehtäisiin kokonais- ja desimaaliosaan, nämä eivät olisi varsinaisia liukulukuja.)
Oleelliset asiat (koodaus jollain tapaa, laskenta aliohjelmilla yksittäisten konekäskyjen sijaan) selvittävästä vastauksesta on saanut täydet pisteet, vähän sinne päin selityksestä on saanut vähemmän.
Yleisenä arvosteluperiaatteena on sovellettu käytäntöä, että yhden pisteen saa nimien muistamisesta ja kolme pistettä ymmärtämisestä, joka pitää osoittaa esimerkkien avulla. Kaksi pistettä on saanut, jos vastaus on ollut "jotain tältä väliltä", tai vastaus on muuten hyvä, mutta sisältää jonkin pienehkön asiavirheen.
a)
1p: nimennyt tilat
3p: ymmärtänyt tilojen välisen eron (mainitsee rajoituksista)
Moni oli kertonut käyttäjätilan rajoituksista b-kohdan vastauksessa. Tämä huomioitiin a-kohdan pisteytyksessä.
b)
1p: nimennyt siirtymät lyhyesti, tai ei käsitellyt niitä tehtäväpaperissa
vaaditulla tavalla (selvästi erikseen), tai käsitellyt vain toisen siirtymistä
3p: antanut siirtymistä myös esimerkkitapahtumat (tätä vaadittiin tehtävänannossa)
c) 1p: nimennyt suoritus- ja odotustilat
2p: kertonut, että tiloja on käytännössä enemmän
3p: kertonut kunkin tilan merkityksen.
Tilojen merkityksen sai kertoa d-kohdan vastauksessa, jolloin tämä huomioitiin c-kohdan pisteytyksessä.
d) 1p: yleisiä, puuttellisia mutta oikealta kuulostavia selityksiä, tai ei
käsitellyt tilasiirtymiä tehtäväpaperissa vaaditulla tavalla (selvästi
erikseen).
3p: käsitellyt siirtymät erikseen ja antanut riittävästi esimerkkejä.
ESIMERKKIVASTAUKSET
a) Suorittimen suoritusaikaisia tiloja ovat käyttäjätila ja etuoikeutettu
tila. Käyttäjätilassa ohjelmille sallittua muistiavaruutta on rajoitettu, eivätkä
kaikki konekäskyt ole käytettävissä. Etuoikeutetussa tilassa kaikki konekäskyt
ovat käytettävissä.
b) Siirtymä käyttäjätila -> etuoikeutettu tila:
Tilamuutos tapahtuu joko konekielen palvelupyyntökäskyllä (esimerkiksi TTK-91-konekielessä SVC) tai keskeytyksen yhteydessä. Palvelupyyntökäskyn tehtävänä on nimenomaan siirtää suoritin etuoikeutettuun tilaan ja suorittaa tässä tilassa käyttöjärjestelmäkoodia. Ohjelma ei voi itse siirtää suoritinta etuoikeutettuun tilaan ja suorittaa mielivaltaisesti mitä tahansa koodia.
Kun suoritin saa ulkoiselta laitteelta jonkin keskeytyksen, suoritin siirtyy etuoikeutettuun tilaan ja ajaa keskeytyskäsittelijän, joka on käyttöjärjestelmän koodia. Keskeytyskäsittelijä siirtää suorittimen takaisin käyttäjätilaan jollakin konekäskyllä. Esimerkiksi Intel Pentium -suorittimessa tämä tapahtuu IRET-käskyllä.
Esimerkki: Suorituksessa oleva ohjelma on saanut tehtävänsä valmiiksi. Ohjelman lopussa oleva käsky "SVC SP, =HALT" siirtää suorittimen etuoikeutettuun tilaan, jonka jälkeen suoritin siirtyy käyttöjärjestelmän palvelurajapinnan toteuttavaan koodiin. Parametri HALT välittyy käyttöjärjestelmälle, joka ohjelman pyynnöstä lopettaa prosessin.
Siirtymä etuoikeutetu tila -> käyttäjätila:
Käyttöjärjestelmä suorittaa tämän tilamuutoksen jonkin konekäskyn avulla. Muutos tehdään, kun käyttöjärjestelmä haluaa antaa suoritusaikaa jollekin prosessille (eli käyttäjän ohjelmalle). Tällaisia tilanteita ovat esimerkiksi keskeytyskäsittelyn päättyminen ja palvelupyynnön valmistuminen.
Esimerkki: Suoritin ajaa prosessin A ohjelmakoodia. Kellolaite tuottaa aikaviipalekeskeytyksen, jolloin suoritin siirtyy etuoikeutettuun tilaan suorittamaan käyttöjärjestelmän vuorottelijaa, joka päättää antaa prosessille B suoritusaikaa. Vuorottelija vaihtaa tietyllä konekäskyllä prosessin B ympäristön käyttöön ja palaa käyttäjätilaan jollakin IRET-tyyppisellä konekäskyllä. Tällöin prosessin B suoritus jatkuu.
c) Prosessin suoritusaikaiset tilat ovat karkeasti jaoteltuna seuraavat:
READY TO RUN - prosessi voidaan ottaa suoritettavaksi
RUNNING - prosessi on nyt suorituksessa
WAITING - prosessi ei ole suorituksessa, eikä prosessia voida ottaa
suoritettavaksi. Prosessi odottaa jotakin tapahtumaa,
esimerkiksi levylohkoa levyohjaimelta.
Tiloja on käytännössä enemmän, prosessilla voi olla esimerkiksi alkutila INIT (prosessi on luotu, muttei vielä valmis suoritettavaksi) ja lopputila TERMINATED (prosessin suoritus on päättynyt tai päättymässä; esimerkiksi Unix-ympäristössä tällaista prosessia kutsutaan zombie-prosessiksi). Tilajaottelu vaihtelee käyttöjärjestelmittäin. Joissakin käyttöjärjestelmissä saattaa olla heittovaihtoon liittyen lisää prosessitiloja.
d) Käyttöjärjestelmä muuttaa prosessin tilaa. Prosessi ei itse voi määrätä omaa tilaansa. Tila muuttuu tapahtumissa, joiden seuraksena prosessin täytyy odottaa jotain, esimerkiksi levylohkoa levyltä, syötettä näppäimistöltä tai verkkoyhteydeltä, tai odottaa tiettyä ajanhetkeä tms. Tila muuttuu myös käyttöjärjestelmän päätöksestä, esimerkiksi käyttöjärjestelmä siirtää prosessin heittovaihtoon tai antaa suoritusaikaa jollekin toiselle prosessille.
Siirtymä INIT -> READY TO RUN:
* prosessi on nyt "perustettu" ja ohjelman suoritus voi alkaa
* esimerkkitapahtuma: prosessin ohjelmakoodi on saatu ladattua keskusmuistiin. Käyttöjärjestelmä
muuttaa prosessin tilaa käsitellessään levyohjaimen tuottamaa keskeytystä.
Siirtymä READY TO RUN -> RUNNING:
* käyttöjärjestelmän vuorottelija valitsee prosessin suoritusvuoroon,
jolloin prosessin tilaksi tulee RUNNING. Jos aiemmin suorituksessa olleen
prosessin tila oli RUNNING, tämä muutetaan READY TO RUN -tilaksi.
* esimerkkitapahtuma: suoritin saa aikaviipalekeskeytyksen, jolloin käyttöjärjestelmän
vuorottelija aktivoituu. Vuorottelija valitsee uuden prosessin suoritukseen.
Siirtymä RUNNING -> WAITING:
* prosessi tarvitsee jotain, joka ei ole välittömästi saatavissa, esimerkiksi
levylohkoa levyltä, syötettä näppäimistöltä tai signaalia toiselta
prosessilta
* prosessi haluaa odottaa tiettyä ajanhetkeä. Käyttöjärjestelmä voi
tarjota tätä varten esimerkiksi sleep-palvelun, joka siirtää prosessin
WAITING-tilaan halutuksi ajaksi. Ajan täytyttyä (huomataan
aikaviipalekeskeytyksen käsittelijässä) prosessi siirretään READY TO RUN
-tilaan.
* esimerkkitapahtuma: sovellus haluaa lukea syötettä verkkoyhteydeltä. Käyttöjärjestelmä
siirtää prosessin WAITING-tilaan.
Siirtymä WAITING -> READY TO RUN:
* prosessin odottama tapahtuma on toteutunut, jonka vuoksi prosessin suoritus
voi jatkua
* esimerkkitapahtuma (jatkoa edelliseen): verkkokortti vastaanottaa paketin
verkosta ja aiheuttaa keskeytyksen. Keskeytyskäsittelijän suorituksen
yhteydessä käyttöjärjestelmä huomaa, että yksi prosessi odottaa syötettä
verkkoyhteydeltä ja siirtää prosessin READY TO RUN -tilaan.
Siirtymä RUNNING -> TERMINATED:
* käyttöjärjestelmä on päättänyt lopettaa prosessin
* esimerkkitapahtuma 1: sovellus yrittää käyttää jotain etuoikeutettua käskyä,
mutta tämä ei ole käyttäjätilassa sallittua. Suoritin aiheuttaa tästä
poikkeustilanteen, jolloin suoritin siirtyy etuoikeutettuun tilaan ajamaan käyttöjärjestelmän
poikkeuskäsittelijän koodia. Poikkeuskäsittelijä lopettaa virheellisesti käyttäytyvän
prosessin ja siirtää sen lopetustilaan.
* esimerkkitapahtuma 2: sovellus haluaa itse lopettaa suorituksensa ja pyytää
tätä käyttöjärjestelmältä.
Luento 11, kalvot 1-16; Laskuharjoitus 6, tehtävät 1 ja 2
a) (3 pistettä)
Java-ohjelmaa suoritetaan Pentium-koneessa tulkitsemalla. Tällöin Java-lähdekoodi on käännetty tavukoodiksi (bytecode), jota ohjelman suoritusaikana tulkitaan. Lausekkeen A=B+C; esitysmuoto suoritusaikana on siis Javan tavukoodi. Muuttujien arvot sijaitsevat muistissa, josta Java-tulkki voi niitä lausekkeen suoritusaikana ladata Pentium-suorittimen rekistereihin laskennan suorittamista varten.
b) (3 pistettä)
Pentium-tietokone suorittaa lausekkeen tulkitsemalla lausekkeen tavukoodi-esitysmuodon oman konekielensä käskyiksi. Pentium-kone siis emuloi omalla käskykannallaan JVM:n toimintaa.
c) (3 pistettä)
Lauseketta suoritettaessa on suoritusvuorossa Java-tulkki (esimerkiksi Sunin JDK:n java), joka tulkitsee tavukoodia ohjelman suoritusaikana.
d) (3 pistettä)
Jos ohjelmaa suoritetaan JIT-käännöksen avulla, lausekkeen sisältämä ohjelman osa käännetään Pentium-suorittimen konekielelle ja linkitetään, kun siihen viitataan 1. kerran. Tällöin lauseke on kääntämisen jälkeen muistissa Pentium-konekielellä, eikä sitä tarvitse enää tulkita suoritusaikana.
e) (1 piste)
C#:ssa Java-ohjelma käännetään suoraan suorittimen konekielelle tai Microsoftin omalle välikielelle, jota ei ole tarkoitettu tulkattavaksi, vaan käännettäväksi. (Microsoftin välikieli on tarkoitettu yleiseksi välikieleksi, jota voidaan Javan lisäksi käyttää muidenkin kielien kanssa; tällöin tälle välikielelle voidaan tehdä optimoitu kääntäjä, jonka avulla ohjelmat voidaan kääntää suorittimen konekielelle riippumatta siitä, millä kielellä ohjelmat on alunperin kirjoitettu.)
Pistejakauma:
0p: 9 ********* 1p: 6 ****** 2p: 14 ************** 3p: 8 ******** 4p: 8 ******** 5p: 10 ********** 6p: 8 ******** 7p: 8 ******** 8p: 9 ********* 9p: 5 ***** 10p: 8 ******** 11p: 3 *** 12p: 6 ****** 13p: 4 ****
Keskiarvo 5,6 pistettä.
a) (3 p)
LOAD R1, K
LOAD R2, T(R1)
OUT R2, =CRT
tai
LOAD R1, =T
ADD R1, K
LOAD R2, @R1
OUT R2, =CRT
- Olisi myös hyvä tarkistaa, että k on välillä 0 - 19, sitä ei
kuitenkaan vaadittu.
- Yleisin virhe oli, että yritettiin indeksoida suoraan muuttujan k avulla
(-1p)
b) (3 p)
LOAD R1, =0
L1 COMP R1, =20
JNLES endL1
LOAD R2, R1
MUL R2, R2
ADD R2, =3
STORE R2, T(R1)
ADD R1, =1
JUMP L1
endL1 NOP
miinusta luuppivirheistä (-1p), vääränlaisista taulukkoon viittauksista
(-1p) ym.
c)
; Alusta (S, N)
; S - call-by-reference
; N - call-by-value
;
parpS EQU -3
parN EQU -2
Alusta PUSHR SP
; talletetaan rekisterit
LOAD R3, parN (FP) ; R3 <-
taulukon alkioiden lkm
LOAD R2, parpS (FP) ; R2 <- käsiteltävänä
olevan taulukon alkion osoite
LOAD R1, =0
; indeksi i = 0
Aloop COMP R1, R3
JNLES EndAL
; (i >= lkm) -> hypätään pois silmukasta
; eli käydään läpi indeksit 0 - lkm-1
LOAD R4, R1
MUL R4, R4
ADD R4, =3
; R4 <- i^2 + 3
STORE R4, @R2 ; talletetaan arvo
ADD R1, =1
; i++
ADD R2, =1
; R2 <- seuraavan taulukon alkion osoite
JUMP Aloop
EndAL POPR SP ; palautetaan rekisterit
EXIT SP, =2 ; 2 param.
Taulukon alkion osoitteen voi myös laskea jokaisessa silmukassa käyttäen taulukon alkuosoitetta ja indeksiä apuna. ADDR-kentässä ei kuitenkaan voi olla rekisteriä! miinusta luuppivirheistä (-0.5, -1p), rekistereiden talletuksen puuttumisesta (-1p), parametrien välitysvirheistä ja parametreihin viittausvirheistä (-1p, -2 p), virheellisistä taulukkoonviittauksista (-1 p) ym.
d) (3p)
PUSH SP, =T
PUSH SP, =20
CALL SP, Alusta
miinusta parametrivirheistä (-1p, -2p), kutsu väärin (-1p) ym. Tyypillinen virhe oli, että =T sijaan oli kirjoitettu T, @T tai S.
Pistejakauma:
0: ****** 1: * 2: ********* 3: *** 4: **** 5: ********** 6: *********** 7: *********** 8: ****** 9: ****** 10: ************ 11: *********** 12: ****************
Keskiarvo: 7,3 pistettä