#8 JerryM
Pracuji ve vlastním emulátoru procesoru od toho kdo mi dal to zadání, zde je popis toho emulátoru:
pro odpovězení: terminál přímo msdos, víc je popsáno v tom textu.
Struktura emulátoru
Emulátor procesoru Intel 8086 v ISu používá pro fázi překladu instrukcí překladač Netwide Assembler (NASM). Překladač dodaný zdrojový kód přeloží do souboru ve formátu Microsoft OMF Object Files (použitím volby -f obj). Překlad z bezpečnostních důvodů neprobíhá na serverech ISu. OMF soubor následně převezme IS. IS pak provede to, co by provedl zavaděč, tj. segmenty umístí do "paměti". Potom IS zahájí interpretaci operačních kódů instrukcí. Program není z bezpečnostních důvodů spuštěn, ale je interpretován instrukce po instrukci.
Proces spuštění programu
Emulátor umísťuje do "paměti" segmenty v pořadí, v jakém jsou v OMF souboru. Segmenty se do paměti umísťují od absolutní adresy 0 (tj. adresy 0000:0000). Chcete-li plnit tabulku přerušovacích vektorů, je nutné ve zdrojovém souboru jako první uvést segment s jejím obsahem.
Pro zjištění adresy, od které se má program spustit, IS v OMF souboru vyhledá první segment s názvem CODE. Do registru CS před spuštěním IS naplní segmentovou část adresy prvního segmentu s názvem CODE a do registru IP vloží offset 0 nebo offset instrukce označené návěštím ..start
Jiné segmentové registry než CS emulátor neplní a musí si je naplnit program sám.
Příklad jednoduchého programu
segment data zprava db "Hura, muj program funguje!",0Dh,0Ah,'$' segment code mov ax,data mov ds,ax ; nastavuji DS na segmentovou část adresy segmentu DATA mov ax,stack mov ss,ax ; nastavuji SS na segmentovou část adresy segmentu STACK mov sp,dno ; nastavuji adresu dna zásobníku do registru SP mov dx,zprava ; služba v DX vyžaduje offset začátku řetězce mov ah,09h ; identifikace služby Řetězec znaků na terminál int 21h ; provedení služby hlt ; ukončení tohoto programu segment stack ; vyhrazení prostoru pro segment se zásobníkem resb 16 ; rezervuji 16 bajtů (pozor, to je malý zásobník) dno db ? ; označuji, kde je dno zásobníku
Zvláštnosti překladače NASM
Direktiva cpu 8086
Emulátor IS podporuje pouze instrukce procesoru 8086. Proto doporučuji použít direktivu překladače
cpu 8086
která už na úrovni překladače nepovolí použít instrukce, které nejsou v repertoáru procesoru 8086. Např. instrukce procesoru 80186 PUSH 1 (tj. PUSH přímá_hodnota), PUSHA, BOUND nebo procesorů vyšších. Také existují instrukce vyšších procesorů, které mají stejnou zkratku, ale jiný operační kód. Uvedením cpu 8086 doporučuji zabránit překladači, aby použil operační kód modernějšího procesoru.
Překladač je case-sensitive
Překladač rozlišuje mezi malými a velkými písmeny. Direktivu segment pište malými písmeny. Neměňte v návěštích velikost písmen.
Odlišení objektu od jeho obsahu (hranaté závorky)
Překladač NASM chce důsledně odlišovat objekt od jeho obsahu. Proto kód:
cpu 8086 segment code mov ax,data mov ds,ax mov bx,promenna mov dx,[promenna] hlt segment data promenna dw 1234h
uloží do registru BX hodnotu 0 (tzn. offset proměnné PROMENNA v segmentu DATA) a uloží do registru DX hodnotu 1234h (tzn. obsah proměnné PROMENNA). Důsledně prosím používejte hranaté závorky u paměťových objektů tam, kde chcete pracovat s obsahem objektu a nikoli s adresou objektu.
Specifikace velikosti objektu (upřesnění byte, word)
Překladač NASM požaduje upřesnit velikost operandu, s nímž má pracovat, pokud velikost není zřejmá např. z druhého parametru instrukce. Proto je třeba psát např.
mov byte[promenna],2 neg byte[promenna] add word[prom],0x20
Alokace prostoru (pseudoinstrukce resb, resw)
Pro vyhrazení (rezervování) např. 1024 bajtů pro zásobník, použijte pseudoinstrukci resb následovně:
segment stack resb 1024 dno db ?
Chceme-li prostor vyhradit v počtu 16bitových slov, použijeme pseudoinstrukci resw.
Některé programovací techniky
Vynulování registru: XOR AX,AX
Služby operačního systému
Emulátor poskytuje programu vybrané služby operačního systému MS-DOS, nebo (historické) služby BIOSu. Volání poskytnutí služeb probíhající přes přerušení (např. INT 21h) nevyžaduje nastavení obsahu tabulky přerušovacích vektorů. Přesněji: pokud byste přerušovací vektor např. 21h nastavili, bude emulátorem ignorován.
Načíst jeden bajt z terminálu (služba MS-DOS)
mov AH,1 ; Identifikace služby Načíst jeden bajt z terminálu int 21h ; resp. int 33 poskytne službu
Služba vrací v registru AL načtený bajt. Pokud byl čtením vstup vyčerpán (už nejsou zadané žádné bajty k načtení), služba nastaví ZF=1 a AL=0. Pokud je předáván bajt v AL, je ZF=0.
Emulátor podporuje i čtení UNICODE znaků v transformaci UTF-8. Pokud je zadán vícebajtový znak, služba předá jen jeden jeho bajt (více se do AL nevejde). Dalším int 21h bude předán další bajt téhož znaku. Nedojde ke ztrátě obsahu.
Vypsat jeden bajt na terminál (služba MS-DOS)
mov AH,2 ; Identifikace služby Vypsat jeden bajt na terminál mov DL,'a' ; Bajt, který se má na terminál vypsat int 21h ; resp. int 33 poskytne službu
Emulátor podporuje i výpis UNICODE znaků v transformaci UTF-8. Pokud má být vypsán vícebajtový znak, musí být postupně vypsán více voláními této služby po jednotlivých bajtech.
Vypsat řetězec bajtů na terminál (služba MS-DOS)
V registrech DS:DX musí být uložena adresa začátku řetězce bajtů. Řetězec končí znakem dolar '$'.
zprava db "Hello, World!", 0Dh,0Ah, '$' ... mov AH,9 ; Identifikace služby Vypsat řetězec bajtů na terminál mov DX,zprava ; Offset začátku řetězce v segmentu dle DS int 21h ; resp. int 33 poskytne službu
Načíst řádek z terminálu (služba MS-DOS)
Nejprve musíte v paměti rezervovat "buffer", do kterého bude služba načtené bajty vkládat. V datovém segmentu vytvořte něco jako:
nacteno db 80, ? resb 80
Buffer nechť obsahuje tři složky: první bajt je vámi zadaná délka bufferu, který dáváte službě k dispozici. V bajtu zadáváte číslo bez znaménka. Číslo odpovídá počtu bajtů, který může služba maximálně načíst. Služba načte jeden celý řádek až do maximální zadané délky.
Dále rezervujete druhý bajt, do kterého služba vloží počet bajtů, které skutečně načetla. Je to číslo bez znaménka v jednom bajtu. Tento počet bajtů nezahrnuje znaky CR a LF. Zdůrazňuji, že se předává počet načtených bajtů, nikoli znaků, protože služba podporuje i čtení vícebajtových UNICODE znaků v transformaci UTF-8.
Od třetího bajtu jsou vloženy načtené bajty.
Adresu bufferu předáváte v DS:DX.
mov AH, 0x0a ; Identifikace služby Načíst řádek z terminálu mov DX,nacteno ; Offset začátku bufferu v segmentu dle DS int 21h ; resp. int 33 poskytne službu
Služba čte vstup po jednotlivých řádcích. Čte řádky, které vepíšete do pole "Zadat vstup z terminálu". Můžete zadat více než jeden řádek. Pokud už byl veškerý vstup čtením vyčerpán, služba vrací ZF=1, načtená délka je 0 a první načtený bajt je 0. Pokud byl načtený řádek (i když prázdný), služba vrací ZF=0. Řádky se oddělují znakem LF, znak CR je ignorován. Oba znaky CR a LF, pokud byly načteny, jsou vlženy do bufferu, ale nejsou započítány do délky ve druhém bajtu.
Zjisti stav čtení z terminálu
mov AH, 0x0b ; Identifikace služby Zjisti stav čtení z terminálu int 21h ; resp. int 33 poskytne službu
Služba vrací AL=0xff, pokud je k dispozici alespoň jeden nepřečtený bajt. Jinak vrací služba AL=0, tzn. že vstup už byl čtením vyčerpán.
Zjisti systémové datum
mov AH, 0x2a ; Identifikace služby Zjisti systémové datum int 21h ; resp. int 33 poskytne službu
Služba vrací rok v registru CX, měsíc v registru DH, den v měsíci v registru DL a pořadí dne v týdnu (neděle je 0) v registru AL.
Nastav systémové datum
mov AH, 0x2b ; Identifikace služby Nastav systémové datum mov AX,2020 ; rok, povoluje se interval 1980-2099 mov DH,1 ; měsíc mov DL,25 ; den v měsíci int 21h ; resp. int 33 poskytne službu
Služba vrací AL=0, pokud bylo datum nastaveno. Vrací AL=0xff, pokud je zadané datum chybné.
Zjisti systémový čas
mov AH, 0x2c ; Identifikace služby Zjisti systémový čas int 21h ; resp. int 33 poskytne službu
Služba vrací hodinu (24hodinový režim) v registru CH, minutu v registru CL, sekundu v registru DH, počet setin sekundy v registru DL.
Nastav systémový čas
mov AH, 0x2d ; Identifikace služby Nastav systémový čas mov CH,23 ; hodina ve 24hodinovém režimu mov CL,59 ; minuta mov DH,59 ; sekunda mov DL,81 ; počet setin sekundy int 21h ; resp. int 33 poskytne službu
Služba vrací AL=0, pokud byl čas nastaven. Vrací AL=0xff, pokud je zadaný čas chybný.
Jeden znak na terminál (služba BIOS)
mov AH,14 ; 14=0Eh Identifikace služby Jeden znak na terminál mov AL,'a' ; ASCII kód znaku, který se má na terminál vypsat int 10h ; resp. int 16 poskytne službu
FAQ
Překladač nasm mi hlásí chybu Line #XXX: error: label `yyyyy' changed during code generation
Problém nastává v případě použití pseudoinstrukcí dw, dd, dq s nedefinovanou hodnotou, tj. např. 'dw ?'. Zkuste nahradit za definovanou hodnotu, např. 'dw 0'.
Překladač nasm mi hlásí chybu "error: no instruction for this cpu level" pro instrukci ROL
Pokud použijete instrukci ROL (či ROR, SHL, SHR, SAL, SAR) s argumentem, kterým je číslo větší než 1, pak skutečně tato instrukce není procesorem 8086 podporována (podporují ji až procesory vyšší řady). Instrukce ROL (apod.) smí rotovat (posouvat) buď o 1 bit, nebo o počet bitů umístěný v registru CL. Tzn. povoleny jsou pouze varianty "ROL reg,1" a "ROL reg,CL".
Ovládání tiskáren
Vizte zvláštní Popis paralelního portu pro tisk na tiskárnu.
Využití přerušovacího systému
Využití přerušovacího systému.