2013年2月20日 星期三

MikeOS組譯程式開發者手冊

這份文件用來解釋如何用組合語言來開發MikeOS的軟體。他將說明所需要的工具,即MikeOS程式是如何運作的。和使用核心所提供的系統呼叫。假如你有任何問題,可以參考MikeOS網站中有連絡的細節與信箱列表資訊。
點擊左邊的連結來瀏覽這份文件。

概觀
介紹
許多MikeOS程式在16位元下撰寫,真實模式的組合語言。(作業系統也包含一個BASIC解譯器)因為MikeOS和他的程式被放在單一個64K記憶體區段,你不需要關注使用的區段暫存器。
MikiOS程式載入到32K 點(32768)的區段,且最大為32K。結果,你的程式將會用下面這兩行做開頭:
BITS 16
ORG 32768
在MikeOS中有許多系統呼叫用來控制螢幕,取得輸入,操作字串,載入/儲存檔案等等。所有的MikeOS系統呼叫通過對暫存器傳遞參數,而非堆疊。要在你的程式中使用它,你需要;
%INCLUDE "mikedev.inc"

這沒有包含任何碼,但會有等同的指令指向核心中的系統呼叫向量。所以,引入這個檔案你才能呼叫。MikeOS的os_print_string常式不用確實知道他在核心的哪個位置。mikedev.inc被包括在MikeOS下載中的programs/目錄─他也提供一個對系統呼叫的快速參考。

假如一個MikeOS程式在指令行下執行,一個或多個參數被提供給指令,他們會藉由SI暫存器傳遞給程式通過一個零結束的字串。

需要的工具

寫下MikeOS程式你需要:
NASM ─ 一個強大,免費且開放原始碼的組譯器
mikedev.inc ─ 系統呼叫向量描述在上面
一個方式新增程式到磁片

細節是:MikeOS使用NASM在他的程式和核心程式碼,所以我們在這裡推薦他。當然你可以使用任何組譯器來輸出二元檔案且接受16位元碼。NASM在許多Linux系統中可以取得,或你也可以從這裡下載Windows版本(取得'win32'檔案)。

第二點,複製programs/mikedev.inc 

第三點,假如你要寫入MikeOS到實體硬碟,你只要複製你的.BIN程式到磁片上(僅有根目錄),啟動MikeOS並測試他。假如你在虛擬mikeos.flp磁片影像下工作,可以查看使用者手冊中的複製檔案。

例子

程式碼

這裡有一個MikeOS程式的範例,使用NASM格式,用來印出一段文字到螢幕上,且等待使用者按下一個按鍵:

BITS 16
    ORG 32768
    %INCLUDE 'mikedev.inc'

start:
    mov si, mystring
    call os_print_string

    call os_wait_for_key

    ret

    mystring db 'My first MikeOS program!', 0

建置

將上面的程式儲存為testapp.asm,並輸入指令來組譯他(在Windows和Linux都適用):
nasm -f bin -o testapp.bin testapp.asm

使用-f bin選項用來告訴NASM,我們想要一個二元檔案:沒有標頭或片段。結果的可執行檔是testapp.bin,我們可以複製到我們的磁片或加入到虛擬磁片影像,描述在使用者手冊中的複製檔案。然後我們可以啟動MikeOS並執行程式。

解釋

這是一個簡短的程式,但是我們將描述他是如何工作的。最初的三行不能被組譯為機器碼指令,但是這個宣告告訴NASM我們要在16位元模式下操作,我們的碼被寫入執行從32K的位置,我們將使用MikeOS API中的系統呼叫。

接下來,我們有一個'start:'標籤,他能讓我們的程式更清楚。我們放入零結束字串的位置到SI暫存器,然後呼叫MikeOS中的os_print_string常式,我們能存取藉由在mikedev.inc中的向量列表。之後我們暫停程式直到使用者按下一個鍵。

所以的MikeOS程式必須以ret指令結束,傳回控制權到作業系統中,之後我們擁有資料給我們的字串。

系統呼叫

介紹

MikeOS核心包含系統呼叫用來輸出到螢幕上,取得鍵盤輸入,操作字串,產生PC聲音,數學運算,硬碟輸入輸出。你可以參考mikedev.inc,和查看使用這些 呼叫的練習從programs/目錄。

這裡我們有一個細節API的描述。你可以查看系統呼叫的完整的程式碼從source/features目錄。每個API都包含了組譯程式碼,所以你能增強系統呼叫的功能,須參考MikeOS系統開發者手冊。

磁碟
os_get_file_list

產生磁碟的檔案字串,檔案間以逗號分隔
IN/OUT: AX = 儲存零結束檔名字串之位置

Example:
    mov ax, .filestring
    call os_get_file_list

    ; Now .filestring will contain something like
    ; 'HELLO.BIN,FOO.BAR,THREE.TXT', 0

    .filestring    times 256 db 0

os_load_file

載入檔案到RAM

IN: AX=檔案位置,CX=在RAM中載入檔案的位置
OUT:BX=檔案大小(以bytes計), carry set if file not found
例子:
    mov ax, filename
    mov cx, 36960        ; 4K after where external program loads
    call os_load_file

    ...

    filename db 'README.TXT'

os_write_file

儲存檔案到磁碟(最大64K)
IN: AX = 檔名, BX = 資料位置, CX = 寫入多少位元組
OUT: Carry clear if OK, set if failure

Example:
    ; For this example, there's some text stored in .data

    mov ax, .filename
    mov bx, .data
    mov cx, 4192
    call os_write_file

    .filename    db 'HELLO.TXT', 0
    .data        times 4192 db 0

os_file_exists

檢查檔案是否存在於磁片
IN: AX = 檔名位置
OUT: carry clear if found, set if not

Example:
    mov ax, .filename
    call os_file_exists
    jc .not_found

    ...

.not_found:
    ; Print error message here

    .filename    db 'HELLO.TXT', 0

os_create_file

建立一個新的零個位元組的檔案到磁片中
IN: AX = 檔名位置
OUT: Nothing

Example:
    mov ax, .filename
    call os_create_file

    ...

    .filename    db 'HELLO.TXT', 0

os_remove_file

從檔案系統中刪除指定的檔案
IN: AX = 要刪除的檔名位置
OUT: Nothing

os_rename_file

在磁碟中更改檔案名稱
IN: AX = 要更改的檔案之檔名, BX = 新的檔名 (零結束字串)
OUT: carry set on error

Example:
    mov ax, .filename1
    mov bx, .filename2
    call os_rename_file

    jc .error

    ...

.error:
    ; File couldn't be renamed (may already exist)

    .filename1    db 'ORIG.TXT', 0
    .filename2    db 'NEW.TXT', 0

os_get_file_size

取得指定檔案之檔案大小資訊
IN: AX = 檔名
OUT: BX = 檔案大小以位元組計 (最大 64K) or carry set if file not found

Keyboard
os_wait_for_key

Waits for keypress and returns key
IN: Nothing
OUT: AX = key pressed, other regs preserved

Example:
.loop:
    call os_wait_for_key
    cmp al, 'y'
    je .yes
    cmp al, 'n'
    je .no
    jmp .loop

os_check_for_key

Scans keyboard for input, but doesn't wait
IN: Nothing
OUT: AX = 0 if no key pressed, otherwise scan code

Example: see code above

Math
os_bcd_to_int

Converts binary coded decimal number to an integer
IN: AL = BCD number
OUT: AX = integer value

Example:
    mov al, 00010110b    ; 0001 0110 = 16 (decimal) or 10h in BCD
    call os_bcd_to_int 
     
    ; AX now contains the 16 bit-integer 00000000 00010000b

os_long_int_negate

Multiply value in DX:AX by -1
IN: DX:AX = long integer 
OUT: DX:AX = -(initial DX:AX)

Example:
    mov dx, 01h
    mov ax, 45h
    call os_long_int_negate

    ; DX now contains 0xFFFF
    ; and AX 0xFEBB

os_get_random

Get a random integer between high and low values (inclusive)
IN: AX = low integer, BX = high
OUT: CX = random number between AX and BX

Misc
os_get_api_version

Return current version of MikeOS API
IN: Nothing 
OUT: AL = API version number

Example:
    call os_get_api_version
    ; AL now contains version number (eg 8)

os_pause

Delay execution for specified 10ths of second
IN: AX = number of 10ths of second to wait
OUT: nothing

Example:
    ; Halt execution for 3 secs

    mov ax, 30
    call os_pause

os_fatal_error

Display error message and halt execution
IN: AX = error message string location
OUT: nothing

Example:
    mov ax, .error_msg
    call os_fatal_error

    .error_msg    db 'Massive error!', 0

Ports
os_port_byte_out

Sends a byte to the specified port
IN: DX = port address, AL = byte
OUT: Nothing

os_port_byte_in

Retrieves a byte from the specified port
IN: DX = port address
OUT: AL = byte

os_serial_port_enable

Turn on the first serial port
IN: AX = 0 for normal mode (9600 baud), or 1 for slow mode (1200 baud)
OUT: AH = bit 7 clear on success

os_send_via_serial

Send a byte via the serial port
IN: AL = byte to send via serial
OUT: AH = bit 7 clear on success

Example:
    mov al, 'a'            ; Place char to transmit in AL
    call os_send_via_serial
    cmp ah, 128            ; If bit 7 is set, there's an error
    jnz all_ok            ; Otherwise it's all OK
    jmp oops_error            ; Deal with the error

os_get_via_serial

Get a byte from the serial port
IN: nothing
OUT: AL = byte that was received; OUT: AH = bit 7 clear on success

Example:
    call os_get_via_serial
    cmp ah, 128        ; If bit 7 is set, there's an error.
    jnz all_ok        ; Otherwise it's all OK
    jmp oops_error        ; Deal with the error

all_ok:           
    mov [rx_byte], al    ; Store byte we retrieved

Screen
os_print_string

Displays text
IN: SI = message location (zero-terminated string)
OUT: Nothing (registers preserved)

Example:
    mov si, .message
    call os_print_string

    ...

    .message    db 'Hello, world', 0

os_clear_screen

Clears the screen to background
IN/OUT: Nothing (registers preserved)

os_move_cursor

Moves cursor in text mode
IN: DH, DL = row, column 
OUT: Nothing (registers preserved)

Example:
    ; Move to middle of screen

    mov dh, 12
    mov dl, 40
    call os_move_cursor

os_get_cursor_pos

Return position of text cursor
OUT: DH, DL = row, column

os_print_horiz_line

Draw a horizontal line on the screen
IN: AX = line type (1 for double, otherwise single)
OUT: Nothing (registers preserved)

os_show_cursor

Turns on cursor in text mode
IN/OUT: Nothing

os_hide_cursor

Turns off cursor in text mode
IN/OUT: Nothing

os_draw_block

Render block of specified colour
IN: BL/DL/DH/SI/DI = colour/start X pos/start Y pos/width/finish Y pos
OUT: Nothing

Example:
    mov bl, 0100111b    ; White on red
    mov dl, 20        ; Start X position
    mov dh, 2        ; Start Y position
    mov si, 40        ; Width
    mov di, 23        ; Finish Y position
    call os_draw_block

os_file_selector

Show a file selection dialog
IN: Nothing 
OUT: AX = location of filename string (or carry set if Esc pressed)

Example:
    call os_file_selector
    jc .esc_pressed

    ; Now AX points to the chosen filename
    ...

.esc_pressed:
    ...

os_list_dialog

Show a dialog with a list of options
IN: AX = comma-separated list of strings to show (zero-terminated),
 BX = first help string, CX = second help string
OUT: AX = number (starts from 1) of entry selected carry set if Esc pressed

Example:
    mov ax, .list
    mov bx, .msg1
    mov cx, .msg2
    call os_list_dialog
    jc .esc_pressed

    ; Now AX = number (from 1) of option chosen
    ...

.esc_pressed:
    ...

    .list    db 'Open,Close,Reboot', 0
    .msg1    db 'Choose an option', 0
    .msg2    db 'Or press Esc to quit', 0

os_draw_background

Clear screen with white top and bottom bars, containing text, and a coloured middle section
IN: AX/BX = top/bottom string locations, CX = colour
OUT: Nothing

Example:
    mov ax, .title_msg
    mov bx, .footer_msg
    mov cx, 00100000b    ; Colour
    call os_draw_background

    ...

    .title_msg    db 'File manager', 0
    .footer_msg    db 'Choose an option...', 0

os_print_newline

Reset cursor to start of next line
IN/OUT: Nothing (registers preserved)

os_dump_registers

Displays register contents in hex on the screen
IN/OUT: AX/BX/CX/DX = registers to show

os_input_dialog

Get text string from user via a dialog box
IN: AX = string location, BX = message to show 
OUT: AX = string location

Example:
    mov bx, .filename_msg
    mov ax, .filename_input
    call os_input_dialog

    ...

    .filename_msg    'Please enter a filename:', 0
    .filename_input    times 12 db 0

os_dialog_box

Print dialog box in middle of screen, with button(s)
IN: AX, BX, CX = string locations (set registers to 0 for no display),
 DX = 0 for single 'OK' dialog, 1 for two-button 'OK' and 'Cancel'
OUT: If two-button mode, AX = 0 for OK and 1 for cancel
NOTE: Each string is limited to 40 characters

Example:
    mov ax, .string1
    mov bx, .string1
    mov cx, .string1
    mov dx, 1
    call os_dialog_box

    cmp ax, 1
    je .first_option_chosen

    ; Otherwise second was chosen
    ...

.first_option_chosen:
    ...

    .string1    db 'Welcome to my program!', 0
    .string2    db 'Please choose to destroy the world,', 0
    .string3    db 'or play with fluffy kittens.', 0

os_print_space

Print a space to the screen
IN/OUT: nothing

os_dump_string

Dump string as hex bytes and printable characters
IN: SI = points to string to dump
OUT: Nothing

os_print_digit

Displays contents of AX as a single digit (works up to base 37, ie digits 0-Z)
IN: AX = "digit" to format and print
OUT: Nothing

os_print_1hex

Displays low nibble of AL in hex format
IN: AL = number to format and print
OUT: Nothing

os_print_2hex

Displays AL in hex format
IN: AL = number to format and print
OUT: Nothing

os_print_4hex

Displays AX in hex format
IN: AX = number to format and print
OUT: Nothing

os_input_string

Take string from keyboard entry
IN/OUT: AX = location of string, other regs preserved
(Location will contain up to 255 characters, zero-terminated)

Example:
    mov ax, .string
    call os_input_string

    ...

    .string        times 256 db 0

String
os_string_length

Return length of a string
IN: AX = string location
OUT AX = length (other regs preserved)

Example:
    jmp Get_Len

    Test_String db 'This string has 46 characters including spaces', 0

Get_Len:
    mov ax, Test_String
    call os_string_length
    ; AX now contains the number 46

os_find_char_in_string

Get the position of a character in a string
IN: SI = string location, AL = character to find
OUT: AX = location in string, or 0 if char not present

Example:
jmp Search_Str

     Test_String db 'This is the test string', 0
     message_1   db 'Character not found', 0
     message_2   db 'Character found at position', 0
     message_3   db '  ', 0
     str_len     dw  0

Search_Str:

     mov ax, Test_String           ; string to search
     mov si, ax
     xor ax, ax                    ; clear ax

     mov al, 'x'                   ; x is the character to find
     call os_find_char_in_string

     mov [str_len], ax             ; store result
     cmp ax, 0                  
     jz Char_Not_Found 
     jmp Char_Found

Char_Not_Found:

     mov si, message_1
     call os_print_string     
     jmp Main_Pgm

Char_Found:

     mov ax, [str_len]             ; convert result into string first
     mov bx, message_3
     call os_int_to_string

     mov si, message_2
     call os_print_string          ; print message_2
     call os_print_space           ; print a space

     mov si, message_3
     call os_print_string          ; print the position at which x was found     

os_string_reverse

Swar order of characters in a string
IN: SI = location of zero-terminated string

Example:
    mov si, mystring
    call os_string_reverse

    ; Now mystring contains 'olleH'

    mystring db 'Hello', 0

os_string_charchange

Change instances of character in a string
IN: SI = string location, AL = char to find, BL = char to replace with

os_string_uppercase

Convert zero-terminated string to upper case
IN/OUT: AX = string location

os_string_lowercase

Convert zero-terminated string to lower case
IN/OUT: AX = string location

os_string_copy

Copy one string into another
IN/OUT: SI = source, DI = destination (programmer ensure sufficient room)

Example:
    mov si, .old_string
    mov di, .new_string
    call os_string_copy

    ...

    .old_string    db 'Hello', 0
    .new_string    times 16 db 0

os_string_truncate

Chop string down to specified number of characters
IN: SI = string location, AX = number of characters
OUT: string modified, registers preserved

Example:
    mov si, .string
    mov ax, 3
    call os_string_truncate

    ; .string now contains 'HEL', 0

    .string        db 'HELLO', 0

os_string_join

Join two strings into a third string
IN/OUT: AX = string one, BX = string two, CX = destination string

Example:
    mov ax, .string1
    mov bx, .string2
    mov cx, .string3

    ; CX now points to 'HELLOYOU', 0

    .string1    db 'HELLO', 0
    .string2    db 'YOU', 0
    .string3    times 50 db 0

os_string_chomp

Strip leading and trailing spaces from a string
IN: AX = string location
OUT: nothing

Example:
    mov ax, .string
    call os_string_chomp

    ; AX now points to 'KITTEN', 0

    .string        db '  KITTEN  ', 0

os_string_strip

Removes specified character from a string
IN: SI = string location, AL = character to remove
OUT: nothing

Example:
    mov si, .string
    mov al, 'U'
    call os_string_strip

    ; .string now contains 'MOSE', 0

    .string        db 'MOUSE', 0

os_string_compare

See if two strings match
IN: SI = string one, DI = string two
OUT: carry set if same, clear if different

Example:
    mov si, .string1
    mov di, .string2
    call os_string_compare
    jc .same

    ; Strings are different

    ...

.same:
    ; Strings are the same

    .string1    db 'ONE', 0
    .string2    db 'TWO', 0

os_string_strincmp

See if two strings match up to set number of chars
IN: SI = string one, DI = string two, CL = chars to check
OUT: carry set if same, clear if different

Example: like above, but with CL = number of characters to check

os_string_parse

Take string (eg "run foo bar baz") and return pointers to zero-terminated strings (eg AX = "run", BX =  "foo" etc.)
IN: SI = string
OUT: AX, BX, CX, DX = individual strings

Example:
    mov si, .string
    call os_string_parse

    ; Now AX points to 'This',
    ; BX to 'is',
    ; CX to 'a',
    ; and DX to 'test'

    .string        db 'This is a test', 0

os_string_to_int

Convert decimal string to integer value
IN: SI = string location (max 5 chars, up to '65536')
OUT: AX = number

Example:
    mov si, .string
    call os_string_to_int

    ; Now AX contains the number 1234

    .string        db '1234', 0

os_int_to_string

Convert unsigned value in AX to string
IN: AX = unsigned integer
OUT: AX = location of internal string buffer with result

Example:
    mov ax, 1234
    call os_int_to_string

    ; Now AX points to '1234', 0

os_sint_to_string

Convert signed value in AX to string
IN: AX = signed integer
OUT: AX = location of internal string buffer with result

Example:
    mov ax, -1234
    call os_int_to_string

    ; Now AX points to '-1234', 0

os_long_int_to_string

Convert value in DX:AX to string
IN: DX:AX = long unsigned integer, BX = number base, DI = string location
OUT: DI = location of converted string

os_set_time_fmt

Set time reporting format (eg '10:25 AM' or '2300 hours')
IN: AL = format flag, 0 = 12-hr format
OUT: nothing

os_get_time_string

Get current time in a string (eg '10:25')
IN/OUT: BX = string location

os_set_date_fmt

Set date reporting format (M/D/Y, D/M/Y or Y/M/D - 0, 1, 2)
IN: AX = format flag, 0-2
 If AX bit 7 = 1 = use name for months
 If AX bit 7 = 0, high byte = separator character
OUT: nothing

os_get_date_string

Get current date in a string (eg '12/31/2007')
IN/OUT: BX = string location

聲音
os_speaker_tone

Generate PC speaker tone (call os_speaker_off to turn off)
IN: AX = note frequency
OUT: Nothing (registers preserved)

Example: see code below

os_speaker_off

Turn off PC speaker
IN/OUT: Nothing (registers preserved)

Example:
; Generate "middle C" 261.626 Hz (263 Hz close enough) for 2 secs
; 2 secs = 2,000,000 uS which is 1E8480h

    jmp Play_It

    music_note  dw  263

Play_It:
    mov ax, [music_note]
    call os_speaker_tone

    mov cx, 1Eh
    mov dx, 8480h
    call os_pause

    call os_speaker_off         

BASIC
os_run_basic

在指定點執行核心BASIC解譯器
IN: AX = lBASIC碼的位置, BX = 碼的大小 (以位元組計)


幫助

假如你有任何關於MikeOS的問題,或你要開發類似的系統且想要分享程式碼和想法,
可前往MikeOS網站,並加入到信箱列表。你就可以寫信到 mikeos-developer@lists.berlios.de
來發表到列表。

授權
MikeOS是個開放原始碼,在BSD-like下授權釋出(可參考在MikeOS.zip檔案中的doc/LI
CENSE.TXT)。它意指你可以為程式碼做任何事情,包括在你的專案中使用它,
你只須保留授權檔案。

沒有留言: