crawos/doc/handbook-appdev-asm.html

1211 lines
41 KiB
HTML
Executable File

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>The MikeOS Assembly App Developer Handbook</title>
<style type="text/css">
body {
font-family: sans-serif;
}
h1 {
margin-top:5px;
color: #DF6020;
}
h2 {
color: #DF6020;
}
h3 {
margin-top:5px;
color: #DF6020;
}
hr {
border: 0;
color: #DF6020;
background-color: #DF6020;
height: 3px;
}
pre {
background-color: #F0F0F0;
border: 5px solid #F0F0F0;
}
a {
text-decoration: none;
color: #0000F0;
}
a:visited {
text-decoration: none;
color: #0000F0;
}
a:hover {
text-decoration: underline;
}
li {
margin-left: -1ex;
}
</style>
</head>
<body>
<table cellpadding="10" border="0">
<tbody>
<tr>
<!-- NAVIGATION PANEL --> <td style="border:1px solid black;
width:160px;" valign="top">
<h3>Navigate</h3>
<p><strong>Overview</strong></p>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#toolsneeded">Tools needed</a></li>
</ul>
<p><strong>Example</strong></p>
<ul>
<li><a href="#sourcecode">Source code</a></li>
<li><a href="#building">Building</a></li>
<li><a href="#explanation">Explanation</a></li>
</ul>
<p><strong>System calls</strong></p>
<ul>
<li><a href="#syscallintro">Introduction</a></li>
<li><a href="#syscalldisk">Disk</a></li>
<li><a href="#syscallkeyboard">Keyboard</a></li>
<li><a href="#syscallmath">Math</a></li>
<li><a href="#syscallmisc">Misc</a></li>
<li><a href="#syscallports">Ports</a></li>
<li><a href="#syscallscreen">Screen</a></li>
<li><a href="#syscallstring">String</a></li>
<li><a href="#syscallsound">Sound</a></li>
<li><a href="#syscallbasic">BASIC</a></li>
</ul>
<p><strong>Extra</strong></p>
<ul>
<li><a href="#help">Help</a></li>
<li><a href="#license">License</a></li>
</ul>
</td>
<!-- MAIN CONTENT PANEL --> <td valign="top">
<h1>The MikeOS Assembly App Developer Handbook</h1>
<h3>For version 4.7, 9 April 2022 - (C) MikeOS Developers</h3>
<p>This documentation file explains how write software for
MikeOS in assembly language. It shows you the tools you
need, how MikeOS programs work, and how to use the system
calls included in the kernel. If you have any questions,
see <a href="http://mikeos.sourceforge.net">the MikeOS
website</a> for contact details and mailing list
information.</p>
<p>Click the links on the left to navigate around this
guide.</p>
<br>
<hr noshade="noshade">
<h2>Overview</h2>
<a name="introduction"></a>
<h3>Introduction</h3>
<p>Many MikeOS programs are written in 16-bit, real mode
assembly language. (The OS also includes a BASIC
interpreter.) Because MikeOS and its programs live in a
single 64KiB memory segment, you do not need to concern
yourself with segment registers.</p>
<p>MikeOS programs are loaded at the <strong>32K point</strong>
(32768) in the segment, and have a maximum size of 32KiB.
Consequently, your programs will need to begin with these
directives:</p>
<pre>BITS 16
ORG 32768
</pre>
<p>There are many system calls available in MikeOS for
controlling the screen, getting input, manipulating
strings, loading/saving files, and more. All parameters to
MikeOS system calls are passed in registers, and not on
the stack. To use them in your programs, you need this
line:</p>
<pre>%INCLUDE "mikedev.inc"
</pre>
<p>This doesn't include any code, but a list of <strong>equ</strong>
directives that point to system call vectors in the
kernel. So, by including this file you can call, for
instance, the MikeOS <strong>os_print_string</strong>
routine without having to know exactly where it is in the
kernel. <strong>mikedev.inc</strong> is included in the <strong>programs/</strong>
directory of the MikeOS download -- it also provides a
quick reference to the system calls.</p>
<p>If a MikeOS program is run from the command line, and one
or more parameters was provided with the command, they are
passed to the program via the SI register as a
zero-terminated string.</p>
<br>
<a name="toolsneeded"></a>
<h3>Tools needed</h3>
<p>To write MikeOS programs you need:</p>
<ul>
<li><strong>NASM</strong> -- A powerful, free and open
source assembler</li>
<li><strong>mikedev.inc</strong> -- The system call
vectors described above</li>
<li>A way to add programs to the floppy disk</li>
</ul>
<p>Let's clarify this: MikeOS uses NASM for its programs and
kernel source code, hence why we recommend it here. You
can, of course, use any other assembler that can output
plain binary files (i.e. with no header) and accept 16-bit
code. NASM is available in most Linux distro repositories,
or you can download the Windows version <a
href="http://www.nasm.us/pub/nasm/releasebuilds/">from
here</a> (get the 'win32' file).</p>
<p>For the second point, copy <strong>programs/mikedev.inc</strong>
so that it's alongside your program's source code for
inclusion.</p>
<p>For the third point, if you've written MikeOS to a real
floppy disk, you can just copy your <strong>.BIN</strong>
programs onto that disk (root directory only!), boot
MikeOS and test them out. If you're working with the
virtual <strong>mikeos.flp</strong> disk image, see the <em>Copying
files</em> section of the User Handbook.</p>
<br>
<hr noshade="noshade">
<h2>Example</h2>
<a name="sourcecode"></a>
<h3>Source code</h3>
<p>Here is an example MikeOS program, in NASM format, which
prints a string to the screen and waits for the user to
press a key before finishing:</p>
<pre> 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
</pre>
<br>
<a name="building"></a>
<h3>Building</h3>
<p>Save the above code as <strong>testapp.asm</strong>, and
enter this command to assemble it (works on both Linux and
Windows):</p>
<pre>nasm -f bin -o testapp.bin testapp.asm
</pre>
<p>Using the '-f bin' option we tell NASM that we just want
a plain binary file: no header or sections. The resulting
executable file is <strong>testapp.bin</strong> that we
can copy to our floppy disk or add to the virtual disk
image as described in <em>Copying files</em> in the User
Handbook. Then we can boot MikeOS and run the program.</p>
<br>
<a name="explanation"></a>
<h3>Explanation</h3>
<p>This is a very short program, but we'll explain exactly
how it works for complete clarity. The first three lines
will not be assembled to machine code instructions, but
are just directives to tell NASM that we want to operate
in 16 bit mode, our code is written to be executed from
the 32KiB mark, and we want to use system calls from the
MikeOS API.</p>
<p>Next we have the 'start:' label, which is not essential
but good practice to make things clear. We put the
location of a zero-terminated string into the <strong>SI</strong>
register, then call the MikeOS <strong>os_print_string</strong>
routine which we can access via the vectors listed in <strong>mikedev.inc</strong>.
After that we pause program until a the user presses a
key.</p>
<p>All MikeOS programs must end with a <strong>ret</strong>
instruction, which passes control back to the OS. After
that instruction we have the data for our string. And
that's it!</p>
<br>
<hr noshade="noshade">
<h2>System calls</h2>
<a name="syscallintro"></a>
<h3>Introduction</h3>
<p>The MikeOS kernel includes system calls for outputting to
the screen, taking keyboard input, manipulating strings,
producing PC speaker sounds, math operations and disk I/O.
You can get a quick reference to the registers that these
calls take and pass back by looking at <strong>mikedev.inc</strong>,
and see practical use of the calls in the <strong>programs/</strong>
directory.</p>
<p>Here we have a detailed API explanation with examples.
You can see the full source code behind these system calls
in the <strong>source/features/</strong> directory in
MikeOS. Each aspect of the API below is contained within
an assembly source file, so you can enhance the system
calls as per the MikeOS System Developer Handbook.</p>
<br>
<hr style="width:50%;"> <a name="syscalldisk"></a>
<h2>Disk</h2>
<h3>os_get_file_list</h3>
<p><strong>Generate comma-separated string of files on
floppy</strong></p>
<ul>
<li>IN/OUT: AX = location to store zero-terminated
filename string (the programmer must ensure the buffer
is large enough to hold the entire root directory)<br>
</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_load_file</h3>
<p><strong>Load file into RAM</strong></p>
<ul>
<li>IN: AX = location of zero-terminated filename string,
CX = location in RAM to load file</li>
<li>OUT: EBX = file size (in bytes: if &lt; 64 KiB testing
BX is sufficient), carry set if file not found</li>
</ul>
<p>Example:</p>
<pre> mov ax, filename
mov cx, 36960 ; 4KiB after where external program loads
call os_load_file
...
filename db 'README.TXT', 0
</pre>
<br>
<h3>os_write_file</h3>
<p><strong>Save (max 64KiB) file to disk</strong></p>
<ul>
<li>IN: AX = filename, BX = data location, CX = bytes to
write</li>
<li>OUT: Carry clear if OK, set if failure (if OK the last
write date and time, archive attribute and file size are
updated in the disk directory)<br>
</li>
</ul>
<p>Example:</p>
<pre> ; 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
</pre>
<br>
<h3>os_file_exists</h3>
<p><strong>Check for presence of file on the floppy</strong></p>
<ul>
<li>IN: AX = filename location</li>
<li>OUT: carry clear if found, set if not</li>
</ul>
<p>Example:</p>
<pre> mov ax, .filename
call os_file_exists
jc .not_found
...
.not_found:
; Print error message here
.filename db 'HELLO.TXT', 0
</pre>
<br>
<h3>os_create_file</h3>
<p><strong>Creates a new 0-byte file on the floppy disk</strong></p>
<ul>
<li>IN: AX = location of filename</li>
<li>OUT: Nothing (if the function is successful, a disk
directory entry is created with the current system date
and time, the archive attribute set and a file size of
0)<br>
</li>
</ul>
<p>Example:</p>
<pre> mov ax, .filename
call os_create_file
...
.filename db 'HELLO.TXT', 0
</pre>
<br>
<h3>os_remove_file</h3>
<p><strong>Deletes the specified file from the filesystem</strong></p>
<ul>
<li>IN: AX = location of filename to remove</li>
<li>OUT: Nothing</li>
</ul>
<br>
<h3>os_rename_file</h3>
<p><strong>Change the name of a file on the disk</strong></p>
<ul>
<li>IN: AX = filename to change, BX = new filename
(zero-terminated strings)</li>
<li>OUT: carry set on error</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_get_file_size</h3>
<p><strong>Get file size information for specified file</strong></p>
<ul>
<li>IN: AX = filename</li>
<li>OUT: EBX = file size in bytes (theoretically, up to 4
GB) or carry set if file not found</li>
</ul>
<br>
<hr style="width:50%;"> <a name="syscallkeyboard"></a>
<h2>Keyboard</h2>
<h3>os_wait_for_key</h3>
<p><strong>Waits for key press and returns key</strong></p>
<ul>
<li>IN: Nothing</li>
<li>OUT: AX = key pressed, other regs preserved</li>
</ul>
<p>Example:</p>
<pre>.loop:
call os_wait_for_key
cmp al, 'y'
je .yes
cmp al, 'n'
je .no
jmp .loop
</pre>
<br>
<h3>os_check_for_key</h3>
<p><strong>Scans keyboard for input, but doesn't wait</strong></p>
<ul>
<li>IN: Nothing</li>
<li>OUT: AX = 0 if no key pressed, otherwise scan code</li>
</ul>
<p>Example: see code above</p>
<br>
<hr style="width:50%;"> <a name="syscallmath"></a>
<h2>Math</h2>
<h3>os_bcd_to_int</h3>
<p><strong>Converts binary coded decimal number to an
integer</strong></p>
<ul>
<li>IN: AL = BCD number</li>
<li>OUT: AX = integer value</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_long_int_negate</h3>
<p><strong>Multiply value in DX:AX by -1</strong></p>
<ul>
<li>IN: DX:AX = long integer </li>
<li>OUT: DX:AX = -(initial DX:AX)</li>
</ul>
<p>Example:</p>
<pre> mov dx, 01h
mov ax, 45h
call os_long_int_negate
; DX now contains 0xFFFF
; and AX 0xFEBB
</pre>
<br>
<h3>os_get_random</h3>
<p><strong>Get a random integer between high and low values
(inclusive)</strong></p>
<ul>
<li>IN: AX = low integer, BX = high</li>
<li>OUT: CX = random number between AX and BX</li>
</ul>
<br>
<hr style="width:50%;"> <a name="syscallmisc"></a>
<h2>Misc</h2>
<h3>os_get_api_version</h3>
<p><strong>Return current version of MikeOS API</strong></p>
<ul>
<li>IN: Nothing </li>
<li>OUT: AL = API version number</li>
</ul>
<p>Example:</p>
<pre> call os_get_api_version
; AL now contains version number (eg 8)
</pre>
<br>
<h3>os_pause</h3>
<p><strong>Delay execution for specified 10ths of second</strong></p>
<ul>
<li>IN: AX = number of 10ths of second to wait</li>
<li>OUT: nothing</li>
</ul>
<p>Example:</p>
<pre> ; Halt execution for 3 secs
mov ax, 30
call os_pause
</pre>
<br>
<h3>os_fatal_error</h3>
<p><strong>Display error message and halt execution</strong></p>
<ul>
<li>IN: AX = error message string location</li>
<li>OUT: nothing</li>
</ul>
<p>Example:</p>
<pre> mov ax, .error_msg
call os_fatal_error
.error_msg db 'Massive error!', 0
</pre>
<br>
<hr style="width:50%;"> <a name="syscallports"></a>
<h2>Ports</h2>
<h3>os_port_byte_out</h3>
<p><strong>Sends a byte to the specified port</strong></p>
<ul>
<li>IN: DX = port address, AL = byte</li>
<li>OUT: Nothing</li>
</ul>
<br>
<h3>os_port_byte_in</h3>
<p><strong>Retrieves a byte from the specified port</strong></p>
<ul>
<li>IN: DX = port address</li>
<li>OUT: AL = byte</li>
</ul>
<br>
<h3>os_serial_port_enable</h3>
<p><strong>Turn on the first serial port</strong></p>
<ul>
<li>IN: AX = 0 for normal mode (9600 baud), or 1 for slow
mode (1200 baud)</li>
<li>OUT: AH = bit 7 clear on success (AH is line status
and AL is modem status)<br>
</li>
</ul>
<br>
<h3>os_send_via_serial</h3>
<p><strong>Send a byte via the serial port</strong></p>
<ul>
<li>IN: AL = byte to send via serial</li>
<li>OUT: AH = bit 7 clear on success</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_get_via_serial</h3>
<p><strong>Get a byte from the serial port</strong></p>
<ul>
<li>IN: nothing</li>
<li>OUT: AL = byte that was received; OUT: AH = bit 7
clear on success</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<hr style="width:50%;"> <a name="syscallscreen"></a>
<h2>Screen</h2>
<h3>os_print_string</h3>
<p><strong>Displays text</strong></p>
<ul>
<li>IN: SI = message location (zero-terminated string)</li>
<li>OUT: Nothing (registers preserved)</li>
</ul>
<p>Example:</p>
<pre> mov si, .message
call os_print_string
...
.message db 'Hello, world', 0
</pre>
<br>
<h3>os_clear_screen</h3>
<p><strong>Clears the screen to background</strong></p>
<ul>
<li>IN/OUT: Nothing (registers preserved)</li>
</ul>
<br>
<h3>os_move_cursor</h3>
<p><strong>Moves cursor in text mode</strong></p>
<ul>
<li>IN: DH, DL = row, column </li>
<li>OUT: Nothing (registers preserved)</li>
</ul>
<p>Example:</p>
<pre> ; Move to middle of screen
mov dh, 12
mov dl, 40
call os_move_cursor
</pre>
<br>
<h3>os_get_cursor_pos</h3>
<p><strong>Return position of text cursor</strong></p>
<ul>
<li>OUT: DH, DL = row, column</li>
</ul>
<br>
<h3>os_print_horiz_line</h3>
<p><strong>Draw a horizontal line on the screen</strong></p>
<ul>
<li>IN: AX = line type (1 for double, otherwise single)</li>
<li>OUT: Nothing (registers preserved)</li>
</ul>
<br>
<h3>os_show_cursor</h3>
<p><strong>Turns on cursor in text mode</strong></p>
<ul>
<li>IN/OUT: Nothing</li>
</ul>
<br>
<h3>os_hide_cursor</h3>
<p><strong>Turns off cursor in text mode</strong></p>
<ul>
<li>IN/OUT: Nothing</li>
</ul>
<br>
<h3>os_draw_block</h3>
<p><strong>Render block of specified colour</strong></p>
<ul>
<li>IN: BL/DL/DH/SI/DI = colour/start X pos/start Y
pos/width/finish Y pos</li>
<li>OUT: Nothing</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_file_selector</h3>
<p><strong>Show a file selection dialog</strong></p>
<ul>
<li>IN: Nothing </li>
<li>OUT: AX = location of filename string (or carry set if
Esc pressed)</li>
</ul>
<p>Example:</p>
<pre> call os_file_selector
jc .esc_pressed
; Now AX points to the chosen filename
...
.esc_pressed:
...
</pre>
<br>
<h3>os_list_dialog</h3>
<p><strong>Show a dialog with a list of options</strong></p>
<ul>
<li>IN: AX = comma-separated list of strings to show
(zero-terminated),</li>
<li>&nbsp;BX = first help string, CX = second help string</li>
<li>OUT: AX = number (starts from 1) of entry selected
carry set if Esc pressed</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_draw_background</h3>
<p><strong>Clear screen with white top and bottom bars,
containing text, and a coloured middle section</strong></p>
<ul>
<li>IN: AX/BX = top/bottom string locations, CX = colour</li>
<li>OUT: Nothing</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_print_newline</h3>
<p><strong>Reset cursor to start of next line</strong></p>
<ul>
<li>IN/OUT: Nothing (registers preserved)</li>
</ul>
<br>
<h3>os_dump_registers</h3>
<p><strong>Displays register contents in hex on the screen</strong></p>
<ul>
<li>IN/OUT: AX/BX/CX/DX/SI/DI/BP = registers to show</li>
</ul>
<br>
<h3>os_input_dialog</h3>
<p><strong>Get text string from user via a dialog box</strong></p>
<ul>
<li>IN: AX = string location, BX = message to show </li>
<li>OUT: AX = string location</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_dialog_box</h3>
<p><strong>Print dialog box in middle of screen, with
button(s)</strong></p>
<ul>
<li>IN: AX, BX, CX = string locations (set registers to 0
for no display),</li>
<li>&nbsp;DX = 0 for single 'OK' dialog, 1 for two-button
'OK' and 'Cancel'</li>
<li>OUT: If two-button mode, AX = 0 for OK and 1 for
cancel</li>
<li>NOTE: Each string is limited to 40 characters</li>
</ul>
<p>Example:</p>
<pre> mov ax, .string1
mov bx, .string2
mov cx, .string3
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
</pre>
<br>
<h3>os_print_space</h3>
<p><strong>Print a space to the screen</strong></p>
<ul>
<li>IN/OUT: nothing</li>
</ul>
<br>
<h3>os_dump_string</h3>
<p><strong>Dump string as hex bytes and printable characters</strong></p>
<ul>
<li>IN: SI = points to string to dump</li>
<li>OUT: Nothing</li>
</ul>
<br>
<h3>os_print_digit</h3>
<p><strong>Displays contents of AX as a single digit (works
up to base 37, ie digits 0-Z)</strong></p>
<ul>
<li>IN: AX = "digit" to format and print</li>
<li>OUT: Nothing</li>
</ul>
<br>
<h3>os_print_1hex</h3>
<p><strong>Displays low nibble of AL in hex format</strong></p>
<ul>
<li>IN: AL = number to format and print</li>
<li>OUT: Nothing</li>
</ul>
<br>
<h3>os_print_2hex</h3>
<p><strong>Displays AL in hex format</strong></p>
<ul>
<li>IN: AL = number to format and print</li>
<li>OUT: Nothing</li>
</ul>
<br>
<h3>os_print_4hex</h3>
<p><strong>Displays AX in hex format</strong></p>
<ul>
<li>IN: AX = number to format and print</li>
<li>OUT: Nothing</li>
</ul>
<br>
<h3>os_input_string</h3>
<p><strong>Take string from keyboard entry</strong></p>
<ul>
<li>IN/OUT: AX = location of string, BX = maximum number
of characters</li>
<li>(Location will contain up to 255 characters,
zero-terminated)</li>
</ul>
<p>Example:</p>
<pre> mov ax, .string
call os_input_string
...
.string times 256 db 0
</pre>
<br>
<hr style="width:50%;"> <a name="syscallstring"></a>
<h2>String</h2>
<h3>os_string_length</h3>
<p><strong>Return length of a string</strong></p>
<ul>
<li>IN: AX = string location</li>
<li>OUT AX = length (other regs preserved)</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_find_char_in_string</h3>
<p><strong>Get the position of a character in a string</strong></p>
<ul>
<li>IN: SI = string location, AL = character to find</li>
<li>OUT: AX = location in string, or 0 if char not present</li>
</ul>
<p>Example:</p>
<pre>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
</pre>
<br>
<h3>os_string_reverse</h3>
<p><strong>Swar order of characters in a string</strong></p>
<ul>
<li>IN: SI = location of zero-terminated string</li>
</ul>
<p>Example:</p>
<pre> mov si, mystring
call os_string_reverse
; Now mystring contains 'olleH'
mystring db 'Hello', 0
</pre>
<br>
<h3>os_string_charchange</h3>
<p><strong>Change instances of character in a string</strong></p>
<ul>
<li>IN: SI = string location, AL = char to find, BL = char
to replace with</li>
</ul>
<br>
<h3>os_string_uppercase</h3>
<p><strong>Convert zero-terminated string to upper case</strong></p>
<ul>
<li>IN/OUT: AX = string location</li>
</ul>
<br>
<h3>os_string_lowercase</h3>
<p><strong>Convert zero-terminated string to lower case</strong></p>
<ul>
<li>IN/OUT: AX = string location</li>
</ul>
<br>
<h3>os_string_copy</h3>
<p><strong>Copy one string into another</strong></p>
<ul>
<li>IN/OUT: SI = source, DI = destination (programmer
ensure sufficient room)</li>
</ul>
<p>Example:</p>
<pre> mov si, .old_string
mov di, .new_string
call os_string_copy
...
.old_string db 'Hello', 0
.new_string times 16 db 0
</pre>
<br>
<h3>os_string_truncate</h3>
<p><strong>Chop string down to specified number of
characters</strong></p>
<ul>
<li>IN: SI = string location, AX = number of characters</li>
<li>OUT: string modified, registers preserved</li>
</ul>
<p>Example:</p>
<pre> mov si, .string
mov ax, 3
call os_string_truncate
; .string now contains 'HEL', 0
.string db 'HELLO', 0
</pre>
<br>
<h3>os_string_join</h3>
<p><strong>Join two strings into a third string</strong></p>
<ul>
<li>IN/OUT: AX = string one, BX = string two, CX =
destination string</li>
</ul>
<p>Example:</p>
<pre> mov ax, .string1
mov bx, .string2
mov cx, .string3
call os_string_join
; CX now points to 'HELLOYOU', 0
.string1 db 'HELLO', 0
.string2 db 'YOU', 0
.string3 times 50 db 0
</pre>
<br>
<h3>os_string_chomp</h3>
<p><strong>Strip leading and trailing spaces from a string</strong></p>
<ul>
<li>IN: AX = string location</li>
<li>OUT: nothing</li>
</ul>
<p>Example:</p>
<pre> mov ax, .string
call os_string_chomp
; AX now points to 'KITTEN', 0
.string db ' KITTEN ', 0
</pre>
<br>
<h3>os_string_strip</h3>
<p><strong>Removes specified character from a string</strong></p>
<ul>
<li>IN: SI = string location, AL = character to remove</li>
<li>OUT: nothing</li>
</ul>
<p>Example:</p>
<pre> mov si, .string
mov al, 'U'
call os_string_strip
; .string now contains 'MOSE', 0
.string db 'MOUSE', 0
</pre>
<br>
<h3>os_string_compare</h3>
<p><strong>See if two strings match</strong></p>
<ul>
<li>IN: SI = string one, DI = string two</li>
<li>OUT: carry set if same, clear if different</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_string_strincmp</h3>
<p><strong>See if two strings match up to set number of
chars</strong></p>
<ul>
<li>IN: SI = string one, DI = string two, CL = chars to
check</li>
<li>OUT: carry set if same, clear if different</li>
</ul>
<p>Example: like above, but with CL = number of characters
to check</p>
<br>
<h3>os_string_parse</h3>
<p><strong>Take string (eg "run foo bar baz") and return
pointers to zero-terminated strings (eg AX = "run", BX =
"foo" etc.)</strong></p>
<ul>
<li>IN: SI = string</li>
<li>OUT: AX, BX, CX, DX = individual strings</li>
</ul>
<p>Example:</p>
<pre> 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
</pre>
<br>
<h3>os_string_to_int</h3>
<p><strong>Convert decimal string to integer value</strong></p>
<ul>
<li>IN: SI = string location (max 5 chars, up to '65536')</li>
<li>OUT: AX = number</li>
</ul>
<p>Example:</p>
<pre> mov si, .string
call os_string_to_int
; Now AX contains the number 1234
.string db '1234', 0
</pre>
<br>
<h3>os_int_to_string</h3>
<p><strong>Convert unsigned value in AX to string</strong></p>
<ul>
<li>IN: AX = unsigned integer</li>
<li>OUT: AX = location of internal string buffer with
result</li>
</ul>
<p>Example:</p>
<pre> mov ax, 1234
call os_int_to_string
; Now AX points to '1234', 0
</pre>
<br>
<h3>os_sint_to_string</h3>
<p><strong>Convert signed value in AX to string</strong></p>
<ul>
<li>IN: AX = signed integer</li>
<li>OUT: AX = location of internal string buffer with
result</li>
</ul>
<p>Example:</p>
<pre> mov ax, -1234
call os_int_to_string
; Now AX points to '-1234', 0
</pre>
<br>
<h3>os_long_int_to_string</h3>
<p><strong>Convert value in DX:AX to string</strong></p>
<ul>
<li>IN: DX:AX = long unsigned integer, BX = number base,
DI = string location</li>
<li>OUT: DI = location of converted string</li>
</ul>
<br>
<h3>os_string_to_long_int</h3>
<p><strong>Convert a decimal string to a long integer value<br>
</strong></p>
<ul>
<li>IN: SI = string location (up to 10 digits, value of
'4294967295')<br>
</li>
<li>OUT: EAX = long unsigned integer<br>
</li>
</ul>
<br>
<h3>os_set_time_fmt</h3>
<p><strong>Set time reporting format (eg '10:25 AM' or '2300
hours')</strong></p>
<ul>
<li>IN: AL = format flag, 0 = 12-hr format</li>
<li>OUT: nothing</li>
</ul>
<br>
<h3>os_get_time_string</h3>
<p><strong>Get current time in a string (eg '10:25')</strong></p>
<ul>
<li>IN/OUT: BX = string location</li>
</ul>
<br>
<h3>os_set_date_fmt</h3>
<p><strong>Set date reporting format (M/D/Y, D/M/Y or Y/M/D
- 0, 1, 2)</strong></p>
<ul>
<li>IN: AX = format flag, 0-2</li>
<li>&nbsp;If AX bit 7 = 1 = use name for months</li>
<li>&nbsp;If AX bit 7 = 0, high byte = separator character</li>
<li>OUT: nothing</li>
</ul>
<br>
<h3>os_get_date_string</h3>
<p><strong>Get current date in a string (eg '12/31/2007')</strong></p>
<ul>
<li>IN/OUT: BX = string location</li>
</ul>
<br>
<hr style="width:50%;"> <a name="syscallsound"></a>
<h2>Sound</h2>
<h3>os_speaker_tone</h3>
<p><strong>Generate PC speaker tone (call os_speaker_off to
turn off)</strong></p>
<ul>
<li>IN: AX = note frequency</li>
<li>OUT: Nothing (registers preserved)</li>
</ul>
<p>Example: see code below</p>
<br>
<h3>os_speaker_off</h3>
<p><strong>Turn off PC speaker</strong></p>
<ul>
<li>IN/OUT: Nothing (registers preserved)</li>
</ul>
<p>Example:</p>
<pre>; 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
</pre>
<br>
<hr style="width:50%;"> <a name="syscallbasic"></a>
<h2>BASIC</h2>
<h3>os_run_basic</h3>
<p><strong>Runs kernel BASIC interpreter at the specified
point</strong></p>
<ul>
<li>IN: AX = location of BASIC code, BX = size of code (in
bytes)</li>
</ul>
<br>
<hr noshade="noshade">
<h2>Extra</h2>
<a name="help"></a>
<h3>Help</h3>
<p>If you have any questions about MikeOS, or you're
developing a similar OS and want to share code and ideas,
go to <a href="http://mikeos.sourceforge.net">the MikeOS
website</a> and join the mailing list as described.</p>
<br>
<a name="license"></a>
<h3>License</h3>
<p>MikeOS is open source and released under a BSD-like
license (see <strong>doc/LICENSE.TXT</strong> in the
MikeOS <strong>.zip</strong> file). Essentially, it means
you can do anything you like with the code, including
basing your own project on it, providing you retain the
license file and give credit to the MikeOS developers for
their work.</p>
<br>
<hr noshade="noshade"> </td>
</tr>
</tbody>
</table>
</body>
</html>