*如果用戶程序的參數指向第3頁(最後一頁)中的一部分內存,則存在衝突,因為內核將始終在SYSCALL期間在完全相同的頁面內映射其RAM頁面。因此,它將將用戶的第3頁重新映射到第2頁(第三頁)中以訪問程序的參數。當然,如果參數是指示符,則將對其進行修改以指向新的虛擬地址(換句話說,指針將由16KB減去,以使其指向第2頁)。
為了能夠將熱情8位OS移植到沒有MMU/內存映射器的基於Z80的計算機,如上所述,內核具有新模式,可以通過menuconfig :noto-noth-mmu選擇。
在此模式下,OS代碼仍預計將在內存的第一個16KB中映射,從0x0000到0x3FFF ,其餘的預計將為RAM。
理想情況下,應從0x4000開始映射48KB的RAM,並將上升到0xFFFF ,但實際上,可以配置內核的期望少於此。為此,必須適當配置menuconfig中的兩個條目:
KERNEL_STACK_ADDR :這標誌著內核RAM區域的末端,顧名思義,將是內核堆棧的底部。KERNEL_RAM_START :這標誌著堆棧,內核和驅動程序使用的所有變量和驅動程序的所有變量都將存儲。當然,它必須足夠大才能存儲所有這些數據。有關信息,當前內核BSS部分大小約為1KB。堆棧深度取決於目標驅動程序的實現。只要沒有(大)緩衝區存儲在其中,為堆棧分配1KB應該足夠長。總體將至少3KB分配給內核RAM應該是安全的,未來的。總而言之,這是顯示內存使用段的圖:
關於用戶程序,堆棧地址將始終設置為KERNEL_RAM_START - 1 。它還對應於其在其可用地址空間中可用的最後一個字節的地址。這意味著程序可以通過執行SP - 0x4000來確定可用RAM的大小,該sp -0x4000在彙編中給出:
ld hl, 0
add hl, sp
ld bc, -0x4000
add hl, bc
; HL contains the size of the available RAM for the program, which includes the program's code and its stack.
Z80呈現多個通用登記冊,並非所有寄存器都在內核中使用,這是每個寄存器的範圍:
| 登記 | 範圍 |
|---|---|
| AF,BC,DE,HL | 系統和應用 |
| af',bc',de',hl' | 中斷處理程序 |
| ix,iy | 應用程序(在操作系統中未使用) |
這意味著操作系統不會更改IX和IY寄存器,因此可以在應用程序中自由使用。
備用寄存器(名稱後跟' )只能在中斷處理程序1中使用。應用程序不應使用這些寄存器。如果出於某種原因,您仍然必須使用它們,請考慮在使用它們的時間內禁用中斷:
my_routine:
di ; disable interrupt
ex af, af' ; exchange af with alternate af' registers
[...] ; use af'
ex af, af' ; exchange them back
ei ; re-enable interrupts
請記住,禁用中斷太久可能是有害的,因為系統不會從硬件(計時器,鍵盤,GPIOS ...)接收任何信號
Z80提供了8個不同的重置向量,因為該系統始終存儲在內存的第一個虛擬頁面中,因此這些都保留給OS::
| 向量 | 用法 |
|---|---|
| $ 00 | 軟件重置 |
| $ 08 | Syscall |
| $ 10 | 跳到HL中的地址(可用於調用HL) |
| $ 18 | 未使用 |
| $ 20 | 未使用 |
| $ 28 | 未使用 |
| $ 30 | 未使用 |
| $ 38 | 保留用於中斷模式1,可用於目標實施 |
執行用戶程序後,內核分配了3頁RAM(48KB),將讀取二進製文件以默認情況下以虛擬地址0x4000開始執行並加載其加載。該入口點虛擬地址可通過menuconfig配置,並具有選項KERNEL_INIT_EXECUTABLE_ADDR ,但請記住,現有程序在運行時無法重新定位,因此現有程序將不再工作。
如下所述, exec SYSCALL採用兩個參數:要執行的二進製文件名和一個參數。
該參數必須是一個無效的終止字符串,將被複製並傳輸到二進製文件以通過DE和BC寄存器執行:
DE包含字符串的地址。該字符串將被複製到新程序的內存空間,通常在堆棧頂部。BC包含該字符串的長度(因此,不包括空字節)。如果BC為0,則必須由用戶程序丟棄DE 。 該系統依靠SYSCALL來執行用戶程序和內核之間的請求。因此,這將是在硬件上執行操作的方法。可能的操作在下表中列出。
| num | 姓名 | 參數。 1 | 參數。 2 | 參數。 3 |
|---|---|---|---|---|
| 0 | 讀 | U8 DEV | U16 buf | U16尺寸 |
| 1 | 寫 | U8 DEV | U16 buf | U16尺寸 |
| 2 | 打開 | U16名稱 | U8標誌 | |
| 3 | 關閉 | U8 DEV | ||
| 4 | DSTAT | U8 DEV | U16 DST | |
| 5 | 統計 | U16名稱 | U16 DST | |
| 6 | 尋找 | U8 DEV | U32偏移 | u8從那裡 |
| 7 | ioctl | U8 DEV | U8 CMD | U16 arg |
| 8 | Mkdir | U16路徑 | ||
| 9 | Chdir | U16路徑 | ||
| 10 | Curdir | U16路徑 | ||
| 11 | OPENDIR | U16路徑 | ||
| 12 | readdir | U8 DEV | U16 DST | |
| 13 | RM | U16路徑 | ||
| 14 | 山 | U8 DEV | U8字母 | U8 fs |
| 15 | 出口 | U8代碼 | ||
| 16 | exec | U16名稱 | U16 argv | |
| 17 | DUP | U8 DEV | U8 NDEV | |
| 18 | msleep | U16持續時間 | ||
| 19 | Settime | U8 ID | U16時間 | |
| 20 | GetTime | U8 ID | U16時間 | |
| 21 | 設定 | U16日期 | ||
| 22 | GetDate | U16日期 | ||
| 23 | 地圖 | U16 DST | U24 SRC | |
| 24 | 交換 | U8 DEV | U8 NDEV |
請檢查以下部分以獲取有關這些呼叫及其參數的更多信息。
注意:某些syscall可能會毫不費力。例如,在不支持目錄的計算機上,可能會省略與目錄相關的SYSCALL。
為了執行SYSCALL,必須將操作編號存儲在寄存器L中,必須按照以下規則存儲參數:
| API中的參數名稱 | Z80註冊 |
|---|---|
| U8 DEV | H |
| U8 NDEV | E |
| U8標誌 | H |
| U8 CMD | C |
| U8字母 | D |
| U8代碼 | H |
| U8 fs | E |
| U8 ID | H |
| u8從那裡 | A |
| U16 buf | DE |
| U16尺寸 | BC |
| U16名稱 | BC |
| U16 DST | DE |
| U16 arg | DE |
| U16路徑 | DE |
| U16 argv | DE |
| U16持續時間 | DE |
| U16時間 | DE |
| U16日期 | DE |
| U24 SRC | HBC |
| U32偏移 | BCDE |
最後,代碼必須執行RST $08指令(請檢查重置向量)。
返回的值放置在A中。該值的含義是每個調用的特定的,請檢查有關例程的文檔以獲取更多信息。
為了最大化用戶程序與Zeal 8位OS內核的兼容性,無論是以MMU還是No-MMU模式編譯的內核,SYSCALLS參數約束都相同:
任何傳遞給Syscall的緩衝區均不得穿越16KB虛擬頁面
換句話說,如果尺寸n的buf位於虛擬頁i中,其最後一個字節,由buf + n - 1指向,也必須位於完全相同的i上。
例如,如果read syscall,請使用:
DE = 0x4000和BC = 0x1000 ,參數是正確的,因為DE指向第1頁的緩衝區(從0x4000到0x7FFF )DE = 0x4000和BC = 0x4000 ,參數是正確的,因為DE指向第1頁的緩衝區(從0x4000到0x7FFF )DE = 0x7FFF和BC = 0x2 ,參數不正確,因為DE指向的緩衝區在第1頁和第2頁之間。exec即使熱情8位OS是一個單一任務操作系統,它也可以執行並保留多個程序的內存。當程序A執行A程序B時,要歸功於exec SYSCALL,它將提供一個mode參數,可以是EXEC_OVERRIDE_PROGRAM或EXEC_PRESERVE_PROGRAM :
EXEC_OVERRIDE_PROGRAM :此選項告訴內核不再需要執行程序A,因此程序B將加載到與程序A相同的地址空間中。EXEC_PRESERVE_PROGRAM :此選項告訴內核需要將程序保存在RAM中,直到程序B完成執行並調用exit syscall。為此,內核將分配3個新的內存頁面( 16KB * 3 = 48KB ),其中它存儲了新加載的程序B。 BertonB退出後,內核釋放了程序B的先前分配的頁面,重新啟動程序A的存儲器頁面,並將手放回程序A中。多虧了選項CONFIG_KERNEL_MAX_NESTED_PROGRAMS ,執行樹的深度是在menuconfig中定義的。它代表了一次可以在RAM中存儲的最大程序數。例如,如果深度為3,則程序A可以調用程序B,程序B可以調用程序C,但是程序C無法調用任何其他程序。但是,如果程序使用EXEC_OVERRIDE_PROGRAM調用exec ,則不會增加深度,因為加載的新程序將覆蓋當前一個。這樣,如果我們拿回上一個示例,則程序C可以在且僅當它在EXEC_OVERRIDE_PROGRAM模式下調用exec SYSCALL時調用程序。
當執行子程序時,請注意整個打開的設備表(包括文件,目錄和驅動程序),當前目錄和CPU寄存器將被共享。
這意味著,如果程序A使用描述符3打開文件,則程序B將繼承該索引,因此也能夠讀取,寫入甚至關閉該描述符。相互互惠,如果B打開文件,目錄或驅動程序並在沒有關閉的情況下退出,則程序A也將可以訪問它。因此,遵循的一般指南是,在退出之前,程序必須始終關閉其打開的描述符。重置打開的設備和當前目錄的唯一時刻是初始程序(上一個示例中的程序A)退出時。在這種情況下,內核將關閉打開的設備表中的所有描述符,重新打開標準輸入和輸出,然後重新加載初始程序。
這也意味著,當在彙編程序中調用exec SYSCALL時,在成功方面,除HL以外的所有寄存器都必須更改,因為它們將由子程序使用。因此,如果您想保存AF , BC , DE , IX或IY ,則必須在調用exec之前將它們推入堆棧上。
SYSCALL都記錄在為彙編和C提供的標頭文件中,您將在kernel_headers/ Directory中找到這些標頭文件,請檢查其doodme文件以獲取更多信息。
駕駛員由包含的結構組成:
SER0 , SER1 , I2C0等。允許但不建議非ASCII字符。init例程的地址,當內核靴子時稱為。read例程的地址,其中參數和返回地址與SYSCALL表中的地址相同。write程序的地址與上述相同。open例程的地址,與上述相同。close程序的地址與上面相同。seek常規的地址,與上述相同。ioctl例程的地址,與上述相同。deinit例程的地址,卸載驅動程序時調用。這是一個簡單的驅動程序註冊的示例:
my_driver0_init:
; Register itself to the VFS
; Do something
xor a ; Success
ret
my_driver0_read:
; Do something
ret
my_driver0_write :
; Do something
ret
my_driver0_open :
; Do something
ret
my_driver0_close :
; Do something
ret
my_driver0_seek :
; Do something
ret
my_driver0_ioctl :
; Do something
ret
my_driver0_deinit :
; Do something
ret
SECTION DRV_VECTORS
DEFB "DRV0"
DEFW my_driver0_init
DEFW my_driver0_read
DEFW my_driver0_write
DEFW my_driver0_open
DEFW my_driver0_close
DEFW my_driver0_seek
DEFW my_driver0_ioctl
DEFW my_driver0_deinit註冊驅動程序在於將此信息(結構)放入稱為DRV_VECTORS的部分中。該訂單非常重要,因為任何駕駛員依賴性都應在編譯時解決。例如,如果驅動程序A取決於驅動程序B ,則必須將B的結構A DRV_VECTORS部分中。
在啟動時, driver組件將瀏覽整個DRV_VECTORS部分,並通過調用其init例程來一個一個一個一個初始化驅動程序。如果此例程返回ERR_SUCCESS ,則將註冊驅動程序,用戶程序可以打開,讀,寫,ioctl等...
驅動程序可以隱藏在程序中,這對於僅由內核文件系統層訪問的磁盤驅動程序來說很方便。為此, init例程應返回ERR_DRIVER_HIDDEN 。
由於應用程序和硬件之間的通信都是通過上述SYSCALL完成的,因此我們需要在用戶應用程序和內核之間進行一層,這將確定我們是否需要調用驅動程序或文件系統。在展示此類建築的層次結構之前,讓我們談談磁盤和驅動程序。
可以這樣看到不同的層:
流程圖TD;
應用程序(用戶程序)
VFS(虛擬文件系統)
DSK(磁盤模塊)
DRV(驅動程序實現:視頻,鍵盤,串行等...)
FS(文件系統)
Sysdis(Syscall調度員)
HW(硬件)
時間(時間和日期模塊)
mem(內存模塊)
加載器(裝載機模塊)
應用 - syscall/rst 8-> sysdis;
sysdis - getDate/time->時間;
sysdis - 安裝 - > dsk;
sysdis-> vfs;
sysdis - map-> mem;
sysdis- ex/exit-> loader;
vfs-> dsk&drv;
DSK <-> fs;
fs-> drv;
drv-> hw;
熱情8位操作系統最多可立即支撐26個磁盤。磁盤用字母(從A到Z)表示。決定將磁盤安裝在系統中的位置是磁盤駕駛員的責任。
第一個驅動器A很特別,因為它是系統將尋找首選項或配置的驅動器。
在應用程序中, path可能是:
my_dir2/file1.txt/my_dir1/my_dir2/file1.txt file1.txtB:/your_dir1/your_dir2/file2.txt 即使操作系統完全可恢復,並且不需要任何文件系統或磁盤即可啟動,但它將在嘗試加載初始程序(默認情況下稱為init.bin時,它將檢查默認磁盤並請求該文件。因此,即使是最基本的存儲也需要一個文件系統或類似的內容。
已經實現的第一個“文件系統”稱為“原始table”。顧名思義,它代表文件的連續性,而不是目錄,在存儲設備中,沒有特別的順序。文件名大小限制與內核的限制相同:16個字符,包括可選.和擴展。如果要將其與C代碼進行比較,它將是定義每個文件的結構數組,然後按照相同的順序進行文件內容。 romdisk包裝器源代碼可在packer/此存儲庫的根部提供。檢查其讀書文件以獲取有關它的更多信息。
也實現的第二個文件系統被命名為Zealfs。它的主要目的是將其嵌入非常小的儲藏室中,從8KB到64KB。它是可讀和可寫的,它支持文件和目錄。在專用存儲庫中有關它的更多信息。
第三個在8位OS上使用的文件系統是FAT16。非常著名的,幾乎所有桌面操作系統都支持,可在CompactFlash甚至SD卡上使用,這幾乎是必備的。它尚未實施,但已計劃。 FAT16並不完美,因為它不適合小型存儲,這就是為什麼需要Zealfs的原因。
熱情8位OS基於兩個主要組件:內核和目標代碼。單獨的內核無濟於事。目標需要實現驅動程序,內核內使用的一些MMU宏和鏈接腳本。鏈接器腳本相當簡單,它以z80asm彙編器中必須在最終二進制中鏈接的順序列出了這些部分。
內核當前使用以下各節,必須包含在任何鏈接腳本中:
RST_VECTORS :包含重置向量SYSCALL_TABLE :包含一個表Syscall i例程地址在索引i上的表格,必須在256上對齊SYSCALL_ROUTINES :包含從重置向量調用的Syscall調度程序KERNEL_TEXT :包含內核代碼KERNEL_STRLIB :包含內核中使用的與字符串相關的例程KERNEL_DRV_VECTORS :代表一個驅動程序來初始化的驅動程序,請檢查驅動程序部分以獲取更多詳細信息。KERNEL_BSS :包含內核代碼使用的數據,必須在RAM中DRIVER_BSS :不直接由內核使用,應在驅動程序中定義並使用。內核將其設置為啟動時的0,必須大於2個字節如前所述,熱情的8位計算機支持仍然是部分的,但足以使命令行計劃運行。 romdisk是在內核構建之前創建的,這是在target/zeal8bit/unit.mk中指定的script.sh中完成的。
該腳本將編譯init.bin程序,並將其嵌入將串聯到編譯的OS二進制中的romdisk中。最終的二進制可以直接閃爍到Nor Flash。
仍然需要以特殊順序實施的是:
已經建立了一個快速到TRS-80 Model-I計算機的端口,以顯示如何對沒有MMU的目標進行端口和配置Zeal 8位OS。
此端口很簡單,因為它只是在屏幕上顯示了引導橫幅,僅此而已。為此,僅實現了用於文本模式的視頻驅動程序。
要擁有一個更有趣的端口,需要實現以下功能:
init.bin /romdisk的磁盤,只能讀取,因此可以存儲在ROM上Shawn Sijnstra撰寫和維護的EZ80驅動Agon Light的港口。隨意將叉子用於特定的錯誤/請求。這使用了非MMU內核,並實現了Zeal 8位計算機實現支持的大多數功能。
此端口需要一個從正確位置存儲和執行二進制的裝載程序。二進制是Osbootz,可在此處提供。
請注意,端口使用終端模式來簡化鍵盤I/O。這也意味著日期函數不可用。
其他值得注意的功能:
要向另一台計算機端口的熱情8位OS MMU版本,請確保首先有一個內存映射器,將Z80的64KB地址空間劃分為MMU版本的16KB的4頁。
要端口No-Mmu Zeal 8位OS,請確保可以從虛擬地址0x4000及更高版本獲得RAM。最理想的案例是ROM是用戶程序和內核RAM剩餘48KB中OS和RAM的第一個16KB。
如果您的目標兼容,請按照說明:
Kconfig文件,然後將條目添加到config TARGET和config COMPILATION_TARGET選項中。以已經存在的例子為例。target/目標中創建一個新目錄,名稱必須與新config TARGET選項中指定的名稱相同。unit.mk文件。這是包含所有要組裝的源文件或要包含的文件的文件。unit.mk文件,為此,您可以填充以下make :SRCS :要組裝的文件列表。通常,這些是驅動程序(強制性)INCLUDES :包含可以包括的標頭文件的目錄PRECMD :在內核開始構建之前要執行的bash命令POSTCMD :bash命令將在內核完成後執行mmu_h.asm文件,該文件將包含在內核中以配置和使用MMU。檢查文件target/zeal8bit/include/mmu_h.asm以查看其外觀。zos_disks_mount ,該驅動程序包含一個init.bin文件,由內核在啟動時加載和執行。zos_vfs_set_stdout 。有關完整的ChangElog,請檢查發布頁面。
歡迎捐款!請隨意修復您可能看到或遇到的任何錯誤,或實現您發現重要的任何功能。
貢獻:
(*)一個很好的提交信息如下:
Module: add/fix/remove a from b
Explanation on what/how/why
例如:
Disks: implement a get_default_disk routine
It is now possible to retrieve the default disk of the system.
根據Apache 2.0許可分發。有關更多信息,請參見LICENSE文件。
您可以自由地將其用於個人和商業用途,因此不能刪除每個文件中存在的樣板。
對於任何建議或請求,您可以在Contact [at] zeal8bit [dot] com上與我聯繫
對於功能請求,您也可以打開問題或拉動請求。
儘管如此,它們仍不被視為非易失性。換句話說,中斷處理程序不得假設其在任何替代寄存器中編寫的數據都將保留在下次調用之前。 ↩