Skip to content

Instantly share code, notes, and snippets.

@aleksmk
Created September 7, 2014 19:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aleksmk/ff48694ca148998b09e6 to your computer and use it in GitHub Desktop.
Save aleksmk/ff48694ca148998b09e6 to your computer and use it in GitHub Desktop.
Семинарска по микропроцесорска електроника
; Семинарска по предметот микропроцесорска електроника
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:
;; 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