Created
September 7, 2014 19:34
-
-
Save aleksmk/ff48694ca148998b09e6 to your computer and use it in GitHub Desktop.
Семинарска по микропроцесорска електроника
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Семинарска по предметот микропроцесорска електроника | |
BITS 16 ; Ќе работиме во реален мод, 16 битни регистри. | |
JMP short POCETOK ; Скокни ги следните дефиниции, тие се потребни само за FAT12 filesystem-от | |
NOP ; NOP пред FAT12 информациите | |
; Следат информации и дефиниции потребни за FAT12 filesystem-от | |
; Ова не е машински код, и треба да се прескокни | |
; Подесувањата подолу се однесуваат на 1.44 MB, 3.5" floppy дискета | |
NasaLabela db "ME BOOT " ; Лабела по која што може да го идентификуваме floppy дискот, мора да е 8 бајти | |
BytesPerSector dw 512 ; Бајти во еден сектор | |
SectorsPerCluster db 1 ; Сектори во еден кластер | |
ReservedForBoot dw 1 ; Колку сектори има boot секцијата (каде што е овој код) | |
NumberOfFats db 2 ; Број на FAT табели | |
RootDirEntries dw 224 ; Бројот на максимални директории/фајлови во root директоријата | |
LogicalSectors dw 2880 ; Бројот на сите сектори | |
MediumByte db 0F0h ; Дескриптор за медиумот | |
SectorsPerFat dw 9 ; Сектори по FAT табела | |
SectorsPerTrack dw 18 ; Сектори по лента | |
HeadsPerCylinder dw 2 ; Број на глави | |
HiddenSectors dd 0 ; Скриени сектори? | |
LargeSectors dd 0 ; FAT32 информации, непотребно за FAT12 | |
DriveNo dw 0 ; Бројот на медиумот: 0 | |
Signature db 41 ; Потпис на медиумот: 41 за floppy | |
VolumeID dd 00000000h ; Volume ID: било кој *единствен* број | |
VolumeLabel db "ME RULES " ; Volume Лабела: стриктно 11 карактери | |
FileSystem db "FAT12 " ; Filesystem тип: FAT12 | |
;; Подесување на сегментните регистри | |
POCETOK: | |
MOV AX, 07C0h ; Податочниот сегмент | |
MOV DS, AX | |
MOV AX, 09E0h ; Стек сегментот | |
MOV SS, AX | |
MOV SP, 4096 ; На крајот од стек сегментот -> стекот расти надолу во меморија | |
MOV EAX, 0 ; Потребно за некои верзии на BIOS системи | |
;Подеси ES:BX да покажува кон нашиот бафер на крај од кодов => ES:BX = 07E0h:0000h | |
;MOV BX, 07E0h | |
;MOV ES, BX | |
;MOV BX, 00h | |
MOV BX, DS | |
MOV ES, BX | |
MOV BX, BUFFER | |
MOV SI, booting | |
CALL PECATI_NIZA | |
;; Читање на root директоријата во BUFFER меморијата | |
CITAJ_ROOT: | |
; Калкулирај ги Cylinder-Head-Sector за логичкиот сектор 19 (ROOT Directory) | |
MOV AX, 19 | |
CALL OD_LBA_VO_CHS | |
MOV AH, 2 ; Читање на сектори за INT 13h | |
MOV AL, 14 ; Колку сектори ќе читаме ? | |
STC ; Подеси го Carry за секој случај, INT 13h ќе го исчисте ако нема грешка | |
PUSHA | |
INT 13h ; Читај! | |
POPA | |
JNC OK_CITANJE ; Доколку CF != 1, продолжи, доколку не печати грешка и рестартирај | |
MOV SI, disk_error | |
CALL PECATI_NIZA | |
JMP RESTARTIRAJ | |
OK_CITANJE: ; Читањето поминало добро, може да ја анализираме root директоријата | |
BARAJ_ROOT_DIR: | |
; Root директоријата се наоѓа во нашиот бафер, подеси ES:DI да покажува кон него | |
MOV AX, DS | |
MOV ES, AX | |
MOV DI, BUFFER | |
MOV CX, word [RootDirEntries] ; Треба да ги бараме сите (16*14=224) записи за нашиот микрокернел | |
MOV AX, 0 ; Ќе почнеме да бараме од офсет 0 т.е. од нултиот запис | |
SLEDEN_ZAPIS: | |
MOV DX, CX ; Зачувај го CX т.е. кој запис го бараме. CX ќе нѝ треба за REP CMPSB | |
MOV CX, 11 ; Големината на името на фајлот. Мора да е 11 бајти. | |
MOV SI, microkernel ; Името на нашиот кернел. | |
rep CMPSB ; Повторувај CMPSB сѐ додека CX не стани 0 | |
JE KERNELOT_E_PRONAJDEN ; Доколку Zero флегот е подесен т.е. дека не постоело разлика помеѓу низите - ES:DI и DS:SI | |
ADD AX, 32 ; Ако не, зголеми го AX за еден запис (32 бајти) и продолжи | |
MOV DI, BUFFER ; CMPSB го има *расипано* DI, го поправаме | |
ADD DI, AX ; Следниот запис | |
MOV CX, DX ; Го враќаме редниот број | |
LOOP SLEDEN_ZAPIS ; Доколку CX != 0, скокни на SLEDEN_ZAPIS и повторно барај | |
MOV SI, file_not_found ; Доколку CX == 0, значи фајлот не е пронајден. Печати порака и рестартирај. | |
CALL PECATI_NIZA | |
JMP RESTARTIRAJ | |
KERNELOT_E_PRONAJDEN: | |
;; Штом знаеме дека го имаме кернел фајлот на флопи дискот, треба да почнеме да го читаме. | |
;; Прво треба да видиме каде се наоѓа неговиот прв кластер. Според табелата во написот, | |
;; првиот кластер се наоѓа на логички офсет од 26 бајти од почетокот на root записот. | |
;; Поради CMPSB во барањето на фајлот, којшто го направи DI да е зголемен за 11 бајти, | |
;; треба да додадеме само 15 бајти за да дојдиме до локацијата каде што е сместена вредноста | |
;; за првиот логички кластер од фајлот. | |
MOV AX, word [ES:DI + 0Fh] ; 11 + 15 = 26 бајти офсет | |
MOV word [cluster], AX ; Ја ставаме вредноста на кластерот во променливата cluster | |
; Припрема за читање на првата FAT табела во меморија | |
; Подеси го ES:BX да покажува кон BUFFER | |
MOV BX, DS | |
MOV ES, BX | |
MOV BX, BUFFER | |
; Калкулирај ги CHS вредностите за првиот логички блог / кластер | |
MOV AX, 1 | |
CALL OD_LBA_VO_CHS | |
MOV AH, 2 ; Читање на диск | |
MOV AL, 9 ; Ќе читаме 9 блокови т.е. целата FAT1 табела | |
STC | |
PUSHA | |
INT 13h | |
POPA | |
JNC FAT_OK ; Доколку CF=0, значи ОК е читањето | |
MOV SI, disk_error ; Доколку пак CF=1, печати грешка и рестартирај се! | |
CALL PECATI_NIZA | |
JMP RESTARTIRAJ | |
FAT_OK: ; Успешно читање на FAT табелата. | |
CITAJ_CLUSTER: | |
MOV AX, word [cluster] ; Во [cluster] ја имаме вредноста на *логичкиот* кластер каде што се наоѓа податокот. | |
ADD AX, 31 ; Треба да додадеме 31 за да ја најдиме бројката на кластерот | |
CALL OD_LBA_VO_CHS ; Пресметај CHS за [cluster] + 31 | |
; Подеси го ES:BX за следното читање со INT 13h | |
MOV AX, 1000h ; Локацијата на сегментот за микрокернелот! | |
MOV ES, AX | |
MOV BX, word [pointer] ; pointer кон 512 бајти блок од меморија во 1000h:0000h сегментот. | |
MOV AH, 2 | |
MOV AL, 1 | |
STC | |
PUSHA | |
INT 13h ; Вчитај го првиот кластер од дискот, во првата мемориска локација. | |
POPA | |
JNC PRESMETAJ_SLEDEN_CLUSTER ; Доколку читањето е успешно, одиме на следниот кластер | |
MOV SI, disk_error ; Доколку не, печати порака и рестартирај | |
CALL PECATI_NIZA | |
JMP RESTARTIRAJ | |
PRESMETAJ_SLEDEN_CLUSTER: | |
;; Употребуваме мала аритметика за да пронајдеме кој кластер од фат табелата треба да го читаме. | |
;; Формулата е : Cluster-Offset = (Cluster * 3) / 2 | |
;; Резултатот од целобројното делење е бројот на бајтите за колку што напреднуваме во BUFFER, | |
;; а остатокот во DX нѝ кажува како ќе го исхендламе фактот што работиме со 12 битни броеви (парен или непарен кластер) | |
MOV AX, [cluster] | |
MOV DX, 0 | |
MOV BX, 3 | |
MUL BX ; 16 битно множење. BX се множи со AX и резултатот се става во DX:AX | |
MOV BX, 2 ; Сега ќе делиме со 2 | |
DIV BX ; Целосното делење се става во AX, а остатокот во DX | |
MOV SI, BUFFER | |
ADD SI, AX ; Офсетот во FAT табелата каде што треба да читаме. | |
MOV AX, word [DS:SI] ; Овде се наоѓа вредноста на следниот кластер во DATA секцијата на флопи дискот | |
CMP DX, 01h ; Доколку DX = 0, работиме со парен кластер, ако пак DX = 1, работиме со непарен кластер | |
JZ short PAREN | |
NEPAREN: ; Доколку е непарен кластерот, избриши ги најмалите 4 бита од AX. Тие припаѓаат на друг кластер | |
SHR AX, 4 | |
JMP short PRODOLZI | |
PAREN: ; Доколку е парен кластерот, избриши ги првите 4 бита на AX. | |
AND AX, 0FFFh | |
PRODOLZI: | |
MOV word [cluster], AX ; Сега ја знаеме локацијата на следниот кластер. Ја ставаме во AX | |
CMP AX, 0FF8h ; Доколку AX == 0FF8h, сме дошле на крај од фајлот. Нашата работа е завршена. | |
JAE FINISH | |
ADD word [pointer], 512 ; Ако не сме завршиле, ги пополнуваме следните 512 бајти од меморијата за кернелот. | |
JMP CITAJ_CLUSTER | |
FINISH: | |
MOV SI, hello_master | |
CALL PECATI_NIZA | |
MOV DL, byte [bootdev] ; Предади ја локацијата за бројот на драјвот каде што сме | |
JMP 1000h:0000h ; Безусловен скок на локацијата на микрокернелот. Far jump. | |
;; Надолу следат процедури потребни за нашиот bootloader. | |
;; ! EDIT ON YOUR OWN RISK ! | |
;; Процедура за претворање на логичкиот сектор во Cylinder-Head-Sector; Влез: AX=LBA, Излез: CH=Cylinder, DH=Head, CL=Sector | |
; за потребите на нижата процедура INT 13h | |
; Формули: | |
; | |
; Sector = (LBA % [SectorsPerTrack]) + 1 | |
; Head = (LBA / [SectorsPerTrack]) % HeadsPerCylinder | |
; Cylinder = (LBA / [SectorsPerTrack]) / HeadsPerCylinder | |
OD_LBA_VO_CHS: | |
PUSH BX ; BX ќе ни е помошна променлива | |
MOV BX, AX | |
; Секторот | |
MOV DX, 0 ; Ќе делиме со 16 битен регистер, операцијата div бара DX:AX | |
DIV word [SectorsPerTrack] ; DIV го дели акумулаторот со [SectorsPerTrack] | |
ADD DL, 01h | |
MOV CL, DL ; CL = Секторот | |
; Главата и цилиндарот | |
MOV AX, BX | |
MOV DX, 0 | |
DIV word [SectorsPerTrack] | |
MOV DX, 0 ; Потребен ни е целосниот дел од LBA / [SectorsPerTrack], па затоа DX=0 | |
DIV word [HeadsPerCylinder] | |
MOV DH, DL | |
MOV CH, AL | |
MOV DL, byte [bootdev] | |
POP BX | |
RET | |
;; Процедура за печатење на одредена низа од ASCII карактери на екран со помош на INT 10h | |
; Влез: Локацијата на првиот бајт од низата во SI | |
PECATI_NIZA: | |
PUSHA ; За секој случај, втисни ги сите регистри на стекот | |
CLD | |
.povtori: | |
LODSB ; Од DS:SI земи го карактерот и смести го во акумулаторот (AL). Зголеми го SI за еден (читаме бајти) | |
CMP AL, 0 | |
JE .kraj | |
MOV AH, 0Eh ; За печатење на карактерот кој ќе се наоѓа во AL - ASCII ONLY! | |
INT 10h | |
JMP .povtori | |
.kraj: | |
POPA ; Врати ги втиснатите регистри | |
RET ; Врати се во нормалниот тек | |
RESTARTIRAJ: | |
MOV AX, 0 | |
INT 16h ; Чекај додека не се притисне копче | |
MOV AX, 0 | |
INT 19h ; Рестарт | |
;; Променливи и константи | |
booting db "Booting....", 0Ah, 0Dh, 0 | |
hello_master db "Hello Master, I'm ready to serve you.", 0Ah, 0Dh, 0 | |
microkernel db "KERNEL IMG" ; Името на микрокернелот, точно 11 карактери | |
disk_error db "Greska na floppy diskot!", 0 | |
file_not_found db "Kernelot ne e pronajden!", 0 | |
bootdev db 0 ; Бројот на boot уредот | |
cluster dw 0 ; Променлива за читање на кластерот од дискот | |
pointer dw 0 ; Покажувач кон локацијата на запис на микрокернелот | |
;; Крај на нашиот код. Следува bootloader потписот: | |
times 510-($-$$) db 0 ; Пополни го останатио дел од 512-те бајти со 0-ли | |
dw 0AA55h ; Стандардерн потпис | |
; Од тука подолу следува 8K бафер простор за потребата на INT 13h - диск операции | |
BUFFER: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; Proof of Concept | |
;; Микрокернел кој што е сместен во мемориски простор 1000h:1000h | |
;; | |
;; | |
BITS 16 ; Ќе работиме во | |
MOV AX, 1000h ; Подеси ги сите сегментни регистри да покажуваат кон 1000h:1000h | |
MOV DS, AX | |
MOV ES, AX | |
MOV FS, AX | |
MOV GS, AX | |
;; Прво ќе го исчистиме целиот екрај | |
MOV AH, 00h | |
MOV AL, 07h | |
INT 10h | |
MOV SI, hello_master | |
CALL PECATI_NIZA | |
PECARI_RTC: | |
CLC ; Исчисти го Carry битот, потребно за INT 1Ah | |
MOV AH, 02h ; Читај го системското време | |
INT 1Ah | |
; Generiraj ASCII od BCD. | |
; CH - HOUR | |
; CL - MINUTES | |
; DH - SECONDS | |
; +48 | |
MOV DI, rtc | |
MOV AL, CH | |
SHR AL, 4 | |
ADD AL, 48 | |
MOV [DI], AL | |
INC DI | |
MOV AL, CH | |
AND AL, 0Fh | |
ADD AL, 48 | |
MOV [DI], AL | |
INC DI | |
INC DI | |
MOV AL, CL | |
SHR AL, 4 | |
ADD AL, 48 | |
MOV [DI], AL | |
INC DI | |
MOV AL, CL | |
AND AL, 0Fh | |
ADD AL, 48 | |
MOV [DI], AL | |
INC DI | |
INC DI | |
MOV AL, DH | |
SHR AL, 4 | |
ADD AL, 48 | |
MOV [DI], AL | |
INC DI | |
MOV AL, DH | |
AND AL, 0Fh | |
ADD AL, 48 | |
MOV [DI], AL | |
INC DI | |
INC DI | |
MOV SI, rtc | |
CALL PECATI_NIZA | |
JMP RESTARTIRAJ | |
;; Процедура за печатење на одредена низа од ASCII карактери на екран со помош на INT 10h | |
; Влез: Локацијата на првиот бајт од низата во SI | |
PECATI_NIZA: | |
PUSHA ; За секој случај, втисни ги сите регистри на стекот | |
CLD | |
.povtori: | |
LODSB ; Од DS:SI земи го карактерот и смести го во акумулаторот (AL). Зголеми го SI за еден (читаме бајти) | |
CMP AL, 0 | |
JE .kraj | |
MOV AH, 0Eh ; За печатење на карактерот кој ќе се наоѓа во AL - ASCII ONLY! | |
INT 10h | |
JMP .povtori | |
.kraj: | |
POPA ; Врати ги втиснатите регистри | |
RET ; Врати се во нормалниот тек | |
RESTARTIRAJ: | |
MOV AX, 0 | |
INT 16h ; Чекај додека не се притисне копче | |
MOV AX, 0 | |
INT 19h ; Рестарт | |
;; ПРОМЕНЛИВИ : | |
hello_master db "Hello Master, I'm ready to serve you.", 0Ah, 0Dh, 0 | |
rtc db "xx:xx:xx", 0Ah, 0Dh, 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment