GEMS Kernel is going to be better than ever...

WOO HOO!
This commit is contained in:
Sam Sneed 2024-06-03 11:16:15 -05:00
parent 3cd3f0482b
commit 58217f5900
737 changed files with 208803 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
opt/**
builds/**

3
BUGS.md Normal file
View file

@ -0,0 +1,3 @@
Finally! *we boot!* But that doesn't mean we are bug free!
See TODO.md for details.

19
COC.md Normal file
View file

@ -0,0 +1,19 @@
# Sparksammy Code of Conduct 1.025
We, overall, must follow one simple rule to each other: *Stay excellent to each other.* This includes but is not limited to:
* Using welcoming behavior and language.
* Do not cuss or use bad language unless you feel strongly about a subject
* Basically: Do not abuse bad language
* No sexual behavior unless approved by the other person.
* *Digital/Virtual hugs do not count.*
* Speaking English is a requirement so everyone understands each other.
* No DOXing
* No murderers and sexual predators that haven't done jail time yet are allowed here. Move on to another project.
* Samuel (Sparksammy) Lord can do whatever he wants.
* This is just so that the owner (Samuel Lord) never gets kicked or banned from his own project[s].
* "The only way to do great work is to love what you do." --Steve Jobs
## About real names.
If you are comfortable going by your real name, that's great! If not, just go by your GitHub username. *If a user does not feel comfortable using their real name, don't use their real name.*
## About punishments.
If you get punished, your punishment will be decided depending on severity of the damage and/or what you did. For example: If you say a curse word that's uncalled for, you will be warned 3 times then kicked. But if you are a murderer and didn't go to jail, you are banned until you go to jail and your sentence is up.

20
LICENSE-GEMS-CODE Normal file
View file

@ -0,0 +1,20 @@
The Samuel Public License Revision 5 (SPL-R5)
Copyright (c) 2024 Sneed Group
This document grants permission, without charge, to any individual acquiring a copy of the software and its associated documentation files (hereinafter referred to as the "Software"). Such individuals are authorized to engage in activities related to the Software with certain restrictions (listed below), including, but not limited to, the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software. These permissions extend to persons to whom the Software is furnished, subject to compliance with the specified conditions outlined below.
THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
In making contributions to the Software, contributors irrevocably assign, transfer, and convey all rights, titles, and interests in and to their contributions to the project owner(s). This assignment is absolute and encompasses all intellectual property rights, including, but not limited to, copyrights, patents, trademarks, and trade secrets. Contributors acknowledge and consent that they have no further claim, right, or interest in their contributions and agree to relinquish any moral rights associated with the contributed content. This assignment is effective upon the act of contributing to the Software, and contributors affirm that they have the authority to make such an assignment. However, contributors retain the right to modify their contributions.
Furthermore, this document permits the reuse and redistribution of both executable binaries and source code, contingent upon the inclusion of the previously mentioned copyright notice and permission notice in all copies or substantial portions of the Software. It is imperative that you explicitly acknowledge and agree that the owner(s) retain ownership rights over the aforementioned source code.
Moreover, companies using the Software are encouraged to contribute upstream. Fortune 500 companies are required to make an annual contribution of at least 20,000 USD or an equivalent amount to support the project's sustainability unless no donation option is provided.
Additionally, note that the use of AI-assisted tools, including but not limited to GitHub Copilot and ChatGPT, is expressly permitted in conjunction with this software. Users are encouraged to leverage these AI tools to enhance their experience in developing, modifying, and interacting with the Software. The permission granted herein extends to the integration and utilization of AI-generated content for coding and communication purposes. The owners(s) of the Software acknowledge and embrace the collaborative nature of AI-assisted development.
In addition, the owner of the code is granted the authority to update their copy of The Samuel Public License (SPL) to the latest revision. This update may be undertaken at the discretion of the owner to ensure alignment with any subsequent revisions made to The Samuel Public License.
The aforementioned copyright notice and this permission notice must be included in all copies or substantial portions of the Software.

6
LICENSE.md Normal file
View file

@ -0,0 +1,6 @@
# About licensing...
GEMS Kernel is a cross-licensed project. Here's how it works:
* GEMS Kernel's own code is covered under "LICENSE-GEMS-CODE"
* Everything in the "THIRDPARTY" folder is covered under it's respective licenses

12
NOTES.md Normal file
View file

@ -0,0 +1,12 @@
Here are all the notes so far:
http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm
this link is useful since it teaches you how to call lua from a running C program by using lua libraries
(PS: Do not use GNU Linker if you run lua in c, however for technical reasons most OSDev Kernels/OSes (including ours) require ld, so we can no longer use LUA..)
You can't make LUA in C bootable. :-(
Seeking outside the file? Try changing linker.ld and changing your bootloader to start.s! Modify accordingly.
*You really do need a i686 cross-compiler, get one here: https://drive.google.com/file/d/0Bw6lG3Ej2746STJaM2dNbC05elE/view?usp=sharing*

6
OLD/bl1.asm Normal file
View file

@ -0,0 +1,6 @@
;BL.asm files are deprecated, please use booloader.asm
mov ax, 9ch
mov ss, ax ;cannot be written directly
mov sp, 4094d
mov ax, 7c0h
mov ds, ax ;cannot be written directly

8
OLD/bl2.asm Normal file
View file

@ -0,0 +1,8 @@
;BL.asm files are deprecated, please use booloader.asm
quit:
hlt
jmp quit
jmp $
times 510-($-$$) db 0
dw 0xAA55

43
OLD/micro.lua Normal file
View file

@ -0,0 +1,43 @@
--NOTE TO DEVS:
--THESE LUA FILES ARE DEPRECATED..
--USE RUSHELL.H INSTEAD...
fn = ""
file = ""
_G.buffer = ""
exitmicro = false
function wait(s)
local ntime = os.time() + s
repeat until os.time() > ntime
end
function save(file)
_G[fn] = _G.buffer
end
function try(code)
if code == "::clear" then
_G.buffer = ""
io.write("Clear OK");
elseif code == "::exit" then
exitmicro = true
io.write("Exit OK");
else
_G.buffer = _G.buffer .. "\n" .. line
end
end
io.write("File name:");
fn = io.read("*l");
if _G[fn] ~= nil then
_G.buffer = _G[fn]
else
_G.buffer = ""
end
while exitmicro == false do
io.write("->");
line = io.read("*l");
try(line);
save(fn)
end
print(_G[fn]);

139
OLD/os.lua Normal file
View file

@ -0,0 +1,139 @@
--NOTE TO DEVS:
--THESE LUA FILES ARE DEPRECATED..
--USE RUSHELL.H INSTEAD...
args = {}
ver = "0.721 - DIY App Update!"
--UDP packet send code begin
function sendPacket(rec, cont)
local socket = require("socket")
local udp = assert(socket.udp())
local data
udp:settimeout(1)
assert(udp:setsockname("*",0))
assert(udp:setpeername(rec,1234))
for i = 0, 2, 1 do
assert(udp:send(cont))
data = udp:receive()
if data then
break
end
end
if data == nil then
print("timeout")
else
print(data)
end
end
--End of socket code
function wait(s)
local ntime = os.time() + s
repeat until os.time() > ntime
end
function clear()
for i = 1,26 do
print("\n\n")
end
end
function dog(fn)
io.write(_G[fn] .. "\n");
end
function lf(fn)
assert(loadstring(_G[fn] .. "\n"))();
end
function rushell(code)
if string.find(_G.cmd, "print") then
io.write(string.gsub(_G.cmd, "print " , "") .. "\n")
elseif _G.cmd == "version" then
io.write("Rushell version " .. ver .. " Beta\n");
elseif _G.cmd == "help" then
io.write("There are seven commands:\n");
io.write("print - Prints to the console - Usage: print hello world\n");
io.write("version - prints the version - Usage: version\n");
io.write("help - lists all the different commands - Usage: help\n");
io.write("clear - clears the screen (not perfect) - Usage: clear\n");
io.write("about - About rushell. - Usage: about\n");
io.write("prompt - Waits until the user presses any key.\n");
io.write("dog - cat alternative\n");
io.write("micro - auto-saving text editor\n");
elseif _G.cmd == "clear" then
clear();
elseif _G.cmd == "about" then
io.write('Rushell is a small shell/scripting language for the GEMS OS.\n');
elseif _G.cmd == "wait" then
wait(args[1]);
elseif _G.cmd == "prompt" then
io.write("Press any key to continue");
io.read("*l");
elseif string.find(_G.cmd, "loadfile") then
lf(string.gsub(_G.cmd, "loadfile ", ""))
elseif string.find(_G.cmd, "loadstring") then
assert(loadstring(string.gsub(_G.cmd, "loadstring ", "")))();
elseif _G.cmd == "micro" then
fn = ""
file = ""
_G.buffer = ""
exitmicro = false
function wait(s)
local ntime = os.time() + s
repeat until os.time() > ntime
end
function save(file)
_G[fn] = _G.buffer
end
function try(code)
if code == "::clear" then
_G.buffer = ""
io.write("Clear OK");
elseif code == "::exit" then
exitmicro = true
io.write("Exit OK");
else
_G.buffer = _G.buffer .. "\n" .. line
end
end
io.write("File name:");
fn = io.read("*l");
if _G[fn] ~= nil then
_G.buffer = _G[fn]
else
_G.buffer = ""
end
while exitmicro == false do
io.write("->");
line = io.read("*l");
try(line);
save(fn)
end
print(_G[fn]);
elseif string.find(_G.cmd, "dog") then
dog(string.gsub(_G.cmd, "dog ", ""))
else
io.write("Unknown command!" .. "\n")
end
args = {}
end
io.write("GEMS LOADED SUCCESSFULLY!\n");
io.write("We didn't plan ahead but you can try the Rushell beta edition while we finish the OS.\n");
io.write("Hello! Welcome to Rushell " .. ver .. " BETA\n");
while true do
io.write("Rushell> ")
line = io.read("*l");
cmd = string.lower(line)
rushell(cmd);
end

55
README.md Normal file
View file

@ -0,0 +1,55 @@
# <img src="gems.png" alt="Logo" width="5%"/> GEMS KERNEL
GEMS - GEMS is ESSENTIALLY MAYBE SPINEL
*Note: This is not Spinel, we are just trying to be better. ;)*
Note: GEMS is in *really early development*, and will probably be that way for ***quite a while.*** Note: This is highly based off of https://wiki.osdev.org/ and user Zesterer's Bare_Bones, however it is modified quite a bit to work for me.
Seriously... You can only boot to a small splash screen, you can't even type anything...
but it has Colors, Sounds, A panic system so it is definitely improving.
However, here are the dependencies (in ubuntu) in case you *want* to compile this
~~sudo apt install -y libc6-dev-i386 grub-common nasm gcc liblua5.1-0-dev liblua50:i386 liblua50-dev:i386 libc-dev:i386 linux-libc-dev:i386 libc6-dev:i386 qemu-system-i386 grub-pc-bin lua-socket xorriso gcc-9-multilib libc6-dev-i386 libc6-x32 gcc-multilib libc6-dev-x32 libc6-dev linux-libc-dev~~
Update: You might want to install these:
```
sudo apt install -y build-essential grub-common nasm gcc git qemu-system-x86 grub-pc-bin lua-socket xorriso gcc-10-multilib gcc-arm-none-eabi git nasm grub2
```
Oh and also: You really need an *i686 cross compiler,* I reccomend this one here for GNU/Linux (put it int the source directory): https://drive.google.com/file/d/0Bw6lG3Ej2746STJaM2dNbC05elE/view?usp=sharing
Or for macOS, run this brew command (requires homebrew)
```
brew install i686-elf-gcc nasm llvm objconv
```
To compile run:
```
cd source && ../build-with-iso.sh
```
Or on a Mac:
```
cd source && ./build-mac.sh
```
And then setup GRUB on macOS if you would wish to create an ISO, as explained here:
https://wiki.osdev.org/GRUB_2#HDD_Image_Instructions_for_OS_X_users
~~(Hint, download LUA 5.0 source here: https://www.lua.org/ftp/lua-5.0.tar.gz)~~ Update: We are switching to mostly custom C/ASM
Oh and a quick warning: *Test.sh / Qemu's PC SPEAKER is VERY LOUD. You will want to turn down your volume.*
# Other files
COC.md is the *old* Code of Conduct for historical purposes. Read the new one [here.](https://github.com/NodeMixaholic/NodeMixaholic-COC/blob/master/COC.md)
BUGS.md is for listing all the different bugs and their origins/source and status.
NOTES.md is for listing everything we have learned so far.
WSLHOWTO.MD is a tutorial on how to build and run GEMS in Windows.
## Note about Licensing...
THIRDPARTY software under the source/THIRDPARTY directory are under their own licenses, shown in their own directories, and not the main license.

9
TODO.md Normal file
View file

@ -0,0 +1,9 @@
# TODO
## FROM GREATEST TO LEAST PRIORITY
* Fix int2str
* Get PS/2 keyboard support working
* Convert keycodes to english characters according to standard en-US layout
* Finish up RuShell
* Fix the ugly identation at the rloadstring("welcomescreen"); and no... running clear(); twice doesn't fix it.
https://cdn.discordapp.com/attachments/708416737774665782/740362200744591440/bug.png

27
WSLHOWTO.MD Normal file
View file

@ -0,0 +1,27 @@
# How to build (and run) GEMS in WSL.
If you decided to switch to Windows like me and you want to develop GEMS then here is a tutorial on how to do so:
How to build:
Setup WSL 2.x (Select Ubuntu or Debian)
Install all of the packages
run ./build.sh
How to test:
Download QEMU for windows.
Copy the GEMS ISO to the QEMU folder
Then run "./qemu-system-x86_64.exe -soundhw pcspk -cdrom builds/gems.iso -m 1000"
There are probably other, better ways of running GEMS in windows.
Try VirtualBox but give it a lot of Memory.
Maybe change the PATH variable to the QEMU Folder.
and then just run the command in Powershell (Without ./ at the beginning)

24
build-macos.sh Executable file
View file

@ -0,0 +1,24 @@
echo "Setting target as 32bit ELF"
export TARGET=i386-elf
echo "Making directories"
mkdir builds
mkdir builds/blocks
mkdir builds/iso
mkdir builds/iso/boot
mkdir builds/iso/boot/grub
echo "Building bootloader"
i686-elf-gcc -std=gnu99 -ffreestanding -g -c start.s -o builds/blocks/bl.o
echo "Building basic keyboard support"
nasm -f elf32 detect-kbinput.asm -o builds/blocks/detectkeys.o
echo "Building time related stuff..."
echo "Building OS"
set disassembly-flavor intel
i686-elf-gcc builds/blocks/bl.o builds/blocks/detectkeys.o os.c -w -g -ffreestanding -m32 -o builds/iso/gems.elf -I"/usr/include" -nostdlib
echo "Creating GRUB config"
echo "set default=0" > builds/iso/boot/grub/grub.cfg
echo "set timeout=60" >> builds/iso/boot/grub/grub.cfg
echo 'menuentry "GEMS" {' >> builds/iso/boot/grub/grub.cfg
echo " multiboot /gems.elf" >> builds/iso/boot/grub/grub.cfg
echo " boot" >> builds/iso/boot/grub/grub.cfg
echo "}" >> builds/iso/boot/grub/grub.cfg
echo "Note: NOT CREATING ISO. USE CREATE-ISO.SH TO DO THIS."

5
build-with-iso.sh Executable file
View file

@ -0,0 +1,5 @@
./build.sh
echo "Creating ISO"
bash ./create-iso.sh

24
build.sh Executable file
View file

@ -0,0 +1,24 @@
echo "Setting target as 32bit ELF"
export TARGET=i386-elf
echo "Making directories"
mkdir builds
mkdir builds/blocks
mkdir builds/iso
mkdir builds/iso/boot
mkdir builds/iso/boot/grub
echo "Building bootloader"
nasm -f elf32 source/bootloader.asm -o builds/blocks/bootloader.o
echo "Building basic keyboard support"
nasm -f elf32 source/detect-kbinput.asm -o builds/blocks/detectkeys.o
echo "Building OS"
set disassembly-flavor intel
opt/cross/bin/i686-elf-gcc builds/blocks/bootloader.o -ffreestanding -nostdlib builds/blocks/detectkeys.o source/os.c -w -g -m32 -o builds/iso/gems.elf -I"/usr/include" -I"source/THIRDPARTY/lwext4-master/include/" -I"source/THIRDPARTY/linux-old/include/linux" -I"source/THIRDPARTY/linux-old/include/asm"
echo "Creating GRUB config"
echo "set default=0" > builds/iso/boot/grub/grub.cfg
echo "set timeout=60" >> builds/iso/boot/grub/grub.cfg
echo 'menuentry "GEMS" {' >> builds/iso/boot/grub/grub.cfg
echo " multiboot /gems.elf" >> builds/iso/boot/grub/grub.cfg
echo " boot" >> builds/iso/boot/grub/grub.cfg
echo "}" >> builds/iso/boot/grub/grub.cfg
echo "Creating ISO"
echo "Note: NOT CREATING ISO. USE CREATE-ISO.SH TO DO THIS."

2
create-iso.sh Executable file
View file

@ -0,0 +1,2 @@
echo "Creating ISO"
grub-mkrescue -d /usr/lib/grub/i386-pc -o builds/gems.iso builds/iso

BIN
gems.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

1
install-depends.sh Executable file
View file

@ -0,0 +1 @@
sudo apt install xorriso libc6-dev-i386

52
linker.ld Normal file
View file

@ -0,0 +1,52 @@
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(_start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* It used to be universally recommended to use 1M as a start offset,
as it was effectively guaranteed to be available under BIOS systems.
However, UEFI has made things more complicated, and experimental data
strongly suggests that 2M is a safer place to load. In 2016, a new
feature was introduced to the multiboot2 spec to inform bootloaders
that a kernel can be loaded anywhere within a range of addresses and
will be able to relocate itself to run from such a loader-selected
address, in order to give the loader freedom in selecting a span of
memory which is verified to be available by the firmware, in order to
work around this issue. This does not use that feature, so 2M was
chosen as a safer option than the traditional 1M. */
. = 2M;
/* First put the multiboot header, as it is required to be put very early
in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
}

5
quick-test.sh Executable file
View file

@ -0,0 +1,5 @@
if grub-file --is-x86-multiboot builds/iso/gems.elf; then
echo multiboot confirmed
else
echo the file is not multiboot
fi

1
source/THIRDPARTY/about Normal file
View file

@ -0,0 +1 @@
This is the thirdparty material that made the kernel possible. The LICENSEs are in their respective folders, and as such these works are seperate.

View file

@ -0,0 +1,133 @@
CHANGES since 0.99 patchlevel 13:
- new kernel source layout: drivers separated
- lots of networking bugs fixed, and new network card drivers (Alan Cox,
Donald Becker &co)
- sound driver added to the default source distribution (Hannu
Savolainen)
- updated SCSI driver code (Eric Youngdale, Drew Eckhardt &co)
- readonly OS/2 filesystem support (HPFS) added (Chris Smith)
- NTP support (Philip Gladstone, Torsten Duwe, ??)
- fixed 16MB swap-area limit
- lots of minor cleanups, buxfixes etc.
CHANGES since 0.99 patchlevel 12 and earlier:
- the bad memory management one-liner bug in pl12 is naturally fixed.
- compiled with plain C by default instead of C++
- ELF binary support (Eric Youngdale)
- Quickport mouse support (and some changes to the PS/2 mouse driver)
by Johan Myreen and co)
- core file name change ("core" -> "core.xxxx" where xxxx is the name
of the program that dumped code). Idea from ???. Also, core-files
now correctly truncate any existing core file before being written.
- some mmap() fixes: better error returns, and handling of non-fixed
maps for /dev/mem etc.
- one kludgy way to fix the wrong arp packets that have plagued net-2d
(resulting in arp packets that had the first four bytes of the
ethernet address as the IP address).
- I fixed the mount-point handling of 'rename()' and 'unlink()/rmdir()'
so that they should now work and/or give appropriate error messages.
An early version of this patch was already sent to the KERNEL
channel, which fixed the rename problem but not a similar bug with
unlink.
- packet mode fixes by Charles Hedrick. Sadly, these are likely to
break old telnet/rlogin binaries, but it had to be done in order to
communicate correctly with the rest of the world.
- FPU emulator patches from Bill Metzenthen. The fprem1 insn should be
correct now (not that anybody seems to have seen the incorrect
behaviour..)
- a few fixes for SCSI (Drew and Eric)
- signal.c changes to handle multiple segments (for Wine) correctly.
- updated drivers from Donald Becker: 3c509 and AT1500 drivers, but
also some other drivers have been edited, and some networking fixes.
CHANGES since 0.99 patchlevel 11 and earlier:
- The memory manager cleanup has continued, and seems to be mostly
ready, as proven by the ease of adding mmap() over NFS with the new
routines. So yes, the pl12 kernel will demand-load your binaries
over NFS, sharing code and clean data, as well as running shared
libraries over NFS. Memory management by Eric and me, while the NFS
mmap code was written by Jon Tombs,
- ** IMPORTANT **: The keyboard driver has been enhanced even further,
and almost everything is completely re-mappable. This means that
there is a new version of 'loadkeys' and 'dumpkeys' that you must use
with this kernel or you'll have problems. The default keyboard is
still the US mapping, but if you want to create your own mappings
you'll have to load them with the new binaries. Get the 'kbd.tar.gz'
archive from the same place you get the kernel.
The new keymappings allow things like function key string changes,
remapping of the control keys, and freedom to remap any of the normal
keyboard functions: including special features like rebooting,
console switching etc. The keyboard remapping code has been done
mostly by Risto Kankkunen (Risto.Kankkunen@Helsinki.FI).
- updated network drivers by Donald Becker
- updated serial drivers - tytso@Athena.mit.edu
- updated 387 emulation (Bill Metzenthen). The updated emulator code
has more exact trigonometric functions and improved exception
handling. It now behaves very much like a real 486, with only small
changes (greater accuracy, slightly different denormal NaN handling
etc - hard to detect the differences even if you are looking for
them).
- network timer fixes by Florian La Roche (much cleaned up net/inet/timer.c
and some bad race-conditions fixed).
- Scsi code updates by Eric Youngdale and others
- Sony CDU-31A CDROM driver by Corey Minyard added to the standard
kernel distribution.
- The Mitsumi CDROM driver is now part of the standard kernel. Driver
by Martin Harriss with patches by stud11@cc4.kuleuven.ac.be (yes, he
probably has a real name, but no, I haven't found it) and Jon Tombs.
- various other minor patches (preliminary ldt support etc)
NOTABLE changes since patchlevel 10 or earlier:
- The memory manager has been cleaned up substantially, and mmap()
works for MAP_PRIVATE. MAP_SHARED is still not supported for
anything else than /dev/mem, but even so it actually is usable for a
lot of applications. The shared library routines have been rewritten
to use mmap() instead of the old hardcoded behaviour.
- The kernel is now compiled with C++ instead of plain C. Very few
actual C++ features are used, but even so C++ allows for more
type-checking and type-safe linkage.
- The filesystem routines have been cleaned up for multiple block
sizes. None of the filesystems use it yet, but people are working on
it.
- named pipes and normal pipes should hopefully have the right select()
semantics in the presense/absense of writers.
- QIC-02 tape driver by Hennus Bergman
- selection patches in the default kernel
- fixed a bug in the pty code which led to busy waiting in some
circumstances instead of sleeping.
- Compressed SLIP support (Charles Hedrick). See net/inet/CONFIG
- the 'clear_bit()' function was changed to return the previous setting
of the bit instead of the old "error-code". This makes use of the
bit operations more logical.
- udelay() function for short delays (busy-waiting) added. Used
currently only by the QIC driver.
- fork() and sheduler changes to make task switches happen only from
kernel mode to kernel mode. Cleaner and more portable than the old
code which counted on being able to task-switch directly into user
mode.
- debugging malloc code.

View file

@ -0,0 +1,351 @@
NOTE! This copyright does *not* cover user programs that use kernel
services by normal system calls - this is merely considered normal use
of the kernel, and does *not* fall under the heading of "derived work".
Also note that the GPL below is copyrighted by the Free Software
Foundation, but the instance of code that it refers to (the linux
kernel) is copyrighted by me and others who actually wrote it.
Linus Torvalds
----------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View file

@ -0,0 +1,242 @@
#! /bin/sh
#
# This script is used to configure the linux kernel.
#
# It was inspired by the challenge in the original Configure script
# to ``do something better'', combined with the actual need to ``do
# something better'' because the old configure script wasn't flexible
# enough.
#
# Please send comments / questions / bug fixes to raymondc@microsoft.com.
#
# Each line in the config file is a command.
#
# # internal comment
#
# Lines beginning with a `#' are ignored.
#
# : message
#
# `:' causes the line to be echoed to the screen.
#
# * external comment
#
# `*' causes the line to be placed in the output
# configuration file as a comment as well as being
# echoed to the screen.
#
# if condition
# ... commands ...
# else
# ... commands ...
# fi
#
# This does the obvious thing. The `else' clause is
# optional. Conditionals can be nested.
#
# The `condition' can be any valid bash expression.
# They typically involve tests against environment
# variables set by configuration options. For example,
#
# if [ "$CONFIG_SCSI" = "y" ]
# ...More stuff...
# fi
#
# Note! That there is no `then' keyword.
#
# bool 'prompt' CONFIG_VARIABLE default
#
# This prompts the user for a boolean value.
# The prompt may not contain an apostrophe.
# `default' should be either `y' or `n'.
# The user's response is recorded in four places.
#
# In .config, if `y'
# CONFIG_VARIABLE = CONFIG_VARIABLE
# In .config, if `n'
# # CONFIG_VARIABLE is not set
#
# In autoconf.h, if `y'
# #define CONFIG_VARIABLE 1
# In autoconf.h, if `n'
# #undef CONFIG_VARIABLE
#
# In config.in, if `y'
# bool 'prompt' CONFIG_VARIABLE y
# In config.in, if `n'
# bool 'prompt' CONFIG_VARIABLE n
#
# In the environment of the Configure script, if `y'
# CONFIG_VARIABLE = y
# In the environment of the Configure script, if `n'
# CONFIG_VARIABLE = n
#
# The value is placed into the environment of the Configure
# script so that later parts of config.in can use the `if'
# command to inspect the results of previous queries.
#
# int 'prompt' CONFIG_VARIABLE default
#
# This prompts the user for an integer value.
# The prompt may not contain an apostrophe.
# `default' should be an integer.
#
# The response is recorded as follows.
#
# In .config
# CONFIG_VARIABLE = response
# In autoconf.h
# #define CONFIG_VARIABLE (response)
# In config.in
# int 'prompt' CONFIG_VARIABLE response
# In the environment of the Configure script
# CONFIG_VARIABLE = response
#
# 050793 - use IFS='@' to get around a bug in a pre-version of bash-1.13
# with an empty IFS.
#
# Make sure we're really running bash.
#
# I would really have preferred to write this script in a language with
# better string handling, but alas, bash is the only scripting language
# that I can be reasonable sure everybody has on their linux machine.
#
[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }
# Disable filename globbing once and for all.
# Enable function cacheing.
set -f -h
#
# readln reads a line into $ans.
#
# readln prompt default
#
function readln () {
echo -n "$1"
IFS='@' read ans </dev/tty || exit 1
[ -z "$ans" ] && ans=$2
}
# bool processes a boolean argument
#
# bool tail
#
function bool () {
# Slimier hack to get bash to rescan a line.
eval "set -- $1"
ans=""
while [ "$ans" != "y" -a "$ans" != "n" ]; do
readln "$1 ($2) [$3] " "$3"
done
if [ "$ans" = "y" ]; then
echo "$2 = $2" >>$CONFIG
echo "#define $2 1" >>$CONFIG_H
else
echo "# $2 is not set" >>$CONFIG
echo "#undef $2" >>$CONFIG_H
fi
raw_input_line="bool '$1' $2 $ans"
eval "$2=$ans"
}
# int processes an integer argument
#
# int tail
#
function int () {
# Slimier hack to get bash to rescan a line.
eval "set -- $1"
ans="x"
while [ $[$ans+0] != "$ans" ]; do
readln "$1 ($2) [$3] " "$3"
done
echo "$2 = $ans" >>$CONFIG
echo "#define $2 ($ans)" >>$CONFIG_H
raw_input_line="int '$1' $2 $ans"
eval "$2=$ans"
}
CONFIG=.config~
CONFIG_H=include/linux/autoconf.h
#
# Make sure we start out with a clean slate.
#
> config.new
echo "#" > $CONFIG
echo "# Automatically generated make config: don't edit" >> $CONFIG
echo "#" >> $CONFIG
echo "/*" > $CONFIG_H
echo " * Automatically generated C config: don't edit" >> $CONFIG_H
echo " */" >> $CONFIG_H
stack=''
branch='t'
while IFS='@' read raw_input_line
do
# Slimy hack to get bash to rescan a line.
read cmd rest <<-END_OF_COMMAND
$raw_input_line
END_OF_COMMAND
if [ "$cmd" = "*" ]; then
if [ "$branch" = "t" ]; then
echo "$raw_input_line"
echo "# $rest" >>$CONFIG
if [ "$prevcmd" != "*" ]; then
echo >>$CONFIG_H
echo "/* $rest" >>$CONFIG_H
else
echo " * $rest" >>$CONFIG_H
fi
prevcmd="*"
fi
else
[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H
prevcmd=""
case "$cmd" in
:) [ "$branch" = "t" ] && echo "$raw_input_line" ;;
int) [ "$branch" = "t" ] && int "$rest" ;;
bool) [ "$branch" = "t" ] && bool "$rest" ;;
exec) [ "$branch" = "t" ] && ( sh -c "$rest" ) ;;
if) stack="$branch $stack"
if [ "$branch" = "t" ] && eval "$rest"; then
branch=t
else
branch=f
fi ;;
else) if [ "$branch" = "t" ]; then
branch=f
else
read branch rest <<-END_OF_STACK
$stack
END_OF_STACK
fi ;;
fi) [ -z "$stack" ] && echo "Error! Extra fi." 1>&2
read branch stack <<-END_OF_STACK
$stack
END_OF_STACK
;;
esac
fi
echo "$raw_input_line" >>config.new
done
[ "$prevcmd" = "*" ] && echo " */" >>$CONFIG_H
[ -z "$stack" ] || echo "Error! Untermiated if." 1>&2
mv config.in config.old
mv config.new config.in
echo
echo "The linux kernel is now hopefully configured for your setup."
echo "Check the top-level Makefile for additional configuration,"
echo "and do a 'make dep ; make clean' if you want to be sure all"
echo "the files are correctly re-made"
echo
exit 0

View file

@ -0,0 +1,296 @@
VERSION = 0.99
PATCHLEVEL = 15
ALPHA =
all: Version zImage
.EXPORT_ALL_VARIABLES:
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
#
# Make "config" the default target if there is no configuration file or
# "depend" the target if there is no top-level dependency information.
#
ifeq (.config,$(wildcard .config))
include .config
ifeq (.depend,$(wildcard .depend))
include .depend
else
CONFIGURATION = depend
endif
else
CONFIGURATION = config
endif
ifdef CONFIGURATION
CONFIGURE = dummy
endif
#
# ROOT_DEV specifies the default root-device when making the image.
# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case
# the default of FLOPPY is used by 'build'.
#
ROOT_DEV = CURRENT
#
# If you want to preset the SVGA mode, uncomment the next line and
# set SVGA_MODE to whatever number you want.
# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
# The number is the same as you would ordinarily press at bootup.
#
SVGA_MODE= -DSVGA_MODE=NORMAL_VGA
#
# standard CFLAGS
#
CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe
ifdef CONFIG_CPP
CFLAGS := $(CFLAGS) -x c++
endif
ifdef CONFIG_M486
CFLAGS := $(CFLAGS) -m486
else
CFLAGS := $(CFLAGS) -m386
endif
#
# if you want the ram-disk device, define this to be the
# size in blocks.
#
#RAMDISK = -DRAMDISK=512
AS86 =as86 -0 -a
LD86 =ld86 -0
AS =as
LD =ld
HOSTCC =gcc
CC =gcc -D__KERNEL__
MAKE =make
CPP =$(CC) -E
AR =ar
STRIP =strip
ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o ipc/ipc.o
FILESYSTEMS =fs/filesystems.a
DRIVERS =drivers/block/block.a \
drivers/char/char.a \
drivers/net/net.a \
ibcs/ibcs.o
LIBS =lib/lib.a
SUBDIRS =kernel drivers mm fs net ipc ibcs lib
KERNELHDRS =/usr/src/linux/include
ifdef CONFIG_SCSI
DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
endif
ifdef CONFIG_SOUND
DRIVERS := $(DRIVERS) drivers/sound/sound.a
endif
ifdef CONFIG_MATH_EMULATION
DRIVERS := $(DRIVERS) drivers/FPU-emu/math.a
endif
.c.s:
$(CC) $(CFLAGS) -S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c -o $*.o $<
Version: dummy
rm -f tools/version.h
config:
$(CONFIG_SHELL) Configure $(OPTS) < config.in
@if grep -s '^CONFIG_SOUND' .config~ ; then \
$(MAKE) -C drivers/sound config; \
else : ; fi
mv .config~ .config
linuxsubdirs: dummy
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
tools/./version.h: tools/version.h
tools/version.h: $(CONFIGURE) Makefile
@./makever.sh
@echo \#define UTS_RELEASE \"$(VERSION).$(PATCHLEVEL)$(ALPHA)\" > tools/version.h
@echo \#define UTS_VERSION \"\#`cat .version` `date`\" >> tools/version.h
@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h
@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h
@echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> tools/version.h
@echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\" >> tools/version.h
tools/build: tools/build.c $(CONFIGURE)
$(HOSTCC) $(CFLAGS) -o $@ $<
boot/head.o: $(CONFIGURE) boot/head.s
boot/head.s: boot/head.S $(CONFIGURE) include/linux/tasks.h
$(CPP) -traditional $< -o $@
tools/version.o: tools/version.c tools/version.h
init/main.o: $(CONFIGURE) init/main.c
$(CC) $(CFLAGS) $(PROFILING) -c -o $*.o $<
tools/system: boot/head.o init/main.o tools/version.o linuxsubdirs
$(LD) $(LDFLAGS) -T 1000 boot/head.o init/main.o tools/version.o \
$(ARCHIVES) \
$(FILESYSTEMS) \
$(DRIVERS) \
$(LIBS) \
-o tools/system
nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
sort > System.map
boot/setup: boot/setup.o
$(LD86) -s -o $@ $<
boot/setup.o: boot/setup.s
$(AS86) -o $@ $<
boot/setup.s: boot/setup.S $(CONFIGURE) include/linux/config.h Makefile
$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
boot/bootsect: boot/bootsect.o
$(LD86) -s -o $@ $<
boot/bootsect.o: boot/bootsect.s
$(AS86) -o $@ $<
boot/bootsect.s: boot/bootsect.S $(CONFIGURE) include/linux/config.h Makefile
$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem
$(MAKE) -C zBoot
zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build
tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage
sync
zdisk: zImage
dd bs=8192 if=zImage of=/dev/fd0
zlilo: $(CONFIGURE) zImage
if [ -f /vmlinuz ]; then mv /vmlinuz /vmlinuz.old; fi
cat zImage > /vmlinuz
if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs
$(LD) $(LDFLAGS) -T 100000 boot/head.o init/main.o tools/version.o \
$(ARCHIVES) \
$(FILESYSTEMS) \
$(DRIVERS) \
$(LIBS) \
-o tools/zSystem
nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
sort > zSystem.map
fs: dummy
$(MAKE) linuxsubdirs SUBDIRS=fs
lib: dummy
$(MAKE) linuxsubdirs SUBDIRS=lib
mm: dummy
$(MAKE) linuxsubdirs SUBDIRS=mm
ipc: dummy
$(MAKE) linuxsubdirs SUBDIRS=ipc
kernel: dummy
$(MAKE) linuxsubdirs SUBDIRS=kernel
drivers: dummy
$(MAKE) linuxsubdirs SUBDIRS=drivers
net: dummy
$(MAKE) linuxsubdirs SUBDIRS=net
clean:
rm -f kernel/ksyms.lst
rm -f core `find . -name '*.[oas]' -print`
rm -f core `find . -name 'core' -print`
rm -f zImage zSystem.map tools/zSystem tools/system
rm -f Image System.map boot/bootsect boot/setup
rm -f zBoot/zSystem zBoot/xtract zBoot/piggyback
rm -f drivers/sound/configure
rm -f init/*.o tools/build boot/*.o tools/*.o
mrproper: clean
rm -f include/linux/autoconf.h tools/version.h
rm -f drivers/sound/local.h
rm -f .version .config* config.old
rm -f .depend `find . -name .depend -print`
distclean: mrproper
backup: mrproper
cd .. && tar cf - linux | gzip -9 > backup.gz
sync
depend dep:
touch tools/version.h
for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done > .depend~
for i in tools/*.c;do echo -n "tools/";$(CPP) -M $$i;done >> .depend~
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
rm -f tools/version.h
mv .depend~ .depend
ifdef CONFIGURATION
..$(CONFIGURATION):
@echo
@echo "You have a bad or nonexistent" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'"
@echo
$(MAKE) $(CONFIGURATION)
@echo
@echo "Successful. Try re-making (ignore the error that follows)"
@echo
exit 1
dummy: ..$(CONFIGURATION)
else
dummy:
endif
#
# Leave these dummy entries for now to tell people that they are going away..
#
lilo:
@echo
@echo Uncompressed kernel images no longer supported. Use
@echo \"make zlilo\" instead.
@echo
@exit 1
Image:
@echo
@echo Uncompressed kernel images no longer supported. Use
@echo \"make zImage\" instead.
@echo
@exit 1
disk:
@echo
@echo Uncompressed kernel images no longer supported. Use
@echo \"make zdisk\" instead.
@echo
@exit 1

View file

@ -0,0 +1,181 @@
Linux kernel release 0.99 patchlevel 14
These are the release notes for linux version 0.99.14. Read them
carefully, as they tell you what's new, explain how to install the
kernel, and what to do if something goes wrong.
INSTALLING the kernel:
- if you install by patching, you need a *clean* 0.99.13 source tree,
which presumably exists in /usr/src/linux. If so, to get the kernel
patched, just do a
cd /usr/src
patch -p0 < linux-0.99.patch14
and you should be ok. You may want to remove the backup files (xxx~
or xxx.orig), and make sure that there are no failed patches (xxx# or
xxx.rej).
- If you install the full sources, do a
cd /usr/src
tar xvf linux-0.99.14.tar
to get it all put in place.
- make sure your /usr/include/linux and /usr/include/asm directories
are just symlinks to the kernel sources:
cd /usr/include
rm -rf linux
rm -rf asm
ln -s /usr/src/linux/include/linux .
ln -s /usr/src/linux/include/asm .
- make sure you have no stale .o files and dependencies lying around:
cd /usr/src/linux
make mrproper
You should now have the sources correctly installed.
CONFIGURING the kernel:
- do a "make config" to configure the basic kernel. "make config"
needs bash to work: it will search for bash in $BASH, /bin/bash and
/bin/sh (in that order), so hopefully one of those is correct.
NOTES on "make config":
- compiling the kernel with "-m486" for a number of 486-specific
will result in a kernel that still works on a 386: it may be
slightly larger and possibly slower by an insignificant amount,
but it should not hurt performance.
- A kernel with math-emulation compiled in will still use the
coprocessor if one is present: the math emulation will just
never get used in that case. The kernel will be slighly larger,
but will work on different machines regardless of whether they
have a math coprocessor or not.
- the "kernel hacking" configuration details usually result in a
bigger or slower kernel (or both), and can even make the kernel
less stable by configuring some routines to actively try to
break bad code to find kernel problems (kmalloc()). Thus you
should probably answer 'n' to the questions for a "production"
kernel.
- edit drivers/net/CONFIG to configure the networking parts of the
kernel. The comments should hopefully clarify it all.
- Check the top Makefile for further site-dependent configuration
(default SVGA mode etc).
- Finally, do a "make dep" to set up all the dependencies correctly.
COMPILING the kernel:
- make sure you have gcc-2.4.5 or newer available with g++. It seems
older gcc versions can have problems compiling linux 0.99.10 and
newer versions. If you upgrade, remember to get the new binutils
package too (for as/ld/nm and company)
- do a "make zImage" to create a compressed kernel image. If you want
to make a bootdisk (without root filesystem or lilo), insert a floppy
in your A: drive, and do a "make zdisk". It is also possible to do
"make zlilo" if you have lilo installed to suit the kernel makefiles,
but you may want to check your particular lilo setup first.
- keep a backup kernel handy in case something goes wrong.
- In order to boot your new kernel, you'll need to copy the kernel
image (found in /usr/src/linux/zImage after compilation) to the place
where your regular bootable kernel is found.
For some, this is on a floppy disk, in which case you can "cp
/usr/src/linux/zImage /dev/fd0" to make a bootable floppy.
If you boot Linux from the hard drive, chances are you use LILO uses
the kernel image as specified in the file /etc/lilo/config. The
kernel image file is usually /vmlinux, or /Image, or /etc/Image. To
use the new kernel, copy the new image over the old one (save a
backup of the original!). Then, you MUST REINSTALL LILO!! If you
don't, you won't be able to boot the new kernel image.
Reinstalling LILO is usually a matter of running /etc/lilo/install.
You may wish to edit /etc/lilo/config to specify an entry for your
old kernel image (say, /vmlinux.old) in case the new one does not
work. See the LILO docs for more information.
After reinstalling LILO, you should be all set. Shutdown the system,
reboot, and enjoy!
If you ever need to change the default root device, video mode,
ramdisk size, etc. in the kernel image, use the 'rdev' program (or
alternatively the LILO boot options when appropriate). No need to
recompile the kernel to change these parameters.
- reboot with the new kernel and enjoy.
IF SOMETHING GOES WRONG:
- if you have problems that seem to be due to kernel bugs, please mail
them to me (Linus.Torvalds@Helsinki.FI), and possibly to any other
relevant mailing-list or to the newsgroup. The mailing-lists are
useful especially for SCSI and NETworking problems, as I can't test
either of those personally anyway.
- In all bug-reports, *please* tell what kernel you are talking about,
how to duplicate the problem, and what your setup is (use your common
sense). If the problem is new, tell me so, and if the problem is
old, please try to tell me when you first noticed it.
- if the bug results in a message like
unable to handle kernel paging request at address C0000010
Oops: 0002
EIP: 0010:xxxxxxxx
eax: xxxxxxxx ebx: xxxxxxxx ecx: xxxxxxxx edx: xxxxxxxx
esi: xxxxxxxx edi: xxxxxxxx ebp: xxxxxxxx
ds: xxxx es: xxxx fs: xxxx gs: xxxx
Pid: xx, process nr: xx
xx xx xx xx xx xx xx xx xx xx
or similar kernel debugging information on your screen or in your
system log, please duplicate it *exactly*. The dump may look
incomprehensible to you, but it does contain information that may
help debugging the problem. The text above the dump is also
important: it tells something about why the kernel dumped code (in
the above example it's due to a bad kernel pointer)
- in debugging dumps like the above, it helps enourmously if you can
look up what the EIP value means. The hex value as such doesn't help
me or anybody else very much: it will depend on your particular
kernel setup. What you should do is take the hex value from the EIP
line (ignore the "0010:"), and look it up in the kernel namelist to
see which kernel function contains the offending address.
To find out the kernel function name, you'll need to find the system
binary associated with the kernel that exhibited the symptom. In the
case of compressed kernels, this will be 'linux/tools/zSystem', while
uncompressed kernels use the file 'tools/system'. To extract the
namelist and match it against the EIP from the kernel crash, do:
nm tools/zSystem | sort | less
This will give you a list of kernel addresses sorted in ascending
order, from which it is simple to find the function that contains the
offending address. Note that the address given by the kernel
debugging messages will not necessarily match exactly with the
function addresses (in fact, that is very unlikely), so you can't
just 'grep' the list: the list will, however, give you the starting
point of each kernel function, so by looking for the function that
has a starting address lower than the one you are searching for but
is followed by a function with a higher address you will find the one
you want. In fact, it may be a good idea to include a bit of
"context" in your problem report, giving a few lines around the
interesting one.
If you for some reason cannot do the above (you have a pre-compiled
kernel image or similar), telling me as much about your setup as
possible will help.

View file

@ -0,0 +1,450 @@
!
! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current
! versions of linux which compress the kernel
!
#include <linux/config.h>
SYSSIZE = DEF_SYSSIZE
!
! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
! modified by Drew Eckhardt
! modified by Bruce Evans (bde)
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! itself out of the way to address 0x90000, and jumps there.
!
! bde - should not jump blindly, there may be systems with only 512K low
! memory. Use int 0x12 to get the top of memory, etc.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts.
!
! NOTE! currently system is at most (8*65536-4096) bytes long. This should
! be no problem, even in the future. I want to keep it simple. This 508 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix (and especially now that the kernel is
! compressed :-)
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole tracks at a time whenever possible.
.text
SETUPSECS = 4 ! nr of setup-sectors
BOOTSEG = 0x07C0 ! original address of boot-sector
INITSEG = DEF_INITSEG ! we move boot here - out of the way
SETUPSEG = DEF_SETUPSEG ! setup starts here
SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
! ROOT_DEV & SWAP_DEV are now written by "build".
ROOT_DEV = 0
SWAP_DEV = 0
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
#ifndef RAMDISK
#define RAMDISK 0
#endif
#ifndef CONFIG_ROOT_RDONLY
#define CONFIG_ROOT_RDONLY 0
#endif
! ld86 requires an entry symbol. This may as well be the usual one.
.globl _main
_main:
#if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
int 3
#endif
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
cld
rep
movsw
jmpi go,INITSEG
go: mov ax,cs
mov dx,#0x4000-12 ! 0x4000 is arbitrary value >= length of
! bootsect + length of setup + room for stack
! 12 is disk parm size
! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
! wouldn't have to worry about this if we checked the top of memory. Also
! my BIOS can be configured to put the wini drive tables in high memory
! instead of in the vector table. The old stack might have clobbered the
! drive table.
mov ds,ax
mov es,ax
mov ss,ax ! put stack at INITSEG:0x4000-12.
mov sp,dx
/*
* Many BIOS's default disk parameter tables will not
* recognize multi-sector reads beyond the maximum sector number
* specified in the default diskette parameter tables - this may
* mean 7 sectors in some cases.
*
* Since single sector reads are slow and out of the question,
* we must take care of this by creating new parameter tables
* (for the first disk) in RAM. We will set the maximum sector
* count to 18 - the most we will encounter on an HD 1.44.
*
* High doesn't hurt. Low does.
*
* Segments are as follows: ds=es=ss=cs - INITSEG,
* fs = 0, gs = parameter table segment
*/
push #0
pop fs
mov bx,#0x78 ! fs:bx is parameter table address
seg fs
lgs si,(bx) ! gs:si is source
mov di,dx ! es:di is destination
mov cx,#6 ! copy 12 bytes
cld
rep
seg gs
movsw
mov di,dx
movb 4(di),*18 ! patch sector count
seg fs
mov (bx),di
seg fs
mov 2(bx),es
mov ax,cs
mov fs,ax
mov gs,ax
xor ah,ah ! reset FDC
xor dl,dl
int 0x13
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
load_setup:
xor dx, dx ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPSECS ! service 2, nr of sectors
! (assume all on head 0, track 0)
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
push ax ! dump error code
call print_nl
mov bp, sp
call print_hex
pop ax
xor dl, dl ! reset FDC
xor ah, ah
int 0x13
jmp load_setup
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
#if 0
! bde - the Phoenix BIOS manual says function 0x08 only works for fixed
! disks. It doesn't work for one of my BIOS's (1987 Award). It was
! fatal not to check the error code.
xor dl,dl
mov ah,#0x08 ! AH=8 is get drive parameters
int 0x13
xor ch,ch
#else
! It seems that there is no BIOS call to get the number of sectors. Guess
! 18 sectors if sector 18 can be read, 15 if sector 15 can be read.
! Otherwise guess 9.
xor dx, dx ! drive 0, head 0
mov cx,#0x0012 ! sector 18, track 0
mov bx,#0x0200+SETUPSECS*0x200 ! address after setup (es = cs)
mov ax,#0x0201 ! service 2, 1 sector
int 0x13
jnc got_sectors
mov cl,#0x0f ! sector 15
mov ax,#0x0201 ! service 2, 1 sector
int 0x13
jnc got_sectors
mov cl,#0x09
#endif
got_sectors:
seg cs
mov sectors,cx
mov ax,#INITSEG
mov es,ax
! Print some inane message
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#9
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! ok, we've written the message, now
! we want to load the system (at 0x10000)
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
call read_it
call kill_motor
call print_nl
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
seg cs
mov ax,root_dev
or ax,ax
jne root_defined
seg cs
mov bx,sectors
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
cmp bx,#15
je root_defined
mov ax,#0x021c ! /dev/PS0 - 1.44Mb
cmp bx,#18
je root_defined
mov ax,#0x0200 ! /dev/fd0 - autodetect
root_defined:
seg cs
mov root_dev,ax
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
jmpi 0,SETUPSEG
! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in: es - starting address segment (normally 0x1000)
!
sread: .word 1+SETUPSECS ! sectors read of current track
head: .word 0 ! current head
track: .word 0 ! current track
read_it:
mov ax,es
test ax,#0x0fff
die: jne die ! es must be at 64kB boundary
xor bx,bx ! bx is starting address within segment
rp_read:
mov ax,es
sub ax,#SYSSEG
cmp ax,syssize ! have we loaded all yet?
jbe ok1_read
ret
ok1_read:
seg cs
mov ax,sectors
sub ax,sread
mov cx,ax
shl cx,#9
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,#9
ok2_read:
call read_track
mov cx,ax
add ax,sread
seg cs
cmp ax,sectors
jne ok3_read
mov ax,#1
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,#9
add bx,cx
jnc rp_read
mov ax,es
add ah,#0x10
mov es,ax
xor bx,bx
jmp rp_read
read_track:
pusha
pusha
mov ax, #0xe2e ! loading... message 2e = .
mov bx, #7
int 0x10
popa
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
and dx,#0x0100
mov ah,#2
push dx ! save for error dump
push cx
push bx
push ax
int 0x13
jc bad_rt
add sp, #8
popa
ret
bad_rt: push ax ! save error code
call print_all ! ah = error, al = read
xor ah,ah
xor dl,dl
int 0x13
add sp, #10
popa
jmp read_track
/*
* print_all is for debugging purposes.
* It will print out all of the registers. The assumption is that this is
* called from a routine, with a stack frame like
* dx
* cx
* bx
* ax
* error
* ret <- sp
*
*/
print_all:
mov cx, #5 ! error code + 4 registers
mov bp, sp
print_loop:
push cx ! save count left
call print_nl ! nl for readability
cmp cl, 5
jae no_reg ! see if register name is needed
mov ax, #0xe05 + 'A - 1
sub al, cl
int 0x10
mov al, #'X
int 0x10
mov al, #':
int 0x10
no_reg:
add bp, #2 ! next register
call print_hex ! print it
pop cx
loop print_loop
ret
print_nl:
mov ax, #0xe0d ! CR
int 0x10
mov al, #0xa ! LF
int 0x10
ret
/*
* print_hex is for debugging purposes, and prints the word
* pointed to by ss:bp in hexadecmial.
*/
print_hex:
mov cx, #4 ! 4 hex digits
mov dx, (bp) ! load word into dx
print_digit:
rol dx, #4 ! rotate so that lowest 4 bits are used
mov ah, #0xe
mov al, dl ! mask off so we have only next nibble
and al, #0xf
add al, #'0 ! convert to 0-based digit
cmp al, #'9 ! check for overflow
jbe good_digit
add al, #'A - '0 - 10
good_digit:
int 0x10
loop print_digit
ret
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
push dx
mov dx,#0x3f2
xor al, al
outb
pop dx
ret
sectors:
.word 0
msg1:
.byte 13,10
.ascii "Loading"
.org 498
root_flags:
.word CONFIG_ROOT_RDONLY
syssize:
.word SYSSIZE
swap_dev:
.word SWAP_DEV
ram_size:
.word RAMDISK
vid_mode:
.word SVGA_MODE
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55

View file

@ -0,0 +1,354 @@
/*
* linux/boot/head.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* head.S contains the 32-bit startup code.
*/
.text
.globl _idt,_gdt,
.globl _swapper_pg_dir,_pg0
.globl _empty_bad_page
.globl _empty_bad_page_table
.globl _empty_zero_page
.globl _tmp_floppy_area,_floppy_track_buffer
#include <linux/tasks.h>
#include <linux/segment.h>
#define CL_MAGIC_ADDR 0x90020
#define CL_MAGIC 0xA33F
#define CL_BASE_ADDR 0x90000
#define CL_OFFSET 0x90022
/*
* swapper_pg_dir is the main page directory, address 0x00001000 (or at
* address 0x00101000 for a compressed boot).
*/
startup_32:
cld
movl $(KERNEL_DS),%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
/*
* Clear BSS first so that there are no surprises...
*/
xorl %eax,%eax
movl $__edata,%edi
movl $__end,%ecx
subl %edi,%ecx
cld
rep
stosb
/*
* start system 32-bit setup. We need to re-do some of the things done
* in 16-bit mode for the "real" operations.
*/
call setup_idt
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b
/*
* Initialize eflags. Some BIOS's leave bits like NT set. This would
* confuse the debugger if this code is traced.
* XXX - best to initialize before switching to protected mode.
*/
pushl $0
popfl
/*
* Copy bootup parameters out of the way. First 2kB of
* _empty_zero_page is for boot parameters, second 2kB
* is for the command line.
*/
movl $0x90000,%esi
movl $_empty_zero_page,%edi
movl $512,%ecx
cld
rep
movsl
xorl %eax,%eax
movl $512,%ecx
rep
stosl
cmpw $(CL_MAGIC),CL_MAGIC_ADDR
jne 1f
movl $_empty_zero_page+2048,%edi
movzwl CL_OFFSET,%esi
addl $(CL_BASE_ADDR),%esi
movl $2048,%ecx
rep
movsb
1:
/* check if it is 486 or 386. */
/*
* XXX - this does a lot of unnecessary setup. Alignment checks don't
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/
movl %esp,%edi # save stack pointer
andl $0xfffffffc,%esp # align stack to avoid AC fault
movl $3,_x86
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
xorl $0x40000,%eax # flip AC bit in EFLAGS
pushl %eax # copy to EFLAGS
popfl # set EFLAGS
pushfl # get new EFLAGS
popl %eax # put it in eax
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
je is386
movl $4,_x86
movl %ecx,%eax
xorl $0x200000,%eax # check ID flag
pushl %eax
popfl # if we are on a straight 486DX, SX, or
pushfl # 487SX we can't change it
popl %eax
xorl %ecx,%eax
andl $0x200000,%eax
je is486
isnew: pushl %ecx # restore original EFLAGS
popfl
movl $1, %eax # Use the CPUID instruction to
.byte 0x0f, 0xa2 # check the processor type
andl $0xf00, %eax # Set _x86 with the family
shrl $8, %eax # returned.
movl %eax, _x86
movl %edi,%esp # restore esp
movl %cr0,%eax # 486+
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
is486: pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
movl %cr0,%eax # 486
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
is386: pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
movl %cr0,%eax # 386
andl $0x80000011,%eax # Save PG,PE,ET
orl $2,%eax # set MP
2: movl %eax,%cr0
call check_x87
call setup_paging
lgdt gdt_descr
lidt idt_descr
ljmp $(KERNEL_CS),$1f
1: movl $(KERNEL_DS),%eax # reload all the segment registers
mov %ax,%ds # after changing gdt.
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
xorl %eax,%eax
lldt %ax
pushl %eax # These are the parameters to main :-)
pushl %eax
pushl %eax
cld # gcc2 wants the direction flag cleared at all times
call _start_kernel
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.
/*
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
movl $0,_hard_math
clts
fninit
fstsw %ax
cmpb $0,%al
je 1f
movl %cr0,%eax /* no coprocessor: have to set bits */
xorl $4,%eax /* set EM */
movl %eax,%cr0
ret
.align 2
1: movl $1,_hard_math
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
/*
* setup_idt
*
* sets up a idt with 256 entries pointing to
* ignore_int, interrupt gates. It doesn't actually load
* idt - that can be done only after paging has been enabled
* and the kernel moved to 0xC0000000. Interrupts
* are enabled elsewhere, when we can be relatively
* sure everything is ok.
*/
setup_idt:
lea ignore_int,%edx
movl $(KERNEL_CS << 16),%eax
movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea _idt,%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret
/*
* Setup_paging
*
* This routine sets up paging by setting the page bit
* in cr0. The page tables are set up, identity-mapping
* the first 4MB. The rest are initialized later.
*
* (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
* (ref: update, 25Sept92) -- croutons@crunchy.uucp
* (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
*/
.align 2
setup_paging:
movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
xorl %eax,%eax
movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */
cld;rep;stosl
/* Identity-map the kernel in low 4MB memory for ease of transition */
movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */
/* But the real place is at 0xC0000000 */
movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */
movl $_pg0+4092,%edi
movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
std
1: stosl /* fill the page backwards - more efficient :-) */
subl $0x1000,%eax
jge 1b
cld
movl $_swapper_pg_dir,%eax
movl %eax,%cr3 /* cr3 - page directory start */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* set paging (PG) bit */
ret /* this also flushes the prefetch-queue */
/*
* page 0 is made non-existent, so that kernel NULL pointer references get
* caught. Thus the swapper page directory has been moved to 0x1000
*
* XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte,
* with the introduction of the compressed boot code. Theoretically,
* the original design of overlaying the startup code with the swapper
* page directory is still possible --- it would reduce the size of the kernel
* by 2-3k. This would be a good thing to do at some point.....
*/
.org 0x1000
_swapper_pg_dir:
/*
* The page tables are initialized to only 4MB here - the final page
* tables are set up later depending on memory size.
*/
.org 0x2000
_pg0:
.org 0x3000
_empty_bad_page:
.org 0x4000
_empty_bad_page_table:
.org 0x5000
_empty_zero_page:
.org 0x6000
/*
* tmp_floppy_area is used by the floppy-driver when DMA cannot
* reach to a buffer-block. It needs to be aligned, so that it isn't
* on a 64kB border.
*/
_tmp_floppy_area:
.fill 1024,1,0
/*
* floppy_track_buffer is used to buffer one track of floppy data: it
* has to be separate from the tmp_floppy area, as otherwise a single-
* sector read/write can mess it up. It can contain one full track of
* data (18*2*512 bytes).
*/
_floppy_track_buffer:
.fill 512*2*18,1,0
/* This is the default interrupt "handler" :-) */
int_msg:
.asciz "Unknown interrupt\n"
.align 2
ignore_int:
cld
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $(KERNEL_DS),%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
pushl $int_msg
call _printk
popl %eax
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
/*
* The interrupt descriptor table has room for 256 idt's
*/
.align 4
.word 0
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long 0xc0000000+_idt
.align 4
_idt:
.fill 256,8,0 # idt is uninitialized
.align 4
.word 0
gdt_descr:
.word (8+2*NR_TASKS)*8-1
.long 0xc0000000+_gdt
/*
* This gdt setup gives the kernel a 1GB address space at virtual
* address 0xC0000000 - space enough for expansion, I hope.
*/
.align 4
_gdt:
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */
.quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */
.quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */
.quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */
.quad 0x0000000000000000 /* not used */
.quad 0x0000000000000000 /* not used */
.fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */

View file

@ -0,0 +1,869 @@
!
! setup.S Copyright (C) 1991, 1992 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
! Move PS/2 aux init code to psaux.c
! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
!
! some changes and additional features by Christoph Niemann, March 1993
! (niemann@rubdv15.ETDV.Ruhr-Uni-Bochum.De)
!
! NOTE! These had better be the same as in bootsect.s!
#include <linux/config.h>
#include <linux/segment.h>
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif
INITSEG = DEF_INITSEG ! we move boot here - out of the way
SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG ! this is the current segment
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
entry start
start:
! ok, the read went well so we get current cursor position and save it for
! posterity.
mov ax,#INITSEG ! this is done in bootsect already, but...
mov ds,ax
! Get memory size (extended mem, kB)
mov ah,#0x88
int 0x15
mov [2],ax
! set the keyboard repeat rate to the max
mov ax,#0x0305
xor bx,bx ! clear bx
int 0x16
! check for EGA/VGA and some config parameters
mov ah,#0x12
mov bl,#0x10
int 0x10
mov [8],ax
mov [10],bx
mov [12],cx
mov ax,#0x5019
cmp bl,#0x10
je novga
mov ax,#0x1a00 ! Added check for EGA/VGA discrimination
int 0x10
mov bx,ax
mov ax,#0x5019
cmp bl,#0x1a ! 1a means VGA, anything else EGA or lower
jne novga
call chsvga
novga: mov [14],ax
mov ah,#0x03 ! read cursor pos
xor bh,bh ! clear bh
int 0x10 ! save it in known place, con_init fetches
mov [0],dx ! it from 0x90000.
! Get video-card data:
mov ah,#0x0f
int 0x10
mov [4],bx ! bh = display page
mov [6],ax ! al = video mode, ah = window width
! Get hd0 data
xor ax,ax ! clear ax
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG
mov es,ax
mov di,#0x0080
mov cx,#0x10
cld
rep
movsb
! Get hd1 data
xor ax,ax ! clear ax
mov ds,ax
lds si,[4*0x46]
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
cld
rep
movsb
! Check that there IS a hd1 :-)
mov ax,#0x01500
mov dl,#0x81
int 0x13
jc no_disk1
cmp ah,#3
je is_disk1
no_disk1:
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
xor ax,ax ! clear ax
cld
rep
stosb
is_disk1:
! check for PS/2 pointing device
mov ax,#INITSEG
mov ds,ax
mov [0x1ff],#0 ! default is no pointing device
int 0x11 ! int 0x11: equipment determination
test al,#0x04 ! check if pointing device installed
jz no_psmouse
mov [0x1ff],#0xaa ! device present
no_psmouse:
! now we want to move to protected mode ...
cli ! no interrupts allowed !
mov al,#0x80 ! disable NMI for the bootup sequence
out #0x70,al
! first we move the system to its rightful place
mov ax,#0x100 ! start of destination segment
mov bx,#0x1000 ! start of source segment
cld ! 'direction'=0, movs moves forward
do_move:
mov es,ax ! destination segment
add ax,#0x100
cmp ax,#0x9000
jz end_move
mov ds,bx ! source segment
add bx,#0x100
sub di,di
sub si,si
mov cx,#0x800
rep
movsw
jmp do_move
! then we load the segment descriptors
end_move:
mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)
mov ds,ax
lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever appropriate
! that was painless, now we enable A20
call empty_8042
mov al,#0xD1 ! command write
out #0x64,al
call empty_8042
mov al,#0xDF ! A20 on
out #0x60,al
call empty_8042
! make sure any possible coprocessor is properly reset..
xor ax,ax
out #0xf0,al
call delay
out #0xf1,al
call delay
! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven't been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259's, and it isn't fun.
mov al,#0x11 ! initialization sequence
out #0x20,al ! send it to 8259A-1
call delay
out #0xA0,al ! and to 8259A-2
call delay
mov al,#0x20 ! start of hardware int's (0x20)
out #0x21,al
call delay
mov al,#0x28 ! start of hardware int's 2 (0x28)
out #0xA1,al
call delay
mov al,#0x04 ! 8259-1 is master
out #0x21,al
call delay
mov al,#0x02 ! 8259-2 is slave
out #0xA1,al
call delay
mov al,#0x01 ! 8086 mode for both
out #0x21,al
call delay
out #0xA1,al
call delay
mov al,#0xFF ! mask off all interrupts for now
out #0xA1,al
call delay
mov al,#0xFB ! mask all irq's but irq2 which
out #0x21,al ! is cascaded
! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.
!
! Note that the short jump isn't strictly needed, althought there are
! reasons why it might be a good idea. It won't hurt in any case.
!
mov ax,#0x0001 ! protected mode (PE) bit
lmsw ax ! This is it!
jmp flush_instr
flush_instr:
jmpi 0x1000,KERNEL_CS ! jmp offset 1000 of segment 0x10 (cs)
! This routine checks that the keyboard command queue is empty
! (after emptying the output buffers)
!
! No timeout is used - if this hangs there is something wrong with
! the machine, and we probably couldn't proceed anyway.
empty_8042:
call delay
in al,#0x64 ! 8042 status port
test al,#1 ! output buffer?
jz no_output
call delay
in al,#0x60 ! read it
jmp empty_8042
no_output:
test al,#2 ! is input buffer full?
jnz empty_8042 ! yes - loop
ret
!
! Read a key and return the (US-)ascii code in al, scan code in ah
!
getkey:
xor ah,ah
int 0x16
ret
!
! Read a key with a timeout of 30 seconds. The cmos clock is used to get
! the time.
!
getkt:
call gettime
add al,#30 ! wait 30 seconds
cmp al,#60
jl lminute
sub al,#60
lminute:
mov cl,al
again: mov ah,#0x01
int 0x16
jnz getkey ! key pressed, so get it
call gettime
cmp al,cl
jne again
mov al,#0x20 ! timeout, return default char `space'
ret
!
! Flush the keyboard buffer
!
flush: mov ah,#0x01
int 0x16
jz empty
xor ah,ah
int 0x16
jmp flush
empty: ret
!
! Read the cmos clock. Return the seconds in al
!
gettime:
push cx
mov ah,#0x02
int 0x1a
mov al,dh ! dh contains the seconds
and al,#0x0f
mov ah,dh
mov cl,#0x04
shr ah,cl
aad
pop cx
ret
!
! Delay is needed after doing i/o
!
delay:
.word 0x00eb ! jmp $+2
ret
! Routine trying to recognize type of SVGA-board present (if any)
! and if it recognize one gives the choices of resolution it offers.
! If one is found the resolution chosen is given by al,ah (rows,cols).
chsvga: cld
push ds
push cs
mov ax,[0x01fa]
pop ds
mov modesave,ax
mov ax,#0xc000
mov es,ax
mov ax,modesave
cmp ax,#NORMAL_VGA
je defvga
cmp ax,#EXTENDED_VGA
je vga50
cmp ax,#ASK_VGA
jne svga
lea si,msg1
call prtstr
call flush
nokey: call getkt
cmp al,#0x0d ! enter ?
je svga ! yes - svga selection
cmp al,#0x20 ! space ?
je defvga ! no - repeat
call beep
jmp nokey
defvga: mov ax,#0x5019
pop ds
ret
/* extended vga mode: 80x50 */
vga50:
mov ax,#0x1112
xor bl,bl
int 0x10 ! use 8x8 font set (50 lines on VGA)
mov ax,#0x1200
mov bl,#0x20
int 0x10 ! use alternate print screen
mov ax,#0x1201
mov bl,#0x34
int 0x10 ! turn off cursor emulation
mov ah,#0x01
mov cx,#0x0607
int 0x10 ! turn on cursor (scan lines 6 to 7)
pop ds
mov ax,#0x5032 ! return 80x50
ret
/* extended vga mode: 80x28 */
vga28:
pop ax ! clean the stack
mov ax,#0x1111
xor bl,bl
int 0x10 ! use 9x14 fontset (28 lines on VGA)
mov ah, #0x01
mov cx,#0x0b0c
int 0x10 ! turn on cursor (scan lines 11 to 12)
pop ds
mov ax,#0x501c ! return 80x28
ret
/* svga modes */
svga: cld
lea si,id9GXE ! Check for the #9GXE (jyanowit@orixa.mtholyoke.edu,thanks dlm40629@uxa.cso.uiuc.edu)
mov di,#0x49 ! id string is at c000:049
mov cx,#0x11 ! length of "Graphics Power By"
repe
cmpsb
jne of1280
is9GXE: lea si,dsc9GXE ! table of descriptions of video modes for BIOS
lea di,mo9GXE ! table of sizes of video modes for my BIOS
br selmod ! go ask for video mode
of1280: cld
lea si,idf1280 ! Check for Orchid F1280 (dingbat@diku.dk)
mov di,#0x10a ! id string is at c000:010a
mov cx,#0x21 ! length
repe
cmpsb
jne nf1280
isVRAM: lea si,dscf1280
lea di,mof1280
br selmod
nf1280: lea si,idVRAM
mov di,#0x10a
mov cx,#0x0c
repe
cmpsb
je isVRAM
cld
lea si,idati ! Check ATI 'clues'
mov di,#0x31
mov cx,#0x09
repe
cmpsb
jne noati
lea si,dscati
lea di,moati
br selmod
noati: mov ax,#0x200f ! Check Ahead 'clues'
mov dx,#0x3ce
out dx,ax
inc dx
in al,dx
cmp al,#0x20
je isahed
cmp al,#0x21
jne noahed
isahed: lea si,dscahead
lea di,moahead
br selmod
noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues'
in al,dx
or al,#0x10
out dx,al
mov dx,#0x104
in al,dx
mov bl,al
mov dx,#0x3c3
in al,dx
and al,#0xef
out dx,al
cmp bl,[idcandt]
jne nocant
lea si,dsccandt
lea di,mocandt
br selmod
nocant: mov dx,#0x3d4 ! Check Cirrus 'clues'
mov al,#0x0c
out dx,al
inc dx
in al,dx
mov bl,al
xor al,al
out dx,al
dec dx
mov al,#0x1f
out dx,al
inc dx
in al,dx
mov bh,al
xor ah,ah
shl al,#4
mov cx,ax
mov al,bh
shr al,#4
add cx,ax
shl cx,#8
add cx,#6
mov ax,cx
mov dx,#0x3c4
out dx,ax
inc dx
in al,dx
and al,al
jnz nocirr
mov al,bh
out dx,al
in al,dx
cmp al,#0x01
jne nocirr
call rst3d4
lea si,dsccirrus
lea di,mocirrus
br selmod
rst3d4: mov dx,#0x3d4
mov al,bl
xor ah,ah
shl ax,#8
add ax,#0x0c
out dx,ax
ret
nocirr: call rst3d4 ! Check Everex 'clues'
mov ax,#0x7000
xor bx,bx
int 0x10
cmp al,#0x70
jne noevrx
shr dx,#4
cmp dx,#0x678
je istrid
cmp dx,#0x236
je istrid
lea si,dsceverex
lea di,moeverex
br selmod
istrid: lea cx,ev2tri
jmp cx
noevrx: lea si,idgenoa ! Check Genoa 'clues'
xor ax,ax
seg es
mov al,[0x37]
mov di,ax
mov cx,#0x04
dec si
dec di
l1: inc si
inc di
mov al,(si)
test al,al
jz l2
seg es
cmp al,(di)
l2: loope l1
cmp cx,#0x00
jne nogen
lea si,dscgenoa
lea di,mogenoa
br selmod
nogen: cld
lea si,idoakvga
mov di,#0x08
mov cx,#0x08
repe
cmpsb
jne nooak
lea si,dscoakvga
lea di,mooakvga
br selmod
nooak: cld
lea si,idparadise ! Check Paradise 'clues'
mov di,#0x7d
mov cx,#0x04
repe
cmpsb
jne nopara
lea si,dscparadise
lea di,moparadise
br selmod
nopara: mov dx,#0x3c4 ! Check Trident 'clues'
mov al,#0x0e
out dx,al
inc dx
in al,dx
xchg ah,al
xor al,al
out dx,al
in al,dx
xchg al,ah
mov bl,al ! Strange thing ... in the book this wasn't
and bl,#0x02 ! necessary but it worked on my card which
jz setb2 ! is a trident. Without it the screen goes
and al,#0xfd ! blurred ...
jmp clrb2 !
setb2: or al,#0x02 !
clrb2: out dx,al
and ah,#0x0f
cmp ah,#0x02
jne notrid
ev2tri: lea si,dsctrident
lea di,motrident
jmp selmod
notrid: mov dx,#0x3cd ! Check Tseng 'clues'
in al,dx ! Could things be this simple ! :-)
mov bl,al
mov al,#0x55
out dx,al
in al,dx
mov ah,al
mov al,bl
out dx,al
cmp ah,#0x55
jne notsen
lea si,dsctseng
lea di,motseng
jmp selmod
notsen: mov dx,#0x3cc ! Check Video7 'clues'
in al,dx
mov dx,#0x3b4
and al,#0x01
jz even7
mov dx,#0x3d4
even7: mov al,#0x0c
out dx,al
inc dx
in al,dx
mov bl,al
mov al,#0x55
out dx,al
in al,dx
dec dx
mov al,#0x1f
out dx,al
inc dx
in al,dx
mov bh,al
dec dx
mov al,#0x0c
out dx,al
inc dx
mov al,bl
out dx,al
mov al,#0x55
xor al,#0xea
cmp al,bh
jne novid7
lea si,dscvideo7
lea di,movideo7
jmp selmod
novid7: lea si,dsunknown
lea di,mounknown
selmod: xor cx,cx
mov cl,(di)
mov ax,modesave
cmp ax,#ASK_VGA
je askmod
cmp ax,#NORMAL_VGA
je askmod
cmp al,cl
jl gotmode
push si
lea si,msg4
call prtstr
pop si
askmod: push si
lea si,msg2
call prtstr
pop si
push si
push cx
tbl: pop bx
push bx
mov al,bl
sub al,cl
call modepr
lodsw
xchg al,ah
call dprnt
xchg ah,al
push ax
mov al,#0x78
call prnt1
pop ax
call dprnt
push si
lea si,crlf ! print CR+LF
call prtstr
pop si
loop tbl
pop cx
lea si,msg3
call prtstr
pop si
add cl,#0x30
jmp nonum
nonumb: call beep
nonum: call getkey
cmp al,#0x30 ! ascii `0'
jb nonumb
cmp al,#0x3a ! ascii `9'
jbe number
cmp al,#0x61 ! ascii `a'
jb nonumb
cmp al,#0x7a ! ascii `z'
ja nonumb
sub al,#0x27
cmp al,cl
jae nonumb
sub al,#0x30
jmp gotmode
number: cmp al,cl
jae nonumb
sub al,#0x30
gotmode: xor ah,ah
or al,al
beq vga50
push ax
dec ax
beq vga28
add di,ax
mov al,(di)
int 0x10
pop ax
shl ax,#1
add si,ax
lodsw
pop ds
ret
! Routine to print asciiz-string at DS:SI
prtstr: lodsb
and al,al
jz fin
call prnt1
jmp prtstr
fin: ret
! Routine to print a decimal value on screen, the value to be
! printed is put in al (i.e 0-255).
dprnt: push ax
push cx
xor ah,ah ! Clear ah
mov cl,#0x0a
idiv cl
cmp al,#0x09
jbe lt100
call dprnt
jmp skip10
lt100: add al,#0x30
call prnt1
skip10: mov al,ah
add al,#0x30
call prnt1
pop cx
pop ax
ret
!
! Routine to print the mode number key on screen. Mode numbers
! 0-9 print the ascii values `0' to '9', 10-35 are represented by
! the letters `a' to `z'. This routine prints some spaces around the
! mode no.
!
modepr: push ax
cmp al,#0x0a
jb digit ! Here is no check for number > 35
add al,#0x27
digit: add al,#0x30
mov modenr, al
push si
lea si, modestring
call prtstr
pop si
pop ax
ret
! Part of above routine, this one just prints ascii al
prnt1: push ax
push cx
xor bh,bh
mov cx,#0x01
mov ah,#0x0e
int 0x10
pop cx
pop ax
ret
beep: mov al,#0x07
jmp prnt1
gdt:
.word 0,0,0,0 ! dummy
.word 0,0,0,0 ! unused
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386
idt_48:
.word 0 ! idt limit=0
.word 0,0 ! idt base=0L
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx
msg1: .ascii "Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs."
db 0x0d, 0x0a, 0x0a, 0x00
msg2: .ascii "Mode: COLSxROWS:"
db 0x0d, 0x0a, 0x0a, 0x00
msg3: db 0x0d, 0x0a
.ascii "Choose mode by pressing the corresponding number or letter."
crlf: db 0x0d, 0x0a, 0x00
msg4: .ascii "You passed an undefined mode number to setup. Please choose a new mode."
db 0x0d, 0x0a, 0x0a, 0x07, 0x00
modestring: .ascii " "
modenr: db 0x00 ! mode number
.ascii ": "
db 0x00
idati: .ascii "761295520"
idcandt: .byte 0xa5
idgenoa: .byte 0x77, 0x00, 0x99, 0x66
idparadise: .ascii "VGA="
idoakvga: .ascii "OAK VGA "
idf1280: .ascii "Orchid Technology Fahrenheit 1280"
id9GXE: .ascii "Graphics Power By"
idVRAM: .ascii "Stealth VRAM"
! Manufacturer: Numofmodes+2: Mode:
! Number of modes is the number of chip-specific svga modes plus the extended
! modes available on any vga (currently 2)
moati: .byte 0x04, 0x23, 0x33
moahead: .byte 0x07, 0x22, 0x23, 0x24, 0x2f, 0x34
mocandt: .byte 0x04, 0x60, 0x61
mocirrus: .byte 0x06, 0x1f, 0x20, 0x22, 0x31
moeverex: .byte 0x0c, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
mogenoa: .byte 0x0c, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
moparadise: .byte 0x04, 0x55, 0x54
motrident: .byte 0x09, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
motseng: .byte 0x07, 0x26, 0x2a, 0x23, 0x24, 0x22
movideo7: .byte 0x08, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45
mooakvga: .byte 0x08, 0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51
mo9GXE: .byte 0x04, 0x54, 0x55
mof1280: .byte 0x04, 0x54, 0x55
mounknown: .byte 0x02
! msb = Cols lsb = Rows:
! The first two modes are standard vga modes available on any vga.
! mode 0 is 80x50 and mode 1 is 80x28
dscati: .word 0x5032, 0x501c, 0x8419, 0x842c
dscahead: .word 0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
dsccandt: .word 0x5032, 0x501c, 0x8419, 0x8432
dsccirrus: .word 0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425
dsceverex: .word 0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
dscgenoa: .word 0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
dscparadise: .word 0x5032, 0x501c, 0x8419, 0x842b
dsctrident: .word 0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
dsctseng: .word 0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
dscvideo7: .word 0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
dscoakvga: .word 0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
dscf1280: .word 0x5032, 0x501c, 0x842b, 0x8419
dsc9GXE: .word 0x5032, 0x501c, 0x842b, 0x8419
dsunknown: .word 0x5032, 0x501c
modesave: .word SVGA_MODE
.text
endtext:
.data
enddata:
.bss
endbss:

View file

@ -0,0 +1,139 @@
#
# For a description of the syntax of this configuration file,
# see the Configure script.
#
*
* General setup
*
bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
bool 'TCP/IP networking' CONFIG_INET y
bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
bool 'System V IPC' CONFIG_SYSVIPC y
bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
*
* Program binary formats
*
bool 'Elf executables' CONFIG_BINFMT_ELF y
bool 'COFF executables' CONFIG_BINFMT_COFF y
*
* SCSI support
*
bool 'SCSI support?' CONFIG_SCSI n
if [ "$CONFIG_SCSI" = "n" ]
:
: Skipping SCSI configuration options...
:
else
*
* SCSI support type (disk, tape, CDrom)
*
bool 'Scsi disk support' CONFIG_BLK_DEV_SD y
bool 'Scsi tape support' CONFIG_CHR_DEV_ST y
bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR y
bool 'Scsi generic support' CONFIG_CHR_DEV_SG y
*
* SCSI low-level drivers
*
bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y
bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN y
bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 y
bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 y
bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE y
bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 y
bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR y
bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST y
fi
*
* Network device support
*
bool 'Network device support?' CONFIG_ETHERCARDS y
if [ "$CONFIG_ETHERCARDS" = "n" ]
:
: Skipping ethercard configuration options...
:
else
bool 'SLIP (serial line) support' CONFIG_SLIP n
if [ "$CONFIG_SLIP" = "y" ]
bool ' CSLIP compressed headers' SL_COMPRESSED y
# bool ' SLIP debugging on' SL_DUMP y
fi
#bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'PLIP (parallel port) support' CONFIG_PLIP n
bool 'NE2000/NE1000 support' CONFIG_NE2000 n
bool 'WD80*3 support' CONFIG_WD80x3 y
bool 'SMC Ultra support' CONFIG_ULTRA n
bool '3c501 support' CONFIG_EL1 n
bool '3c503 support' CONFIG_EL2 n
#bool '3c505 support' CONFIG_ELPLUS n
#bool '3c507 support' CONFIG_EL16 n
bool '3c509/3c579 support' CONFIG_EL3 n
bool 'HP PCLAN support' CONFIG_HPLAN n
bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n
bool 'AT1700 support' CONFIG_AT1700 n
#bool 'Zenith Z-Note support' CONFIG_ZNET n
#bool 'EtherExpress support' CONFIG_EEXPRESS n
#bool 'DEPCA support' CONFIG_DEPCA n
#bool 'NI52** support' CONFIG_NI52 n
#bool 'NI65** support' CONFIG_NI65 n
#bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
#bool 'Cabletron E21xx support (not recommended)' CONFIG_E21 n
bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
fi
*
bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
bool 'Mitsumi CDROM driver support' CONFIG_MCD n
bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
*
* Filesystems
*
bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
bool 'Extended fs support' CONFIG_EXT_FS n
bool 'Second extended fs support' CONFIG_EXT2_FS y
bool 'xiafs filesystem support' CONFIG_XIA_FS n
bool 'msdos fs support' CONFIG_MSDOS_FS y
bool '/proc filesystem support' CONFIG_PROC_FS y
bool 'NFS filesystem support' CONFIG_NFS_FS y
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
*
* character devices
*
#bool 'Keyboard meta-key sends ESC-prefix' CONFIG_KBD_META y
#bool 'Keyboard Num Lock on by default' CONFIG_KBD_NUML y
bool 'Parallel printer support' CONFIG_PRINTER y
bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
if [ "$CONFIG_PSMOUSE" = "y" ]
bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
fi
bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION y
bool 'QIC-02 tape support' CONFIG_TAPE_QIC02 n
bool 'QIC-117 tape support' CONFIG_FTAPE n
if [ "$CONFIG_FTAPE" = "y" ]
int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
fi
*
* Sound
*
bool 'Sound card support' CONFIG_SOUND n
*
* Kernel hacking
*
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
bool 'Kernel profiling support' CONFIG_PROFILE n
if [ "$CONFIG_SCSI" = "y" ]
bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
fi
if [ "$CONFIG_SOUND" = "y" ]
exec touch .makesound
else
exec rm -f .makesound
fi

View file

@ -0,0 +1,50 @@
#
# Makefile for wm-FPU-emu
#
#DEBUG = -DDEBUGGING
DEBUG =
PARANOID = -DPARANOID
REENTRANT = -DREENTRANT_FPU
CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin
.c.o:
$(CC) $(CFLAGS) $(MATH_EMULATION) -c $<
.S.o:
$(CC) -D__ASSEMBLER__ $(PARANOID) $(REENTRANT) -c $<
.s.o:
$(CC) -c $<
OBJS = fpu_entry.o div_small.o errors.o \
fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \
load_store.o get_address.o \
poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \
poly_div.o poly_mul64.o polynomial.o \
reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \
reg_div.o reg_mul.o reg_norm.o \
reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
reg_round.o \
wm_shrx.o wm_sqrt.o
math.a: $(OBJS)
rm -f math.a
$(AR) rcs math.a $(OBJS)
sync
dep:
$(CPP) -M *.c > .depend
$(CPP) -D__ASSEMBLER__ -M *.S >> .depend
proto:
cproto -e -DMAKING_PROTO *.c >fpu_proto.h
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,312 @@
+---------------------------------------------------------------------------+
| wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License version 2 as |
| published by the Free Software Foundation. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
+---------------------------------------------------------------------------+
wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
which is my 80387 emulator for djgpp (gcc under msdos); wm-emu387 was
in turn based upon emu387 which was written by DJ Delorie for djgpp.
The interface to the Linux kernel is based upon the original Linux
math emulator by Linus Torvalds.
My target FPU for wm-FPU-emu is that described in the Intel486
Programmer's Reference Manual (1992 edition). Unfortunately, numerous
facets of the functioning of the FPU are not well covered in the
Reference Manual. The information in the manual has been supplemented
with measurements on real 80486's. Unfortunately, it is simply not
possible to be sure that all of the peculiarities of the 80486 have
been discovered, so there is always likely to be obscure differences
in the detailed behaviour of the emulator and a real 80486.
wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
See "Limitations" later in this file for a list of some differences.
Please report bugs, etc to me at:
billm@vaxc.cc.monash.edu.au
or at:
billm@jacobi.maths.monash.edu.au
--Bill Metzenthen
Jan 1994
----------------------- Internals of wm-FPU-emu -----------------------
Numeric algorithms:
(1) Add, subtract, and multiply. Nothing remarkable in these.
(2) Divide has been tuned to get reasonable performance. The algorithm
is not the obvious one which most people seem to use, but is designed
to take advantage of the characteristics of the 80386. I expect that
it has been invented many times before I discovered it, but I have not
seen it. It is based upon one of those ideas which one carries around
for years without ever bothering to check it out.
(3) The sqrt function has been tuned to get good performance. It is based
upon Newton's classic method. Performance was improved by capitalizing
upon the properties of Newton's method, and the code is once again
structured taking account of the 80386 characteristics.
(4) The trig, log, and exp functions are based in each case upon quasi-
"optimal" polynomial approximations. My definition of "optimal" was
based upon getting good accuracy with reasonable speed.
(5) The argument reducing code for the trig function effectively uses
a value of pi which is accurate to more than 128 bits. As a consequence,
the reduced argument is accurate to more than 64 bits for arguments up
to a few pi, and accurate to more than 64 bits for most arguments,
even for arguments approaching 2^63. This is far superior to an
80486, which uses a value of pi which is accurate to 66 bits.
The code of the emulator is complicated slightly by the need to
account for a limited form of re-entrancy. Normally, the emulator will
emulate each FPU instruction to completion without interruption.
However, it may happen that when the emulator is accessing the user
memory space, swapping may be needed. In this case the emulator may be
temporarily suspended while disk i/o takes place. During this time
another process may use the emulator, thereby changing some static
variables (eg FPU_st0_ptr, etc). The code which accesses user memory
is confined to five files:
fpu_entry.c
reg_ld_str.c
load_store.c
get_address.c
errors.c
----------------------- Limitations of wm-FPU-emu -----------------------
There are a number of differences between the current wm-FPU-emu
(version beta 1.5) and the 80486 FPU (apart from bugs). Some of the
more important differences are listed below:
Segment overrides don't do anything yet.
All internal computations are performed at 64 bit or higher precision
and the results rounded etc as required by the PC bits of the FPU
control word. Under the crt0 version for Linux current at June 1993,
the FPU PC bits specify 64 bits precision.
The precision flag (PE of the FPU status word) and the Roundup flag
(C1 of the status word) are now implemented. Does anyone write code
which uses these features? The Roundup flag does not have much meaning
for the transcendental functions and its 80486 value with these
functions is likely to differ from its emulator value.
In a few rare cases the Underflow flag obtained with the emulator will
be different from that obtained with an 80486. This occurs when the
following conditions apply simultaneously:
(a) the operands have a higher precision than the current setting of the
precision control (PC) flags.
(b) the underflow exception is masked.
(c) the magnitude of the exact result (before rounding) is less than 2^-16382.
(d) the magnitude of the final result (after rounding) is exactly 2^-16382.
(e) the magnitude of the exact result would be exactly 2^-16382 if the
operands were rounded to the current precision before the arithmetic
operation was performed.
If all of these apply, the emulator will set the Underflow flag but a real
80486 will not.
NOTE: Certain formats of Extended Real are UNSUPPORTED. They are
unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities,
and Unnormals. None of these will be generated by an 80486 or by the
emulator. Do not use them. The emulator treats them differently in
detail from the way an 80486 does.
The emulator treats PseudoDenormals differently from an 80486. These
numbers are in fact properly normalised numbers with the exponent
offset by 1, and the emulator treats them as such. Unlike the 80486,
the emulator does not generate a Denormal Operand exception for these
numbers. The arithmetical results produced when using such a number as
an operand are the same for the emulator and a real 80486 (apart from
any slight precision difference for the transcendental functions).
Neither the emulator nor an 80486 produces one of these numbers as the
result of any arithmetic operation. An 80486 can keep one of these
numbers in an FPU register with its identity as a PseudoDenormal, but
the emulator will not; they are always converted to a valid number.
----------------------- Performance of wm-FPU-emu -----------------------
Speed.
-----
The speed of floating point computation with the emulator will depend
upon instruction mix. Relative performance is best for the instructions
which require most computation. The simple instructions are adversely
affected by the fpu instruction trap overhead.
Timing: Some simple timing tests have been made on the emulator functions.
The times include load/store instructions. All times are in microseconds
measured on a 33MHz 386 with 64k cache. The Turbo C tests were under
ms-dos, the next two columns are for emulators running with the djgpp
ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97,
using libm4.0 (hard).
function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu
+ 60.5 154.8 76.5 139.4
- 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7
* 71.0 190.8 79.6 146.6
/ 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1
sin() 310.8 4692.0 319.0 398.5
cos() 284.4 4855.2 308.0 388.7
tan() 495.0 8807.1 394.9 504.7
atan() 328.9 4866.4 601.1 419.5-491.9
sqrt() 128.7 crashed 145.2 227.0
log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1
exp() 479.1 6619.2 469.1 850.8
The performance under Linux is improved by the use of look-ahead code.
The following results show the improvement which is obtained under
Linux due to the look-ahead code. Also given are the times for the
original Linux emulator with the 4.1 'soft' lib.
[ Linus' note: I changed look-ahead to be the default under linux, as
there was no reason not to use it after I had edited it to be
disabled during tracing ]
wm-FPU-emu w original w
look-ahead 'soft' lib
+ 106.4 190.2
- 108.6-111.6 192.4-216.2
* 113.4 193.1
/ 108.8-124.4 700.1-706.2
sin() 390.5 2642.0
cos() 381.5 2767.4
tan() 496.5 3153.3
atan() 367.2-435.5 2439.4-3396.8
sqrt() 195.1 4732.5
log() 358.0-387.5 3359.2-3390.3
exp() 619.3 4046.4
These figures are now somewhat out-of-date. The emulator has become
progressively slower for most functions as more of the 80486 features
have been implemented.
----------------------- Accuracy of wm-FPU-emu -----------------------
Accuracy: The following table gives the accuracy of the sqrt(), trig
and log functions. Each function was tested at about 400 points. Ideal
results would be 64 bits. The reduced accuracy of cos() and tan() for
arguments greater than pi/4 can be thought of as being due to the
precision of the argument x; e.g. an argument of pi/2-(1e-10) which is
accurate to 64 bits can result in a relative accuracy in cos() of about
64 + log2(cos(x)) = 31 bits. Results for the Turbo C emulator are given
in the last column.
Function Tested x range Worst result Turbo C
(relative bits)
sqrt(x) 1 .. 2 64.1 63.2
atan(x) 1e-10 .. 200 62.6 62.8
cos(x) 0 .. pi/2-(1e-10) 63.2 (x <= pi/4) 62.4
35.2 (x = pi/2-(1e-10)) 31.9
sin(x) 1e-10 .. pi/2 63.0 62.8
tan(x) 1e-10 .. pi/2-(1e-10) 62.4 (x <= pi/4) 62.1
35.2 (x = pi/2-(1e-10)) 31.9
exp(x) 0 .. 1 63.1 62.9
log(x) 1+1e-6 .. 2 62.4 62.1
As of version 1.3 of the emulator, the accuracy of the basic
arithmetic has been improved (by a small fraction of a bit). Care has
been taken to ensure full accuracy of the rounding of the basic
arithmetic functions (+,-,*,/,and fsqrt), and they all now produce
results which are exact to the 64th bit (unless there are any bugs
left). To ensure this, it was necessary to effectively get information
of up to about 128 bits precision. The emulator now passes the
"paranoia" tests (compiled with gcc 2.3.3) for 'float' variables (24
bit precision numbers) when precision control is set to 24, 53 or 64
bits, and for 'double' variables (53 bit precision numbers) when
precision control is set to 53 bits (a properly performing FPU cannot
pass the 'paranoia' tests for 'double' variables when precision
control is set to 64 bits).
For version 1.5, the accuracy of fprem and fprem1 has been improved.
These functions now produce exact results. The code for reducing the
argument for the trig functions (fsin, fcos, fptan and fsincos) has
been improved and now effectively uses a value for pi which is
accurate to more than 128 bits precision. As a consquence, the
accuracy of these functions for large arguments has been dramatically
improved (and is now very much better than an 80486 FPU). There is
also now no degradation of accuracy for fcos and ftan for operands
close to pi/2. Measured results are (note that the definition of
accuracy has changed slightly from that used for the above table):
Function Tested x range Worst result
(absolute bits)
cos(x) 0 .. 9.22e+18 62.0
sin(x) 1e-16 .. 9.22e+18 62.1
tan(x) 1e-16 .. 9.22e+18 61.8
It is possible with some effort to find very large arguments which
give much degraded precision. For example, the integer number
8227740058411162616.0
is within about 10e-7 of a multiple of pi. To find the tan (for
example) of this number to 64 bits precision it would be necessary to
have a value of pi which had about 150 bits precision. The FPU
emulator computes the result to about 42.6 bits precision (the correct
result is about -9.739715e-8). On the other hand, an 80486 FPU returns
0.01059, which in relative terms is hopelessly inaccurate.
For arguments close to critical angles (which occur at multiples of
pi/2) the emulator is more accurate than an 80486 FPU. For very large
arguments, the emulator is far more accurate.
------------------------- Contributors -------------------------------
A number of people have contributed to the development of the
emulator, often by just reporting bugs, sometimes with suggested
fixes, and a few kind people have provided me with access in one way
or another to an 80486 machine. Contributors include (to those people
who I may have forgotten, please forgive me):
Linus Torvalds
Tommy.Thorn@daimi.aau.dk
Andrew.Tridgell@anu.edu.au
Nick Holloway, alfie@dcs.warwick.ac.uk
Hermano Moura, moura@dcs.gla.ac.uk
Jon Jagger, J.Jagger@scp.ac.uk
Lennart Benschop
Brian Gallew, geek+@CMU.EDU
Thomas Staniszewski, ts3v+@andrew.cmu.edu
Martin Howell, mph@plasma.apana.org.au
M Saggaf, alsaggaf@athena.mit.edu
Peter Barker, PETER@socpsy.sci.fau.edu
tom@vlsivie.tuwien.ac.at
Dan Russel, russed@rpi.edu
Daniel Carosone, danielce@ee.mu.oz.au
cae@jpmorgan.com
Hamish Coleman, t933093@minyos.xx.rmit.oz.au
Bruce Evans, bde@kralizec.zeta.org.au
Timo Korvola, Timo.Korvola@hut.fi
...and numerous others who responded to my request for help with
a real 80486.

View file

@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------+
| control_w.h |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _CONTROLW_H_
#define _CONTROLW_H_
#ifdef __ASSEMBLER__
#define _Const_(x) $##x
#else
#define _Const_(x) x
#endif
#define CW_RC _Const_(0x0C00) /* rounding control */
#define CW_PC _Const_(0x0300) /* precision control */
#define CW_Precision Const_(0x0020) /* loss of precision mask */
#define CW_Underflow Const_(0x0010) /* underflow mask */
#define CW_Overflow Const_(0x0008) /* overflow mask */
#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
#define CW_Invalid Const_(0x0001) /* invalid operation mask */
#define CW_Exceptions _Const_(0x003f) /* all masks */
#define RC_RND _Const_(0x0000)
#define RC_DOWN _Const_(0x0400)
#define RC_UP _Const_(0x0800)
#define RC_CHOP _Const_(0x0C00)
/* p 15-5: Precision control bits affect only the following:
ADD, SUB(R), MUL, DIV(R), and SQRT */
#define PR_24_BITS _Const_(0x000)
#define PR_53_BITS _Const_(0x200)
#define PR_64_BITS _Const_(0x300)
/* FULL_PRECISION simulates all exceptions masked */
#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
#endif _CONTROLW_H_

View file

@ -0,0 +1,50 @@
.file "div_small.S"
/*---------------------------------------------------------------------------+
| div_small.S |
| |
| Divide a 64 bit integer by a 32 bit integer & return remainder. |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| unsigned long div_small(unsigned long long *x, unsigned long y) |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
.globl _div_small
_div_small:
pushl %ebp
movl %esp,%ebp
pushl %esi
movl PARAM1,%esi /* pointer to num */
movl PARAM2,%ecx /* The denominator */
movl 4(%esi),%eax /* Get the current num msw */
xorl %edx,%edx
divl %ecx
movl %eax,4(%esi)
movl (%esi),%eax /* Get the num lsw */
divl %ecx
movl %eax,(%esi)
movl %edx,%eax /* Return the remainder in eax */
popl %esi
leave
ret

View file

@ -0,0 +1,628 @@
/*---------------------------------------------------------------------------+
| errors.c |
| |
| The error handling functions for wm-FPU-emu |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/signal.h>
#include <asm/segment.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
#include "reg_constant.h"
#include "version.h"
/* */
#undef PRINT_MESSAGES
/* */
void Un_impl(void)
{
unsigned char byte1, FPU_modrm;
unsigned long address = FPU_ORIG_EIP;
RE_ENTRANT_CHECK_OFF;
/* No need to verify_area(), we have previously fetched these bytes. */
printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address);
while ( 1 )
{
byte1 = get_fs_byte((unsigned char *) address);
if ( (byte1 & 0xf8) == 0xd8 ) break;
printk("[%02x]", byte1);
address++;
}
printk("%02x ", byte1);
FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
if (FPU_modrm >= 0300)
printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
else
printk("/%d\n", (FPU_modrm >> 3) & 7);
RE_ENTRANT_CHECK_ON;
EXCEPTION(EX_Invalid);
}
/*
Called for opcodes which are illegal and which are known to result in a
SIGILL with a real 80486.
*/
void FPU_illegal(void)
{
math_abort(FPU_info,SIGILL);
}
void emu_printall()
{
int i;
static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
"DeNorm", "Inf", "NaN", "Empty" };
unsigned char byte1, FPU_modrm;
unsigned long address = FPU_ORIG_EIP;
RE_ENTRANT_CHECK_OFF;
/* No need to verify_area(), we have previously fetched these bytes. */
printk("At %p: ", (void *) address);
while ( 1 )
{
byte1 = get_fs_byte((unsigned char *) address);
if ( (byte1 & 0xf8) == 0xd8 ) break;
printk("[%02x]", byte1);
address++;
}
printk("%02x ", byte1);
FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
partial_status = status_word();
#ifdef DEBUGGING
if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n");
if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n");
if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n");
if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n");
if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n");
if ( partial_status & SW_Summary ) printk("SW: exception summary\n");
if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n");
if ( partial_status & SW_Precision ) printk("SW: loss of precision\n");
if ( partial_status & SW_Underflow ) printk("SW: underflow\n");
if ( partial_status & SW_Overflow ) printk("SW: overflow\n");
if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n");
if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n");
if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n");
#endif DEBUGGING
if (FPU_modrm >= 0300)
printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
else
printk("/%d, mod=%d rm=%d\n",
(FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
partial_status & 0x8000 ? 1 : 0, /* busy */
(partial_status & 0x3800) >> 11, /* stack top pointer */
partial_status & 0x80 ? 1 : 0, /* Error summary status */
partial_status & 0x40 ? 1 : 0, /* Stack flag */
partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */
partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */
partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0,
partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0,
partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0);
printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n",
control_word & 0x1000 ? 1 : 0,
(control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
(control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
control_word & 0x80 ? 1 : 0,
control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0,
control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0,
control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0);
for ( i = 0; i < 8; i++ )
{
FPU_REG *r = &st(i);
switch (r->tag)
{
case TW_Empty:
continue;
break;
case TW_Zero:
#if 0
printk("st(%d) %c .0000 0000 0000 0000 ",
i, r->sign ? '-' : '+');
break;
#endif
case TW_Valid:
case TW_NaN:
/* case TW_Denormal: */
case TW_Infinity:
printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i,
r->sign ? '-' : '+',
(long)(r->sigh >> 16),
(long)(r->sigh & 0xFFFF),
(long)(r->sigl >> 16),
(long)(r->sigl & 0xFFFF),
r->exp - EXP_BIAS + 1);
break;
default:
printk("Whoops! Error in errors.c ");
break;
}
printk("%s\n", tag_desc[(int) (unsigned) r->tag]);
}
printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ",
FPU_loaded_data.sign ? '-' : '+',
(long)(FPU_loaded_data.sigh >> 16),
(long)(FPU_loaded_data.sigh & 0xFFFF),
(long)(FPU_loaded_data.sigl >> 16),
(long)(FPU_loaded_data.sigl & 0xFFFF),
FPU_loaded_data.exp - EXP_BIAS + 1);
printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
RE_ENTRANT_CHECK_ON;
}
static struct {
int type;
char *name;
} exception_names[] = {
{ EX_StackOver, "stack overflow" },
{ EX_StackUnder, "stack underflow" },
{ EX_Precision, "loss of precision" },
{ EX_Underflow, "underflow" },
{ EX_Overflow, "overflow" },
{ EX_ZeroDiv, "divide by zero" },
{ EX_Denormal, "denormalized operand" },
{ EX_Invalid, "invalid operation" },
{ EX_INTERNAL, "INTERNAL BUG in "FPU_VERSION },
{ 0, NULL }
};
/*
EX_INTERNAL is always given with a code which indicates where the
error was detected.
Internal error types:
0 in load_store.c
0x14 in fpu_etc.c
0x1nn in a *.c file:
0x101 in reg_add_sub.c
0x102 in reg_mul.c
0x103 in poly_sin.c
0x104 in poly_atan.c
0x105 in reg_mul.c
0x106 in reg_ld_str.c
0x107 in fpu_trig.c
0x108 in reg_compare.c
0x109 in reg_compare.c
0x110 in reg_add_sub.c
0x111 in fpe_entry.c
0x112 in fpu_trig.c
0x113 in errors.c
0x114 in reg_ld_str.c
0x115 in fpu_trig.c
0x116 in fpu_trig.c
0x117 in fpu_trig.c
0x118 in fpu_trig.c
0x119 in fpu_trig.c
0x120 in poly_atan.c
0x121 in reg_compare.c
0x122 in reg_compare.c
0x123 in reg_compare.c
0x125 in fpu_trig.c
0x126 in fpu_entry.c
0x127 in poly_2xm1.c
0x128 in fpu_entry.c
0x2nn in an *.S file:
0x201 in reg_u_add.S, reg_round.S
0x202 in reg_u_div.S
0x203 in reg_u_div.S
0x204 in reg_u_div.S
0x205 in reg_u_mul.S
0x206 in reg_u_sub.S
0x207 in wm_sqrt.S
0x208 in reg_div.S
0x209 in reg_u_sub.S
0x210 in reg_u_sub.S
0x211 in reg_u_sub.S
0x212 in reg_u_sub.S
0x213 in wm_sqrt.S
0x214 in wm_sqrt.S
0x215 in wm_sqrt.S
0x216 in reg_round.S
0x217 in reg_round.S
0x218 in reg_round.S
0x220 in reg_norm.S
0x221 in reg_norm.S
*/
void exception(int n)
{
int i, int_type;
int_type = 0; /* Needed only to stop compiler warnings */
if ( n & EX_INTERNAL )
{
int_type = n - EX_INTERNAL;
n = EX_INTERNAL;
/* Set lots of exception bits! */
partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
}
else
{
/* Extract only the bits which we use to set the status word */
n &= (SW_Exc_Mask);
/* Set the corresponding exception bit */
partial_status |= n;
/* Set summary bits iff exception isn't masked */
if ( partial_status & ~control_word & CW_Exceptions )
partial_status |= (SW_Summary | SW_Backward);
if ( n & (SW_Stack_Fault | EX_Precision) )
{
if ( !(n & SW_C1) )
/* This bit distinguishes over- from underflow for a stack fault,
and roundup from round-down for precision loss. */
partial_status &= ~SW_C1;
}
}
RE_ENTRANT_CHECK_OFF;
if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) )
{
#ifdef PRINT_MESSAGES
/* My message from the sponsor */
printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n");
#endif PRINT_MESSAGES
/* Get a name string for error reporting */
for (i=0; exception_names[i].type; i++)
if ( (exception_names[i].type & n) == exception_names[i].type )
break;
if (exception_names[i].type)
{
#ifdef PRINT_MESSAGES
printk("FP Exception: %s!\n", exception_names[i].name);
#endif PRINT_MESSAGES
}
else
printk("FP emulator: Unknown Exception: 0x%04x!\n", n);
if ( n == EX_INTERNAL )
{
printk("FP emulator: Internal error type 0x%04x\n", int_type);
emu_printall();
}
#ifdef PRINT_MESSAGES
else
emu_printall();
#endif PRINT_MESSAGES
/*
* The 80486 generates an interrupt on the next non-control FPU
* instruction. So we need some means of flagging it.
* We use the ES (Error Summary) bit for this, assuming that
* this is the way a real FPU does it (until I can check it out),
* if not, then some method such as the following kludge might
* be needed.
*/
/* regs[0].tag |= TW_FPU_Interrupt; */
}
RE_ENTRANT_CHECK_ON;
#ifdef __DEBUG__
math_abort(FPU_info,SIGFPE);
#endif __DEBUG__
}
/* Real operation attempted on two operands, one a NaN. */
/* Returns nz if the exception is unmasked */
asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
{
FPU_REG const *x;
int signalling;
/* The default result for the case of two "equal" NaNs (signs may
differ) is chosen to reproduce 80486 behaviour */
x = a;
if (a->tag == TW_NaN)
{
if (b->tag == TW_NaN)
{
signalling = !(a->sigh & b->sigh & 0x40000000);
/* find the "larger" */
if ( significand(a) < significand(b) )
x = b;
}
else
{
/* return the quiet version of the NaN in a */
signalling = !(a->sigh & 0x40000000);
}
}
else
#ifdef PARANOID
if (b->tag == TW_NaN)
#endif PARANOID
{
signalling = !(b->sigh & 0x40000000);
x = b;
}
#ifdef PARANOID
else
{
signalling = 0;
EXCEPTION(EX_INTERNAL|0x113);
x = &CONST_QNaN;
}
#endif PARANOID
if ( !signalling )
{
if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
x = &CONST_QNaN;
reg_move(x, dest);
return 0;
}
if ( control_word & CW_Invalid )
{
/* The masked response */
if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
x = &CONST_QNaN;
reg_move(x, dest);
/* ensure a Quiet NaN */
dest->sigh |= 0x40000000;
}
EXCEPTION(EX_Invalid);
return !(control_word & CW_Invalid);
}
/* Invalid arith operation on Valid registers */
/* Returns nz if the exception is unmasked */
asmlinkage int arith_invalid(FPU_REG *dest)
{
EXCEPTION(EX_Invalid);
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, dest);
}
return !(control_word & CW_Invalid);
}
/* Divide a finite number by zero */
asmlinkage int divide_by_zero(int sign, FPU_REG *dest)
{
if ( control_word & CW_ZeroDiv )
{
/* The masked response */
reg_move(&CONST_INF, dest);
dest->sign = (unsigned char)sign;
}
EXCEPTION(EX_ZeroDiv);
return !(control_word & CW_ZeroDiv);
}
/* This may be called often, so keep it lean */
int set_precision_flag(int flags)
{
if ( control_word & CW_Precision )
{
partial_status &= ~(SW_C1 & flags);
partial_status |= flags; /* The masked response */
return 0;
}
else
{
exception(flags);
return 1;
}
}
/* This may be called often, so keep it lean */
asmlinkage void set_precision_flag_up(void)
{
if ( control_word & CW_Precision )
partial_status |= (SW_Precision | SW_C1); /* The masked response */
else
exception(EX_Precision | SW_C1);
}
/* This may be called often, so keep it lean */
asmlinkage void set_precision_flag_down(void)
{
if ( control_word & CW_Precision )
{ /* The masked response */
partial_status &= ~SW_C1;
partial_status |= SW_Precision;
}
else
exception(EX_Precision);
}
asmlinkage int denormal_operand(void)
{
if ( control_word & CW_Denormal )
{ /* The masked response */
partial_status |= SW_Denorm_Op;
return 0;
}
else
{
exception(EX_Denormal);
return 1;
}
}
asmlinkage int arith_overflow(FPU_REG *dest)
{
if ( control_word & CW_Overflow )
{
char sign;
/* The masked response */
/* ###### The response here depends upon the rounding mode */
sign = dest->sign;
reg_move(&CONST_INF, dest);
dest->sign = sign;
}
else
{
/* Subtract the magic number from the exponent */
dest->exp -= (3 * (1 << 13));
}
EXCEPTION(EX_Overflow);
if ( control_word & CW_Overflow )
{
/* The overflow exception is masked. */
/* By definition, precision is lost.
The roundup bit (C1) is also set because we have
"rounded" upwards to Infinity. */
EXCEPTION(EX_Precision | SW_C1);
return !(control_word & CW_Precision);
}
return !(control_word & CW_Overflow);
}
asmlinkage int arith_underflow(FPU_REG *dest)
{
if ( control_word & CW_Underflow )
{
/* The masked response */
if ( dest->exp <= EXP_UNDER - 63 )
{
reg_move(&CONST_Z, dest);
partial_status &= ~SW_C1; /* Round down. */
}
}
else
{
/* Add the magic number to the exponent. */
dest->exp += (3 * (1 << 13));
}
EXCEPTION(EX_Underflow);
if ( control_word & CW_Underflow )
{
/* The underflow exception is masked. */
EXCEPTION(EX_Precision);
return !(control_word & CW_Precision);
}
return !(control_word & CW_Underflow);
}
void stack_overflow(void)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
top--;
reg_move(&CONST_QNaN, FPU_st0_ptr = &st(0));
}
EXCEPTION(EX_StackOver);
return;
}
void stack_underflow(void)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, FPU_st0_ptr);
}
EXCEPTION(EX_StackUnder);
return;
}
void stack_underflow_i(int i)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, &(st(i)));
}
EXCEPTION(EX_StackUnder);
return;
}
void stack_underflow_pop(int i)
{
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, &(st(i)));
pop();
}
EXCEPTION(EX_StackUnder);
return;
}

View file

@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------+
| exception.h |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _EXCEPTION_H_
#define _EXCEPTION_H_
#ifdef __ASSEMBLER__
#define Const_(x) $##x
#else
#define Const_(x) x
#endif
#ifndef SW_C1
#include "fpu_emu.h"
#endif SW_C1
#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */
#define EX_ErrorSummary Const_(0x0080) /* Error summary status */
/* Special exceptions: */
#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */
#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */
#define EX_StackUnder Const_(0x0041) /* stack underflow */
/* Exception flags: */
#define EX_Precision Const_(0x0020) /* loss of precision */
#define EX_Underflow Const_(0x0010) /* underflow */
#define EX_Overflow Const_(0x0008) /* overflow */
#define EX_ZeroDiv Const_(0x0004) /* divide by zero */
#define EX_Denormal Const_(0x0002) /* denormalized operand */
#define EX_Invalid Const_(0x0001) /* invalid operation */
#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1))
#define PRECISION_LOST_DOWN Const_(EX_Precision)
#ifndef __ASSEMBLER__
#ifdef DEBUG
#define EXCEPTION(x) { printk("exception in %s at line %d\n", \
__FILE__, __LINE__); exception(x); }
#else
#define EXCEPTION(x) exception(x)
#endif
#endif __ASSEMBLER__
#endif _EXCEPTION_H_

View file

@ -0,0 +1,179 @@
/*---------------------------------------------------------------------------+
| fpu_arith.c |
| |
| Code to implement the FPU register/register arithmetic instructions |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "status_w.h"
void fadd__()
{
/* fadd st,st(i) */
clear_C1();
reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fmul__()
{
/* fmul st,st(i) */
clear_C1();
reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fsub__()
{
/* fsub st,st(i) */
clear_C1();
reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fsubr_()
{
/* fsubr st,st(i) */
clear_C1();
reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
}
void fdiv__()
{
/* fdiv st,st(i) */
clear_C1();
reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
void fdivr_()
{
/* fdivr st,st(i) */
clear_C1();
reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
}
void fadd_i()
{
/* fadd st(i),st */
clear_C1();
reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fmul_i()
{
/* fmul st(i),st */
clear_C1();
reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fsubri()
{
/* fsubr st(i),st */
/* This is the sense of the 80486 manual
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
clear_C1();
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fsub_i()
{
/* fsub st(i),st */
/* This is the sense of the 80486 manual
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
clear_C1();
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
}
void fdivri()
{
/* fdivr st(i),st */
clear_C1();
reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
void fdiv_i()
{
/* fdiv st(i),st */
clear_C1();
reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
}
void faddp_()
{
/* faddp st(i),st */
clear_C1();
if ( !reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fmulp_()
{
/* fmulp st(i),st */
clear_C1();
if ( !reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fsubrp()
{
/* fsubrp st(i),st */
/* This is the sense of the 80486 manual
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
clear_C1();
if ( !reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fsubp_()
{
/* fsubp st(i),st */
/* This is the sense of the 80486 manual
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
clear_C1();
if ( !reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
pop();
}
void fdivrp()
{
/* fdivrp st(i),st */
clear_C1();
if ( !reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
pop();
}
void fdivp_()
{
/* fdivp st(i),st */
clear_C1();
if ( !reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
pop();
}

View file

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------+
| fpu_asm.h |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_ASM_H_
#define _FPU_ASM_H_
#include "fpu_emu.h"
#define EXCEPTION _exception
#define PARAM1 8(%ebp)
#define PARAM2 12(%ebp)
#define PARAM3 16(%ebp)
#define PARAM4 20(%ebp)
#define SIGL_OFFSET 8
#define SIGN(x) (x)
#define TAG(x) 1(x)
#define EXP(x) 4(x)
#define SIG(x) SIGL_OFFSET##(x)
#define SIGL(x) SIGL_OFFSET##(x)
#define SIGH(x) 12(x)
#endif _FPU_ASM_H_

View file

@ -0,0 +1,180 @@
/*---------------------------------------------------------------------------+
| fpu_aux.c |
| |
| Code to implement some of the FPU auxiliary instructions. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
void fclex(void)
{
partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op|
SW_Invalid);
NO_NET_DATA_EFFECT;
FPU_entry_eip = ip_offset; /* We want no net effect */
}
/* Needs to be externally visible */
void finit()
{
int r;
control_word = 0x037f;
partial_status = 0;
top = 0; /* We don't keep top in the status word internally. */
for (r = 0; r < 8; r++)
{
regs[r].tag = TW_Empty;
}
/* The behaviour is different to that detailed in
Section 15.1.6 of the Intel manual */
data_operand_offset = 0;
operand_selector = 0;
NO_NET_DATA_EFFECT;
FPU_entry_op_cs = 0;
FPU_entry_eip = ip_offset = 0;
}
static FUNC const finit_table[] = {
Un_impl, Un_impl, fclex, finit,
Un_impl, FPU_illegal, FPU_illegal, FPU_illegal
};
void finit_()
{
(finit_table[FPU_rm])();
}
static void fstsw_ax(void)
{
*(short *) &FPU_EAX = status_word();
NO_NET_INSTR_EFFECT;
}
static FUNC const fstsw_table[] = {
fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal,
FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
};
void fstsw_()
{
(fstsw_table[FPU_rm])();
}
static void fnop(void)
{
}
static FUNC const fp_nop_table[] = {
fnop, FPU_illegal, FPU_illegal, FPU_illegal,
FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
};
void fp_nop()
{
(fp_nop_table[FPU_rm])();
}
void fld_i_()
{
FPU_REG *st_new_ptr;
if ( STACK_OVERFLOW )
{ stack_overflow(); return; }
/* fld st(i) */
if ( NOT_EMPTY(FPU_rm) )
{ reg_move(&st(FPU_rm), st_new_ptr); push(); }
else
{
if ( control_word & EX_Invalid )
{
/* The masked response */
push();
stack_underflow();
}
else
EXCEPTION(EX_StackUnder);
}
}
void fxch_i()
{
/* fxch st(i) */
FPU_REG t;
register FPU_REG *sti_ptr = &st(FPU_rm);
if ( FPU_st0_tag == TW_Empty )
{
if ( sti_ptr->tag == TW_Empty )
{
stack_underflow();
stack_underflow_i(FPU_rm);
return;
}
if ( control_word & CW_Invalid )
reg_move(sti_ptr, FPU_st0_ptr); /* Masked response */
stack_underflow_i(FPU_rm);
return;
}
if ( sti_ptr->tag == TW_Empty )
{
if ( control_word & CW_Invalid )
reg_move(FPU_st0_ptr, sti_ptr); /* Masked response */
stack_underflow();
return;
}
clear_C1();
reg_move(FPU_st0_ptr, &t);
reg_move(sti_ptr, FPU_st0_ptr);
reg_move(&t, sti_ptr);
}
void ffree_()
{
/* ffree st(i) */
st(FPU_rm).tag = TW_Empty;
}
void ffreep()
{
/* ffree st(i) + pop - unofficial code */
st(FPU_rm).tag = TW_Empty;
pop();
}
void fst_i_()
{
/* fst st(i) */
reg_move(FPU_st0_ptr, &st(FPU_rm));
}
void fstp_i()
{
/* fstp st(i) */
reg_move(FPU_st0_ptr, &st(FPU_rm));
pop();
}

View file

@ -0,0 +1,166 @@
/*---------------------------------------------------------------------------+
| fpu_emu.h |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_EMU_H_
#define _FPU_EMU_H_
/*
* Define DENORM_OPERAND to make the emulator detect denormals
* and use the denormal flag of the status word. Note: this only
* affects the flag and corresponding interrupt, the emulator
* will always generate denormals and operate upon them as required.
*/
#define DENORM_OPERAND
/*
* Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
* rather than behaviour which appears to be cleaner.
* This is a matter of opinion: for all I know, the 80486 may simply
* be complying with the IEEE spec. Maybe one day I'll get to see the
* spec...
*/
#define PECULIAR_486
#ifdef __ASSEMBLER__
#include "fpu_asm.h"
#define Const(x) $##x
#else
#define Const(x) x
#endif
#define EXP_BIAS Const(0)
#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
#define EXP_Infinity EXP_OVER
#define EXP_NaN EXP_OVER
#define SIGN_POS Const(0)
#define SIGN_NEG Const(1)
/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
#define TW_Valid Const(0) /* valid */
#define TW_Zero Const(1) /* zero */
/* The following fold to 2 (Special) in the Tag Word */
/* #define TW_Denormal Const(4) */ /* De-normal */
#define TW_Infinity Const(5) /* + or - infinity */
#define TW_NaN Const(6) /* Not a Number */
#define TW_Empty Const(7) /* empty */
#ifndef __ASSEMBLER__
#include <linux/math_emu.h>
#include <linux/linkage.h>
#ifdef PARANOID
extern char emulating;
# define RE_ENTRANT_CHECK_OFF emulating = 0
# define RE_ENTRANT_CHECK_ON emulating = 1
#else
# define RE_ENTRANT_CHECK_OFF
# define RE_ENTRANT_CHECK_ON
#endif PARANOID
#define FWAIT_OPCODE 0x9b
#define OP_SIZE_PREFIX 0x66
#define ADDR_SIZE_PREFIX 0x67
#define PREFIX_CS 0x2e
#define PREFIX_DS 0x3e
#define PREFIX_ES 0x26
#define PREFIX_SS 0x36
#define PREFIX_FS 0x64
#define PREFIX_GS 0x65
#define PREFIX_REPE 0xf3
#define PREFIX_REPNE 0xf2
#define PREFIX_LOCK 0xf0
/* These are to defeat the default action, giving the instruction
no net effect: */
#define NO_NET_DATA_EFFECT \
{ FPU_data_address = (void *)data_operand_offset; \
FPU_data_selector = operand_selector; }
#define NO_NET_INSTR_EFFECT \
{ FPU_entry_eip = ip_offset; \
FPU_entry_op_cs = cs_selector; }
typedef void (*FUNC)(void);
typedef struct fpu_reg FPU_REG;
typedef struct { unsigned char address_size, segment; } overrides;
#define st(x) ( regs[((top+x) &7 )] )
#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty)
#define NOT_EMPTY(i) (st(i).tag != TW_Empty)
#define NOT_EMPTY_0 (FPU_st0_tag ^ TW_Empty)
extern unsigned char FPU_rm;
extern char FPU_st0_tag;
extern FPU_REG *FPU_st0_ptr;
/* ###### These need to be shifted to somewhere safe. */
/* extern void *FPU_data_address; has been shifted */
extern unsigned short FPU_data_selector;
extern unsigned long FPU_entry_op_cs;
extern FPU_REG FPU_loaded_data;
#define pop() { FPU_st0_ptr->tag = TW_Empty; top++; }
/* push() does not affect the tags */
#define push() { top--; FPU_st0_ptr = st_new_ptr; }
#define reg_move(x, y) { \
*(short *)&((y)->sign) = *(short *)&((x)->sign); \
*(long *)&((y)->exp) = *(long *)&((x)->exp); \
*(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); }
#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] )
/*----- Prototypes for functions written in assembler -----*/
/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b,
unsigned long long *result);
asmlinkage void poly_div2(unsigned long long *x);
asmlinkage void poly_div4(unsigned long long *x);
asmlinkage void poly_div16(unsigned long long *x);
asmlinkage void polynomial(unsigned accum[], unsigned const x[],
unsigned short const terms[][4], int const n);
asmlinkage void normalize(FPU_REG *x);
asmlinkage void normalize_nuo(FPU_REG *x);
asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w);
asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w);
asmlinkage unsigned shrx(void *l, unsigned x);
asmlinkage unsigned shrxs(void *v, unsigned x);
asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y);
asmlinkage void round_reg(FPU_REG *arg, unsigned int extent,
unsigned int control_w);
#ifndef MAKING_PROTO
#include "fpu_proto.h"
#endif
#endif __ASSEMBLER__
#endif _FPU_EMU_H_

View file

@ -0,0 +1,622 @@
/*---------------------------------------------------------------------------+
| fpu_entry.c |
| |
| The entry function for wm-FPU-emu |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| See the files "README" and "COPYING" for further copyright and warranty |
| information. |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| math_emulate() is the sole entry point for wm-FPU-emu |
+---------------------------------------------------------------------------*/
#include <linux/signal.h>
#include <linux/segment.h>
#include "fpu_system.h"
#include "fpu_emu.h"
#include "exception.h"
#include "control_w.h"
#include "status_w.h"
#include <asm/segment.h>
#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */
/* WARNING: These codes are not documented by Intel in their 80486 manual
and may not work on FPU clones or later Intel FPUs. */
/* Changes to support the un-doc codes provided by Linus Torvalds. */
#define _d9_d8_ fstp_i /* unofficial code (19) */
#define _dc_d0_ fcom_st /* unofficial code (14) */
#define _dc_d8_ fcompst /* unofficial code (1c) */
#define _dd_c8_ fxch_i /* unofficial code (0d) */
#define _de_d0_ fcompst /* unofficial code (16) */
#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
#define _df_c8_ fxch_i /* unofficial code (0f) */
#define _df_d0_ fstp_i /* unofficial code (17) */
#define _df_d8_ fstp_i /* unofficial code (1f) */
static FUNC const st_instr_table[64] = {
fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
};
#else /* Support only documented FPU op-codes */
static FUNC const st_instr_table[64] = {
fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
};
#endif NO_UNDOC_CODE
#define _NONE_ 0 /* Take no special action */
#define _REG0_ 1 /* Need to check for not empty st(0) */
#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */
#define _REGi_ 0 /* Uses st(rm) */
#define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */
#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
#define _REGIc 0 /* Compare st(0) and st(rm) */
#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
#ifndef NO_UNDOC_CODE
/* Un-documented FPU op-codes supported by default. (see above) */
static unsigned char const type_table[64] = {
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
_REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
_REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
_REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
_REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
};
#else /* Support only documented FPU op-codes */
static unsigned char const type_table[64] = {
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
_REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
_REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
_REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
_REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
};
#endif NO_UNDOC_CODE
/* Be careful when using any of these global variables...
they might change if swapping is triggered */
unsigned char FPU_rm;
char FPU_st0_tag;
FPU_REG *FPU_st0_ptr;
/* ######## To be shifted */
unsigned long FPU_entry_op_cs;
unsigned short FPU_data_selector;
#ifdef PARANOID
char emulating=0;
#endif PARANOID
static int valid_prefix(unsigned char *byte, overrides *override);
asmlinkage void math_emulate(long arg)
{
unsigned char FPU_modrm, byte1;
overrides override;
int unmasked;
#ifdef PARANOID
if ( emulating )
{
printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
}
RE_ENTRANT_CHECK_ON;
#endif PARANOID
if (!current->used_math)
{
int i;
for ( i = 0; i < 8; i++ )
{
/* Make sure that the registers are compatible
with the assumptions of the emulator. */
regs[i].exp = 0;
regs[i].sigh = 0x80000000;
}
finit();
current->used_math = 1;
}
SETUP_DATA_AREA(arg);
FPU_ORIG_EIP = FPU_EIP;
/* We cannot handle emulation in v86-mode */
if (FPU_EFLAGS & 0x00020000)
{
math_abort(FPU_info,SIGILL);
}
/* user code space? */
if (FPU_CS == KERNEL_CS)
{
printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP);
panic("Math emulation needed in kernel");
}
/* We cannot handle multiple segments yet */
if (FPU_CS != USER_CS || FPU_DS != USER_DS)
{
math_abort(FPU_info,SIGILL);
}
FPU_lookahead = 1;
if (current->flags & PF_PTRACED)
FPU_lookahead = 0;
if ( !valid_prefix(&byte1, &override) )
{
RE_ENTRANT_CHECK_OFF;
printk("FPU emulator: Unknown prefix byte 0x%02x\n", byte1);
RE_ENTRANT_CHECK_ON;
EXCEPTION(EX_INTERNAL|0x126);
math_abort(FPU_info,SIGILL);
}
do_another_FPU_instruction:
FPU_EIP++; /* We have fetched the prefix and first code bytes. */
#ifdef PECULIAR_486
/* It would be more logical to do this only in get_address(),
but although it is supposed to be undefined for many fpu
instructions, an 80486 behaves as if this were done here: */
FPU_data_selector = FPU_DS;
#endif PECULIAR_486
if ( (byte1 & 0xf8) != 0xd8 )
{
if ( byte1 == FWAIT_OPCODE )
{
if (partial_status & SW_Summary)
goto do_the_FPU_interrupt;
else
goto FPU_fwait_done;
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x128);
math_abort(FPU_info,SIGILL);
#endif PARANOID
}
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
FPU_modrm = get_fs_byte((unsigned short *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
if (partial_status & SW_Summary)
{
/* Ignore the error for now if the current instruction is a no-wait
control instruction */
/* The 80486 manual contradicts itself on this topic,
but a real 80486 uses the following instructions:
fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
*/
unsigned short code = (FPU_modrm << 8) | byte1;
if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
(((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
fnstsw */
((code & 0xc000) != 0xc000))) ) )
{
/*
* We need to simulate the action of the kernel to FPU
* interrupts here.
* Currently, the "real FPU" part of the kernel (0.99.10)
* clears the exception flags, sets the registers to empty,
* and passes information back to the interrupted process
* via the cs selector and operand selector, so we do the same.
*/
do_the_FPU_interrupt:
cs_selector &= 0xffff0000;
cs_selector |= status_word();
operand_selector = tag_word();
partial_status = 0;
top = 0;
{
int r;
for (r = 0; r < 8; r++)
{
regs[r].tag = TW_Empty;
}
}
RE_ENTRANT_CHECK_OFF;
current->tss.trap_no = 16;
current->tss.error_code = 0;
send_sig(SIGFPE, current, 1);
return;
}
}
FPU_entry_eip = FPU_ORIG_EIP;
FPU_entry_op_cs = (byte1 << 24) | (FPU_modrm << 16) | (FPU_CS & 0xffff) ;
FPU_rm = FPU_modrm & 7;
if ( FPU_modrm < 0300 )
{
/* All of these instructions use the mod/rm byte to get a data address */
get_address(FPU_modrm, override);
if ( !(byte1 & 1) )
{
unsigned short status1 = partial_status;
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
/* Stack underflow has priority */
if ( NOT_EMPTY_0 )
{
unmasked = 0; /* Do this here to stop compiler warnings. */
switch ( (byte1 >> 1) & 3 )
{
case 0:
unmasked = reg_load_single(override);
break;
case 1:
reg_load_int32(override);
break;
case 2:
unmasked = reg_load_double(override);
break;
case 3:
reg_load_int16(override);
break;
}
/* No more access to user memory, it is safe
to use static data now */
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
/* NaN operands have the next priority. */
/* We have to delay looking at st(0) until after
loading the data, because that data might contain an SNaN */
if ( (FPU_st0_tag == TW_NaN) ||
(FPU_loaded_data.tag == TW_NaN) )
{
/* Restore the status word; we might have loaded a
denormal. */
partial_status = status1;
if ( (FPU_modrm & 0x30) == 0x10 )
{
/* fcom or fcomp */
EXCEPTION(EX_Invalid);
setcc(SW_C3 | SW_C2 | SW_C0);
if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
pop(); /* fcomp, masked, so we pop. */
}
else
{
#ifdef PECULIAR_486
/* This is not really needed, but gives behaviour
identical to an 80486 */
if ( (FPU_modrm & 0x28) == 0x20 )
/* fdiv or fsub */
real_2op_NaN(&FPU_loaded_data, FPU_st0_ptr,
FPU_st0_ptr);
else
#endif PECULIAR_486
/* fadd, fdivr, fmul, or fsubr */
real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data,
FPU_st0_ptr);
}
goto reg_mem_instr_done;
}
if ( unmasked && !((FPU_modrm & 0x30) == 0x10) )
{
/* Is not a comparison instruction. */
if ( (FPU_modrm & 0x38) == 0x38 )
{
/* fdivr */
if ( (FPU_st0_tag == TW_Zero) &&
(FPU_loaded_data.tag == TW_Valid) )
{
if ( divide_by_zero(FPU_loaded_data.sign,
FPU_st0_ptr) )
{
/* We use the fact here that the unmasked
exception in the loaded data was for a
denormal operand */
/* Restore the state of the denormal op bit */
partial_status &= ~SW_Denorm_Op;
partial_status |= status1 & SW_Denorm_Op;
}
}
}
goto reg_mem_instr_done;
}
switch ( (FPU_modrm >> 3) & 7 )
{
case 0: /* fadd */
clear_C1();
reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 1: /* fmul */
clear_C1();
reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 2: /* fcom */
compare_st_data();
break;
case 3: /* fcomp */
if ( !compare_st_data() && !unmasked )
pop();
break;
case 4: /* fsub */
clear_C1();
reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 5: /* fsubr */
clear_C1();
reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
control_word);
break;
case 6: /* fdiv */
clear_C1();
reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 7: /* fdivr */
clear_C1();
if ( FPU_st0_tag == TW_Zero )
partial_status = status1; /* Undo any denorm tag,
zero-divide has priority. */
reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
control_word);
break;
}
}
else
{
if ( (FPU_modrm & 0x30) == 0x10 )
{
/* The instruction is fcom or fcomp */
EXCEPTION(EX_StackUnder);
setcc(SW_C3 | SW_C2 | SW_C0);
if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
pop(); /* fcomp */
}
else
stack_underflow();
}
}
else
{
load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, override);
}
reg_mem_instr_done:
#ifndef PECULIAR_486
*(unsigned short *)&operand_selector = FPU_data_selector;
#endif PECULIAR_486
;
}
else
{
/* None of these instructions access user memory */
unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
#ifdef PECULIAR_486
/* This is supposed to be undefined, but a real 80486 seems
to do this: */
FPU_data_address = 0;
#endif PECULIAR_486
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
switch ( type_table[(int) instr_index] )
{
case _NONE_: /* also _REGIc: _REGIn */
break;
case _REG0_:
if ( !NOT_EMPTY_0 )
{
stack_underflow();
goto FPU_instruction_done;
}
break;
case _REGIi:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
stack_underflow_i(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGIp:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
stack_underflow_pop(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGI_:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
stack_underflow();
goto FPU_instruction_done;
}
break;
case _PUSH_: /* Only used by the fld st(i) instruction */
break;
case _null_:
FPU_illegal();
goto FPU_instruction_done;
default:
EXCEPTION(EX_INTERNAL|0x111);
goto FPU_instruction_done;
}
(*st_instr_table[(int) instr_index])();
}
FPU_instruction_done:
ip_offset = FPU_entry_eip;
cs_selector = FPU_entry_op_cs;
data_operand_offset = (unsigned long)FPU_data_address;
#ifdef PECULIAR_486
*(unsigned short *)&operand_selector = FPU_data_selector;
#endif PECULIAR_486
FPU_fwait_done:
#ifdef DEBUG
RE_ENTRANT_CHECK_OFF;
emu_printall();
RE_ENTRANT_CHECK_ON;
#endif DEBUG
if (FPU_lookahead && !need_resched)
{
FPU_ORIG_EIP = FPU_EIP;
if ( valid_prefix(&byte1, &override) )
goto do_another_FPU_instruction;
}
RE_ENTRANT_CHECK_OFF;
}
/* Support for prefix bytes is not yet complete. To properly handle
all prefix bytes, further changes are needed in the emulator code
which accesses user address space. Access to separate segments is
important for msdos emulation. */
static int valid_prefix(unsigned char *Byte, overrides *override)
{
unsigned char byte;
unsigned long ip = FPU_EIP;
*override = (overrides) { 0, PREFIX_DS }; /* defaults */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
byte = get_fs_byte((unsigned char *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
while ( 1 )
{
switch ( byte )
{
case ADDR_SIZE_PREFIX:
override->address_size = ADDR_SIZE_PREFIX;
goto do_next_byte;
case PREFIX_CS:
override->segment = PREFIX_CS;
goto do_next_byte;
case PREFIX_ES:
override->segment = PREFIX_ES;
goto do_next_byte;
case PREFIX_SS:
override->segment = PREFIX_SS;
goto do_next_byte;
case PREFIX_FS:
override->segment = PREFIX_FS;
goto do_next_byte;
case PREFIX_GS:
override->segment = PREFIX_GS;
goto do_next_byte;
case PREFIX_DS: /* Redundant unless preceded by another override. */
override->segment = PREFIX_DS;
/* rep.. prefixes have no meaning for FPU instructions */
case PREFIX_LOCK:
case PREFIX_REPE:
case PREFIX_REPNE:
case OP_SIZE_PREFIX: /* Used often by gcc, but has no effect. */
do_next_byte:
FPU_EIP++;
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
byte = get_fs_byte((unsigned char *) (FPU_EIP));
RE_ENTRANT_CHECK_ON;
break;
case FWAIT_OPCODE:
*Byte = byte;
return 1;
default:
if ( (byte & 0xf8) == 0xd8 )
{
*Byte = byte;
return 1;
}
else
{
FPU_EIP = ip;
return 0;
}
}
}
}
void __math_abort(struct info * info, unsigned int signal)
{
FPU_EIP = FPU_ORIG_EIP;
current->tss.trap_no = 16;
current->tss.error_code = 0;
send_sig(signal,current,1);
RE_ENTRANT_CHECK_OFF;
__asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4));
#ifdef PARANOID
printk("ERROR: wm-FPU-emu math_abort failed!\n");
#endif PARANOID
}

View file

@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------+
| fpu_etc.c |
| |
| Implement a few FPU instructions. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "reg_constant.h"
static void fchs(void)
{
if ( NOT_EMPTY_0 )
{
FPU_st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
clear_C1();
}
else
stack_underflow();
}
static void fabs(void)
{
if ( FPU_st0_tag ^ TW_Empty )
{
FPU_st0_ptr->sign = SIGN_POS;
clear_C1();
}
else
stack_underflow();
}
static void ftst_(void)
{
switch (FPU_st0_tag)
{
case TW_Zero:
setcc(SW_C3);
break;
case TW_Valid:
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
#ifdef DENORM_OPERAND
if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
{
#ifdef PECULIAR_486
/* This is wierd! */
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(SW_C3);
#endif PECULIAR_486
return;
}
#endif DENORM_OPERAND
break;
case TW_NaN:
setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
EXCEPTION(EX_Invalid);
break;
case TW_Infinity:
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
break;
case TW_Empty:
setcc(SW_C0|SW_C2|SW_C3);
EXCEPTION(EX_StackUnder);
break;
default:
setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
EXCEPTION(EX_INTERNAL|0x14);
break;
}
}
static void fxam(void)
{
int c=0;
switch (FPU_st0_tag)
{
case TW_Empty:
c = SW_C3|SW_C0;
break;
case TW_Zero:
c = SW_C3;
break;
case TW_Valid:
/* This will need to be changed if TW_Denormal is ever used. */
if ( FPU_st0_ptr->exp <= EXP_UNDER )
c = SW_C2|SW_C3; /* Denormal */
else
c = SW_C2;
break;
case TW_NaN:
c = SW_C0;
break;
case TW_Infinity:
c = SW_C2|SW_C0;
break;
}
if (FPU_st0_ptr->sign == SIGN_NEG)
c |= SW_C1;
setcc(c);
}
static FUNC const fp_etc_table[] = {
fchs, fabs, FPU_illegal, FPU_illegal, ftst_, fxam, FPU_illegal, FPU_illegal
};
void fp_etc()
{
(fp_etc_table[FPU_rm])();
}

View file

@ -0,0 +1,131 @@
/* errors.c */
extern void Un_impl(void);
extern void FPU_illegal(void);
extern void emu_printall(void);
extern void stack_overflow(void);
extern void stack_underflow(void);
extern void stack_underflow_i(int i);
extern void stack_underflow_pop(int i);
extern int set_precision_flag(int flags);
asmlinkage void exception(int n);
asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest);
asmlinkage int arith_invalid(FPU_REG *dest);
asmlinkage int divide_by_zero(int sign, FPU_REG *dest);
asmlinkage void set_precision_flag_up(void);
asmlinkage void set_precision_flag_down(void);
asmlinkage int denormal_operand(void);
asmlinkage int arith_overflow(FPU_REG *dest);
asmlinkage int arith_underflow(FPU_REG *dest);
/* fpu_arith.c */
extern void fadd__(void);
extern void fmul__(void);
extern void fsub__(void);
extern void fsubr_(void);
extern void fdiv__(void);
extern void fdivr_(void);
extern void fadd_i(void);
extern void fmul_i(void);
extern void fsubri(void);
extern void fsub_i(void);
extern void fdivri(void);
extern void fdiv_i(void);
extern void faddp_(void);
extern void fmulp_(void);
extern void fsubrp(void);
extern void fsubp_(void);
extern void fdivrp(void);
extern void fdivp_(void);
/* fpu_aux.c */
extern void fclex(void);
extern void finit(void);
extern void finit_(void);
extern void fstsw_(void);
extern void fp_nop(void);
extern void fld_i_(void);
extern void fxch_i(void);
extern void ffree_(void);
extern void ffreep(void);
extern void fst_i_(void);
extern void fstp_i(void);
/* fpu_entry.c */
asmlinkage void math_emulate(long arg);
extern void __math_abort(struct info *info, unsigned int signal);
/* fpu_etc.c */
extern void fp_etc(void);
/* fpu_trig.c */
extern void convert_l2reg(long const *arg, FPU_REG *dest);
extern void trig_a(void);
extern void trig_b(void);
/* get_address.c */
extern void get_address(unsigned char FPU_modrm, overrides override);
/* load_store.c */
extern void load_store_instr(char type, overrides override);
/* poly_2xm1.c */
extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result);
/* poly_atan.c */
extern void poly_atan(FPU_REG *arg);
extern void poly_add_1(FPU_REG *src);
/* poly_l2.c */
extern void poly_l2(FPU_REG const *arg, FPU_REG *result);
extern int poly_l2p1(FPU_REG const *arg, FPU_REG *result);
/* poly_sin.c */
extern void poly_sine(FPU_REG const *arg, FPU_REG *result);
/* poly_tan.c */
extern void poly_tan(FPU_REG const *arg, FPU_REG *result, int invert);
/* reg_add_sub.c */
extern int reg_add(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, int control_w);
extern int reg_sub(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, int control_w);
/* reg_compare.c */
extern int compare(FPU_REG const *b);
extern int compare_st_data(void);
extern void fcom_st(void);
extern void fcompst(void);
extern void fcompp(void);
extern void fucom_(void);
extern void fucomp(void);
extern void fucompp(void);
/* reg_constant.c */
extern void fconst(void);
/* reg_ld_str.c */
extern int reg_load_extended(overrides override);
extern int reg_load_double(overrides override);
extern int reg_load_single(overrides override);
extern void reg_load_int64(overrides override);
extern void reg_load_int32(overrides override);
extern void reg_load_int16(overrides override);
extern void reg_load_bcd(overrides override);
extern int reg_store_extended(overrides override);
extern int reg_store_double(overrides override);
extern int reg_store_single(overrides override);
extern int reg_store_int64(overrides override);
extern int reg_store_int32(overrides override);
extern int reg_store_int16(overrides override);
extern int reg_store_bcd(overrides override);
extern int round_to_int(FPU_REG *r);
extern char *fldenv(void);
extern void frstor(void);
extern unsigned short tag_word(void);
extern char *fstenv(void);
extern void fsave(void);
/* reg_mul.c */
extern int reg_mul(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, unsigned int control_w);

View file

@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------+
| fpu_system.h |
| |
| Copyright (C) 1992,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _FPU_SYSTEM_H
#define _FPU_SYSTEM_H
/* system dependent definitions */
#include <linux/sched.h>
#include <linux/kernel.h>
/* This sets the pointer FPU_info to point to the argument part
of the stack frame of math_emulate() */
#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg
#define I387 (current->tss.i387)
#define FPU_info (I387.soft.info)
#define FPU_CS (*(unsigned short *) &(FPU_info->___cs))
#define FPU_DS (*(unsigned short *) &(FPU_info->___ds))
#define FPU_EAX (FPU_info->___eax)
#define FPU_EFLAGS (FPU_info->___eflags)
#define FPU_EIP (FPU_info->___eip)
#define FPU_ORIG_EIP (FPU_info->___orig_eip)
#define FPU_lookahead (I387.soft.lookahead)
#define FPU_entry_eip (I387.soft.entry_eip)
#define partial_status (I387.soft.swd)
#define control_word (I387.soft.cwd)
#define regs (I387.soft.regs)
#define top (I387.soft.top)
#define ip_offset (I387.soft.fip)
#define cs_selector (I387.soft.fcs)
#define data_operand_offset (I387.soft.foo)
#define operand_selector (I387.soft.fos)
#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \
math_abort(FPU_info,SIGSEGV)
#undef FPU_IGNORE_CODE_SEGV
#ifdef FPU_IGNORE_CODE_SEGV
/* verify_area() is very expensive, and causes the emulator to run
about 20% slower if applied to the code. Anyway, errors due to bad
code addresses should be much rarer than errors due to bad data
addresses. */
#define FPU_code_verify_area(z)
#else
/* A simpler test than verify_area() can probably be done for
FPU_code_verify_area() because the only possible error is to step
past the upper boundary of a legal code area. */
#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z)
#endif
/* ######## temporary and ugly ;-) */
#define FPU_data_address ((void *)(I387.soft.twd))
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,197 @@
/*---------------------------------------------------------------------------+
| get_address.c |
| |
| Get the effective address from an FPU instruction. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/stddef.h>
#include <asm/segment.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
static int reg_offset[] = {
offsetof(struct info,___eax),
offsetof(struct info,___ecx),
offsetof(struct info,___edx),
offsetof(struct info,___ebx),
offsetof(struct info,___esp),
offsetof(struct info,___ebp),
offsetof(struct info,___esi),
offsetof(struct info,___edi)
};
#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
/* Decode the SIB byte. This function assumes mod != 0 */
static void *sib(int mod)
{
unsigned char ss,index,base;
long offset;
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
base = get_fs_byte((char *) FPU_EIP); /* The SIB byte */
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
ss = base >> 6;
index = (base >> 3) & 7;
base &= 7;
if ((mod == 0) && (base == 5))
offset = 0; /* No base register */
else
offset = REG_(base);
if (index == 4)
{
/* No index register */
/* A non-zero ss is illegal */
if ( ss )
EXCEPTION(EX_Invalid);
}
else
{
offset += (REG_(index)) << ss;
}
if (mod == 1)
{
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset += (signed char) get_fs_byte((char *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
}
else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
{
/* 32 bit displacment */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4);
offset += (signed) get_fs_long((unsigned long *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP += 4;
}
return (void *) offset;
}
/*
MOD R/M byte: MOD == 3 has a special use for the FPU
SIB byte used iff R/M = 100b
7 6 5 4 3 2 1 0
..... ......... .........
MOD OPCODE(2) R/M
SIB byte
7 6 5 4 3 2 1 0
..... ......... .........
SS INDEX BASE
*/
void get_address(unsigned char FPU_modrm, overrides override)
{
unsigned char mod;
long *cpu_reg_ptr;
int offset = 0; /* Initialized just to stop compiler warnings. */
#ifndef PECULIAR_486
/* This is a reasonable place to do this */
FPU_data_selector = FPU_DS;
#endif PECULIAR_486
mod = (FPU_modrm >> 6) & 3;
if (FPU_rm == 4 && mod != 3)
{
FPU_data_address = sib(mod);
return;
}
cpu_reg_ptr = & REG_(FPU_rm);
switch (mod)
{
case 0:
if (FPU_rm == 5)
{
/* Special case: disp16 or disp32 */
RE_ENTRANT_CHECK_OFF;
if ( override.address_size == ADDR_SIZE_PREFIX )
{
FPU_code_verify_area(2);
offset = get_fs_word((unsigned short *) FPU_EIP);
FPU_EIP += 2;
}
else
{
FPU_code_verify_area(4);
offset = get_fs_long((unsigned long *) FPU_EIP);
FPU_EIP += 4;
}
RE_ENTRANT_CHECK_ON;
FPU_data_address = (void *) offset;
return;
}
else
{
FPU_data_address = (void *)*cpu_reg_ptr; /* Just return the contents
of the cpu register */
return;
}
case 1:
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset = (signed char) get_fs_byte((char *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
break;
case 2:
/* 16 or 32 bit displacement */
RE_ENTRANT_CHECK_OFF;
if ( override.address_size == ADDR_SIZE_PREFIX )
{
FPU_code_verify_area(2);
offset = (signed) get_fs_word((unsigned short *) FPU_EIP);
FPU_EIP += 2;
}
else
{
FPU_code_verify_area(4);
offset = (signed) get_fs_long((unsigned long *) FPU_EIP);
FPU_EIP += 4;
}
RE_ENTRANT_CHECK_ON;
break;
case 3:
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
}
FPU_data_address = offset + (char *)*cpu_reg_ptr;
if ( override.address_size == ADDR_SIZE_PREFIX )
FPU_data_address = (void *)((long)FPU_data_address & 0xffff);
}

View file

@ -0,0 +1,237 @@
/*---------------------------------------------------------------------------+
| load_store.c |
| |
| This file contains most of the code to interpret the FPU instructions |
| which load and store from user memory. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <asm/segment.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "control_w.h"
#define _NONE_ 0 /* FPU_st0_ptr etc not needed */
#define _REG0_ 1 /* Will be storing st(0) */
#define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */
#define pop_0() { pop_ptr->tag = TW_Empty; top++; }
static unsigned char const type_table[32] = {
_PUSH_, _PUSH_, _PUSH_, _PUSH_,
_null_, _null_, _null_, _null_,
_REG0_, _REG0_, _REG0_, _REG0_,
_REG0_, _REG0_, _REG0_, _REG0_,
_NONE_, _null_, _NONE_, _PUSH_,
_NONE_, _PUSH_, _null_, _PUSH_,
_NONE_, _null_, _NONE_, _REG0_,
_NONE_, _REG0_, _NONE_, _REG0_
};
void load_store_instr(char type, overrides override)
{
FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't
change if the emulator is re-entered. */
pop_ptr = NULL; /* Initialized just to stop compiler warnings. */
switch ( type_table[(int) (unsigned) type] )
{
case _NONE_:
break;
case _REG0_:
pop_ptr = &st(0); /* Some of these instructions pop after
storing */
FPU_st0_ptr = pop_ptr; /* Set the global variables. */
FPU_st0_tag = FPU_st0_ptr->tag;
break;
case _PUSH_:
{
pop_ptr = &st(-1);
if ( pop_ptr->tag != TW_Empty )
{ stack_overflow(); return; }
top--;
}
break;
case _null_:
FPU_illegal();
return;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL);
return;
#endif PARANOID
}
switch ( type )
{
case 000: /* fld m32real */
clear_C1();
reg_load_single(override);
if ( (FPU_loaded_data.tag == TW_NaN) &&
real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
{
top++;
break;
}
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 001: /* fild m32int */
clear_C1();
reg_load_int32(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 002: /* fld m64real */
clear_C1();
reg_load_double(override);
if ( (FPU_loaded_data.tag == TW_NaN) &&
real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
{
top++;
break;
}
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 003: /* fild m16int */
clear_C1();
reg_load_int16(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 010: /* fst m32real */
clear_C1();
reg_store_single(override);
break;
case 011: /* fist m32int */
clear_C1();
reg_store_int32(override);
break;
case 012: /* fst m64real */
clear_C1();
reg_store_double(override);
break;
case 013: /* fist m16int */
clear_C1();
reg_store_int16(override);
break;
case 014: /* fstp m32real */
clear_C1();
if ( reg_store_single(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 015: /* fistp m32int */
clear_C1();
if ( reg_store_int32(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 016: /* fstp m64real */
clear_C1();
if ( reg_store_double(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 017: /* fistp m16int */
clear_C1();
if ( reg_store_int16(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 020: /* fldenv m14/28byte */
fldenv();
break;
case 022: /* frstor m94/108byte */
frstor();
break;
case 023: /* fbld m80dec */
clear_C1();
reg_load_bcd(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 024: /* fldcw */
RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_READ, FPU_data_address, 2);
control_word = get_fs_word((unsigned short *) FPU_data_address);
RE_ENTRANT_CHECK_ON;
if ( partial_status & ~control_word & CW_Exceptions )
partial_status |= (SW_Summary | SW_Backward);
else
partial_status &= ~(SW_Summary | SW_Backward);
#ifdef PECULIAR_486
control_word |= 0x40; /* An 80486 appears to always set this bit */
#endif PECULIAR_486
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
case 025: /* fld m80real */
clear_C1();
reg_load_extended(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 027: /* fild m64int */
clear_C1();
reg_load_int64(override);
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 030: /* fstenv m14/28byte */
fstenv();
NO_NET_DATA_EFFECT;
break;
case 032: /* fsave */
fsave();
NO_NET_DATA_EFFECT;
break;
case 033: /* fbstp m80dec */
clear_C1();
if ( reg_store_bcd(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 034: /* fstcw m16int */
RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(control_word, (short *) FPU_data_address);
RE_ENTRANT_CHECK_ON;
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
case 035: /* fstp m80real */
clear_C1();
if ( reg_store_extended(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 036: /* fstsw m2byte */
RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(status_word(),(short *) FPU_data_address);
RE_ENTRANT_CHECK_ON;
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
case 037: /* fistp m64int */
clear_C1();
if ( reg_store_int64(override) )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
}
}

View file

@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------+
| poly_2xm1.c |
| |
| Function to compute 2^x-1 by a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#define HIPOWER 13
static unsigned short const lterms[HIPOWER][4] =
{
{ 0x79b5, 0xd1cf, 0x17f7, 0xb172 },
{ 0x1b56, 0x058b, 0x7bff, 0x3d7f },
{ 0x8bb0, 0x8250, 0x846b, 0x0e35 },
{ 0xbc65, 0xf747, 0x556d, 0x0276 },
{ 0x17cb, 0x9e39, 0x61ff, 0x0057 },
{ 0xe018, 0x9776, 0x1848, 0x000a },
{ 0x66f2, 0xff30, 0xffe5, 0x0000 },
{ 0x682f, 0xffb6, 0x162b, 0x0000 },
{ 0xb7ca, 0x2956, 0x01b5, 0x0000 },
{ 0xcd3e, 0x4817, 0x001e, 0x0000 },
{ 0xb7e2, 0xecbe, 0x0001, 0x0000 },
{ 0x0ed5, 0x1a27, 0x0000, 0x0000 },
{ 0x101d, 0x0222, 0x0000, 0x0000 },
};
/*--- poly_2xm1() -----------------------------------------------------------+
| Requires a positive argument which is TW_Valid and < 1. |
+---------------------------------------------------------------------------*/
int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
{
short exponent;
long long Xll;
FPU_REG accum;
exponent = arg->exp - EXP_BIAS;
#ifdef PARANOID
if ( (arg->sign != SIGN_POS) /* Can't hack a number < 0.0 */
|| (exponent >= 0) /* or a |number| >= 1.0 */
|| (arg->tag != TW_Valid) )
{
/* Number negative, too large, or not Valid. */
EXCEPTION(EX_INTERNAL|0x127);
return 1;
}
#endif PARANOID
*(unsigned *)&Xll = arg->sigl;
*(((unsigned *)&Xll)+1) = arg->sigh;
if ( exponent < -1 )
{
/* Shift the argument right by the required places. */
if ( shrx(&Xll, -1-exponent) >= 0x80000000U )
Xll++; /* round up */
}
*(short *)&(accum.sign) = 0; /* Will be a valid positive nr with expon = 0 */
accum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
polynomial((unsigned *)&accum.sigl, (unsigned *)&Xll, lterms, HIPOWER-1);
/* Convert to 64 bit signed-compatible */
accum.exp += EXP_BIAS - 1;
reg_move(&accum, result);
normalize(result);
return 0;
}

View file

@ -0,0 +1,204 @@
/*---------------------------------------------------------------------------+
| p_atan.c |
| |
| Compute the tan of a FPU_REG, using a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWERon 6 /* odd poly, negative terms */
static unsigned const oddnegterms[HIPOWERon][2] =
{
{ 0x00000000, 0x00000000 }, /* for + 1.0 */
{ 0x763b6f3d, 0x1adc4428 },
{ 0x20f0630b, 0x0502909d },
{ 0x4e825578, 0x0198ce38 },
{ 0x22b7cb87, 0x008da6e3 },
{ 0x9b30ca03, 0x00239c79 }
} ;
#define HIPOWERop 6 /* odd poly, positive terms */
static unsigned const oddplterms[HIPOWERop][2] =
{
{ 0xa6f67cb8, 0x94d910bd },
{ 0xa02ffab4, 0x0a43cb45 },
{ 0x04265e6b, 0x02bf5655 },
{ 0x0a728914, 0x00f280f7 },
{ 0x6d640e01, 0x004d6556 },
{ 0xf1dd2dbf, 0x000a530a }
};
static unsigned long long const denomterm = 0xea2e6612fc4bd208LL;
/*--- poly_atan() -----------------------------------------------------------+
| |
+---------------------------------------------------------------------------*/
void poly_atan(FPU_REG *arg)
{
char recursions = 0;
short exponent;
FPU_REG odd_poly, even_poly, pos_poly, neg_poly, ratio;
FPU_REG argSq;
unsigned long long arg_signif, argSqSq;
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
{ arith_invalid(arg); return; } /* Need a positive number */
#endif PARANOID
exponent = arg->exp - EXP_BIAS;
if ( arg->tag == TW_Zero )
{
/* Return 0.0 */
reg_move(&CONST_Z, arg);
return;
}
if ( exponent >= -2 )
{
/* argument is in the range [0.25 .. 1.0] */
if ( exponent >= 0 )
{
#ifdef PARANOID
if ( (exponent == 0) &&
(arg->sigl == 0) && (arg->sigh == 0x80000000) )
#endif PARANOID
{
reg_move(&CONST_PI4, arg);
return;
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
return;
#endif PARANOID
}
/* If the argument is greater than sqrt(2)-1 (=0.414213562...) */
/* convert the argument by an identity for atan */
if ( (exponent >= -1) || (arg->sigh > 0xd413ccd0) )
{
FPU_REG numerator, denom;
recursions++;
arg_signif = significand(arg);
if ( exponent < -1 )
{
if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
arg_signif++; /* round up */
}
significand(&numerator) = -arg_signif;
numerator.exp = EXP_BIAS - 1;
normalize(&numerator); /* 1 - arg */
arg_signif = significand(arg);
if ( shrx(&arg_signif, -exponent) >= 0x80000000U )
arg_signif++; /* round up */
significand(&denom) = arg_signif;
denom.sigh |= 0x80000000; /* 1 + arg */
arg->exp = numerator.exp;
reg_u_div(&numerator, &denom, arg, FULL_PRECISION);
exponent = arg->exp - EXP_BIAS;
}
}
arg_signif = significand(arg);
#ifdef PARANOID
/* This must always be true */
if ( exponent >= -1 )
{
EXCEPTION(EX_INTERNAL|0x120); /* There must be a logic error */
}
#endif PARANOID
/* shift the argument right by the required places */
if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
arg_signif++; /* round up */
/* Now have arg_signif with binary point at the left
.1xxxxxxxx */
mul64(&arg_signif, &arg_signif, &significand(&argSq));
mul64(&significand(&argSq), &significand(&argSq), &argSqSq);
/* will be a valid positive nr with expon = 0 */
*(short *)&(pos_poly.sign) = 0;
pos_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&pos_poly.sigl, (unsigned *)&argSqSq,
(unsigned short (*)[4])oddplterms, HIPOWERop-1);
mul64(&significand(&argSq), &significand(&pos_poly),
&significand(&pos_poly));
/* will be a valid positive nr with expon = 0 */
*(short *)&(neg_poly.sign) = 0;
neg_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&neg_poly.sigl, (unsigned *)&argSqSq,
(unsigned short (*)[4])oddnegterms, HIPOWERon-1);
/* Subtract the mantissas */
significand(&pos_poly) -= significand(&neg_poly);
reg_move(&pos_poly, &odd_poly);
poly_add_1(&odd_poly);
/* will be a valid positive nr with expon = 0 */
*(short *)&(even_poly.sign) = 0;
mul64(&significand(&argSq), &denomterm, &significand(&even_poly));
poly_add_1(&even_poly);
reg_div(&odd_poly, &even_poly, &ratio, FULL_PRECISION);
reg_u_mul(&ratio, arg, arg, FULL_PRECISION);
if ( recursions )
reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION);
}
/* The argument to this function must be polynomial() compatible,
i.e. have an exponent (not checked) of EXP_BIAS-1 but need not
be normalized.
This function adds 1.0 to the (assumed positive) argument. */
void poly_add_1(FPU_REG *src)
{
/* Rounding in a consistent direction produces better results
for the use of this function in poly_atan. Simple truncation
is used here instead of round-to-nearest. */
#ifdef OBSOLETE
char round = (src->sigl & 3) == 3;
#endif OBSOLETE
shrx(&src->sigl, 1);
#ifdef OBSOLETE
if ( round ) significand(src)++; /* Round to even */
#endif OBSOLETE
src->sigh |= 0x80000000;
src->exp = EXP_BIAS;
}

View file

@ -0,0 +1,97 @@
.file "poly_div.S"
/*---------------------------------------------------------------------------+
| poly_div.S |
| |
| A set of functions to divide 64 bit integers by fixed numbers. |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void poly_div2(unsigned long long *x) |
| void poly_div4(unsigned long long *x) |
| void poly_div16(unsigned long long *x) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
/*---------------------------------------------------------------------------*/
.align 2,144
.globl _poly_div2
_poly_div2:
pushl %ebp
movl %esp,%ebp
movl PARAM1,%ecx
movw (%ecx),%ax
shrl $1,4(%ecx)
rcrl $1,(%ecx)
testw $1,%ax
je poly_div2_exit
addl $1,(%ecx)
adcl $0,4(%ecx)
poly_div2_exit:
leave
ret
/*---------------------------------------------------------------------------*/
.align 2,144
.globl _poly_div4
_poly_div4:
pushl %ebp
movl %esp,%ebp
movl PARAM1,%ecx
movw (%ecx),%ax
movl 4(%ecx),%edx
shll $30,%edx
shrl $2,4(%ecx)
shrl $2,(%ecx)
orl %edx,(%ecx)
testw $2,%ax
je poly_div4_exit
addl $1,(%ecx)
adcl $0,4(%ecx)
poly_div4_exit:
leave
ret
/*---------------------------------------------------------------------------*/
.align 2,144
.globl _poly_div16
_poly_div16:
pushl %ebp
movl %esp,%ebp
movl PARAM1,%ecx
movw (%ecx),%ax
movl 4(%ecx),%edx
shll $28,%edx
shrl $4,4(%ecx)
shrl $4,(%ecx)
orl %edx,(%ecx)
testw $8,%ax
je poly_div16_exit
addl $1,(%ecx)
adcl $0,4(%ecx)
poly_div16_exit:
leave
ret
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,289 @@
/*---------------------------------------------------------------------------+
| poly_l2.c |
| |
| Compute the base 2 log of a FPU_REG, using a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWER 9
static unsigned short const lterms[HIPOWER][4] =
{
/* Ideal computation with these coeffs gives about
64.6 bit rel accuracy. */
{ 0xe177, 0xb82f, 0x7652, 0x7154 },
{ 0xee0f, 0xe80f, 0x2770, 0x7b1c },
{ 0x0fc0, 0xbe87, 0xb143, 0x49dd },
{ 0x78b9, 0xdadd, 0xec54, 0x34c2 },
{ 0x003a, 0x5de9, 0x628b, 0x2909 },
{ 0x5588, 0xed16, 0x4abf, 0x2193 },
{ 0xb461, 0x85f7, 0x347a, 0x1c6a },
{ 0x0975, 0x87b3, 0xd5bf, 0x1876 },
{ 0xe85c, 0xcec9, 0x84e7, 0x187d }
};
/*--- poly_l2() -------------------------------------------------------------+
| Base 2 logarithm by a polynomial approximation. |
+---------------------------------------------------------------------------*/
void poly_l2(FPU_REG const *arg, FPU_REG *result)
{
short exponent;
char zero; /* flag for an Xx == 0 */
unsigned short bits, shift;
unsigned long long Xsq;
FPU_REG accum, denom, num, Xx;
exponent = arg->exp - EXP_BIAS;
accum.tag = TW_Valid; /* set the tags to Valid */
if ( arg->sigh > (unsigned)0xb504f334 )
{
/* This is good enough for the computation of the polynomial
sum, but actually results in a loss of precision for
the computation of Xx. This will matter only if exponent
becomes zero. */
exponent++;
accum.sign = 1; /* sign to negative */
num.exp = EXP_BIAS; /* needed to prevent errors in div routine */
reg_u_div(&CONST_1, arg, &num, FULL_PRECISION);
}
else
{
accum.sign = 0; /* set the sign to positive */
num.sigl = arg->sigl; /* copy the mantissa */
num.sigh = arg->sigh;
}
/* shift num left, lose the ms bit */
num.sigh <<= 1;
if ( num.sigl & 0x80000000 ) num.sigh |= 1;
num.sigl <<= 1;
denom.sigl = num.sigl;
denom.sigh = num.sigh;
poly_div4(&significand(&denom));
denom.sigh += 0x80000000; /* set the msb */
Xx.exp = EXP_BIAS; /* needed to prevent errors in div routine */
reg_u_div(&num, &denom, &Xx, FULL_PRECISION);
zero = !(Xx.sigh | Xx.sigl);
mul64(&significand(&Xx), &significand(&Xx), &Xsq);
poly_div16(&Xsq);
accum.exp = -1; /* exponent of accum */
/* Do the basic fixed point polynomial evaluation */
polynomial((unsigned *)&accum.sigl, (unsigned *)&Xsq, lterms, HIPOWER-1);
if ( !exponent )
{
/* If the exponent is zero, then we would lose precision by
sticking to fixed point computation here */
/* We need to re-compute Xx because of loss of precision. */
FPU_REG lXx;
char sign;
sign = accum.sign;
accum.sign = 0;
/* make accum compatible and normalize */
accum.exp = EXP_BIAS + accum.exp;
normalize(&accum);
if ( zero )
{
reg_move(&CONST_Z, result);
}
else
{
/* we need to re-compute lXx to better accuracy */
num.tag = TW_Valid; /* set the tags to Vaild */
num.sign = 0; /* set the sign to positive */
num.exp = EXP_BIAS - 1;
if ( sign )
{
/* The argument is of the form 1-x */
/* Use 1-1/(1-x) = x/(1-x) */
significand(&num) = - significand(arg);
normalize(&num);
reg_div(&num, arg, &num, FULL_PRECISION);
}
else
{
normalize(&num);
}
denom.tag = TW_Valid; /* set the tags to Valid */
denom.sign = SIGN_POS; /* set the sign to positive */
denom.exp = EXP_BIAS;
reg_div(&num, &denom, &lXx, FULL_PRECISION);
reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION);
reg_u_add(&lXx, &accum, result, FULL_PRECISION);
normalize(result);
}
result->sign = sign;
return;
}
mul64(&significand(&accum),
&significand(&Xx), &significand(&accum));
significand(&accum) += significand(&Xx);
if ( Xx.sigh > accum.sigh )
{
/* There was an overflow */
poly_div2(&significand(&accum));
accum.sigh |= 0x80000000;
accum.exp++;
}
/* When we add the exponent to the accum result later, we will
require that their signs are the same. Here we ensure that
this is so. */
if ( exponent && ((exponent < 0) ^ (accum.sign)) )
{
/* signs are different */
accum.sign = !accum.sign;
/* An exceptional case is when accum is zero */
if ( accum.sigl | accum.sigh )
{
/* find 1-accum */
/* Shift to get exponent == 0 */
if ( accum.exp < 0 )
{
poly_div2(&significand(&accum));
accum.exp++;
}
/* Just negate, but throw away the sign */
significand(&accum) = - significand(&accum);
if ( exponent < 0 )
exponent++;
else
exponent--;
}
}
shift = exponent >= 0 ? exponent : -exponent ;
bits = 0;
if ( shift )
{
if ( accum.exp )
{
accum.exp++;
poly_div2(&significand(&accum));
}
while ( shift )
{
poly_div2(&significand(&accum));
if ( shift & 1)
accum.sigh |= 0x80000000;
shift >>= 1;
bits++;
}
}
/* Convert to 64 bit signed-compatible */
accum.exp += bits + EXP_BIAS - 1;
reg_move(&accum, result);
normalize(result);
return;
}
/*--- poly_l2p1() -----------------------------------------------------------+
| Base 2 logarithm by a polynomial approximation. |
| log2(x+1) |
+---------------------------------------------------------------------------*/
int poly_l2p1(FPU_REG const *arg, FPU_REG *result)
{
char sign = 0;
unsigned long long Xsq;
FPU_REG arg_pl1, denom, accum, local_arg, poly_arg;
sign = arg->sign;
reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION);
if ( (arg_pl1.sign) | (arg_pl1.tag) )
{ /* We need a valid positive number! */
return 1;
}
reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION);
reg_div(arg, &denom, &local_arg, FULL_PRECISION);
local_arg.sign = 0; /* Make the sign positive */
/* Now we need to check that |local_arg| is less than
3-2*sqrt(2) = 0.17157.. = .0xafb0ccc0 * 2^-2 */
if ( local_arg.exp >= EXP_BIAS - 3 )
{
if ( (local_arg.exp > EXP_BIAS - 3) ||
(local_arg.sigh > (unsigned)0xafb0ccc0) )
{
/* The argument is large */
poly_l2(&arg_pl1, result); return 0;
}
}
/* Make a copy of local_arg */
reg_move(&local_arg, &poly_arg);
/* Get poly_arg bits aligned as required */
shrx((unsigned *)&(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3));
mul64(&significand(&poly_arg), &significand(&poly_arg), &Xsq);
poly_div16(&Xsq);
/* Do the basic fixed point polynomial evaluation */
polynomial(&(accum.sigl), (unsigned *)&Xsq, lterms, HIPOWER-1);
accum.tag = TW_Valid; /* set the tags to Valid */
accum.sign = SIGN_POS; /* and make accum positive */
/* make accum compatible and normalize */
accum.exp = EXP_BIAS - 1;
normalize(&accum);
reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION);
reg_u_add(&local_arg, &accum, result, FULL_PRECISION);
/* Multiply the result by 2 */
result->exp++;
result->sign = sign;
return 0;
}

View file

@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------+
| poly_mul64.S |
| |
| Multiply two 64 bit integers. |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void mul64(long long *a, long long *b, long long *result) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
.globl _mul64
_mul64:
pushl %ebp
movl %esp,%ebp
subl $16,%esp
pushl %esi
pushl %ebx
movl PARAM1,%esi
movl PARAM2,%ecx
movl PARAM3,%ebx
xor %eax,%eax
movl %eax,-4(%ebp)
movl %eax,-8(%ebp)
movl (%esi),%eax
mull (%ecx)
movl %eax,-16(%ebp) /* Not used */
movl %edx,-12(%ebp)
movl (%esi),%eax
mull 4(%ecx)
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 4(%esi),%eax
mull (%ecx)
addl %eax,-12(%ebp)
adcl %edx,-8(%ebp)
adcl $0,-4(%ebp)
movl 4(%esi),%eax
mull 4(%ecx)
addl %eax,-8(%ebp)
adcl %edx,-4(%ebp)
testb $128,-9(%ebp)
je L_no_round
addl $1,-8(%ebp)
adcl $0,-4(%ebp)
L_no_round:
movl -8(%ebp),%esi
movl %esi,(%ebx)
movl -4(%ebp),%esi
movl %esi,4(%ebx)
popl %ebx
popl %esi
leave
ret

View file

@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------+
| poly_sin.c |
| |
| Computation of an approximation of the sin function by a polynomial |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWER 5
static unsigned short const lterms[HIPOWER][4] =
{
{ 0x846a, 0x42d1, 0xb544, 0x921f},
{ 0xe110, 0x75aa, 0xbc67, 0x1466},
{ 0x503d, 0xa43f, 0x83c1, 0x000a},
{ 0x8f9d, 0x7a19, 0x00f4, 0x0000},
{ 0xda03, 0x06aa, 0x0000, 0x0000},
};
static unsigned short const negterms[HIPOWER][4] =
{
{ 0x95ed, 0x2df2, 0xe731, 0xa55d},
{ 0xd159, 0xe62b, 0xd2cc, 0x0132},
{ 0x6342, 0xe9fb, 0x3c60, 0x0000},
{ 0x6256, 0xdf5a, 0x0002, 0x0000},
{ 0xf279, 0x000b, 0x0000, 0x0000},
};
/*--- poly_sine() -----------------------------------------------------------+
| |
+---------------------------------------------------------------------------*/
void poly_sine(FPU_REG const *arg, FPU_REG *result)
{
short exponent;
FPU_REG fixed_arg, arg_sqrd, arg_to_4, accum, negaccum;
exponent = arg->exp - EXP_BIAS;
if ( arg->tag == TW_Zero )
{
/* Return 0.0 */
reg_move(&CONST_Z, result);
return;
}
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
{
EXCEPTION(EX_Invalid);
reg_move(&CONST_QNaN, result);
return;
}
if ( exponent >= 0 ) /* Can't hack a number > 1.0 */
{
if ( (exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000) )
{
reg_move(&CONST_1, result);
return;
}
EXCEPTION(EX_Invalid);
reg_move(&CONST_QNaN, result);
return;
}
#endif PARANOID
fixed_arg.sigl = arg->sigl;
fixed_arg.sigh = arg->sigh;
if ( exponent < -1 )
{
/* shift the argument right by the required places */
if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U )
significand(&fixed_arg)++; /* round up */
}
mul64(&significand(&fixed_arg), &significand(&fixed_arg),
&significand(&arg_sqrd));
mul64(&significand(&arg_sqrd), &significand(&arg_sqrd),
&significand(&arg_to_4));
/* will be a valid positive nr with expon = 0 */
*(short *)&(accum.sign) = 0;
accum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1);
/* will be a valid positive nr with expon = 0 */
*(short *)&(negaccum.sign) = 0;
negaccum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1);
mul64(&significand(&arg_sqrd), &significand(&negaccum),
&significand(&negaccum));
/* Subtract the mantissas */
significand(&accum) -= significand(&negaccum);
/* Convert to 64 bit signed-compatible */
accum.exp = EXP_BIAS - 1 + accum.exp;
reg_move(&accum, result);
normalize(result);
reg_mul(result, arg, result, FULL_PRECISION);
reg_u_add(result, arg, result, FULL_PRECISION);
if ( result->exp >= EXP_BIAS )
{
/* A small overflow may be possible... but an illegal result. */
if ( (result->exp > EXP_BIAS) /* Larger or equal 2.0 */
|| (result->sigl > 1) /* Larger than 1.0+msb */
|| (result->sigh != 0x80000000) /* Much > 1.0 */
)
{
#ifdef DEBUGGING
RE_ENTRANT_CHECK_OFF;
printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp,
result->sigh, result->sigl);
RE_ENTRANT_CHECK_ON;
#endif DEBUGGING
EXCEPTION(EX_INTERNAL|0x103);
}
#ifdef DEBUGGING
RE_ENTRANT_CHECK_OFF;
printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n");
printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp,
result->sigh, result->sigl);
RE_ENTRANT_CHECK_ON;
#endif DEBUGGING
result->sigl = 0; /* Truncate the result to 1.00 */
}
}

View file

@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------+
| poly_tan.c |
| |
| Compute the tan of a FPU_REG, using a polynomial approximation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#define HIPOWERop 3 /* odd poly, positive terms */
static unsigned short const oddplterms[HIPOWERop][4] =
{
{ 0x846a, 0x42d1, 0xb544, 0x921f},
{ 0x6fb2, 0x0215, 0x95c0, 0x099c},
{ 0xfce6, 0x0cc8, 0x1c9a, 0x0000}
};
#define HIPOWERon 2 /* odd poly, negative terms */
static unsigned short const oddnegterms[HIPOWERon][4] =
{
{ 0x6906, 0xe205, 0x25c8, 0x8838},
{ 0x1dd7, 0x3fe3, 0x944e, 0x002c}
};
#define HIPOWERep 2 /* even poly, positive terms */
static unsigned short const evenplterms[HIPOWERep][4] =
{
{ 0xdb8f, 0x3761, 0x1432, 0x2acf},
{ 0x16eb, 0x13c1, 0x3099, 0x0003}
};
#define HIPOWERen 2 /* even poly, negative terms */
static unsigned short const evennegterms[HIPOWERen][4] =
{
{ 0x3a7c, 0xe4c5, 0x7f87, 0x2945},
{ 0x572b, 0x664c, 0xc543, 0x018c}
};
/*--- poly_tan() ------------------------------------------------------------+
| |
+---------------------------------------------------------------------------*/
void poly_tan(FPU_REG const *arg, FPU_REG *result, int invert)
{
short exponent;
FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
FPU_REG argSq;
unsigned long long arg_signif, argSqSq;
exponent = arg->exp - EXP_BIAS;
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
{ arith_invalid(result); return; } /* Need a positive number */
#endif PARANOID
arg_signif = significand(arg);
if ( exponent < -1 )
{
/* shift the argument right by the required places */
if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
arg_signif++; /* round up */
}
mul64(&arg_signif, &arg_signif, &significand(&argSq));
mul64(&significand(&argSq), &significand(&argSq), &argSqSq);
/* will be a valid positive nr with expon = 0 */
*(short *)&(pos_poly.sign) = 0;
pos_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, oddplterms, HIPOWERop-1);
/* will be a valid positive nr with expon = 0 */
*(short *)&(neg_poly.sign) = 0;
neg_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, oddnegterms, HIPOWERon-1);
mul64(&significand(&argSq), &significand(&neg_poly),
&significand(&neg_poly));
/* Subtract the mantissas */
significand(&pos_poly) -= significand(&neg_poly);
/* Convert to 64 bit signed-compatible */
pos_poly.exp -= 1;
reg_move(&pos_poly, &odd_poly);
normalize(&odd_poly);
reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
/* Complete the odd polynomial. */
reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION);
/* will be a valid positive nr with expon = 0 */
*(short *)&(pos_poly.sign) = 0;
pos_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, evenplterms, HIPOWERep-1);
mul64(&significand(&argSq),
&significand(&pos_poly), &significand(&pos_poly));
/* will be a valid positive nr with expon = 0 */
*(short *)&(neg_poly.sign) = 0;
neg_poly.exp = EXP_BIAS;
/* Do the basic fixed point polynomial evaluation */
polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, evennegterms, HIPOWERen-1);
/* Subtract the mantissas */
significand(&neg_poly) -= significand(&pos_poly);
/* and multiply by argSq */
/* Convert argSq to a valid reg number */
*(short *)&(argSq.sign) = 0;
argSq.exp = EXP_BIAS - 1;
normalize(&argSq);
/* Convert to 64 bit signed-compatible */
neg_poly.exp -= 1;
reg_move(&neg_poly, &even_poly);
normalize(&even_poly);
reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION);
reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION);
/* Complete the even polynomial */
reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION);
/* Now ready to copy the results */
if ( invert )
{ reg_div(&even_poly, &odd_poly, result, FULL_PRECISION); }
else
{ reg_div(&odd_poly, &even_poly, result, FULL_PRECISION); }
}

View file

@ -0,0 +1,141 @@
/*---------------------------------------------------------------------------+
| polynomial.S |
| |
| Fixed point arithmetic polynomial evaluation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void polynomial(unsigned accum[], unsigned x[], unsigned terms[][2], |
| int n) |
| |
| Computes: |
| terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x |
| The result is returned in accum. |
| |
+---------------------------------------------------------------------------*/
.file "fpolynom.s"
#include "fpu_asm.h"
/* #define EXTRA_PRECISE // Do not use: not complete */
#define TERM_SIZE $8
#define SUM_MS -20(%ebp) /* sum ms long */
#define SUM_MIDDLE -24(%ebp) /* sum middle long */
#define SUM_LS -28(%ebp) /* sum ls long */
#define SUM_LS_HI -25(%ebp) /* high byte of sum ls */
#define ACCUM_MS -4(%ebp) /* accum ms long */
#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */
#define ACCUM_LS -12(%ebp) /* accum ls long */
#define ACCUM_LS_HI -9(%ebp) /* high byte of accum ls */
.text
.align 2,144
.globl _polynomial
_polynomial:
pushl %ebp
movl %esp,%ebp
subl $32,%esp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM2,%esi /* x */
movl PARAM3,%edi /* terms */
movl TERM_SIZE,%eax
mull PARAM4 /* n */
addl %eax,%edi
movl 4(%edi),%edx /* terms[n] */
movl %edx,SUM_MS
movl (%edi),%edx /* terms[n] */
movl %edx,SUM_MIDDLE
xor %eax,%eax
movl %eax,SUM_LS
subl TERM_SIZE,%edi
decl PARAM4
js L_accum_done
L_accum_loop:
xor %eax,%eax
movl %eax,ACCUM_MS
movl %eax,ACCUM_MIDDLE
movl SUM_MIDDLE,%eax
mull (%esi) /* x ls long */
/* movl %eax,-16(%ebp) // Not needed */
movl %edx,ACCUM_LS
movl SUM_MIDDLE,%eax
mull 4(%esi) /* x ms long */
addl %eax,ACCUM_LS
adcl %edx,ACCUM_MIDDLE
adcl $0,ACCUM_MS
movl SUM_MS,%eax
mull (%esi) /* x ls long */
addl %eax,ACCUM_LS
adcl %edx,ACCUM_MIDDLE
adcl $0,ACCUM_MS
movl SUM_MS,%eax
mull 4(%esi) /* x ms long */
addl %eax,ACCUM_MIDDLE
adcl %edx,ACCUM_MS
/*
* Now put the sum of next term and the accumulator
* into the sum register
*/
movl ACCUM_MIDDLE,%eax
addl (%edi),%eax /* term ls long */
movl %eax,SUM_MIDDLE
movl ACCUM_MS,%eax
adcl 4(%edi),%eax /* term ms long */
movl %eax,SUM_MS
#ifdef EXTRA_PRECISE
movl ACCUM_LS,%eax
movl %eax,SUM_LS
#else
testb $0x80,ACCUM_LS_HI /* ms bit of ACCUM_LS */
je L_no_poly_round
addl $1,SUM_MIDDLE
adcl $0,SUM_MS
L_no_poly_round:
#endif EXTRA_PRECISE
subl TERM_SIZE,%edi
decl PARAM4
jns L_accum_loop
L_accum_done:
#ifdef EXTRA_PRECISE
/* Round the result */
testb $128,SUM_LS_HI
je L_poly_done
addl $1,SUM_MIDDLE
adcl $0,SUM_MS
#endif EXTRA_PRECISE
L_poly_done:
movl PARAM1,%edi /* accum */
movl SUM_MIDDLE,%eax
movl %eax,(%edi)
movl SUM_MS,%eax
movl %eax,4(%edi)
popl %ebx
popl %edi
popl %esi
leave
ret

View file

@ -0,0 +1,318 @@
/*---------------------------------------------------------------------------+
| reg_add_sub.c |
| |
| Functions to add or subtract two registers and put the result in a third. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| For each function, the destination may be any FPU_REG, including one of |
| the source FPU_REGs. |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "fpu_system.h"
int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
{
char saved_sign = dest->sign;
int diff;
if ( !(a->tag | b->tag) )
{
/* Both registers are valid */
if (!(a->sign ^ b->sign))
{
/* signs are the same */
dest->sign = a->sign;
if ( reg_u_add(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
return 0;
}
/* The signs are different, so do a subtraction */
diff = a->exp - b->exp;
if (!diff)
{
diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
if (!diff)
{
diff = a->sigl > b->sigl;
if (!diff)
diff = -(a->sigl < b->sigl);
}
}
if (diff > 0)
{
dest->sign = a->sign;
if ( reg_u_sub(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
}
else if ( diff == 0 )
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(&CONST_Z, dest);
/* sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
else
{
dest->sign = b->sign;
if ( reg_u_sub(b, a, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
}
return 0;
}
else
{
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
{ return real_2op_NaN(a, b, dest); }
else if (a->tag == TW_Zero)
{
if (b->tag == TW_Zero)
{
char different_signs = a->sign ^ b->sign;
/* Both are zero, result will be zero. */
reg_move(a, dest);
if (different_signs)
{
/* Signs are different. */
/* Sign of answer depends upon rounding mode. */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
}
else
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
}
return 0;
}
else if (b->tag == TW_Zero)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest); return 0;
}
else if (a->tag == TW_Infinity)
{
if (b->tag != TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest); return 0;
}
if (a->sign == b->sign)
{
/* They are both + or - infinity */
reg_move(a, dest); return 0;
}
return arith_invalid(dest); /* Infinity-Infinity is undefined. */
}
else if (b->tag == TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest); return 0;
}
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x101);
#endif
return 1;
}
/* Subtract b from a. (a-b) -> dest */
int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
{
char saved_sign = dest->sign;
int diff;
if ( !(a->tag | b->tag) )
{
/* Both registers are valid */
diff = a->exp - b->exp;
if (!diff)
{
diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
if (!diff)
{
diff = a->sigl > b->sigl;
if (!diff)
diff = -(a->sigl < b->sigl);
}
}
switch (a->sign*2 + b->sign)
{
case 0: /* P - P */
case 3: /* N - N */
if (diff > 0)
{
/* |a| > |b| */
dest->sign = a->sign;
if ( reg_u_sub(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
return 0;
}
else if ( diff == 0 )
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(&CONST_Z, dest);
/* sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
else
{
dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
if ( reg_u_sub(b, a, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
}
break;
case 1: /* P - N */
dest->sign = SIGN_POS;
if ( reg_u_add(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
break;
case 2: /* N - P */
dest->sign = SIGN_NEG;
if ( reg_u_add(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
break;
}
return 0;
}
else
{
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
{ return real_2op_NaN(b, a, dest); }
else if (b->tag == TW_Zero)
{
if (a->tag == TW_Zero)
{
char same_signs = !(a->sign ^ b->sign);
/* Both are zero, result will be zero. */
reg_move(a, dest); /* Answer for different signs. */
if (same_signs)
{
/* Sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
? SIGN_POS : SIGN_NEG;
}
}
else
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest);
}
return 0;
}
else if (a->tag == TW_Zero)
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
return 0;
}
else if (a->tag == TW_Infinity)
{
if (b->tag != TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest); return 0;
}
/* Both args are Infinity */
if (a->sign == b->sign)
{
/* Infinity-Infinity is undefined. */
return arith_invalid(dest);
}
reg_move(a, dest);
return 0;
}
else if (b->tag == TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
return 0;
}
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x110);
#endif
return 1;
}

View file

@ -0,0 +1,379 @@
/*---------------------------------------------------------------------------+
| reg_compare.c |
| |
| Compare two floating point registers |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| compare() is the core FPU_REG comparison function |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "status_w.h"
int compare(FPU_REG const *b)
{
int diff;
if ( FPU_st0_ptr->tag | b->tag )
{
if ( FPU_st0_ptr->tag == TW_Zero )
{
if ( b->tag == TW_Zero ) return COMP_A_eq_B;
if ( b->tag == TW_Valid )
{
return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
#ifdef DENORM_OPERAND
| ((b->exp <= EXP_UNDER) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
}
else if ( b->tag == TW_Zero )
{
if ( FPU_st0_ptr->tag == TW_Valid )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
: COMP_A_lt_B)
#ifdef DENORM_OPERAND
| ((FPU_st0_ptr->exp <= EXP_UNDER )
? COMP_Denormal : 0 )
#endif DENORM_OPERAND
;
}
}
if ( FPU_st0_ptr->tag == TW_Infinity )
{
if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
: COMP_A_lt_B)
#ifdef DENORM_OPERAND
| (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0 )
#endif DENORM_OPERAND
;
}
else if ( b->tag == TW_Infinity )
{
/* The 80486 book says that infinities can be equal! */
return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B :
((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
}
/* Fall through to the NaN code */
}
else if ( b->tag == TW_Infinity )
{
if ( (FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero) )
{
return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
#ifdef DENORM_OPERAND
| (((FPU_st0_ptr->tag == TW_Valid)
&& (FPU_st0_ptr->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
/* Fall through to the NaN code */
}
/* The only possibility now should be that one of the arguments
is a NaN */
if ( (FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN) )
{
if ( ((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000))
|| ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) )
/* At least one arg is a signaling NaN */
return COMP_No_Comp | COMP_SNaN | COMP_NaN;
else
/* Neither is a signaling NaN */
return COMP_No_Comp | COMP_NaN;
}
EXCEPTION(EX_Invalid);
}
#ifdef PARANOID
if (!(FPU_st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
#endif PARANOID
if (FPU_st0_ptr->sign != b->sign)
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
diff = FPU_st0_ptr->exp - b->exp;
if ( diff == 0 )
{
diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits are
identical */
if ( diff == 0 )
{
diff = FPU_st0_ptr->sigl > b->sigl;
if ( diff == 0 )
diff = -(FPU_st0_ptr->sigl < b->sigl);
}
}
if ( diff > 0 )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
if ( diff < 0 )
{
return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
return COMP_A_eq_B
#ifdef DENORM_OPERAND
|
( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
/* This function requires that st(0) is not empty */
int compare_st_data(void)
{
int f, c;
c = compare(&FPU_loaded_data);
if (c & COMP_NaN)
{
EXCEPTION(EX_Invalid);
f = SW_C3 | SW_C2 | SW_C0;
}
else
switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
break;
case COMP_A_eq_B:
f = SW_C3;
break;
case COMP_A_gt_B:
f = 0;
break;
case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL|0x121);
f = SW_C3 | SW_C2 | SW_C0;
break;
#endif PARANOID
}
setcc(f);
if (c & COMP_Denormal)
{
return denormal_operand();
}
return 0;
}
static int compare_st_st(int nr)
{
int f, c;
if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) )
{
setcc(SW_C3 | SW_C2 | SW_C0);
/* Stack fault */
EXCEPTION(EX_StackUnder);
return !(control_word & CW_Invalid);
}
c = compare(&st(nr));
if (c & COMP_NaN)
{
setcc(SW_C3 | SW_C2 | SW_C0);
EXCEPTION(EX_Invalid);
return !(control_word & CW_Invalid);
}
else
switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
break;
case COMP_A_eq_B:
f = SW_C3;
break;
case COMP_A_gt_B:
f = 0;
break;
case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL|0x122);
f = SW_C3 | SW_C2 | SW_C0;
break;
#endif PARANOID
}
setcc(f);
if (c & COMP_Denormal)
{
return denormal_operand();
}
return 0;
}
static int compare_u_st_st(int nr)
{
int f, c;
if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) )
{
setcc(SW_C3 | SW_C2 | SW_C0);
/* Stack fault */
EXCEPTION(EX_StackUnder);
return !(control_word & CW_Invalid);
}
c = compare(&st(nr));
if (c & COMP_NaN)
{
setcc(SW_C3 | SW_C2 | SW_C0);
if (c & COMP_SNaN) /* This is the only difference between
un-ordered and ordinary comparisons */
{
EXCEPTION(EX_Invalid);
return !(control_word & CW_Invalid);
}
return 0;
}
else
switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
break;
case COMP_A_eq_B:
f = SW_C3;
break;
case COMP_A_gt_B:
f = 0;
break;
case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
default:
EXCEPTION(EX_INTERNAL|0x123);
f = SW_C3 | SW_C2 | SW_C0;
break;
#endif PARANOID
}
setcc(f);
if (c & COMP_Denormal)
{
return denormal_operand();
}
return 0;
}
/*---------------------------------------------------------------------------*/
void fcom_st()
{
/* fcom st(i) */
compare_st_st(FPU_rm);
}
void fcompst()
{
/* fcomp st(i) */
if ( !compare_st_st(FPU_rm) )
pop();
}
void fcompp()
{
/* fcompp */
if (FPU_rm != 1)
{
FPU_illegal();
return;
}
if ( !compare_st_st(1) )
{
pop(); FPU_st0_ptr = &st(0);
pop();
}
}
void fucom_()
{
/* fucom st(i) */
compare_u_st_st(FPU_rm);
}
void fucomp()
{
/* fucomp st(i) */
if ( !compare_u_st_st(FPU_rm) )
pop();
}
void fucompp()
{
/* fucompp */
if (FPU_rm == 1)
{
if ( !compare_u_st_st(1) )
{
pop(); FPU_st0_ptr = &st(0);
pop();
}
}
else
FPU_illegal();
}

View file

@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------+
| reg_constant.c |
| |
| All of the constant FPU_REGs |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_system.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "reg_constant.h"
FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS,
0x00000000, 0x80000000 };
FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1,
0x00000000, 0x80000000 };
FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
0x00000000, 0x80000000 };
FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1,
0xcd1b8afe, 0xd49a784b };
FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS,
0x5c17f0bc, 0xb8aa3b29 };
FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1,
0x2168c235, 0xc90fdaa2 };
FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS,
0x2168c235, 0xc90fdaa2 };
FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
0x2168c235, 0xc90fdaa2 };
FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2,
0xfbcff799, 0x9a209a84 };
FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
0xd1cf79ac, 0xb17217f7 };
/* Extra bits to take pi/2 to more than 128 bits precision. */
FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
0xfc8f8cbb, 0xece675d1 };
/* Only the sign (and tag) is used in internal zeroes */
FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
/* Only the sign and significand (and tag) are used in internal NaNs */
/* The 80486 never generates one of these
FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
*/
/* This is the real indefinite QNaN */
FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
/* Only the sign (and tag) is used in internal infinities */
FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
static void fld_const(FPU_REG const *c)
{
FPU_REG *st_new_ptr;
if ( STACK_OVERFLOW )
{
stack_overflow();
return;
}
push();
reg_move(c, FPU_st0_ptr);
clear_C1();
}
static void fld1(void)
{
fld_const(&CONST_1);
}
static void fldl2t(void)
{
fld_const(&CONST_L2T);
}
static void fldl2e(void)
{
fld_const(&CONST_L2E);
}
static void fldpi(void)
{
fld_const(&CONST_PI);
}
static void fldlg2(void)
{
fld_const(&CONST_LG2);
}
static void fldln2(void)
{
fld_const(&CONST_LN2);
}
static void fldz(void)
{
fld_const(&CONST_Z);
}
static FUNC constants_table[] = {
fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, FPU_illegal
};
void fconst(void)
{
(constants_table[FPU_rm])();
}

View file

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------+
| reg_constant.h |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _REG_CONSTANT_H_
#define _REG_CONSTANT_H_
#include "fpu_emu.h"
extern FPU_REG const CONST_1;
extern FPU_REG const CONST_2;
extern FPU_REG const CONST_HALF;
extern FPU_REG const CONST_L2T;
extern FPU_REG const CONST_L2E;
extern FPU_REG const CONST_PI;
extern FPU_REG const CONST_PI2;
extern FPU_REG const CONST_PI2extra;
extern FPU_REG const CONST_PI4;
extern FPU_REG const CONST_LG2;
extern FPU_REG const CONST_LN2;
extern FPU_REG const CONST_Z;
extern FPU_REG const CONST_PINF;
extern FPU_REG const CONST_INF;
extern FPU_REG const CONST_MINF;
extern FPU_REG const CONST_QNaN;
#endif _REG_CONSTANT_H_

View file

@ -0,0 +1,251 @@
.file "reg_div.S"
/*---------------------------------------------------------------------------+
| reg_div.S |
| |
| Divide one FPU_REG by another and put the result in a destination FPU_REG.|
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |
| unsigned int control_word) |
| |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
.text
.align 2
.globl _reg_div
_reg_div:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $28,%esp /* Needed by divide_kernel */
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi
movl PARAM2,%ebx
movl PARAM3,%edi
movb TAG(%esi),%al
orb TAG(%ebx),%al
jne L_div_special /* Not (both numbers TW_Valid) */
#ifdef DENORM_OPERAND
/* Check for denormals */
cmpl EXP_UNDER,EXP(%esi)
jg xL_arg1_not_denormal
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xL_arg1_not_denormal:
cmpl EXP_UNDER,EXP(%ebx)
jg xL_arg2_not_denormal
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xL_arg2_not_denormal:
#endif DENORM_OPERAND
/* Both arguments are TW_Valid */
movb TW_Valid,TAG(%edi)
movb SIGN(%esi),%cl
cmpb %cl,SIGN(%ebx)
setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */
movl EXP(%esi),%edx
movl EXP(%ebx),%eax
subl %eax,%edx
addl EXP_BIAS,%edx
movl %edx,EXP(%edi)
jmp _divide_kernel
/*-----------------------------------------------------------------------*/
L_div_special:
cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */
je L_arg1_NaN
cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */
jne L_no_NaN_arg
/* Operations on NaNs */
L_arg1_NaN:
L_arg2_NaN:
pushl %edi /* Destination */
pushl %esi
pushl %ebx /* Ordering is important here */
call _real_2op_NaN
jmp LDiv_exit
/* Invalid operations */
L_zero_zero:
L_inf_inf:
pushl %edi /* Destination */
call _arith_invalid /* 0/0 or Infinity/Infinity */
jmp LDiv_exit
L_no_NaN_arg:
cmpb TW_Infinity,TAG(%esi)
jne L_arg1_not_inf
cmpb TW_Infinity,TAG(%ebx)
je L_inf_inf /* invalid operation */
cmpb TW_Valid,TAG(%ebx)
je L_inf_valid
#ifdef PARANOID
/* arg2 must be zero or valid */
cmpb TW_Zero,TAG(%ebx)
ja L_unknown_tags
#endif PARANOID
/* Note that p16-9 says that infinity/0 returns infinity */
jmp L_copy_arg1 /* Answer is Inf */
L_inf_valid:
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%ebx)
jg L_copy_arg1 /* Answer is Inf */
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
#endif DENORM_OPERAND
jmp L_copy_arg1 /* Answer is Inf */
L_arg1_not_inf:
cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */
jne L_arg2_not_zero
cmpb TW_Zero,TAG(%esi)
je L_zero_zero /* invalid operation */
#ifdef PARANOID
/* arg1 must be valid */
cmpb TW_Valid,TAG(%esi)
ja L_unknown_tags
#endif PARANOID
/* Division by zero error */
pushl %edi /* destination */
movb SIGN(%esi),%al
xorb SIGN(%ebx),%al
pushl %eax /* lower 8 bits have the sign */
call _divide_by_zero
jmp LDiv_exit
L_arg2_not_zero:
cmpb TW_Infinity,TAG(%ebx)
jne L_arg2_not_inf
#ifdef DENORM_OPERAND
cmpb TW_Valid,TAG(%esi)
jne L_return_zero
cmpl EXP_UNDER,EXP(%esi)
jg L_return_zero /* Answer is zero */
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
#endif DENORM_OPERAND
jmp L_return_zero /* Answer is zero */
L_arg2_not_inf:
#ifdef PARANOID
cmpb TW_Zero,TAG(%esi)
jne L_unknown_tags
#endif PARANOID
/* arg1 is zero, arg2 is not Infinity or a NaN */
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%ebx)
jg L_copy_arg1 /* Answer is zero */
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
#endif DENORM_OPERAND
L_copy_arg1:
movb TAG(%esi),%ax
movb %ax,TAG(%edi)
movl EXP(%esi),%eax
movl %eax,EXP(%edi)
movl SIGL(%esi),%eax
movl %eax,SIGL(%edi)
movl SIGH(%esi),%eax
movl %eax,SIGH(%edi)
LDiv_set_result_sign:
movb SIGN(%esi),%cl
cmpb %cl,SIGN(%ebx)
jne LDiv_negative_result
movb SIGN_POS,SIGN(%edi)
xorl %eax,%eax /* Valid result */
jmp LDiv_exit
LDiv_negative_result:
movb SIGN_NEG,SIGN(%edi)
xorl %eax,%eax /* Valid result */
LDiv_exit:
#ifdef REENTRANT_FPU
leal -40(%ebp),%esp
#else
leal -12(%ebp),%esp
#endif REENTRANT_FPU
popl %ebx
popl %edi
popl %esi
leave
ret
L_return_zero:
xorl %eax,%eax
movl %eax,SIGH(%edi)
movl %eax,SIGL(%edi)
movl EXP_UNDER,EXP(%edi)
movb TW_Zero,TAG(%edi)
jmp LDiv_set_result_sign
#ifdef PARANOID
L_unknown_tags:
pushl EX_INTERNAL | 0x208
call EXCEPTION
/* Generate a NaN for unknown tags */
movl _CONST_QNaN,%eax
movl %eax,(%edi)
movl _CONST_QNaN+4,%eax
movl %eax,SIGL(%edi)
movl _CONST_QNaN+8,%eax
movl %eax,SIGH(%edi)
jmp LDiv_exit /* %eax is nz */
#endif PARANOID

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------+
| reg_mul.c |
| |
| Multiply one FPU_REG by another, put the result in a destination FPU_REG. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| The destination may be any FPU_REG, including one of the source FPU_REGs. |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "fpu_system.h"
/* This routine must be called with non-empty source registers */
int reg_mul(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, unsigned int control_w)
{
char saved_sign = dest->sign;
char sign = (a->sign ^ b->sign);
if (!(a->tag | b->tag))
{
/* Both regs Valid, this should be the most common case. */
dest->sign = sign;
if ( reg_u_mul(a, b, dest, control_w) )
{
dest->sign = saved_sign;
return 1;
}
return 0;
}
else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
{
#ifdef DENORM_OPERAND
if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
{
if ( denormal_operand() ) return 1;
}
#endif DENORM_OPERAND
/* Must have either both arguments == zero, or
one valid and the other zero.
The result is therefore zero. */
reg_move(&CONST_Z, dest);
#ifdef PECULIAR_486
/* The 80486 book says that the answer is +0, but a real
80486 appears to behave this way... */
dest->sign = sign;
#endif PECULIAR_486
return 0;
}
else
{
/* Must have infinities, NaNs, etc */
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
{ return real_2op_NaN(a, b, dest); }
else if (a->tag == TW_Infinity)
{
if (b->tag == TW_Zero)
{ return arith_invalid(dest); } /* Zero*Infinity is invalid */
else
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(a, dest);
dest->sign = sign;
}
return 0;
}
else if (b->tag == TW_Infinity)
{
if (a->tag == TW_Zero)
{ return arith_invalid(dest); } /* Zero*Infinity is invalid */
else
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign = sign;
}
return 0;
}
#ifdef PARANOID
else
{
EXCEPTION(EX_INTERNAL|0x102);
return 1;
}
#endif PARANOID
}
}

View file

@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------+
| reg_norm.S |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Normalize the value in a FPU_REG. |
| |
| Call from C as: |
| void normalize(FPU_REG *n) |
| |
| void normalize_nuo(FPU_REG *n) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
.globl _normalize
_normalize:
pushl %ebp
movl %esp,%ebp
pushl %ebx
movl PARAM1,%ebx
#ifdef PARANOID
cmpb TW_Valid,TAG(%ebx)
je L_ok
pushl $0x220
call _exception
addl $4,%esp
L_ok:
#endif PARANOID
movl SIGH(%ebx),%edx
movl SIGL(%ebx),%eax
orl %edx,%edx /* ms bits */
js L_done /* Already normalized */
jnz L_shift_1 /* Shift left 1 - 31 bits */
orl %eax,%eax
jz L_zero /* The contents are zero */
movl %eax,%edx
xorl %eax,%eax
subl $32,EXP(%ebx) /* This can cause an underflow */
/* We need to shift left by 1 - 31 bits */
L_shift_1:
bsrl %edx,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%eax,%edx
shl %cl,%eax
subl %ecx,EXP(%ebx) /* This can cause an underflow */
movl %edx,SIGH(%ebx)
movl %eax,SIGL(%ebx)
L_done:
cmpl EXP_OVER,EXP(%ebx)
jge L_overflow
cmpl EXP_UNDER,EXP(%ebx)
jle L_underflow
L_exit:
popl %ebx
leave
ret
L_zero:
movl EXP_UNDER,EXP(%ebx)
movb TW_Zero,TAG(%ebx)
jmp L_exit
L_underflow:
push %ebx
call _arith_underflow
pop %ebx
jmp L_exit
L_overflow:
push %ebx
call _arith_overflow
pop %ebx
jmp L_exit
/* Normalise without reporting underflow or overflow */
.align 2,144
.globl _normalize_nuo
_normalize_nuo:
pushl %ebp
movl %esp,%ebp
pushl %ebx
movl PARAM1,%ebx
#ifdef PARANOID
cmpb TW_Valid,TAG(%ebx)
je L_ok_nuo
pushl $0x221
call _exception
addl $4,%esp
L_ok_nuo:
#endif PARANOID
movl SIGH(%ebx),%edx
movl SIGL(%ebx),%eax
orl %edx,%edx /* ms bits */
js L_exit /* Already normalized */
jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */
orl %eax,%eax
jz L_zero /* The contents are zero */
movl %eax,%edx
xorl %eax,%eax
subl $32,EXP(%ebx) /* This can cause an underflow */
/* We need to shift left by 1 - 31 bits */
L_nuo_shift_1:
bsrl %edx,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%eax,%edx
shl %cl,%eax
subl %ecx,EXP(%ebx) /* This can cause an underflow */
movl %edx,SIGH(%ebx)
movl %eax,SIGL(%ebx)
jmp L_exit

View file

@ -0,0 +1,666 @@
.file "reg_round.S"
/*---------------------------------------------------------------------------+
| reg_round.S |
| |
| Rounding/truncation/etc for FPU basic arithmetic functions. |
| |
| Copyright (C) 1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| This code has four possible entry points. |
| The following must be entered by a jmp intruction: |
| fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. |
| |
| The _round_reg entry point is intended to be used by C code. |
| From C, call as: |
| void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
| |
| For correct "up" and "down" rounding, the argument must have the correct |
| sign. |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Four entry points. |
| |
| Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: |
| %eax:%ebx 64 bit significand |
| %edx 32 bit extension of the significand |
| %edi pointer to an FPU_REG for the result to be stored |
| stack calling function must have set up a C stack frame and |
| pushed %esi, %edi, and %ebx |
| |
| Needed just for the fpu_reg_round_sqrt entry point: |
| %cx A control word in the same format as the FPU control word. |
| Otherwise, PARAM4 must give such a value. |
| |
| |
| The significand and its extension are assumed to be exact in the |
| following sense: |
| If the significand by itself is the exact result then the significand |
| extension (%edx) must contain 0, otherwise the significand extension |
| must be non-zero. |
| If the significand extension is non-zero then the significand is |
| smaller than the magnitude of the correct exact result by an amount |
| greater than zero and less than one ls bit of the significand. |
| The significand extension is only required to have three possible |
| non-zero values: |
| less than 0x80000000 <=> the significand is less than 1/2 an ls |
| bit smaller than the magnitude of the |
| true exact result. |
| exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
| smaller than the magnitude of the true |
| exact result. |
| greater than 0x80000000 <=> the significand is more than 1/2 an ls |
| bit smaller than the magnitude of the |
| true exact result. |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| The code in this module has become quite complex, but it should handle |
| all of the FPU flags which are set at this stage of the basic arithmetic |
| computations. |
| There are a few rare cases where the results are not set identically to |
| a real FPU. These require a bit more thought because at this stage the |
| results of the code here appear to be more consistent... |
| This may be changed in a future version. |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
#include "exception.h"
#include "control_w.h"
/* Flags for FPU_bits_lost */
#define LOST_DOWN $1
#define LOST_UP $2
/* Flags for FPU_denormal */
#define DENORMAL $1
#define UNMASKED_UNDERFLOW $2
#ifdef REENTRANT_FPU
/* Make the code re-entrant by putting
local storage on the stack: */
#define FPU_bits_lost (%esp)
#define FPU_denormal 1(%esp)
#else
/* Not re-entrant, so we can gain speed by putting
local storage in a static area: */
.data
.align 2,0
FPU_bits_lost:
.byte 0
FPU_denormal:
.byte 0
#endif REENTRANT_FPU
.text
.align 2,144
.globl fpu_reg_round
.globl fpu_reg_round_sqrt
.globl fpu_Arith_exit
.globl _round_reg
/* Entry point when called from C */
_round_reg:
pushl %ebp
movl %esp,%ebp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%edi
movl SIGH(%edi),%eax
movl SIGL(%edi),%ebx
movl PARAM2,%edx
movl PARAM3,%ecx
jmp fpu_reg_round_sqrt
fpu_reg_round: /* Normal entry point */
movl PARAM4,%ecx
fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */
#ifdef REENTRANT_FPU
pushl %ebx /* adjust the stack pointer */
#endif REENTRANT_FPU
#ifdef PARANOID
/* Cannot use this here yet */
/* orl %eax,%eax */
/* jns L_entry_bugged */
#endif PARANOID
cmpl EXP_UNDER,EXP(%edi)
jle xMake_denorm /* The number is a de-normal */
movb $0,FPU_denormal /* 0 -> not a de-normal */
xDenorm_done:
movb $0,FPU_bits_lost /* No bits yet lost in rounding */
movl %ecx,%esi
andl CW_PC,%ecx
cmpl PR_64_BITS,%ecx
je LRound_To_64
cmpl PR_53_BITS,%ecx
je LRound_To_53
cmpl PR_24_BITS,%ecx
je LRound_To_24
#ifdef PARANOID
jmp L_bugged /* There is no bug, just a bad control word */
#endif PARANOID
/* Round etc to 24 bit precision */
LRound_To_24:
movl %esi,%ecx
andl CW_RC,%ecx
cmpl RC_RND,%ecx
je LRound_nearest_24
cmpl RC_CHOP,%ecx
je LCheck_truncate_24
cmpl RC_UP,%ecx /* Towards +infinity */
je LUp_24
cmpl RC_DOWN,%ecx /* Towards -infinity */
je LDown_24
#ifdef PARANOID
jmp L_bugged
#endif PARANOID
LUp_24:
cmpb SIGN_POS,SIGN(%edi)
jne LCheck_truncate_24 /* If negative then up==truncate */
jmp LCheck_24_round_up
LDown_24:
cmpb SIGN_POS,SIGN(%edi)
je LCheck_truncate_24 /* If positive then down==truncate */
LCheck_24_round_up:
movl %eax,%ecx
andl $0x000000ff,%ecx
orl %ebx,%ecx
orl %edx,%ecx
jnz LDo_24_round_up
jmp LRe_normalise
LRound_nearest_24:
/* Do rounding of the 24th bit if needed (nearest or even) */
movl %eax,%ecx
andl $0x000000ff,%ecx
cmpl $0x00000080,%ecx
jc LCheck_truncate_24 /* less than half, no increment needed */
jne LGreater_Half_24 /* greater than half, increment needed */
/* Possibly half, we need to check the ls bits */
orl %ebx,%ebx
jnz LGreater_Half_24 /* greater than half, increment needed */
orl %edx,%edx
jnz LGreater_Half_24 /* greater than half, increment needed */
/* Exactly half, increment only if 24th bit is 1 (round to even) */
testl $0x00000100,%eax
jz LDo_truncate_24
LGreater_Half_24: /* Rounding: increment at the 24th bit */
LDo_24_round_up:
andl $0xffffff00,%eax /* Truncate to 24 bits */
xorl %ebx,%ebx
movb LOST_UP,FPU_bits_lost
addl $0x00000100,%eax
jmp LCheck_Round_Overflow
LCheck_truncate_24:
movl %eax,%ecx
andl $0x000000ff,%ecx
orl %ebx,%ecx
orl %edx,%ecx
jz LRe_normalise /* No truncation needed */
LDo_truncate_24:
andl $0xffffff00,%eax /* Truncate to 24 bits */
xorl %ebx,%ebx
movb LOST_DOWN,FPU_bits_lost
jmp LRe_normalise
/* Round etc to 53 bit precision */
LRound_To_53:
movl %esi,%ecx
andl CW_RC,%ecx
cmpl RC_RND,%ecx
je LRound_nearest_53
cmpl RC_CHOP,%ecx
je LCheck_truncate_53
cmpl RC_UP,%ecx /* Towards +infinity */
je LUp_53
cmpl RC_DOWN,%ecx /* Towards -infinity */
je LDown_53
#ifdef PARANOID
jmp L_bugged
#endif PARANOID
LUp_53:
cmpb SIGN_POS,SIGN(%edi)
jne LCheck_truncate_53 /* If negative then up==truncate */
jmp LCheck_53_round_up
LDown_53:
cmpb SIGN_POS,SIGN(%edi)
je LCheck_truncate_53 /* If positive then down==truncate */
LCheck_53_round_up:
movl %ebx,%ecx
andl $0x000007ff,%ecx
orl %edx,%ecx
jnz LDo_53_round_up
jmp LRe_normalise
LRound_nearest_53:
/* Do rounding of the 53rd bit if needed (nearest or even) */
movl %ebx,%ecx
andl $0x000007ff,%ecx
cmpl $0x00000400,%ecx
jc LCheck_truncate_53 /* less than half, no increment needed */
jnz LGreater_Half_53 /* greater than half, increment needed */
/* Possibly half, we need to check the ls bits */
orl %edx,%edx
jnz LGreater_Half_53 /* greater than half, increment needed */
/* Exactly half, increment only if 53rd bit is 1 (round to even) */
testl $0x00000800,%ebx
jz LTruncate_53
LGreater_Half_53: /* Rounding: increment at the 53rd bit */
LDo_53_round_up:
movb LOST_UP,FPU_bits_lost
andl $0xfffff800,%ebx /* Truncate to 53 bits */
addl $0x00000800,%ebx
adcl $0,%eax
jmp LCheck_Round_Overflow
LCheck_truncate_53:
movl %ebx,%ecx
andl $0x000007ff,%ecx
orl %edx,%ecx
jz LRe_normalise
LTruncate_53:
movb LOST_DOWN,FPU_bits_lost
andl $0xfffff800,%ebx /* Truncate to 53 bits */
jmp LRe_normalise
/* Round etc to 64 bit precision */
LRound_To_64:
movl %esi,%ecx
andl CW_RC,%ecx
cmpl RC_RND,%ecx
je LRound_nearest_64
cmpl RC_CHOP,%ecx
je LCheck_truncate_64
cmpl RC_UP,%ecx /* Towards +infinity */
je LUp_64
cmpl RC_DOWN,%ecx /* Towards -infinity */
je LDown_64
#ifdef PARANOID
jmp L_bugged
#endif PARANOID
LUp_64:
cmpb SIGN_POS,SIGN(%edi)
jne LCheck_truncate_64 /* If negative then up==truncate */
orl %edx,%edx
jnz LDo_64_round_up
jmp LRe_normalise
LDown_64:
cmpb SIGN_POS,SIGN(%edi)
je LCheck_truncate_64 /* If positive then down==truncate */
orl %edx,%edx
jnz LDo_64_round_up
jmp LRe_normalise
LRound_nearest_64:
cmpl $0x80000000,%edx
jc LCheck_truncate_64
jne LDo_64_round_up
/* Now test for round-to-even */
testb $1,%ebx
jz LCheck_truncate_64
LDo_64_round_up:
movb LOST_UP,FPU_bits_lost
addl $1,%ebx
adcl $0,%eax
LCheck_Round_Overflow:
jnc LRe_normalise
/* Overflow, adjust the result (significand to 1.0) */
rcrl $1,%eax
rcrl $1,%ebx
incl EXP(%edi)
jmp LRe_normalise
LCheck_truncate_64:
orl %edx,%edx
jz LRe_normalise
LTruncate_64:
movb LOST_DOWN,FPU_bits_lost
LRe_normalise:
testb $0xff,FPU_denormal
jnz xNormalise_result
xL_Normalised:
cmpb LOST_UP,FPU_bits_lost
je xL_precision_lost_up
cmpb LOST_DOWN,FPU_bits_lost
je xL_precision_lost_down
xL_no_precision_loss:
/* store the result */
movb TW_Valid,TAG(%edi)
xL_Store_significand:
movl %eax,SIGH(%edi)
movl %ebx,SIGL(%edi)
xorl %eax,%eax /* No errors detected. */
cmpl EXP_OVER,EXP(%edi)
jge L_overflow
fpu_reg_round_exit:
#ifdef REENTRANT_FPU
popl %ebx /* adjust the stack pointer */
#endif REENTRANT_FPU
fpu_Arith_exit:
popl %ebx
popl %edi
popl %esi
leave
ret
/*
* Set the FPU status flags to represent precision loss due to
* round-up.
*/
xL_precision_lost_up:
push %eax
call _set_precision_flag_up
popl %eax
jmp xL_no_precision_loss
/*
* Set the FPU status flags to represent precision loss due to
* truncation.
*/
xL_precision_lost_down:
push %eax
call _set_precision_flag_down
popl %eax
jmp xL_no_precision_loss
/*
* The number is a denormal (which might get rounded up to a normal)
* Shift the number right the required number of bits, which will
* have to be undone later...
*/
xMake_denorm:
/* The action to be taken depends upon whether the underflow
exception is masked */
testb CW_Underflow,%cl /* Underflow mask. */
jz xUnmasked_underflow /* Do not make a denormal. */
movb DENORMAL,FPU_denormal
pushl %ecx /* Save */
movl EXP_UNDER+1,%ecx
subl EXP(%edi),%ecx
cmpl $64,%ecx /* shrd only works for 0..31 bits */
jnc xDenorm_shift_more_than_63
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jnc xDenorm_shift_more_than_32
/*
* We got here without jumps by assuming that the most common requirement
* is for a small de-normalising shift.
* Shift by [1..31] bits
*/
addl %ecx,EXP(%edi)
orl %edx,%edx /* extension */
setne %ch /* Save whether %edx is non-zero */
xorl %edx,%edx
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
orb %ch,%dl
popl %ecx
jmp xDenorm_done
/* Shift by [32..63] bits */
xDenorm_shift_more_than_32:
addl %ecx,EXP(%edi)
subb $32,%cl
orl %edx,%edx
setne %ch
orb %ch,%bl
xorl %edx,%edx
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
orl %edx,%edx /* test these 32 bits */
setne %cl
orb %ch,%bl
orb %cl,%bl
movl %ebx,%edx
movl %eax,%ebx
xorl %eax,%eax
popl %ecx
jmp xDenorm_done
/* Shift by [64..) bits */
xDenorm_shift_more_than_63:
cmpl $64,%ecx
jne xDenorm_shift_more_than_64
/* Exactly 64 bit shift */
addl %ecx,EXP(%edi)
xorl %ecx,%ecx
orl %edx,%edx
setne %cl
orl %ebx,%ebx
setne %ch
orb %ch,%cl
orb %cl,%al
movl %eax,%edx
xorl %eax,%eax
xorl %ebx,%ebx
popl %ecx
jmp xDenorm_done
xDenorm_shift_more_than_64:
movl EXP_UNDER+1,EXP(%edi)
/* This is easy, %eax must be non-zero, so.. */
movl $1,%edx
xorl %eax,%eax
xorl %ebx,%ebx
popl %ecx
jmp xDenorm_done
xUnmasked_underflow:
movb UNMASKED_UNDERFLOW,FPU_denormal
jmp xDenorm_done
/* Undo the de-normalisation. */
xNormalise_result:
cmpb UNMASKED_UNDERFLOW,FPU_denormal
je xSignal_underflow
/* The number must be a denormal if we got here. */
#ifdef PARANOID
/* But check it... just in case. */
cmpl EXP_UNDER+1,EXP(%edi)
jne L_norm_bugged
#endif PARANOID
#ifdef PECULIAR_486
/*
* This implements a special feature of 80486 behaviour.
* Underflow will be signalled even if the number is
* not a denormal after rounding.
* This difference occurs only for masked underflow, and not
* in the unmasked case.
* Actual 80486 behaviour differs from this in some circumstances.
*/
orl %eax,%eax /* ms bits */
js LNormalise_shift_done /* Will be masked underflow */
#endif PECULIAR_486
orl %eax,%eax /* ms bits */
js xL_Normalised /* No longer a denormal */
jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits */
orl %ebx,%ebx
jz L_underflow_to_zero /* The contents are zero */
/* Shift left 32 - 63 bits */
movl %ebx,%eax
xorl %ebx,%ebx
subl $32,EXP(%edi)
LNormalise_shift_up_to_31:
bsrl %eax,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%ebx,%eax
shl %cl,%ebx
subl %ecx,EXP(%edi)
LNormalise_shift_done:
testb $0xff,FPU_bits_lost /* bits lost == underflow */
jz xL_Normalised
/* There must be a masked underflow */
push %eax
pushl EX_Underflow
call _exception
popl %eax
popl %eax
jmp xL_Normalised
/*
* The operations resulted in a number too small to represent.
* Masked response.
*/
L_underflow_to_zero:
push %eax
call _set_precision_flag_down
popl %eax
push %eax
pushl EX_Underflow
call _exception
popl %eax
popl %eax
/* Reduce the exponent to EXP_UNDER */
movl EXP_UNDER,EXP(%edi)
movb TW_Zero,TAG(%edi)
jmp xL_Store_significand
/* The operations resulted in a number too large to represent. */
L_overflow:
push %edi
call _arith_overflow
pop %edi
jmp fpu_reg_round_exit
xSignal_underflow:
/* The number may have been changed to a non-denormal */
/* by the rounding operations. */
cmpl EXP_UNDER,EXP(%edi)
jle xDo_unmasked_underflow
jmp xL_Normalised
xDo_unmasked_underflow:
/* Increase the exponent by the magic number */
addl $(3*(1<<13)),EXP(%edi)
push %eax
pushl EX_Underflow
call EXCEPTION
popl %eax
popl %eax
jmp xL_Normalised
#ifdef PARANOID
/* If we ever get here then we have problems! */
L_bugged:
pushl EX_INTERNAL|0x201
call EXCEPTION
popl %ebx
jmp L_exception_exit
L_norm_bugged:
pushl EX_INTERNAL|0x216
call EXCEPTION
popl %ebx
jmp L_exception_exit
L_entry_bugged:
pushl EX_INTERNAL|0x217
call EXCEPTION
popl %ebx
L_exception_exit:
mov $1,%eax
jmp fpu_reg_round_exit
#endif PARANOID

View file

@ -0,0 +1,189 @@
.file "reg_u_add.S"
/*---------------------------------------------------------------------------+
| reg_u_add.S |
| |
| Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the |
| result in a destination FPU_REG. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
| int control_w) |
| |
+---------------------------------------------------------------------------*/
/*
| Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ).
| Takes two valid reg f.p. numbers (TW_Valid), which are
| treated as unsigned numbers,
| and returns their sum as a TW_Valid or TW_S f.p. number.
| The returned number is normalized.
| Basic checks are performed if PARANOID is defined.
*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
.text
.align 2,144
.globl _reg_u_add
_reg_u_add:
pushl %ebp
movl %esp,%ebp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi /* source 1 */
movl PARAM2,%edi /* source 2 */
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%esi)
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
cmpl EXP_UNDER,EXP(%edi)
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
movl EXP(%esi),%ecx
subl EXP(%edi),%ecx /* exp1 - exp2 */
jge L_arg1_larger
/* num1 is smaller */
movl SIGL(%esi),%ebx
movl SIGH(%esi),%eax
movl %edi,%esi
negw %cx
jmp L_accum_loaded
L_arg1_larger:
/* num1 has larger or equal exponent */
movl SIGL(%edi),%ebx
movl SIGH(%edi),%eax
L_accum_loaded:
movl PARAM3,%edi /* destination */
/* movb SIGN(%esi),%dl
movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
movl EXP(%esi),%edx
movl %edx,EXP(%edi) /* Copy exponent to destination */
xorl %edx,%edx /* clear the extension */
#ifdef PARANOID
testl $0x80000000,%eax
je L_bugged
testl $0x80000000,SIGH(%esi)
je L_bugged
#endif PARANOID
/* The number to be shifted is in %eax:%ebx:%edx */
cmpw $32,%cx /* shrd only works for 0..31 bits */
jnc L_more_than_31
/* less than 32 bits */
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
jmp L_shift_done
L_more_than_31:
cmpw $64,%cx
jnc L_more_than_63
subb $32,%cl
jz L_exactly_32
shrd %cl,%eax,%edx
shr %cl,%eax
orl %ebx,%ebx
jz L_more_31_no_low /* none of the lowest bits is set */
orl $1,%edx /* record the fact in the extension */
L_more_31_no_low:
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_exactly_32:
movl %ebx,%edx
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_more_than_63:
cmpw $65,%cx
jnc L_more_than_64
movl %eax,%edx
orl %ebx,%ebx
jz L_more_63_no_low
orl $1,%edx
jmp L_more_63_no_low
L_more_than_64:
movl $1,%edx /* The shifted nr always at least one '1' */
L_more_63_no_low:
xorl %ebx,%ebx
xorl %eax,%eax
L_shift_done:
/* Now do the addition */
addl SIGL(%esi),%ebx
adcl SIGH(%esi),%eax
jnc L_round_the_result
/* Overflow, adjust the result */
rcrl $1,%eax
rcrl $1,%ebx
rcrl $1,%edx
jnc L_no_bit_lost
orl $1,%edx
L_no_bit_lost:
incl EXP(%edi)
L_round_the_result:
jmp fpu_reg_round /* Round the result */
#ifdef PARANOID
/* If we ever get here then we have problems! */
L_bugged:
pushl EX_INTERNAL|0x201
call EXCEPTION
pop %ebx
jmp L_exit
#endif PARANOID
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret

View file

@ -0,0 +1,477 @@
.file "reg_u_div.S"
/*---------------------------------------------------------------------------+
| reg_u_div.S |
| |
| Core division routines |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Kernel for the division routines. |
| |
| void reg_u_div(FPU_REG *a, FPU_REG *a, |
| FPU_REG *dest, unsigned int control_word) |
| |
| Does not compute the destination exponent, but does adjust it. |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
/* #define dSIGL(x) (x) */
/* #define dSIGH(x) 4(x) */
#ifdef REENTRANT_FPU
/*
Local storage on the stack:
Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
Overflow flag: ovfl_flag
*/
#define FPU_accum_3 -4(%ebp)
#define FPU_accum_2 -8(%ebp)
#define FPU_accum_1 -12(%ebp)
#define FPU_accum_0 -16(%ebp)
#define FPU_result_1 -20(%ebp)
#define FPU_result_2 -24(%ebp)
#define FPU_ovfl_flag -28(%ebp)
#else
.data
/*
Local storage in a static area:
Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
Overflow flag: ovfl_flag
*/
.align 2,0
FPU_accum_3:
.long 0
FPU_accum_2:
.long 0
FPU_accum_1:
.long 0
FPU_accum_0:
.long 0
FPU_result_1:
.long 0
FPU_result_2:
.long 0
FPU_ovfl_flag:
.byte 0
#endif REENTRANT_FPU
.text
.align 2,144
.globl _reg_u_div
.globl _divide_kernel
_reg_u_div:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $28,%esp
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi /* pointer to num */
movl PARAM2,%ebx /* pointer to denom */
movl PARAM3,%edi /* pointer to answer */
#ifdef DENORM_OPERAND
movl EXP(%esi),%eax
cmpl EXP_UNDER,%eax
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
movl EXP(%ebx),%eax
cmpl EXP_UNDER,%eax
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
_divide_kernel:
#ifdef PARANOID
/* testl $0x80000000, SIGH(%esi) // Dividend */
/* je L_bugged */
testl $0x80000000, SIGH(%ebx) /* Divisor */
je L_bugged
#endif PARANOID
/* Check if the divisor can be treated as having just 32 bits */
cmpl $0,SIGL(%ebx)
jnz L_Full_Division /* Can't do a quick divide */
/* We should be able to zip through the division here */
movl SIGH(%ebx),%ecx /* The divisor */
movl SIGH(%esi),%edx /* Dividend */
movl SIGL(%esi),%eax /* Dividend */
cmpl %ecx,%edx
setaeb FPU_ovfl_flag /* Keep a record */
jb L_no_adjust
subl %ecx,%edx /* Prevent the overflow */
L_no_adjust:
/* Divide the 64 bit number by the 32 bit denominator */
divl %ecx
movl %eax,FPU_result_2
/* Work on the remainder of the first division */
xorl %eax,%eax
divl %ecx
movl %eax,FPU_result_1
/* Work on the remainder of the 64 bit division */
xorl %eax,%eax
divl %ecx
testb $255,FPU_ovfl_flag /* was the num > denom ? */
je L_no_overflow
/* Do the shifting here */
/* increase the exponent */
incl EXP(%edi)
/* shift the mantissa right one bit */
stc /* To set the ms bit */
rcrl FPU_result_2
rcrl FPU_result_1
rcrl %eax
L_no_overflow:
jmp LRound_precision /* Do the rounding as required */
/*---------------------------------------------------------------------------+
| Divide: Return arg1/arg2 to arg3. |
| |
| This routine does not use the exponents of arg1 and arg2, but does |
| adjust the exponent of arg3. |
| |
| The maximum returned value is (ignoring exponents) |
| .ffffffff ffffffff |
| ------------------ = 1.ffffffff fffffffe |
| .80000000 00000000 |
| and the minimum is |
| .80000000 00000000 |
| ------------------ = .80000000 00000001 (rounded) |
| .ffffffff ffffffff |
| |
+---------------------------------------------------------------------------*/
L_Full_Division:
/* Save extended dividend in local register */
movl SIGL(%esi),%eax
movl %eax,FPU_accum_2
movl SIGH(%esi),%eax
movl %eax,FPU_accum_3
xorl %eax,%eax
movl %eax,FPU_accum_1 /* zero the extension */
movl %eax,FPU_accum_0 /* zero the extension */
movl SIGL(%esi),%eax /* Get the current num */
movl SIGH(%esi),%edx
/*----------------------------------------------------------------------*/
/* Initialization done.
Do the first 32 bits. */
movb $0,FPU_ovfl_flag
cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
jb LLess_than_1
ja LGreater_than_1
cmpl SIGL(%ebx),%eax
jb LLess_than_1
LGreater_than_1:
/* The dividend is greater or equal, would cause overflow */
setaeb FPU_ovfl_flag /* Keep a record */
subl SIGL(%ebx),%eax
sbbl SIGH(%ebx),%edx /* Prevent the overflow */
movl %eax,FPU_accum_2
movl %edx,FPU_accum_3
LLess_than_1:
/* At this point, we have a dividend < divisor, with a record of
adjustment in FPU_ovfl_flag */
/* We will divide by a number which is too large */
movl SIGH(%ebx),%ecx
addl $1,%ecx
jnc LFirst_div_not_1
/* here we need to divide by 100000000h,
i.e., no division at all.. */
mov %edx,%eax
jmp LFirst_div_done
LFirst_div_not_1:
divl %ecx /* Divide the numerator by the augmented
denom ms dw */
LFirst_div_done:
movl %eax,FPU_result_2 /* Put the result in the answer */
mull SIGH(%ebx) /* mul by the ms dw of the denom */
subl %eax,FPU_accum_2 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_3
movl FPU_result_2,%eax /* Get the result back */
mull SIGL(%ebx) /* now mul the ls dw of the denom */
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
sbbl $0,FPU_accum_3
je LDo_2nd_32_bits /* Must check for non-zero result here */
#ifdef PARANOID
jb L_bugged_1
#endif PARANOID
/* need to subtract another once of the denom */
incl FPU_result_2 /* Correct the answer */
movl SIGL(%ebx),%eax
movl SIGH(%ebx),%edx
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
#ifdef PARANOID
sbbl $0,FPU_accum_3
jne L_bugged_1 /* Must check for non-zero result here */
#endif PARANOID
/*----------------------------------------------------------------------*/
/* Half of the main problem is done, there is just a reduced numerator
to handle now.
Work with the second 32 bits, FPU_accum_0 not used from now on */
LDo_2nd_32_bits:
movl FPU_accum_2,%edx /* get the reduced num */
movl FPU_accum_1,%eax
/* need to check for possible subsequent overflow */
cmpl SIGH(%ebx),%edx
jb LDo_2nd_div
ja LPrevent_2nd_overflow
cmpl SIGL(%ebx),%eax
jb LDo_2nd_div
LPrevent_2nd_overflow:
/* The numerator is greater or equal, would cause overflow */
/* prevent overflow */
subl SIGL(%ebx),%eax
sbbl SIGH(%ebx),%edx
movl %edx,FPU_accum_2
movl %eax,FPU_accum_1
incl FPU_result_2 /* Reflect the subtraction in the answer */
#ifdef PARANOID
je L_bugged_2 /* Can't bump the result to 1.0 */
#endif PARANOID
LDo_2nd_div:
cmpl $0,%ecx /* augmented denom msw */
jnz LSecond_div_not_1
/* %ecx == 0, we are dividing by 1.0 */
mov %edx,%eax
jmp LSecond_div_done
LSecond_div_not_1:
divl %ecx /* Divide the numerator by the denom ms dw */
LSecond_div_done:
movl %eax,FPU_result_1 /* Put the result in the answer */
mull SIGH(%ebx) /* mul by the ms dw of the denom */
subl %eax,FPU_accum_1 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
#endif PARANOID
movl FPU_result_1,%eax /* Get the result back */
mull SIGL(%ebx) /* now mul the ls dw of the denom */
subl %eax,FPU_accum_0 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */
sbbl $0,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
#endif PARANOID
jz LDo_3rd_32_bits
#ifdef PARANOID
cmpl $1,FPU_accum_2
jne L_bugged_2
#endif PARANOID
/* need to subtract another once of the denom */
movl SIGL(%ebx),%eax
movl SIGH(%ebx),%edx
subl %eax,FPU_accum_0 /* Subtract from the num local reg */
sbbl %edx,FPU_accum_1
sbbl $0,FPU_accum_2
#ifdef PARANOID
jc L_bugged_2
jne L_bugged_2
#endif PARANOID
addl $1,FPU_result_1 /* Correct the answer */
adcl $0,FPU_result_2
#ifdef PARANOID
jc L_bugged_2 /* Must check for non-zero result here */
#endif PARANOID
/*----------------------------------------------------------------------*/
/* The division is essentially finished here, we just need to perform
tidying operations.
Deal with the 3rd 32 bits */
LDo_3rd_32_bits:
movl FPU_accum_1,%edx /* get the reduced num */
movl FPU_accum_0,%eax
/* need to check for possible subsequent overflow */
cmpl SIGH(%ebx),%edx /* denom */
jb LRound_prep
ja LPrevent_3rd_overflow
cmpl SIGL(%ebx),%eax /* denom */
jb LRound_prep
LPrevent_3rd_overflow:
/* prevent overflow */
subl SIGL(%ebx),%eax
sbbl SIGH(%ebx),%edx
movl %edx,FPU_accum_1
movl %eax,FPU_accum_0
addl $1,FPU_result_1 /* Reflect the subtraction in the answer */
adcl $0,FPU_result_2
jne LRound_prep
jnc LRound_prep
/* This is a tricky spot, there is an overflow of the answer */
movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */
LRound_prep:
/*
* Prepare for rounding.
* To test for rounding, we just need to compare 2*accum with the
* denom.
*/
movl FPU_accum_0,%ecx
movl FPU_accum_1,%edx
movl %ecx,%eax
orl %edx,%eax
jz LRound_ovfl /* The accumulator contains zero. */
/* Multiply by 2 */
clc
rcll $1,%ecx
rcll $1,%edx
jc LRound_large /* No need to compare, denom smaller */
subl SIGL(%ebx),%ecx
sbbl SIGH(%ebx),%edx
jnc LRound_not_small
movl $0x70000000,%eax /* Denom was larger */
jmp LRound_ovfl
LRound_not_small:
jnz LRound_large
movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
jmp LRound_ovfl
LRound_large:
movl $0xff000000,%eax /* Denom was smaller */
LRound_ovfl:
/* We are now ready to deal with rounding, but first we must get
the bits properly aligned */
testb $255,FPU_ovfl_flag /* was the num > denom ? */
je LRound_precision
incl EXP(%edi)
/* shift the mantissa right one bit */
stc /* Will set the ms bit */
rcrl FPU_result_2
rcrl FPU_result_1
rcrl %eax
/* Round the result as required */
LRound_precision:
decl EXP(%edi) /* binary point between 1st & 2nd bits */
movl %eax,%edx
movl FPU_result_1,%ebx
movl FPU_result_2,%eax
jmp fpu_reg_round
#ifdef PARANOID
/* The logic is wrong if we got here */
L_bugged:
pushl EX_INTERNAL|0x202
call EXCEPTION
pop %ebx
jmp L_exit
L_bugged_1:
pushl EX_INTERNAL|0x203
call EXCEPTION
pop %ebx
jmp L_exit
L_bugged_2:
pushl EX_INTERNAL|0x204
call EXCEPTION
pop %ebx
jmp L_exit
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret
#endif PARANOID

View file

@ -0,0 +1,163 @@
.file "reg_u_mul.S"
/*---------------------------------------------------------------------------+
| reg_u_mul.S |
| |
| Core multiplication routine |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Basic multiplication routine. |
| Does not check the resulting exponent for overflow/underflow |
| |
| reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); |
| |
| Internal working is at approx 128 bits. |
| Result is rounded to nearest 53 or 64 bits, using "nearest or even". |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
#ifdef REENTRANT_FPU
/* Local storage on the stack: */
#define FPU_accum_0 -4(%ebp) /* ms word */
#define FPU_accum_1 -8(%ebp)
#else
/* Local storage in a static area: */
.data
.align 4,0
FPU_accum_0:
.long 0
FPU_accum_1:
.long 0
#endif REENTRANT_FPU
.text
.align 2,144
.globl _reg_u_mul
_reg_u_mul:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $8,%esp
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi
movl PARAM2,%edi
#ifdef PARANOID
testl $0x80000000,SIGH(%esi)
jz L_bugged
testl $0x80000000,SIGH(%edi)
jz L_bugged
#endif PARANOID
#ifdef DENORM_OPERAND
movl EXP(%esi),%eax
cmpl EXP_UNDER,%eax
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
movl EXP(%edi),%eax
cmpl EXP_UNDER,%eax
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
xorl %ecx,%ecx
xorl %ebx,%ebx
movl SIGL(%esi),%eax
mull SIGL(%edi)
movl %eax,FPU_accum_0
movl %edx,FPU_accum_1
movl SIGL(%esi),%eax
mull SIGH(%edi)
addl %eax,FPU_accum_1
adcl %edx,%ebx
/* adcl $0,%ecx // overflow here is not possible */
movl SIGH(%esi),%eax
mull SIGL(%edi)
addl %eax,FPU_accum_1
adcl %edx,%ebx
adcl $0,%ecx
movl SIGH(%esi),%eax
mull SIGH(%edi)
addl %eax,%ebx
adcl %edx,%ecx
movl EXP(%esi),%eax /* Compute the exponent */
addl EXP(%edi),%eax
subl EXP_BIAS-1,%eax
/* Have now finished with the sources */
movl PARAM3,%edi /* Point to the destination */
movl %eax,EXP(%edi)
/* Now make sure that the result is normalized */
testl $0x80000000,%ecx
jnz LResult_Normalised
/* Normalize by shifting left one bit */
shll $1,FPU_accum_0
rcll $1,FPU_accum_1
rcll $1,%ebx
rcll $1,%ecx
decl EXP(%edi)
LResult_Normalised:
movl FPU_accum_0,%eax
movl FPU_accum_1,%edx
orl %eax,%eax
jz L_extent_zero
orl $1,%edx
L_extent_zero:
movl %ecx,%eax
jmp fpu_reg_round
#ifdef PARANOID
L_bugged:
pushl EX_INTERNAL|0x205
call EXCEPTION
pop %ebx
jmp L_exit
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret
#endif PARANOID

View file

@ -0,0 +1,292 @@
.file "reg_u_sub.S"
/*---------------------------------------------------------------------------+
| reg_u_sub.S |
| |
| Core floating point subtraction routine. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
| int control_w) |
| |
+---------------------------------------------------------------------------*/
/*
| Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ).
| Takes two valid reg f.p. numbers (TW_Valid), which are
| treated as unsigned numbers,
| and returns their difference as a TW_Valid or TW_Zero f.p.
| number.
| The first number (arg1) must be the larger.
| The returned number is normalized.
| Basic checks are performed if PARANOID is defined.
*/
#include "exception.h"
#include "fpu_asm.h"
#include "control_w.h"
.text
.align 2,144
.globl _reg_u_sub
_reg_u_sub:
pushl %ebp
movl %esp,%ebp
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi /* source 1 */
movl PARAM2,%edi /* source 2 */
#ifdef DENORM_OPERAND
cmpl EXP_UNDER,EXP(%esi)
jg xOp1_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp1_not_denorm:
cmpl EXP_UNDER,EXP(%edi)
jg xOp2_not_denorm
call _denormal_operand
orl %eax,%eax
jnz fpu_Arith_exit
xOp2_not_denorm:
#endif DENORM_OPERAND
movl EXP(%esi),%ecx
subl EXP(%edi),%ecx /* exp1 - exp2 */
#ifdef PARANOID
/* source 2 is always smaller than source 1 */
js L_bugged_1
testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */
je L_bugged_2
testl $0x80000000,SIGH(%esi)
je L_bugged_2
#endif PARANOID
/*--------------------------------------+
| Form a register holding the |
| smaller number |
+--------------------------------------*/
movl SIGH(%edi),%eax /* register ms word */
movl SIGL(%edi),%ebx /* register ls word */
movl PARAM3,%edi /* destination */
movl EXP(%esi),%edx
movl %edx,EXP(%edi) /* Copy exponent to destination */
/* movb SIGN(%esi),%dl
movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
xorl %edx,%edx /* register extension */
/*--------------------------------------+
| Shift the temporary register |
| right the required number of |
| places. |
+--------------------------------------*/
L_shift_r:
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jnc L_more_than_31
/* less than 32 bits */
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
jmp L_shift_done
L_more_than_31:
cmpl $64,%ecx
jnc L_more_than_63
subb $32,%cl
jz L_exactly_32
shrd %cl,%eax,%edx
shr %cl,%eax
orl %ebx,%ebx
jz L_more_31_no_low /* none of the lowest bits is set */
orl $1,%edx /* record the fact in the extension */
L_more_31_no_low:
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_exactly_32:
movl %ebx,%edx
movl %eax,%ebx
xorl %eax,%eax
jmp L_shift_done
L_more_than_63:
cmpw $65,%cx
jnc L_more_than_64
/* Shift right by 64 bits */
movl %eax,%edx
orl %ebx,%ebx
jz L_more_63_no_low
orl $1,%edx
jmp L_more_63_no_low
L_more_than_64:
jne L_more_than_65
/* Shift right by 65 bits */
/* Carry is clear if we get here */
movl %eax,%edx
rcrl %edx
jnc L_shift_65_nc
orl $1,%edx
jmp L_more_63_no_low
L_shift_65_nc:
orl %ebx,%ebx
jz L_more_63_no_low
orl $1,%edx
jmp L_more_63_no_low
L_more_than_65:
movl $1,%edx /* The shifted nr always at least one '1' */
L_more_63_no_low:
xorl %ebx,%ebx
xorl %eax,%eax
L_shift_done:
L_subtr:
/*------------------------------+
| Do the subtraction |
+------------------------------*/
xorl %ecx,%ecx
subl %edx,%ecx
movl %ecx,%edx
movl SIGL(%esi),%ecx
sbbl %ebx,%ecx
movl %ecx,%ebx
movl SIGH(%esi),%ecx
sbbl %eax,%ecx
movl %ecx,%eax
#ifdef PARANOID
/* We can never get a borrow */
jc L_bugged
#endif PARANOID
/*--------------------------------------+
| Normalize the result |
+--------------------------------------*/
testl $0x80000000,%eax
jnz L_round /* no shifting needed */
orl %eax,%eax
jnz L_shift_1 /* shift left 1 - 31 bits */
orl %ebx,%ebx
jnz L_shift_32 /* shift left 32 - 63 bits */
/*
* A rare case, the only one which is non-zero if we got here
* is: 1000000 .... 0000
* -0111111 .... 1111 1
* --------------------
* 0000000 .... 0000 1
*/
cmpl $0x80000000,%edx
jnz L_must_be_zero
/* Shift left 64 bits */
subl $64,EXP(%edi)
xchg %edx,%eax
jmp fpu_reg_round
L_must_be_zero:
#ifdef PARANOID
orl %edx,%edx
jnz L_bugged_3
#endif PARANOID
/* The result is zero */
movb TW_Zero,TAG(%edi)
movl $0,EXP(%edi) /* exponent */
movl $0,SIGL(%edi)
movl $0,SIGH(%edi)
jmp L_exit /* %eax contains zero */
L_shift_32:
movl %ebx,%eax
movl %edx,%ebx
movl $0,%edx
subl $32,EXP(%edi) /* Can get underflow here */
/* We need to shift left by 1 - 31 bits */
L_shift_1:
bsrl %eax,%ecx /* get the required shift in %ecx */
subl $31,%ecx
negl %ecx
shld %cl,%ebx,%eax
shld %cl,%edx,%ebx
shl %cl,%edx
subl %ecx,EXP(%edi) /* Can get underflow here */
L_round:
jmp fpu_reg_round /* Round the result */
#ifdef PARANOID
L_bugged_1:
pushl EX_INTERNAL|0x206
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged_2:
pushl EX_INTERNAL|0x209
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged_3:
pushl EX_INTERNAL|0x210
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged_4:
pushl EX_INTERNAL|0x211
call EXCEPTION
pop %ebx
jmp L_error_exit
L_bugged:
pushl EX_INTERNAL|0x212
call EXCEPTION
pop %ebx
jmp L_error_exit
#endif PARANOID
L_error_exit:
movl $1,%eax
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret

View file

@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------+
| status_w.h |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
#ifndef _STATUS_H_
#define _STATUS_H_
#include "fpu_emu.h" /* for definition of PECULIAR_486 */
#ifdef __ASSEMBLER__
#define Const__(x) $##x
#else
#define Const__(x) x
#endif
#define SW_Backward Const__(0x8000) /* backward compatibility */
#define SW_C3 Const__(0x4000) /* condition bit 3 */
#define SW_Top Const__(0x3800) /* top of stack */
#define SW_Top_Shift Const__(11) /* shift for top of stack bits */
#define SW_C2 Const__(0x0400) /* condition bit 2 */
#define SW_C1 Const__(0x0200) /* condition bit 1 */
#define SW_C0 Const__(0x0100) /* condition bit 0 */
#define SW_Summary Const__(0x0080) /* exception summary */
#define SW_Stack_Fault Const__(0x0040) /* stack fault */
#define SW_Precision Const__(0x0020) /* loss of precision */
#define SW_Underflow Const__(0x0010) /* underflow */
#define SW_Overflow Const__(0x0008) /* overflow */
#define SW_Zero_Div Const__(0x0004) /* divide by zero */
#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */
#define SW_Invalid Const__(0x0001) /* invalid operation */
#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */
#ifndef __ASSEMBLER__
#define COMP_A_gt_B 1
#define COMP_A_eq_B 2
#define COMP_A_lt_B 3
#define COMP_No_Comp 4
#define COMP_Denormal 0x20
#define COMP_NaN 0x40
#define COMP_SNaN 0x80
#define status_word() \
((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top))
#define setcc(cc) ({ \
partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
#ifdef PECULIAR_486
/* Default, this conveys no information, but an 80486 does it. */
/* Clear the SW_C1 bit, "other bits undefined". */
# define clear_C1() { partial_status &= ~SW_C1; }
# else
# define clear_C1()
#endif PECULIAR_486
#endif __ASSEMBLER__
#endif _STATUS_H_

View file

@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------+
| version.h |
| |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#define FPU_VERSION "wm-FPU-emu version Beta 1.9"

View file

@ -0,0 +1,208 @@
.file "wm_shrx.S"
/*---------------------------------------------------------------------------+
| wm_shrx.S |
| |
| 64 bit right shift functions |
| |
| Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| unsigned shrx(void *arg1, unsigned arg2) |
| and |
| unsigned shrxs(void *arg1, unsigned arg2) |
| |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
.text
.align 2,144
/*---------------------------------------------------------------------------+
| unsigned shrx(void *arg1, unsigned arg2) |
| |
| Extended shift right function. |
| Fastest for small shifts. |
| Shifts the 64 bit quantity pointed to by the first arg (arg1) |
| right by the number of bits specified by the second arg (arg2). |
| Forms a 96 bit quantity from the 64 bit arg and eax: |
| [ 64 bit arg ][ eax ] |
| shift right ---------> |
| The eax register is initialized to 0 before the shifting. |
| Results returned in the 64 bit arg and eax. |
+---------------------------------------------------------------------------*/
.globl _shrx
_shrx:
push %ebp
movl %esp,%ebp
pushl %esi
movl PARAM2,%ecx
movl PARAM1,%esi
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jnc L_more_than_31
/* less than 32 bits */
pushl %ebx
movl (%esi),%ebx /* lsl */
movl 4(%esi),%edx /* msl */
xorl %eax,%eax /* extension */
shrd %cl,%ebx,%eax
shrd %cl,%edx,%ebx
shr %cl,%edx
movl %ebx,(%esi)
movl %edx,4(%esi)
popl %ebx
popl %esi
leave
ret
L_more_than_31:
cmpl $64,%ecx
jnc L_more_than_63
subb $32,%cl
movl (%esi),%eax /* lsl */
movl 4(%esi),%edx /* msl */
shrd %cl,%edx,%eax
shr %cl,%edx
movl %edx,(%esi)
movl $0,4(%esi)
popl %esi
leave
ret
L_more_than_63:
cmpl $96,%ecx
jnc L_more_than_95
subb $64,%cl
movl 4(%esi),%eax /* msl */
shr %cl,%eax
xorl %edx,%edx
movl %edx,(%esi)
movl %edx,4(%esi)
popl %esi
leave
ret
L_more_than_95:
xorl %eax,%eax
movl %eax,(%esi)
movl %eax,4(%esi)
popl %esi
leave
ret
/*---------------------------------------------------------------------------+
| unsigned shrxs(void *arg1, unsigned arg2) |
| |
| Extended shift right function (optimized for small floating point |
| integers). |
| Shifts the 64 bit quantity pointed to by the first arg (arg1) |
| right by the number of bits specified by the second arg (arg2). |
| Forms a 96 bit quantity from the 64 bit arg and eax: |
| [ 64 bit arg ][ eax ] |
| shift right ---------> |
| The eax register is initialized to 0 before the shifting. |
| The lower 8 bits of eax are lost and replaced by a flag which is |
| set (to 0x01) if any bit, apart from the first one, is set in the |
| part which has been shifted out of the arg. |
| Results returned in the 64 bit arg and eax. |
+---------------------------------------------------------------------------*/
.globl _shrxs
_shrxs:
push %ebp
movl %esp,%ebp
pushl %esi
pushl %ebx
movl PARAM2,%ecx
movl PARAM1,%esi
cmpl $64,%ecx /* shrd only works for 0..31 bits */
jnc Ls_more_than_63
cmpl $32,%ecx /* shrd only works for 0..31 bits */
jc Ls_less_than_32
/* We got here without jumps by assuming that the most common requirement
is for small integers */
/* Shift by [32..63] bits */
subb $32,%cl
movl (%esi),%eax /* lsl */
movl 4(%esi),%edx /* msl */
xorl %ebx,%ebx
shrd %cl,%eax,%ebx
shrd %cl,%edx,%eax
shr %cl,%edx
orl %ebx,%ebx /* test these 32 bits */
setne %bl
test $0x7fffffff,%eax /* and 31 bits here */
setne %bh
orw %bx,%bx /* Any of the 63 bit set ? */
setne %al
movl %edx,(%esi)
movl $0,4(%esi)
popl %ebx
popl %esi
leave
ret
/* Shift by [0..31] bits */
Ls_less_than_32:
movl (%esi),%ebx /* lsl */
movl 4(%esi),%edx /* msl */
xorl %eax,%eax /* extension */
shrd %cl,%ebx,%eax
shrd %cl,%edx,%ebx
shr %cl,%edx
test $0x7fffffff,%eax /* only need to look at eax here */
setne %al
movl %ebx,(%esi)
movl %edx,4(%esi)
popl %ebx
popl %esi
leave
ret
/* Shift by [64..95] bits */
Ls_more_than_63:
cmpl $96,%ecx
jnc Ls_more_than_95
subb $64,%cl
movl (%esi),%ebx /* lsl */
movl 4(%esi),%eax /* msl */
xorl %edx,%edx /* extension */
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
shr %cl,%eax
orl %ebx,%edx
setne %bl
test $0x7fffffff,%eax /* only need to look at eax here */
setne %bh
orw %bx,%bx
setne %al
xorl %edx,%edx
movl %edx,(%esi) /* set to zero */
movl %edx,4(%esi) /* set to zero */
popl %ebx
popl %esi
leave
ret
Ls_more_than_95:
/* Shift by [96..inf) bits */
xorl %eax,%eax
movl (%esi),%ebx
orl 4(%esi),%ebx
setne %al
xorl %ebx,%ebx
movl %ebx,(%esi)
movl %ebx,4(%esi)
popl %ebx
popl %esi
leave
ret

View file

@ -0,0 +1,474 @@
.file "wm_sqrt.S"
/*---------------------------------------------------------------------------+
| wm_sqrt.S |
| |
| Fixed point arithmetic square root evaluation. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| Call from C as: |
| void wm_sqrt(FPU_REG *n, unsigned int control_word) |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| wm_sqrt(FPU_REG *n, unsigned int control_word) |
| returns the square root of n in n. |
| |
| Use Newton's method to compute the square root of a number, which must |
| be in the range [1.0 .. 4.0), to 64 bits accuracy. |
| Does not check the sign or tag of the argument. |
| Sets the exponent, but not the sign or tag of the result. |
| |
| The guess is kept in %esi:%edi |
+---------------------------------------------------------------------------*/
#include "exception.h"
#include "fpu_asm.h"
#ifdef REENTRANT_FPU
/* Local storage on the stack: */
#define FPU_accum_3 -4(%ebp) /* ms word */
#define FPU_accum_2 -8(%ebp)
#define FPU_accum_1 -12(%ebp)
#define FPU_accum_0 -16(%ebp)
/*
* The de-normalised argument:
* sq_2 sq_1 sq_0
* b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
* ^ binary point here
*/
#define FPU_fsqrt_arg_2 -20(%ebp) /* ms word */
#define FPU_fsqrt_arg_1 -24(%ebp)
#define FPU_fsqrt_arg_0 -28(%ebp) /* ls word, at most the ms bit is set */
#else
/* Local storage in a static area: */
.data
.align 4,0
FPU_accum_3:
.long 0 /* ms word */
FPU_accum_2:
.long 0
FPU_accum_1:
.long 0
FPU_accum_0:
.long 0
/* The de-normalised argument:
sq_2 sq_1 sq_0
b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
^ binary point here
*/
FPU_fsqrt_arg_2:
.long 0 /* ms word */
FPU_fsqrt_arg_1:
.long 0
FPU_fsqrt_arg_0:
.long 0 /* ls word, at most the ms bit is set */
#endif REENTRANT_FPU
.text
.align 2,144
.globl _wm_sqrt
_wm_sqrt:
pushl %ebp
movl %esp,%ebp
#ifdef REENTRANT_FPU
subl $28,%esp
#endif REENTRANT_FPU
pushl %esi
pushl %edi
pushl %ebx
movl PARAM1,%esi
movl SIGH(%esi),%eax
movl SIGL(%esi),%ecx
xorl %edx,%edx
/* We use a rough linear estimate for the first guess.. */
cmpl EXP_BIAS,EXP(%esi)
jnz sqrt_arg_ge_2
shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */
rcrl $1,%ecx
rcrl $1,%edx
sqrt_arg_ge_2:
/* From here on, n is never accessed directly again until it is
replaced by the answer. */
movl %eax,FPU_fsqrt_arg_2 /* ms word of n */
movl %ecx,FPU_fsqrt_arg_1
movl %edx,FPU_fsqrt_arg_0
/* Make a linear first estimate */
shrl $1,%eax
addl $0x40000000,%eax
movl $0xaaaaaaaa,%ecx
mull %ecx
shll %edx /* max result was 7fff... */
testl $0x80000000,%edx /* but min was 3fff... */
jnz sqrt_prelim_no_adjust
movl $0x80000000,%edx /* round up */
sqrt_prelim_no_adjust:
movl %edx,%esi /* Our first guess */
/* We have now computed (approx) (2 + x) / 3, which forms the basis
for a few iterations of Newton's method */
movl FPU_fsqrt_arg_2,%ecx /* ms word */
/*
* From our initial estimate, three iterations are enough to get us
* to 30 bits or so. This will then allow two iterations at better
* precision to complete the process.
*/
/* Compute (g + n/g)/2 at each iteration (g is the guess). */
shrl %ecx /* Doing this first will prevent a divide */
/* overflow later. */
movl %ecx,%edx /* msw of the arg / 2 */
divl %esi /* current estimate */
shrl %esi /* divide by 2 */
addl %eax,%esi /* the new estimate */
movl %ecx,%edx
divl %esi
shrl %esi
addl %eax,%esi
movl %ecx,%edx
divl %esi
shrl %esi
addl %eax,%esi
/*
* Now that an estimate accurate to about 30 bits has been obtained (in %esi),
* we improve it to 60 bits or so.
*
* The strategy from now on is to compute new estimates from
* guess := guess + (n - guess^2) / (2 * guess)
*/
/* First, find the square of the guess */
movl %esi,%eax
mull %esi
/* guess^2 now in %edx:%eax */
movl FPU_fsqrt_arg_1,%ecx
subl %ecx,%eax
movl FPU_fsqrt_arg_2,%ecx /* ms word of normalized n */
sbbl %ecx,%edx
jnc sqrt_stage_2_positive
/* Subtraction gives a negative result,
negate the result before division. */
notl %edx
notl %eax
addl $1,%eax
adcl $0,%edx
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
jmp sqrt_stage_2_finish
sqrt_stage_2_positive:
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
notl %ecx
notl %eax
addl $1,%eax
adcl $0,%ecx
sqrt_stage_2_finish:
sarl $1,%ecx /* divide by 2 */
rcrl $1,%eax
/* Form the new estimate in %esi:%edi */
movl %eax,%edi
addl %ecx,%esi
jnz sqrt_stage_2_done /* result should be [1..2) */
#ifdef PARANOID
/* It should be possible to get here only if the arg is ffff....ffff */
cmp $0xffffffff,FPU_fsqrt_arg_1
jnz sqrt_stage_2_error
#endif PARANOID
/* The best rounded result. */
xorl %eax,%eax
decl %eax
movl %eax,%edi
movl %eax,%esi
movl $0x7fffffff,%eax
jmp sqrt_round_result
#ifdef PARANOID
sqrt_stage_2_error:
pushl EX_INTERNAL|0x213
call EXCEPTION
#endif PARANOID
sqrt_stage_2_done:
/* Now the square root has been computed to better than 60 bits. */
/* Find the square of the guess. */
movl %edi,%eax /* ls word of guess */
mull %edi
movl %edx,FPU_accum_1
movl %esi,%eax
mull %esi
movl %edx,FPU_accum_3
movl %eax,FPU_accum_2
movl %edi,%eax
mull %esi
addl %eax,FPU_accum_1
adcl %edx,FPU_accum_2
adcl $0,FPU_accum_3
/* movl %esi,%eax */
/* mull %edi */
addl %eax,FPU_accum_1
adcl %edx,FPU_accum_2
adcl $0,FPU_accum_3
/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */
movl FPU_fsqrt_arg_0,%eax /* get normalized n */
subl %eax,FPU_accum_1
movl FPU_fsqrt_arg_1,%eax
sbbl %eax,FPU_accum_2
movl FPU_fsqrt_arg_2,%eax /* ms word of normalized n */
sbbl %eax,FPU_accum_3
jnc sqrt_stage_3_positive
/* Subtraction gives a negative result,
negate the result before division */
notl FPU_accum_1
notl FPU_accum_2
notl FPU_accum_3
addl $1,FPU_accum_1
adcl $0,FPU_accum_2
#ifdef PARANOID
adcl $0,FPU_accum_3 /* This must be zero */
jz sqrt_stage_3_no_error
sqrt_stage_3_error:
pushl EX_INTERNAL|0x207
call EXCEPTION
sqrt_stage_3_no_error:
#endif PARANOID
movl FPU_accum_2,%edx
movl FPU_accum_1,%eax
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
sarl $1,%ecx /* divide by 2 */
rcrl $1,%eax
/* prepare to round the result */
addl %ecx,%edi
adcl $0,%esi
jmp sqrt_stage_3_finished
sqrt_stage_3_positive:
movl FPU_accum_2,%edx
movl FPU_accum_1,%eax
divl %esi
movl %eax,%ecx
movl %edx,%eax
divl %esi
sarl $1,%ecx /* divide by 2 */
rcrl $1,%eax
/* prepare to round the result */
notl %eax /* Negate the correction term */
notl %ecx
addl $1,%eax
adcl $0,%ecx /* carry here ==> correction == 0 */
adcl $0xffffffff,%esi
addl %ecx,%edi
adcl $0,%esi
sqrt_stage_3_finished:
/*
* The result in %esi:%edi:%esi should be good to about 90 bits here,
* and the rounding information here does not have sufficient accuracy
* in a few rare cases.
*/
cmpl $0xffffffe0,%eax
ja sqrt_near_exact_x
cmpl $0x00000020,%eax
jb sqrt_near_exact
cmpl $0x7fffffe0,%eax
jb sqrt_round_result
cmpl $0x80000020,%eax
jb sqrt_get_more_precision
sqrt_round_result:
/* Set up for rounding operations */
movl %eax,%edx
movl %esi,%eax
movl %edi,%ebx
movl PARAM1,%edi
movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0) */
movl PARAM2,%ecx
jmp fpu_reg_round_sqrt
sqrt_near_exact_x:
/* First, the estimate must be rounded up. */
addl $1,%edi
adcl $0,%esi
sqrt_near_exact:
/*
* This is an easy case because x^1/2 is monotonic.
* We need just find the square of our estimate, compare it
* with the argument, and deduce whether our estimate is
* above, below, or exact. We use the fact that the estimate
* is known to be accurate to about 90 bits.
*/
movl %edi,%eax /* ls word of guess */
mull %edi
movl %edx,%ebx /* 2nd ls word of square */
movl %eax,%ecx /* ls word of square */
movl %edi,%eax
mull %esi
addl %eax,%ebx
addl %eax,%ebx
#ifdef PARANOID
cmp $0xffffffb0,%ebx
jb sqrt_near_exact_ok
cmp $0x00000050,%ebx
ja sqrt_near_exact_ok
pushl EX_INTERNAL|0x214
call EXCEPTION
sqrt_near_exact_ok:
#endif PARANOID
or %ebx,%ebx
js sqrt_near_exact_small
jnz sqrt_near_exact_large
or %ebx,%edx
jnz sqrt_near_exact_large
/* Our estimate is exactly the right answer */
xorl %eax,%eax
jmp sqrt_round_result
sqrt_near_exact_small:
/* Our estimate is too small */
movl $0x000000ff,%eax
jmp sqrt_round_result
sqrt_near_exact_large:
/* Our estimate is too large, we need to decrement it */
subl $1,%edi
sbbl $0,%esi
movl $0xffffff00,%eax
jmp sqrt_round_result
sqrt_get_more_precision:
/* This case is almost the same as the above, except we start
with an extra bit of precision in the estimate. */
stc /* The extra bit. */
rcll $1,%edi /* Shift the estimate left one bit */
rcll $1,%esi
movl %edi,%eax /* ls word of guess */
mull %edi
movl %edx,%ebx /* 2nd ls word of square */
movl %eax,%ecx /* ls word of square */
movl %edi,%eax
mull %esi
addl %eax,%ebx
addl %eax,%ebx
/* Put our estimate back to its original value */
stc /* The ms bit. */
rcrl $1,%esi /* Shift the estimate left one bit */
rcrl $1,%edi
#ifdef PARANOID
cmp $0xffffff60,%ebx
jb sqrt_more_prec_ok
cmp $0x000000a0,%ebx
ja sqrt_more_prec_ok
pushl EX_INTERNAL|0x215
call EXCEPTION
sqrt_more_prec_ok:
#endif PARANOID
or %ebx,%ebx
js sqrt_more_prec_small
jnz sqrt_more_prec_large
or %ebx,%ecx
jnz sqrt_more_prec_large
/* Our estimate is exactly the right answer */
movl $0x80000000,%eax
jmp sqrt_round_result
sqrt_more_prec_small:
/* Our estimate is too small */
movl $0x800000ff,%eax
jmp sqrt_round_result
sqrt_more_prec_large:
/* Our estimate is too large */
movl $0x7fffff00,%eax
jmp sqrt_round_result

View file

@ -0,0 +1,49 @@
#
# Makefile for the linux kernel device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
.S.s:
$(CPP) -traditional $< -o $*.s
.c.s:
$(CC) $(CFLAGS) -S $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c $<
SUBDIRS = block char net
ifdef CONFIG_MATH_EMULATION
SUBDIRS := $(SUBDIRS) FPU-emu
endif
ifdef CONFIG_SCSI
SUBDIRS := $(SUBDIRS) scsi
endif
ifdef CONFIG_SOUND
SUBDIRS := $(SUBDIRS) sound
endif
all: driversubdirs
driversubdirs: dummy
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
dep:
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,72 @@
#
# Makefile for the kernel block device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now inherited from the
# parent makefile.
#
.c.s:
$(CC) $(CFLAGS) -S $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c $<
#
# Note : at this point, these files are compiled on all systems.
# In the future, some of these should be built conditionally.
#
OBJS := ll_rw_blk.o floppy.o ramdisk.o genhd.o
SRCS := ll_rw_blk.c floppy.c ramdisk.c genhd.c
ifdef CONFIG_CDU31A
OBJS := $(OBJS) cdu31a.o
SRCS := $(SRCS) cdu31a.c
endif
ifdef CONFIG_MCD
OBJS := $(OBJS) mcd.o
SRCS := $(SRCS) mcd.c
endif
ifdef CONFIG_SBPCD
OBJS := $(OBJS) sbpcd.o
SRCS := $(SRCS) sbpcd.c
ifdef PATCHLEVEL
CFLAGS := $(CFLAGS) -DPATCHLEVEL=$(PATCHLEVEL)
endif
endif #CONFIG_SBPCD
ifdef CONFIG_BLK_DEV_HD
OBJS := $(OBJS) hd.o
SRCS := $(SRCS) hd.c
endif
ifdef CONFIG_BLK_DEV_XD
OBJS := $(OBJS) xd.o
SRCS := $(SRCS) xd.c
endif
all: block.a
block.a: $(OBJS)
rm -f block.a
$(AR) rcs block.a $(OBJS)
sync
dep:
$(CPP) -M $(SRCS) > .depend
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,214 @@
This is release 1.2 of the SoundBlaster Pro (Matsushita, Kotobuki,
Panasonic, CreativeLabs, Aztech) CD-ROM driver for Linux.
The driver is able to drive the whole family of IDE-style
Matsushita/Kotobuki/Panasonic drives (the "double speed" versions
like CR-562 and CR-563, too), and it will work with the soundcard
interfaces (SB Pro, SB 16, Galaxy, SoundFX, ...) and/or with
the "no-sound" cards (Panasonic CI-101P, LaserMate, Aztech, ...).
The interface type has to get configured, because the behavior
is different.
The driver respects different drive firmware releases - my drive
is a 2.11, but it should work with "old" drives <2.01 ... >3.00
and with "new" drives (which count the releases around 0.75 or
1.00).
Up to 4 drives are supported. CR-52x and CR-56x drives can be mixed,
but the CR-521 ones are hard-wired to drive ID 0. The drives have
to use different drive IDs, but the same controller (it will be a
little bit harder to support up to four interface cards - but I plan
to do it the day somebody wishes to connect a fifth drive).
Each drive has to get a unique minor number (0...3), corresponding
to it's drive ID. The drive IDs may be selected freely from 0 to 3 -
they must not be in consecutive order.
If this driver doesn't work with your equipment, mail me a
description, please.
The driver supports reading of data from the CD and playing of
audio tracks. The audio part should run with WorkMan, xcdplayer,
with the "non-X11" products CDplayer and WorkBone - tell me if
it is not compatible with other software.
MultiSession is supported, "ManySession" (see below) alternatively.
Photo CDs should work, too. At ftp.gwdg.de:/pub/linux/hpcdtoppm
is a package to convert photo CD image files.
I did not have a chance to play with XA or mixed mode CDs yet.
Send one over, if you would like sbpcd to support that.
The transfer rate will reach 150 kB/sec with standard drives and
the full 300 kB/sec with double-speed drives.
This release is part of the standard kernel and consists of
- this README file
- the driver file linux/drivers/block/sbpcd.c
- the header file linux/include/linux/sbpcd.h.
To install:
-----------
1. Setup your hardware parameters. Though the driver does "auto-probing"
now, this step is recommended for every-day use.
a. Go into /usr/src/linux/include/linux/sbpcd.h and configure
it for your hardware (near the beginning):
a1. Set it up for the appropriate type of interface board.
Most "compatible" sound boards (for example "Highscreen",
"SoundFX" and "Galaxy") need the "SBPRO 0" setup. The
"no-sound" board from OmniCd needs the "SBPRO 1" setup.
sbpcd.c holds some examples in it's auto-probe list.
a2. Tell the address of your CDROM_PORT.
b. Additionally for 2.a1 and 2.a2, the setup may be done during
boot time (via the "kernel command line" or "LILO option"):
sbpcd=0x230,SoundBlaster
or
sbpcd=0x320,LaserMate
(these strings are case sensitive!).
2. Do a "make config" and select "yes" for Matsushita CD-ROM
support and for ISO9660 FileSystem support.
SCSI and/or SCSI CD-ROM support is not needed.
3. Then do a "make dep", then make the kernel image ("make zlilo"
or else).
4. Make the device file(s). The driver uses definitely and exclusive
the MAJOR 25, so do
mknod /dev/sbpcd b 25 0 (if you have only drive #0)
and/or
mknod /dev/sbpcd0 b 25 0
mknod /dev/sbpcd1 b 25 1
mknod /dev/sbpcd2 b 25 2
mknod /dev/sbpcd3 b 25 3
to make the node(s).
Take care that you create a node with the same MINOR as your drive
id is. So, if the DOS driver tells you have drive id #3, you have to
mknod /dev/<any_name> b 25 3
If you further make a link like
ln -s sbpcd /dev/cdrom
you can use the name /dev/cdrom, too.
5. Reboot with the new kernel.
You should now be able to do "mount -t iso9660 /dev/sbpcd /mnt"
and see the contents of your CD in the /mnt directory, and/or
hear music with "workman -c /dev/sbpcd &".
Things of interest:
-------------------
The driver is configured to try the SoundBlaster Pro type of
interface at I/O port 0x0230 first. If this is not appropriate,
sbpcd.h should get changed (you will find the right place -
just at the beginning).
No DMA and no IRQ is used, so the IRQ adjusting is not necessary,
and the IRQ line stays free for the SB Pro sound drivers.
To reduce or increase the amount of kernel messages, edit
sbpcd.c and change the initialization of the variable
"sbpcd_debug". This is the way to get rid of the initial
warning message block, too.
With "#define MANY_SESSION 1" (sbpcd.c), the driver can use
"many-session" CDs. This will work only with "new" drives like
CR-562 or CR-563. That is NOT multisession - it is a CD
with multiple independent sessions, each containing block
addresses as if it were the only session. With this feature
enabled, the driver will read the LAST session. Without it,
the FIRST session gets read.
If you would like the support of reading "in-between" sessions,
drop me a mail and some food for the soul. :-)
Those "many-session" CDs can get made by CDROM writers like
Philips CDD 521.
With this feature enabled, it is impossible to read true
multisession CDs.
Auto-probing at boot time:
--------------------------
The driver does auto-probing at all well-known interface card
addresses now. The idea to do that came from Adam J. Richter
(YGGDRASIL).
This auto-probing looks first at the configured address resp.
the address submitted by the kernel command line. With this,
it is possible to use this driver within installation boot
floppies, and for any non-standard address, too.
Auto-probing will make an assumption about the interface type
("SBPRO" or not), based upon the address. That assumption may
be wrong (initialization will be o.k., but you will get I/O
errors during mount). In that case, use the "kernel command
line" feature and specify address & type at boot time to find
out the right setup.
SBPCD's auto-probing happens before the initialization of the
net drivers. That makes a hang possible if an ethernet card
gets touched.
For every-day use, address and type should get configured
within sbpcd.h. That will stop the auto-probing due to success
with the first try.
Setting up address and interface type:
--------------------------------------
If your I/O port address is not 0x0230 or if you use a "no-sound"
interface other than OmniCD, you have to look for the #defines
near the beginning of sbpcd.h and configure them: set SBPRO to
0 or 1, and change CDROM_PORT to the address of your CDROM I/O port.
Most of the "SoundBlaster compatible" cards behave like the
no-sound interfaces!
With "original" SB Pro cards, an initial setting of CD_volume
through the sound cards MIXER register gets done. That happens
at the end of "sbpcd_init". If you are using a "compatible"
sound card of type "LaserMate", you can change that code to get
it done with your card, too...
Using audio CDs:
----------------
Workman, WorkBone, xcdplayer and cdplayer should work good now,
even with the double-speed drives.
The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer
wants "/dev/rsr0", workman loves "/dev/sr0" - so, do the appropriate
links for using them without the need of supplying parameters.
Known problems:
---------------
Currently, the detection of disk change or removal does not
work as good as it should.
Further, I do not know if this driver can live together with a
SCSI CD-ROM driver and/or device, but I hope so.
Bug reports, comments, wishes, donations (technical information
is a donation, too :-) etc. to
emoenke@gwdg.de
or to eberhard_moenkeberg@rollo.central.de
or to my FIDO address: Eberhard Moenkeberg, 2:2437/210.27
SnailMail address, preferable for CD editors if they want to submit
a free "cooperation" copy:
Eberhard Moenkeberg
Reinholdstr. 14
D-37083 Goettingen
Germany

View file

@ -0,0 +1,310 @@
#ifndef _BLK_H
#define _BLK_H
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/genhd.h>
/*
* NR_REQUEST is the number of entries in the request-queue.
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
*
* 32 seems to be a reasonable number: enough to get some benefit
* from the elevator-mechanism, but not so much as to lock a lot of
* buffers when they are in the queue. 64 seems to be too many (easily
* long pauses in reading when heavy writing/syncing is going on)
*/
#define NR_REQUEST 64
/*
* Ok, this is an expanded form so that we can use the same
* request for paging requests when that is implemented. In
* paging, 'bh' is NULL, and 'waiting' is used to wait for
* read/write completion.
*/
struct request {
int dev; /* -1 if no request */
int cmd; /* READ or WRITE */
int errors;
unsigned long sector;
unsigned long nr_sectors;
unsigned long current_nr_sectors;
char * buffer;
struct task_struct * waiting;
struct buffer_head * bh;
struct buffer_head * bhtail;
struct request * next;
};
/*
* This is used in the elevator algorithm: Note that
* reads always go before writes. This is natural: reads
* are much more time-critical than writes.
*/
#define IN_ORDER(s1,s2) \
((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
((s1)->dev < (s2)->dev || (((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector)))))
struct blk_dev_struct {
void (*request_fn)(void);
struct request * current_request;
};
struct sec_size {
unsigned block_size;
unsigned block_size_bits;
};
/*
* These will have to be changed to be aware of different buffer
* sizes etc.. It actually needs a major cleanup.
*/
#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] ? \
((blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] >> 9) - 1) : \
((BLOCK_SIZE >> 9) - 1))
#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
extern struct sec_size * blk_sec[MAX_BLKDEV];
extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
extern struct wait_queue * wait_for_request;
extern void resetup_one_dev(struct gendisk *dev, int drive);
extern int * blk_size[MAX_BLKDEV];
extern int * blksize_size[MAX_BLKDEV];
extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
extern int is_read_only(int dev);
extern void set_device_ro(int dev,int flag);
extern void rd_load(void);
extern long rd_init(long mem_start, int length);
extern int ramdisk_size;
extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end);
#define RO_IOCTLS(dev,where) \
case BLKROSET: if (!suser()) return -EPERM; \
set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
if (!__err) put_fs_long(is_read_only(dev),(long *) (where)); return __err; }
#ifdef MAJOR_NR
/*
* Add entries as needed. Currently the only block devices
* supported are hard-disks and floppies.
*/
#if (MAJOR_NR == MEM_MAJOR)
/* ram disk */
#define DEVICE_NAME "ramdisk"
#define DEVICE_REQUEST do_rd_request
#define DEVICE_NR(device) ((device) & 7)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == FLOPPY_MAJOR)
static void floppy_on(unsigned int nr);
static void floppy_off(unsigned int nr);
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
#define DEVICE_NR(device) ((device) & 3)
#define DEVICE_ON(device) floppy_on(DEVICE_NR(device))
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
#elif (MAJOR_NR == HD_MAJOR)
/* harddisk: timeout is 6 seconds.. */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
#define DEVICE_TIMEOUT HD_TIMER
#define TIMEOUT_VALUE 600
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)>>6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == SCSI_DISK_MAJOR)
#define DEVICE_NAME "scsidisk"
#define DEVICE_INTR do_sd
#define TIMEOUT_VALUE 200
#define DEVICE_REQUEST do_sd_request
#define DEVICE_NR(device) (MINOR(device) >> 4)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
#define DEVICE_NAME "scsitape"
#define DEVICE_INTR do_st
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
#define DEVICE_NAME "CD-ROM"
#define DEVICE_INTR do_sr
#define DEVICE_REQUEST do_sr_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == XT_DISK_MAJOR)
#define DEVICE_NAME "xt disk"
#define DEVICE_REQUEST do_xd_request
#define DEVICE_NR(device) (MINOR(device) >> 6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
#define DEVICE_NAME "CDU31A"
#define DEVICE_REQUEST do_cdu31a_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
#define DEVICE_NAME "Mitsumi CD-ROM"
/* #define DEVICE_INTR do_mcd */
#define DEVICE_REQUEST do_mcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
#define DEVICE_NAME "Matsushita CD-ROM"
#define DEVICE_REQUEST do_sbpcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#else
#error "unknown blk device"
#endif
#if (MAJOR_NR != SCSI_TAPE_MAJOR)
#ifndef CURRENT
#define CURRENT (blk_dev[MAJOR_NR].current_request)
#endif
#define CURRENT_DEV DEVICE_NR(CURRENT->dev)
#ifdef DEVICE_INTR
void (*DEVICE_INTR)(void) = NULL;
#endif
#ifdef DEVICE_TIMEOUT
#define SET_TIMER \
((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
(timer_active |= 1<<DEVICE_TIMEOUT))
#define CLEAR_TIMER \
timer_active &= ~(1<<DEVICE_TIMEOUT)
#define SET_INTR(x) \
if ((DEVICE_INTR = (x)) != NULL) \
SET_TIMER; \
else \
CLEAR_TIMER;
#else
#define SET_INTR(x) (DEVICE_INTR = (x))
#endif
static void (DEVICE_REQUEST)(void);
/* end_request() - SCSI devices have their own version */
#if ! SCSI_MAJOR(MAJOR_NR)
static void end_request(int uptodate)
{
struct request * req;
struct buffer_head * bh;
struct task_struct * p;
req = CURRENT;
req->errors = 0;
if (!uptodate) {
printk(DEVICE_NAME " I/O error\n");
printk("dev %04lX, sector %lu\n",
(unsigned long)req->dev, req->sector);
req->nr_sectors--;
req->nr_sectors &= ~SECTOR_MASK;
req->sector += (BLOCK_SIZE / 512);
req->sector &= ~SECTOR_MASK;
}
if ((bh = req->bh) != NULL) {
req->bh = bh->b_reqnext;
bh->b_reqnext = NULL;
bh->b_uptodate = uptodate;
unlock_buffer(bh);
if ((bh = req->bh) != NULL) {
req->current_nr_sectors = bh->b_size >> 9;
if (req->nr_sectors < req->current_nr_sectors) {
req->nr_sectors = req->current_nr_sectors;
printk("end_request: buffer-list destroyed\n");
}
req->buffer = bh->b_data;
return;
}
}
DEVICE_OFF(req->dev);
CURRENT = req->next;
if ((p = req->waiting) != NULL) {
req->waiting = NULL;
p->state = TASK_RUNNING;
if (p->counter > current->counter)
need_resched = 1;
}
req->dev = -1;
wake_up(&wait_for_request);
}
#endif
#ifdef DEVICE_INTR
#define CLEAR_INTR SET_INTR(NULL)
#else
#define CLEAR_INTR
#endif
#define INIT_REQUEST \
if (!CURRENT) {\
CLEAR_INTR; \
return; \
} \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) { \
if (!CURRENT->bh->b_lock) \
panic(DEVICE_NAME ": block not locked"); \
}
#endif
#endif
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,216 @@
/*
* Code extracted from
* linux/kernel/hd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
struct gendisk *gendisk_head = NULL;
static int current_minor = 0;
extern int *blk_size[];
extern void rd_load(void);
extern int ramdisk_size;
/*
* Create devices for each logical partition in an extended partition.
* The logical partitions form a linked list, with each entry being
* a partition table with two entries. The first entry
* is the real data partition (with a start relative to the partition
* table start). The second is a pointer to the next logical partition
* (with a start relative to the entire extended partition).
* We do not create a Linux partition for the partition tables, but
* only for the actual data partitions.
*/
static void extended_partition(struct gendisk *hd, int dev)
{
struct buffer_head *bh;
struct partition *p;
unsigned long first_sector, this_sector;
int mask = (1 << hd->minor_shift) - 1;
first_sector = hd->part[MINOR(dev)].start_sect;
this_sector = first_sector;
while (1) {
if ((current_minor & mask) >= (4 + hd->max_p))
return;
if (!(bh = bread(dev,0,1024)))
return;
/*
* This block is from a device that we're about to stomp on.
* So make sure nobody thinks this block is usable.
*/
bh->b_dirt=0;
bh->b_uptodate=0;
if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
p = (struct partition *) (0x1BE + bh->b_data);
/*
* Process the first entry, which should be the real
* data partition.
*/
if (p->sys_ind == EXTENDED_PARTITION ||
!(hd->part[current_minor].nr_sects = p->nr_sects))
goto done; /* shouldn't happen */
hd->part[current_minor].start_sect = this_sector + p->start_sect;
printk(" %s%c%d", hd->major_name,
'a'+(current_minor >> hd->minor_shift),
mask & current_minor);
current_minor++;
p++;
/*
* Process the second entry, which should be a link
* to the next logical partition. Create a minor
* for this just long enough to get the next partition
* table. The minor will be reused for the real
* data partition.
*/
if (p->sys_ind != EXTENDED_PARTITION ||
!(hd->part[current_minor].nr_sects = p->nr_sects))
goto done; /* no more logicals in this partition */
hd->part[current_minor].start_sect = first_sector + p->start_sect;
this_sector = first_sector + p->start_sect;
dev = ((hd->major) << 8) | current_minor;
brelse(bh);
} else
goto done;
}
done:
brelse(bh);
}
static void check_partition(struct gendisk *hd, unsigned int dev)
{
static int first_time = 1;
int i, minor = current_minor;
struct buffer_head *bh;
struct partition *p;
unsigned long first_sector;
int mask = (1 << hd->minor_shift) - 1;
if (first_time)
printk("Partition check:\n");
first_time = 0;
first_sector = hd->part[MINOR(dev)].start_sect;
if (!(bh = bread(dev,0,1024))) {
printk(" unable to read partition table of device %04x\n",dev);
return;
}
printk(" %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
current_minor += 4; /* first "extra" minor */
if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
p = (struct partition *) (0x1BE + bh->b_data);
for (i=1 ; i<=4 ; minor++,i++,p++) {
if (!(hd->part[minor].nr_sects = p->nr_sects))
continue;
hd->part[minor].start_sect = first_sector + p->start_sect;
printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), i);
if ((current_minor & 0x3f) >= 60)
continue;
if (p->sys_ind == EXTENDED_PARTITION) {
printk(" <");
extended_partition(hd, (hd->major << 8) | minor);
printk(" >");
}
}
/*
* check for Disk Manager partition table
*/
if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
p = (struct partition *) (0x1BE + bh->b_data);
for (i = 4 ; i < 16 ; i++, current_minor++) {
p--;
if ((current_minor & mask) >= mask-2)
break;
if (!(p->start_sect && p->nr_sects))
continue;
hd->part[current_minor].start_sect = p->start_sect;
hd->part[current_minor].nr_sects = p->nr_sects;
printk(" %s%c%d", hd->major_name,
'a'+(current_minor >> hd->minor_shift),
current_minor & mask);
}
}
} else
printk(" bad partition table");
printk("\n");
brelse(bh);
}
/* This function is used to re-read partition tables for removable disks.
Much of the cleanup from the old partition tables should have already been
done */
/* This function will re-read the partition tables for a given device,
and set things back up again. There are some important caveats,
however. You must ensure that no one is using the device, and no one
can start using the device while this function is being executed. */
void resetup_one_dev(struct gendisk *dev, int drive)
{
int i;
int start = drive<<dev->minor_shift;
int j = start + dev->max_p;
int major = dev->major << 8;
current_minor = 1+(drive<<dev->minor_shift);
check_partition(dev, major+(drive<<dev->minor_shift));
for (i=start ; i < j ; i++)
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
}
static void setup_dev(struct gendisk *dev)
{
int i;
int j = dev->max_nr * dev->max_p;
int major = dev->major << 8;
int drive;
for (i = 0 ; i < j; i++) {
dev->part[i].start_sect = 0;
dev->part[i].nr_sects = 0;
}
dev->init();
for (drive=0 ; drive<dev->nr_real ; drive++) {
current_minor = 1+(drive<<dev->minor_shift);
check_partition(dev, major+(drive<<dev->minor_shift));
}
for (i=0 ; i < j ; i++)
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
blk_size[dev->major] = dev->sizes;
}
/* This may be used only once, enforced by 'static int callable' */
asmlinkage int sys_setup(void * BIOS)
{
static int callable = 1;
struct gendisk *p;
int nr=0;
if (!callable)
return -1;
callable = 0;
for (p = gendisk_head ; p ; p=p->next) {
setup_dev(p);
nr += p->nr_real;
}
if (ramdisk_size)
rd_load();
mount_root();
return (0);
}

View file

@ -0,0 +1,797 @@
/*
* linux/kernel/hd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* This is the low-level hd interrupt support. It traverses the
* request-list, using interrupts to jump between functions. As
* all the functions are called within interrupts, we may not
* sleep. Special care is recommended.
*
* modified by Drew Eckhardt to check nr of hd's from the CMOS.
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*/
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/config.h>
#define REALLY_SLOW_IO
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define MAJOR_NR HD_MAJOR
#include "blk.h"
#define HD_IRQ 14
static int revalidate_hddisk(int, int);
static inline unsigned char CMOS_READ(unsigned char addr)
{
outb_p(addr,0x70);
return inb_p(0x71);
}
#define HD_DELAY 0
#define MAX_ERRORS 16 /* Max read/write errors/sector */
#define RESET_FREQ 8 /* Reset controller every 8th retry */
#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
#define MAX_HD 2
static void recal_intr(void);
static void bad_rw_intr(void);
static char recalibrate[ MAX_HD ] = { 0, };
static int access_count[MAX_HD] = {0, };
static char busy[MAX_HD] = {0, };
static struct wait_queue * busy_wait = NULL;
static int reset = 0;
static int hd_error = 0;
#if (HD_DELAY > 0)
unsigned long last_req, read_timer();
#endif
/*
* This struct defines the HD's and their types.
*/
struct hd_i_struct {
unsigned int head,sect,cyl,wpcom,lzone,ctl;
};
#ifdef HD_TYPE
struct hd_i_struct hd_info[] = { HD_TYPE };
static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
#else
struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
static int NR_HD = 0;
#endif
static struct hd_struct hd[MAX_HD<<6]={{0,0},};
static int hd_sizes[MAX_HD<<6] = {0, };
static int hd_blocksizes[MAX_HD<<6] = {0, };
#if (HD_DELAY > 0)
unsigned long read_timer(void)
{
unsigned long t;
int i;
cli();
t = jiffies * 11932;
outb_p(0, 0x43);
i = inb_p(0x40);
i |= inb(0x40) << 8;
sti();
return(t - i);
}
#endif
void hd_setup(char *str, int *ints)
{
int hdind = 0;
if (ints[0] != 3)
return;
if (hd_info[0].head != 0)
hdind=1;
hd_info[hdind].head = ints[2];
hd_info[hdind].sect = ints[3];
hd_info[hdind].cyl = ints[1];
hd_info[hdind].wpcom = 0;
hd_info[hdind].lzone = ints[1];
hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
NR_HD = hdind+1;
}
static int win_result(void)
{
int i=inb_p(HD_STATUS);
if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
== (READY_STAT | SEEK_STAT)) {
hd_error = 0;
return 0; /* ok */
}
printk("HD: win_result: status = 0x%02x\n",i);
if (i&1) {
hd_error = inb(HD_ERROR);
printk("HD: win_result: error = 0x%02x\n",hd_error);
}
return 1;
}
static int controller_busy(void);
static int status_ok(void);
static int controller_ready(unsigned int drive, unsigned int head)
{
int retry = 100;
do {
if (controller_busy() & BUSY_STAT)
return 0;
outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
if (status_ok())
return 1;
} while (--retry);
return 0;
}
static int status_ok(void)
{
unsigned char status = inb_p(HD_STATUS);
if (status & BUSY_STAT)
return 1;
if (status & WRERR_STAT)
return 0;
if (!(status & READY_STAT))
return 0;
if (!(status & SEEK_STAT))
return 0;
return 1;
}
static int controller_busy(void)
{
int retries = 100000;
unsigned char status;
do {
status = inb_p(HD_STATUS);
} while ((status & BUSY_STAT) && --retries);
return status;
}
static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
unsigned int head,unsigned int cyl,unsigned int cmd,
void (*intr_addr)(void))
{
unsigned short port;
if (drive>1 || head>15)
panic("Trying to write bad sector");
#if (HD_DELAY > 0)
while (read_timer() - last_req < HD_DELAY)
/* nothing */;
#endif
if (reset)
return;
if (!controller_ready(drive, head)) {
reset = 1;
return;
}
SET_INTR(intr_addr);
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
outb_p(cmd,++port);
}
static int drive_busy(void)
{
unsigned int i;
unsigned char c;
for (i = 0; i < 500000 ; i++) {
c = inb_p(HD_STATUS);
c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
if (c == (READY_STAT | SEEK_STAT))
return 0;
}
printk("HD controller times out, status = 0x%02x\n",c);
return 1;
}
static void reset_controller(void)
{
int i;
printk(KERN_DEBUG "HD-controller reset\n");
outb_p(4,HD_CMD);
for(i = 0; i < 1000; i++) nop();
outb(hd_info[0].ctl & 0x0f ,HD_CMD);
if (drive_busy())
printk("HD-controller still busy\n");
if ((hd_error = inb(HD_ERROR)) != 1)
printk("HD-controller reset failed: %02x\n",hd_error);
}
static void reset_hd(void)
{
static int i;
repeat:
if (reset) {
reset = 0;
i = -1;
reset_controller();
} else if (win_result()) {
bad_rw_intr();
if (reset)
goto repeat;
}
i++;
if (i < NR_HD) {
hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
if (reset)
goto repeat;
} else
do_hd_request();
}
/*
* Ok, don't know what to do with the unexpected interrupts: on some machines
* doing a reset and a retry seems to result in an eternal loop. Right now I
* ignore it, and just set the timeout.
*/
void unexpected_hd_interrupt(void)
{
sti();
printk(KERN_DEBUG "Unexpected HD interrupt\n");
SET_TIMER;
}
/*
* bad_rw_intr() now tries to be a bit smarter and does things
* according to the error returned by the controller.
* -Mika Liljeberg (liljeber@cs.Helsinki.FI)
*/
static void bad_rw_intr(void)
{
int dev;
if (!CURRENT)
return;
dev = MINOR(CURRENT->dev) >> 6;
if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
end_request(0);
recalibrate[dev] = 1;
} else if (CURRENT->errors % RESET_FREQ == 0)
reset = 1;
else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
recalibrate[dev] = 1;
/* Otherwise just retry */
}
static inline int wait_DRQ(void)
{
int retries = 100000;
while (--retries > 0)
if (inb_p(HD_STATUS) & DRQ_STAT)
return 0;
return -1;
}
#define STAT_MASK (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)
#define STAT_OK (READY_STAT | SEEK_STAT)
static void read_intr(void)
{
int i;
int retries = 100000;
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
continue;
if ((i & STAT_MASK) != STAT_OK)
break;
if (i & DRQ_STAT)
goto ok_to_read;
} while (--retries > 0);
sti();
printk("HD: read_intr: status = 0x%02x\n",i);
if (i & ERR_STAT) {
hd_error = (unsigned) inb(HD_ERROR);
printk("HD: read_intr: error = 0x%02x\n",hd_error);
}
bad_rw_intr();
cli();
do_hd_request();
return;
ok_to_read:
insw(HD_DATA,CURRENT->buffer,256);
CURRENT->errors = 0;
CURRENT->buffer += 512;
CURRENT->sector++;
i = --CURRENT->nr_sectors;
--CURRENT->current_nr_sectors;
#ifdef DEBUG
printk("hd%d : sector = %d, %d remaining to buffer = %08x\n",
MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT->
buffer);
#endif
if (!i || (CURRENT->bh && !SUBSECTOR(i)))
end_request(1);
if (i > 0) {
SET_INTR(&read_intr);
sti();
return;
}
(void) inb_p(HD_STATUS);
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
do_hd_request();
return;
}
static void write_intr(void)
{
int i;
int retries = 100000;
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
continue;
if ((i & STAT_MASK) != STAT_OK)
break;
if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
goto ok_to_write;
} while (--retries > 0);
sti();
printk("HD: write_intr: status = 0x%02x\n",i);
if (i & ERR_STAT) {
hd_error = (unsigned) inb(HD_ERROR);
printk("HD: write_intr: error = 0x%02x\n",hd_error);
}
bad_rw_intr();
cli();
do_hd_request();
return;
ok_to_write:
CURRENT->sector++;
i = --CURRENT->nr_sectors;
--CURRENT->current_nr_sectors;
CURRENT->buffer += 512;
if (!i || (CURRENT->bh && !SUBSECTOR(i)))
end_request(1);
if (i > 0) {
SET_INTR(&write_intr);
outsw(HD_DATA,CURRENT->buffer,256);
sti();
} else {
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
do_hd_request();
}
return;
}
static void recal_intr(void)
{
if (win_result())
bad_rw_intr();
do_hd_request();
}
/*
* This is another of the error-routines I don't know what to do with. The
* best idea seems to just set reset, and start all over again.
*/
static void hd_times_out(void)
{
DEVICE_INTR = NULL;
sti();
reset = 1;
if (!CURRENT)
return;
printk(KERN_DEBUG "HD timeout\n");
cli();
if (++CURRENT->errors >= MAX_ERRORS) {
#ifdef DEBUG
printk("hd : too many errors.\n");
#endif
end_request(0);
}
do_hd_request();
}
/*
* The driver has been modified to enable interrupts a bit more: in order to
* do this we first (a) disable the timeout-interrupt and (b) clear the
* device-interrupt. This way the interrupts won't mess with out code (the
* worst that can happen is that an unexpected HD-interrupt comes in and
* sets the "reset" variable and starts the timer)
*/
static void do_hd_request(void)
{
unsigned int block,dev;
unsigned int sec,head,cyl,track;
unsigned int nsect;
if (CURRENT && CURRENT->dev < 0) return;
if (DEVICE_INTR)
return;
repeat:
timer_active &= ~(1<<HD_TIMER);
sti();
INIT_REQUEST;
dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
#ifdef DEBUG
printk("hd%d : attempted read for sector %d past end of device at sector %d.\n",
block, hd[dev].nr_sects);
#endif
end_request(0);
goto repeat;
}
block += hd[dev].start_sect;
dev >>= 6;
sec = block % hd_info[dev].sect + 1;
track = block / hd_info[dev].sect;
head = track % hd_info[dev].head;
cyl = track / hd_info[dev].head;
#ifdef DEBUG
printk("hd%d : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
dev, cyl, head, sec, CURRENT->buffer);
#endif
cli();
if (reset) {
int i;
for (i=0; i < NR_HD; i++)
recalibrate[i] = 1;
reset_hd();
sti();
return;
}
if (recalibrate[dev]) {
recalibrate[dev] = 0;
hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
if (reset)
goto repeat;
sti();
return;
}
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
if (reset)
goto repeat;
if (wait_DRQ()) {
printk("HD: do_hd_request: no DRQ\n");
bad_rw_intr();
goto repeat;
}
outsw(HD_DATA,CURRENT->buffer,256);
sti();
return;
}
if (CURRENT->cmd == READ) {
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
if (reset)
goto repeat;
sti();
return;
}
panic("unknown hd-command");
}
static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct hd_geometry *loc = (struct hd_geometry *) arg;
int dev, err;
if (!inode)
return -EINVAL;
dev = MINOR(inode->i_rdev) >> 6;
if (dev >= NR_HD)
return -EINVAL;
switch (cmd) {
case HDIO_GETGEO:
if (!loc) return -EINVAL;
err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
if (err)
return err;
put_fs_byte(hd_info[dev].head,
(char *) &loc->heads);
put_fs_byte(hd_info[dev].sect,
(char *) &loc->sectors);
put_fs_word(hd_info[dev].cyl,
(short *) &loc->cylinders);
put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
(long *) &loc->start);
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
return 0;
case BLKFLSBUF:
if(!suser()) return -EACCES;
if(!inode->i_rdev) return -EINVAL;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
case BLKRRPART: /* Re-read partition tables */
return revalidate_hddisk(inode->i_rdev, 1);
RO_IOCTLS(inode->i_rdev,arg);
default:
return -EINVAL;
}
}
static int hd_open(struct inode * inode, struct file * filp)
{
int target;
target = DEVICE_NR(MINOR(inode->i_rdev));
while (busy[target])
sleep_on(&busy_wait);
access_count[target]++;
return 0;
}
/*
* Releasing a block device means we sync() it, so that it can safely
* be forgotten about...
*/
static void hd_release(struct inode * inode, struct file * file)
{
int target;
sync_dev(inode->i_rdev);
target = DEVICE_NR(MINOR(inode->i_rdev));
access_count[target]--;
}
static void hd_geninit(void);
static struct gendisk hd_gendisk = {
MAJOR_NR, /* Major number */
"hd", /* Major name */
6, /* Bits to shift to get real from partition */
1 << 6, /* Number of partitions per real */
MAX_HD, /* maximum number of real */
hd_geninit, /* init function */
hd, /* hd struct */
hd_sizes, /* block sizes */
0, /* number */
(void *) hd_info, /* internal */
NULL /* next */
};
static void hd_interrupt(int unused)
{
void (*handler)(void) = DEVICE_INTR;
DEVICE_INTR = NULL;
timer_active &= ~(1<<HD_TIMER);
if (!handler)
handler = unexpected_hd_interrupt;
handler();
sti();
}
/*
* This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
* means we run the IRQ-handler with interrupts disabled: this is bad for
* interrupt latency, but anything else has led to problems on some
* machines...
*
* We enable interrupts in some of the routines after making sure it's
* safe.
*/
static struct sigaction hd_sigaction = {
hd_interrupt,
0,
SA_INTERRUPT,
NULL
};
static void hd_geninit(void)
{
int drive, i;
extern struct drive_info drive_info;
unsigned char *BIOS = (unsigned char *) &drive_info;
int cmos_disks;
if (!NR_HD) {
for (drive=0 ; drive<2 ; drive++) {
hd_info[drive].cyl = *(unsigned short *) BIOS;
hd_info[drive].head = *(2+BIOS);
hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
hd_info[drive].ctl = *(8+BIOS);
hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
hd_info[drive].sect = *(14+BIOS);
BIOS += 16;
}
/*
We querry CMOS about hard disks : it could be that
we have a SCSI/ESDI/etc controller that is BIOS
compatable with ST-506, and thus showing up in our
BIOS table, but not register compatable, and therefore
not present in CMOS.
Furthurmore, we will assume that our ST-506 drives
<if any> are the primary drives in the system, and
the ones reflected as drive 1 or 2.
The first drive is stored in the high nibble of CMOS
byte 0x12, the second in the low nibble. This will be
either a 4 bit drive type or 0xf indicating use byte 0x19
for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
Needless to say, a non-zero value means we have
an AT controller hard disk for that drive.
*/
if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
if (cmos_disks & 0x0f)
NR_HD = 2;
else
NR_HD = 1;
}
i = NR_HD;
while (i-- > 0) {
hd[i<<6].nr_sects = 0;
if (hd_info[i].head > 16) {
printk("hd.c: ST-506 interface disk with more than 16 heads detected,\n");
printk(" probably due to non-standard sector translation. Giving up.\n");
printk(" (disk %d: cyl=%d, sect=%d, head=%d)\n", i,
hd_info[i].cyl,
hd_info[i].sect,
hd_info[i].head);
if (i+1 == NR_HD)
NR_HD--;
continue;
}
hd[i<<6].nr_sects = hd_info[i].head*
hd_info[i].sect*hd_info[i].cyl;
}
if (NR_HD) {
if (irqaction(HD_IRQ,&hd_sigaction)) {
printk("hd.c: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
NR_HD = 0;
}
}
hd_gendisk.nr_real = NR_HD;
for(i=0;i<(MAX_HD << 6);i++) hd_blocksizes[i] = 1024;
blksize_size[MAJOR_NR] = hd_blocksizes;
}
static struct file_operations hd_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
hd_ioctl, /* ioctl */
NULL, /* mmap */
hd_open, /* open */
hd_release, /* release */
block_fsync /* fsync */
};
unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
{
if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
printk("Unable to get major %d for harddisk\n",MAJOR_NR);
return mem_start;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
hd_gendisk.next = gendisk_head;
gendisk_head = &hd_gendisk;
timer_table[HD_TIMER].fn = hd_times_out;
return mem_start;
}
#define DEVICE_BUSY busy[target]
#define USAGE access_count[target]
#define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl)
/* We assume that the the bios parameters do not change, so the disk capacity
will not change */
#undef MAYBE_REINIT
#define GENDISK_STRUCT hd_gendisk
/*
* This routine is called to flush all partitions and partition tables
* for a changed scsi disk, and then re-read the new partition table.
* If we are revalidating a disk because of a media change, then we
* enter with usage == 0. If we are using an ioctl, we automatically have
* usage == 1 (we need an open channel to use an ioctl :-), so this
* is our limit.
*/
static int revalidate_hddisk(int dev, int maxusage)
{
int target, major;
struct gendisk * gdev;
int max_p;
int start;
int i;
target = DEVICE_NR(MINOR(dev));
gdev = &GENDISK_STRUCT;
cli();
if (DEVICE_BUSY || USAGE > maxusage) {
sti();
return -EBUSY;
};
DEVICE_BUSY = 1;
sti();
max_p = gdev->max_p;
start = target << gdev->minor_shift;
major = MAJOR_NR << 8;
for (i=max_p - 1; i >=0 ; i--) {
sync_dev(major | start | i);
invalidate_inodes(major | start | i);
invalidate_buffers(major | start | i);
gdev->part[start+i].start_sect = 0;
gdev->part[start+i].nr_sects = 0;
};
#ifdef MAYBE_REINIT
MAYBE_REINIT;
#endif
gdev->part[start].nr_sects = CAPACITY;
resetup_one_dev(gdev, target);
DEVICE_BUSY = 0;
wake_up(&busy_wait);
return 0;
}

View file

@ -0,0 +1,503 @@
/*
* linux/kernel/blk_dev/ll_rw.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* This handles all read/write requests to block devices
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/config.h>
#include <linux/locks.h>
#include <asm/system.h>
#include "blk.h"
#ifdef CONFIG_SBPCD
extern u_long sbpcd_init(u_long, u_long);
#endif CONFIG_SBPCD
/*
* The request-struct contains all necessary data
* to load a nr of sectors into memory
*/
static struct request all_requests[NR_REQUEST];
/*
* used to wait on when there are no free requests
*/
struct wait_queue * wait_for_request = NULL;
/* This specifies how many sectors to read ahead on the disk. */
int read_ahead[MAX_BLKDEV] = {0, };
/* blk_dev_struct is:
* do_request-address
* next-request
*/
struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
{ NULL, NULL }, /* no_dev */
{ NULL, NULL }, /* dev mem */
{ NULL, NULL }, /* dev fd */
{ NULL, NULL }, /* dev hd */
{ NULL, NULL }, /* dev ttyx */
{ NULL, NULL }, /* dev tty */
{ NULL, NULL }, /* dev lp */
{ NULL, NULL }, /* dev pipes */
{ NULL, NULL }, /* dev sd */
{ NULL, NULL } /* dev st */
};
/*
* blk_size contains the size of all block-devices in units of 1024 byte
* sectors:
*
* blk_size[MAJOR][MINOR]
*
* if (!blk_size[MAJOR]) then no minor size checking is done.
*/
int * blk_size[MAX_BLKDEV] = { NULL, NULL, };
/*
* blksize_size contains the size of all block-devices:
*
* blksize_size[MAJOR][MINOR]
*
* if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
*/
int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
/*
* look for a free request in the first N entries.
* NOTE: interrupts must be disabled on the way in, and will still
* be disabled on the way out.
*/
static inline struct request * get_request(int n, int dev)
{
static struct request *prev_found = NULL, *prev_limit = NULL;
register struct request *req, *limit;
if (n <= 0)
panic("get_request(%d): impossible!\n", n);
limit = all_requests + n;
if (limit != prev_limit) {
prev_limit = limit;
prev_found = all_requests;
}
req = prev_found;
for (;;) {
req = ((req > all_requests) ? req : limit) - 1;
if (req->dev < 0)
break;
if (req == prev_found)
return NULL;
}
prev_found = req;
req->dev = dev;
return req;
}
/*
* wait until a free request in the first N entries is available.
* NOTE: interrupts must be disabled on the way in, and will still
* be disabled on the way out.
*/
static inline struct request * get_request_wait(int n, int dev)
{
register struct request *req;
while ((req = get_request(n, dev)) == NULL)
sleep_on(&wait_for_request);
return req;
}
/* RO fail safe mechanism */
static long ro_bits[MAX_BLKDEV][8];
int is_read_only(int dev)
{
int minor,major;
major = MAJOR(dev);
minor = MINOR(dev);
if (major < 0 || major >= MAX_BLKDEV) return 0;
return ro_bits[major][minor >> 5] & (1 << (minor & 31));
}
void set_device_ro(int dev,int flag)
{
int minor,major;
major = MAJOR(dev);
minor = MINOR(dev);
if (major < 0 || major >= MAX_BLKDEV) return;
if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
}
/*
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
*/
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
req->next = NULL;
cli();
if (req->bh)
req->bh->b_dirt = 0;
if (!(tmp = dev->current_request)) {
dev->current_request = req;
(dev->request_fn)();
sti();
return;
}
for ( ; tmp->next ; tmp = tmp->next) {
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
}
req->next = tmp->next;
tmp->next = req;
/* for SCSI devices, call request_fn unconditionally */
if (scsi_major(MAJOR(req->dev)))
(dev->request_fn)();
sti();
}
static void make_request(int major,int rw, struct buffer_head * bh)
{
unsigned int sector, count;
struct request * req;
int rw_ahead, max_req;
/* WRITEA/READA is special case - it is not really needed, so if the */
/* buffer is locked, we just forget about it, else it's a normal read */
rw_ahead = (rw == READA || rw == WRITEA);
if (rw_ahead) {
if (bh->b_lock)
return;
if (rw == READA)
rw = READ;
else
rw = WRITE;
}
if (rw!=READ && rw!=WRITE) {
printk("Bad block dev command, must be R/W/RA/WA\n");
return;
}
count = bh->b_size >> 9;
sector = bh->b_blocknr * count;
if (blk_size[major])
if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
bh->b_dirt = bh->b_uptodate = 0;
return;
}
lock_buffer(bh);
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
unlock_buffer(bh);
return;
}
/* we don't allow the write-requests to fill up the queue completely:
* we want some room for reads: they take precedence. The last third
* of the requests are only for reads.
*/
max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
/* big loop: look for a free request. */
repeat:
cli();
/* The scsi disk drivers completely remove the request from the queue when
* they start processing an entry. For this reason it is safe to continue
* to add links to the top entry for scsi devices.
*/
if ((major == HD_MAJOR
|| major == SCSI_DISK_MAJOR
|| major == SCSI_CDROM_MAJOR)
&& (req = blk_dev[major].current_request))
{
if (major == HD_MAJOR)
req = req->next;
while (req) {
if (req->dev == bh->b_dev &&
!req->waiting &&
req->cmd == rw &&
req->sector + req->nr_sectors == sector &&
req->nr_sectors < 254)
{
req->bhtail->b_reqnext = bh;
req->bhtail = bh;
req->nr_sectors += count;
bh->b_dirt = 0;
sti();
return;
}
if (req->dev == bh->b_dev &&
!req->waiting &&
req->cmd == rw &&
req->sector - count == sector &&
req->nr_sectors < 254)
{
req->nr_sectors += count;
bh->b_reqnext = req->bh;
req->buffer = bh->b_data;
req->current_nr_sectors = count;
req->sector = sector;
bh->b_dirt = 0;
req->bh = bh;
sti();
return;
}
req = req->next;
}
}
/* find an unused request. */
req = get_request(max_req, bh->b_dev);
/* if no request available: if rw_ahead, forget it; otherwise try again. */
if (! req) {
if (rw_ahead) {
sti();
unlock_buffer(bh);
return;
}
sleep_on(&wait_for_request);
sti();
goto repeat;
}
/* we found a request. */
sti();
/* fill up the request-info, and add it to the queue */
req->cmd = rw;
req->errors = 0;
req->sector = sector;
req->nr_sectors = count;
req->current_nr_sectors = count;
req->buffer = bh->b_data;
req->waiting = NULL;
req->bh = bh;
req->bhtail = bh;
req->next = NULL;
add_request(major+blk_dev,req);
}
void ll_rw_page(int rw, int dev, int page, char * buffer)
{
struct request * req;
unsigned int major = MAJOR(dev);
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
return;
}
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W");
if (rw == WRITE && is_read_only(dev)) {
printk("Can't page to read-only device 0x%X\n",dev);
return;
}
cli();
req = get_request_wait(NR_REQUEST, dev);
sti();
/* fill up the request-info, and add it to the queue */
req->cmd = rw;
req->errors = 0;
req->sector = page<<3;
req->nr_sectors = 8;
req->current_nr_sectors = 8;
req->buffer = buffer;
req->waiting = current;
req->bh = NULL;
req->next = NULL;
current->state = TASK_SWAPPING;
add_request(major+blk_dev,req);
schedule();
}
/* This function can be used to request a number of buffers from a block
device. Currently the only restriction is that all buffers must belong to
the same device */
void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
{
unsigned int major;
struct request plug;
int plugged;
int correct_size;
struct blk_dev_struct * dev;
int i;
/* Make sure that the first block contains something reasonable */
while (!*bh) {
bh++;
if (--nr <= 0)
return;
};
dev = NULL;
if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
dev = blk_dev + major;
if (!dev || !dev->request_fn) {
printk(
"ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
(unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
goto sorry;
}
/* Determine correct block size for this device. */
correct_size = BLOCK_SIZE;
if (blksize_size[major]) {
i = blksize_size[major][MINOR(bh[0]->b_dev)];
if (i)
correct_size = i;
}
/* Verify requested block sizees. */
for (i = 0; i < nr; i++) {
if (bh[i] && bh[i]->b_size != correct_size) {
printk(
"ll_rw_block: only %d-char blocks implemented (%lu)\n",
correct_size, bh[i]->b_size);
goto sorry;
}
}
if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
goto sorry;
}
/* If there are no pending requests for this device, then we insert
a dummy request for that device. This will prevent the request
from starting until we have shoved all of the blocks into the
queue, and then we let it rip. */
plugged = 0;
cli();
if (!dev->current_request && nr > 1) {
dev->current_request = &plug;
plug.dev = -1;
plug.next = NULL;
plugged = 1;
}
sti();
for (i = 0; i < nr; i++) {
if (bh[i]) {
bh[i]->b_req = 1;
make_request(major, rw, bh[i]);
if (rw == READ || rw == READA)
kstat.pgpgin++;
else
kstat.pgpgout++;
}
}
if (plugged) {
cli();
dev->current_request = plug.next;
(dev->request_fn)();
sti();
}
return;
sorry:
for (i = 0; i < nr; i++) {
if (bh[i])
bh[i]->b_dirt = bh[i]->b_uptodate = 0;
}
return;
}
void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
{
int i;
int buffersize;
struct request * req;
unsigned int major = MAJOR(dev);
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
return;
}
if (rw!=READ && rw!=WRITE) {
printk("ll_rw_swap: bad block dev command, must be R/W");
return;
}
if (rw == WRITE && is_read_only(dev)) {
printk("Can't swap to read-only device 0x%X\n",dev);
return;
}
buffersize = PAGE_SIZE / nb;
for (i=0; i<nb; i++, buf += buffersize)
{
cli();
req = get_request_wait(NR_REQUEST, dev);
sti();
req->cmd = rw;
req->errors = 0;
req->sector = (b[i] * buffersize) >> 9;
req->nr_sectors = buffersize >> 9;
req->current_nr_sectors = buffersize >> 9;
req->buffer = buf;
req->waiting = current;
req->bh = NULL;
req->next = NULL;
current->state = TASK_UNINTERRUPTIBLE;
add_request(major+blk_dev,req);
schedule();
}
}
long blk_dev_init(long mem_start, long mem_end)
{
struct request * req;
req = all_requests + NR_REQUEST;
while (--req >= all_requests) {
req->dev = -1;
req->next = NULL;
}
memset(ro_bits,0,sizeof(ro_bits));
#ifdef CONFIG_BLK_DEV_HD
mem_start = hd_init(mem_start,mem_end);
#endif
#ifdef CONFIG_BLK_DEV_XD
mem_start = xd_init(mem_start,mem_end);
#endif
#ifdef CONFIG_CDU31A
mem_start = cdu31a_init(mem_start,mem_end);
#endif
#ifdef CONFIG_MCD
mem_start = mcd_init(mem_start,mem_end);
#endif
#ifdef CONFIG_SBPCD
mem_start = sbpcd_init(mem_start, mem_end);
#endif CONFIG_SBPCD
if (ramdisk_size)
mem_start += rd_init(mem_start, ramdisk_size*1024);
return mem_start;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,178 @@
/*
* linux/kernel/blk_drv/ramdisk.c
*
* Written by Theodore Ts'o, 12/2/91
*
* Modifications by Fred N. van Kempen to allow for bootable root
* disks (which are used in LINUX/Pro). Also some cleanups. 03/03/93
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/segment.h>
#define MAJOR_NR MEM_MAJOR
#include "blk.h"
#define RAMDISK_MINOR 1
char *rd_start;
int rd_length = 0;
static int rd_blocksizes[2] = {0, 0};
static void do_rd_request(void)
{
int len;
char *addr;
repeat:
INIT_REQUEST;
addr = rd_start + (CURRENT->sector << 9);
len = CURRENT->current_nr_sectors << 9;
if ((MINOR(CURRENT->dev) != RAMDISK_MINOR) ||
(addr+len > rd_start+rd_length)) {
end_request(0);
goto repeat;
}
if (CURRENT-> cmd == WRITE) {
(void ) memcpy(addr,
CURRENT->buffer,
len);
} else if (CURRENT->cmd == READ) {
(void) memcpy(CURRENT->buffer,
addr,
len);
} else
panic("RAMDISK: unknown RAM disk command !\n");
end_request(1);
goto repeat;
}
static struct file_operations rd_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
block_fsync /* fsync */
};
/*
* Returns amount of memory which needs to be reserved.
*/
long rd_init(long mem_start, int length)
{
int i;
char *cp;
if (register_blkdev(MEM_MAJOR,"rd",&rd_fops)) {
printk("RAMDISK: Unable to get major %d.\n", MEM_MAJOR);
return 0;
}
blk_dev[MEM_MAJOR].request_fn = DEVICE_REQUEST;
rd_start = (char *) mem_start;
rd_length = length;
cp = rd_start;
for (i=0; i < length; i++)
*cp++ = '\0';
for(i=0;i<2;i++) rd_blocksizes[i] = 1024;
blksize_size[MAJOR_NR] = rd_blocksizes;
return(length);
}
/*
* If the root device is the RAM disk, try to load it.
* In order to do this, the root device is originally set to the
* floppy, and we later change it to be RAM disk.
*/
void rd_load(void)
{
struct buffer_head *bh;
struct minix_super_block s;
int block, tries;
int i = 1;
int nblocks;
char *cp;
/* If no RAM disk specified, give up early. */
if (!rd_length) return;
printk("RAMDISK: %d bytes, starting at 0x%x\n",
rd_length, (int) rd_start);
/* If we are doing a diskette boot, we might have to pre-load it. */
if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
/*
* Check for a super block on the diskette.
* The old-style boot/root diskettes had their RAM image
* starting at block 512 of the boot diskette. LINUX/Pro
* uses the enire diskette as a file system, so in that
* case, we have to look at block 0. Be intelligent about
* this, and check both... - FvK
*/
for (tries = 0; tries < 1000; tries += 512) {
block = tries;
bh = breada(ROOT_DEV,block+1,block,block+2,-1);
if (!bh) {
printk("RAMDISK: I/O error while looking for super block!\n");
return;
}
/* This is silly- why do we require it to be a MINIX FS? */
*((struct minix_super_block *) &s) =
*((struct minix_super_block *) bh->b_data);
brelse(bh);
nblocks = s.s_nzones << s.s_log_zone_size;
if (s.s_magic != MINIX_SUPER_MAGIC &&
s.s_magic != MINIX_SUPER_MAGIC2) {
printk("RAMDISK: trying old-style RAM image.\n");
continue;
}
if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
printk("RAMDISK: image too big! (%d/%d blocks)\n",
nblocks, rd_length >> BLOCK_SIZE_BITS);
return;
}
printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
/* We found an image file system. Load it into core! */
cp = rd_start;
while (nblocks) {
if (nblocks > 2)
bh = breada(ROOT_DEV, block, block+1, block+2, -1);
else
bh = bread(ROOT_DEV, block, BLOCK_SIZE);
if (!bh) {
printk("RAMDISK: I/O error on block %d, aborting!\n",
block);
return;
}
(void) memcpy(cp, bh->b_data, BLOCK_SIZE);
brelse(bh);
if (!(nblocks-- & 15)) printk(".");
cp += BLOCK_SIZE;
block++;
i++;
}
printk("\ndone\n");
/* We loaded the file system image. Prepare for mounting it. */
ROOT_DEV = ((MEM_MAJOR << 8) | RAMDISK_MINOR);
return;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,638 @@
/*
* This file contains the driver for an XT hard disk controller (at least the DTC 5150X) for Linux.
*
* Author: Pat Mackinlay, smackinla@cc.curtin.edu.au
* Date: 29/09/92
*
* Revised: 01/01/93, ...
*
* Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com)
* Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst.
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
#include <linux/xd.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/dma.h>
#define MAJOR_NR XT_DISK_MAJOR
#include "blk.h"
XD_INFO xd_info[XD_MAXDRIVES];
/* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS
signature and details to the following list of signatures. A BIOS signature is a string embedded into the first
few bytes of your controller's on-board ROM BIOS. To find out what yours is, use something like MS-DOS's DEBUG
command. Run DEBUG, and then you can examine your BIOS signature with:
d xxxx:0000
where xxxx is the segment of your controller (like C800 or D000 or something). On the ASCII dump at the right, you should
be able to see a string mentioning the manufacturer's copyright etc. Add this string into the table below. The parameters
in the table are, in order:
offset ; this is the offset (in bytes) from the start of your ROM where the signature starts
signature ; this is the actual text of the signature
xd_?_init_controller ; this is the controller init routine used by your controller
xd_?_init_drive ; this is the drive init routine used by your controller
The controllers directly supported at the moment are: DTC 5150x, WD 1004A27X, ST11M/R and override. If your controller is
made by the same manufacturer as one of these, try using the same init routines as they do. If that doesn't work, your
best bet is to use the "override" routines. These routines use a "portable" method of getting the disk's geometry, and
may work with your card. If none of these seem to work, try sending me some email and I'll see what I can do <grin>.
NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver
should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */
static XD_SIGNATURE xd_sigs[] = {
{ 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, smackinla@cc.curtin.edu.au (pat@gu.uwa.edu.au) */
{ 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, smackinla@cc.curtin.edu.au (pat@gu.uwa.edu.au) */
{ 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */
{ 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */
{ 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Digital WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */
{ 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */
{ 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */
};
static u_char *xd_bases[] =
{
(u_char *) 0xC8000,(u_char *) 0xCA000,(u_char *) 0xCC000,
(u_char *) 0xCE000,(u_char *) 0xD0000,(u_char *) 0xD8000,
(u_char *) 0xE0000
};
static struct hd_struct xd[XD_MAXDRIVES << 6];
static int xd_sizes[XD_MAXDRIVES << 6],xd_access[XD_MAXDRIVES] = { 0,0 };
static int xd_blocksizes[XD_MAXDRIVES << 6];
static struct gendisk xd_gendisk = { MAJOR_NR,"xd",6,1 << 6,XD_MAXDRIVES,xd_geninit,xd,xd_sizes,0,(void *) xd_info,NULL };
static struct file_operations xd_fops = { NULL,block_read,block_write,NULL,NULL,xd_ioctl,NULL,xd_open,xd_release,block_fsync };
static struct wait_queue *xd_wait_int = NULL,*xd_wait_open = NULL;
static u_char xd_valid[XD_MAXDRIVES] = { 0,0 };
static u_char xd_drives = 0,xd_irq = 0,xd_dma = 0,xd_maxsectors,xd_override = 0,xd_type = 0;
static u_short xd_iobase = 0;
/* xd_init: grab the IRQ and DMA channel and initialise the drives */
u_long xd_init (u_long mem_start,u_long mem_end)
{
u_char i,controller,*address;
if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) {
printk("xd_init: unable to get major number %d\n",MAJOR_NR);
return (mem_start);
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
xd_gendisk.next = gendisk_head;
gendisk_head = &xd_gendisk;
if (xd_detect(&controller,&address)) {
printk("xd_init: detected a%s controller (type %d) at address %p\n",xd_sigs[controller].name,controller,address);
if (controller)
xd_sigs[controller].init_controller(address);
xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
printk("xd_init: detected %d hard drive%s (using IRQ%d & DMA%d)\n",xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma);
for (i = 0; i < xd_drives; i++)
printk("xd_init: drive %d geometry - heads = %d, cylinders = %d, sectors = %d\n",i,xd_info[i].heads,xd_info[i].cylinders,xd_info[i].sectors);
if (!request_irq(xd_irq,xd_interrupt_handler)) {
if (request_dma(xd_dma)) {
printk("xd_init: unable to get DMA%d\n",xd_dma);
free_irq(xd_irq);
}
}
else
printk("xd_init: unable to get IRQ%d\n",xd_irq);
}
return mem_start;
}
/* xd_detect: scan the possible BIOS ROM locations for the signature strings */
static u_char xd_detect (u_char *controller,u_char **address)
{
u_char i,j,found = 0;
if (xd_override)
{
*controller = xd_type;
*address = NULL;
return(1);
}
for (i = 0; i < (sizeof(xd_bases) / sizeof(xd_bases[0])) && !found; i++)
for (j = 1; j < (sizeof(xd_sigs) / sizeof(xd_sigs[0])) && !found; j++)
if (!memcmp(xd_bases[i] + xd_sigs[j].offset,xd_sigs[j].string,strlen(xd_sigs[j].string))) {
*controller = j;
*address = xd_bases[i];
found++;
}
return (found);
}
/* xd_geninit: set up the "raw" device entries in the table */
static void xd_geninit (void)
{
u_char i;
for (i = 0; i < xd_drives; i++) {
xd[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors;
xd_valid[i] = 1;
}
xd_gendisk.nr_real = xd_drives;
for(i=0;i<(XD_MAXDRIVES << 6);i++) xd_blocksizes[i] = 1024;
blksize_size[MAJOR_NR] = xd_blocksizes;
}
/* xd_open: open a device */
static int xd_open (struct inode *inode,struct file *file)
{
int dev = DEVICE_NR(MINOR(inode->i_rdev));
if (dev < xd_drives) {
while (!xd_valid[dev])
sleep_on(&xd_wait_open);
xd_access[dev]++;
return (0);
}
else
return (-ENODEV);
}
/* do_xd_request: handle an incoming request */
static void do_xd_request (void)
{
u_int block,count,retry;
int code;
sti();
while (code = 0, CURRENT) {
INIT_REQUEST; /* do some checking on the request structure */
if (CURRENT_DEV < xd_drives && CURRENT->sector + CURRENT->nr_sectors <= xd[MINOR(CURRENT->dev)].nr_sects) {
block = CURRENT->sector + xd[MINOR(CURRENT->dev)].start_sect;
count = CURRENT->nr_sectors;
switch (CURRENT->cmd) {
case READ:
case WRITE: for (retry = 0; (retry < XD_RETRIES) && !code; retry++)
code = xd_readwrite(CURRENT->cmd,CURRENT_DEV,CURRENT->buffer,block,count);
break;
default: printk("do_xd_request: unknown request\n"); break;
}
}
end_request(code); /* wrap up, 0 = fail, 1 = success */
}
}
/* xd_ioctl: handle device ioctl's */
static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
{
XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg;
int dev = DEVICE_NR(MINOR(inode->i_rdev)),err;
if (inode && (dev < xd_drives))
switch (cmd) {
case HDIO_GETGEO: if (arg) {
if ((err = verify_area(VERIFY_WRITE,geometry,sizeof(*geometry))))
return (err);
put_fs_byte(xd_info[dev].heads,(char *) &geometry->heads);
put_fs_byte(xd_info[dev].sectors,(char *) &geometry->sectors);
put_fs_word(xd_info[dev].cylinders,(short *) &geometry->cylinders);
put_fs_long(xd[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start);
return (0);
}
break;
case BLKGETSIZE: if (arg) {
if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long))))
return (err);
put_fs_long(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
return (0);
}
break;
case BLKFLSBUF:
if(!suser()) return -EACCES;
if(!inode->i_rdev) return -EINVAL;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
case BLKRRPART: return (xd_reread_partitions(inode->i_rdev));
RO_IOCTLS(inode->i_rdev,arg);
}
return (-EINVAL);
}
/* xd_release: release the device */
static void xd_release (struct inode *inode, struct file *file)
{
int dev = DEVICE_NR(MINOR(inode->i_rdev));
if (dev < xd_drives) {
sync_dev(dev);
xd_access[dev]--;
}
}
/* xd_reread_partitions: rereads the partition table from a drive */
static int xd_reread_partitions(int dev)
{
int target = DEVICE_NR(MINOR(dev)),start = target << xd_gendisk.minor_shift,partition;
cli(); xd_valid[target] = (xd_access[target] != 1); sti();
if (xd_valid[target])
return (-EBUSY);
for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) {
sync_dev(MAJOR_NR << 8 | start | partition);
invalidate_inodes(MAJOR_NR << 8 | start | partition);
invalidate_buffers(MAJOR_NR << 8 | start | partition);
xd_gendisk.part[start + partition].start_sect = 0;
xd_gendisk.part[start + partition].nr_sects = 0;
};
xd_gendisk.part[start].nr_sects = xd_info[target].heads * xd_info[target].cylinders * xd_info[target].sectors;
resetup_one_dev(&xd_gendisk,target);
xd_valid[target] = 1;
wake_up(&xd_wait_open);
return (0);
}
/* xd_readwrite: handle a read/write request */
static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,u_int count)
{
u_char cmdblk[6],sense[4];
u_short track,cylinder;
u_char head,sector,control,mode,temp;
#ifdef DEBUG_READWRITE
printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count);
#endif /* DEBUG_READWRITE */
control = xd_info[drive].control;
while (count) {
temp = count < xd_maxsectors ? count : xd_maxsectors;
track = block / xd_info[drive].sectors;
head = track % xd_info[drive].heads;
cylinder = track / xd_info[drive].heads;
sector = block % xd_info[drive].sectors;
#ifdef DEBUG_READWRITE
printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp);
#endif /* DEBUG_READWRITE */
mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)buffer,temp * 0x200);
xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control);
switch (xd_command(cmdblk,mode,(u_char *) buffer,(u_char *) buffer,sense,XD_TIMEOUT)) {
case 1: printk("xd_readwrite: timeout, recalibrating drive\n"); xd_recalibrate(drive); return (0);
case 2: switch ((sense[0] & 0x30) >> 4) {
case 0: printk("xd_readwrite: drive error, code = 0x%X",sense[0] & 0x0F); break;
case 1: printk("xd_readwrite: controller error, code = 0x%X",sense[0] & 0x0F); break;
case 2: printk("xd_readwrite: command error, code = 0x%X",sense[0] & 0x0F); break;
case 3: printk("xd_readwrite: miscellaneous error, code = 0x%X",sense[0] & 0x0F); break;
}
if (sense[0] & 0x80)
printk(" - drive = %d, head = %d, cylinder = %d, sector = %d\n",sense[1] & 0xE0,sense[1] & 0x1F,((sense[2] & 0xC0) << 2) | sense[3],sense[2] & 0x3F);
else
printk(" - no valid disk address\n");
return (0);
}
count -= temp, buffer += temp * 0x200, block += temp;
}
return (1);
}
/* xd_recalibrate: recalibrate a given drive and reset controller if necessary */
static void xd_recalibrate (u_char drive)
{
u_char cmdblk[6];
xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0);
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8))
printk("xd_recalibrate: warning! error recalibrating, controller may be unstable\n");
}
/* xd_interrupt_handler: interrupt service routine */
static void xd_interrupt_handler (int unused)
{
if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */
#ifdef DEBUG_OTHER
printk("xd_interrupt_handler: interrupt detected\n");
#endif /* DEBUG_OTHER */
outb(0,XD_CONTROL); /* acknowledge interrupt */
wake_up(&xd_wait_int); /* and wake up sleeping processes */
}
else
printk("xd_interrupt_handler: unexpected interrupt\n");
}
/* xd_dma: set up the DMA controller for a data transfer */
static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count)
{
if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */
if (((u_int) buffer & 0xFFFF0000) != ((u_int) buffer + count) & 0xFFFF0000) {
#ifdef DEBUG_OTHER
printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n");
#endif /* DEBUG_OTHER */
return (PIO_MODE);
}
disable_dma(xd_dma);
clear_dma_ff(xd_dma);
set_dma_mode(xd_dma,mode);
set_dma_addr(xd_dma,(u_int) buffer);
set_dma_count(xd_dma,count);
return (DMA_MODE); /* use DMA and INT */
}
#ifdef DEBUG_OTHER
printk("xd_setup_dma: using PIO, cannot DMA above 16 meg\n");
#endif /* DEBUG_OTHER */
return (PIO_MODE);
}
/* xd_build: put stuff into an array in a format suitable for the controller */
static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control)
{
cmdblk[0] = command;
cmdblk[1] = ((drive & 0x07) << 5) | (head & 0x1F);
cmdblk[2] = ((cylinder & 0x300) >> 2) | (sector & 0x3F);
cmdblk[3] = cylinder & 0xFF;
cmdblk[4] = count;
cmdblk[5] = control;
return (cmdblk);
}
/* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */
static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout)
{
u_long expiry = jiffies + timeout;
while (((inb(port) & mask) != flags) && (jiffies < expiry))
;
return (jiffies >= expiry);
}
/* xd_command: handle all data transfers necessary for a single command */
static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout)
{
u_char cmdblk[6],csb,complete = 0;
#ifdef DEBUG_COMMAND
printk("xd_command: command = 0x%X, mode = 0x%X, indata = 0x%X, outdata = 0x%X, sense = 0x%X\n",command,mode,indata,outdata,sense);
#endif /* DEBUG_COMMAND */
outb(0,XD_SELECT);
outb(mode,XD_CONTROL);
if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout))
return (1);
while (!complete) {
if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout))
return (1);
switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) {
case 0: if (mode == DMA_MODE) {
enable_dma(xd_dma);
sleep_on(&xd_wait_int);
disable_dma(xd_dma);
}
else
outb(outdata ? *outdata++ : 0,XD_DATA);
break;
case STAT_INPUT: if (mode == DMA_MODE) {
enable_dma(xd_dma);
sleep_on(&xd_wait_int);
disable_dma(xd_dma);
}
else
if (indata)
*indata++ = inb(XD_DATA);
else
inb(XD_DATA);
break;
case STAT_COMMAND: outb(command ? *command++ : 0,XD_DATA); break;
case STAT_COMMAND
| STAT_INPUT: complete = 1; break;
}
}
csb = inb(XD_DATA);
if (xd_waitport(XD_STATUS,0,STAT_SELECT,timeout)) /* wait until deselected */
return (1);
if (csb & CSB_ERROR) { /* read sense data if error */
xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0);
if (xd_command(cmdblk,0,sense,0,0,XD_TIMEOUT))
printk("xd_command: warning! sense command failed!\n");
}
#ifdef DEBUG_COMMAND
printk("xd_command: completed with csb = 0x%X\n",csb);
#endif /* DEBUG_COMMAND */
return (csb & CSB_ERROR);
}
static u_char xd_initdrives (void (*init_drive)(u_char drive))
{
u_char cmdblk[6],i,count = 0;
for (i = 0; i < XD_MAXDRIVES; i++) {
xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0);
if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) {
init_drive(count);
count++;
}
}
return (count);
}
static void xd_dtc_init_controller (u_char *address)
{
switch ((u_long) address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xCA000: xd_iobase = 0x324; break;
default: printk("xd_dtc_init_controller: unsupported BIOS address %p\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* the IRQ _can_ be changed on this card, but requires a hardware mod */
xd_dma = 3;
xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */
outb(0,XD_RESET); /* reset the controller */
}
static void xd_dtc_init_drive (u_char drive)
{
u_char cmdblk[6],buf[64];
xd_build(cmdblk,CMD_DTCGETGEOM,drive,0,0,0,0,0);
if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) {
xd_info[drive].heads = buf[0x0A]; /* heads */
xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */
xd_info[drive].sectors = 17; /* sectors */
#if 0
xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */
xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */
xd_info[drive].ecc = buf[0x0F]; /* ecc length */
#endif /* 0 */
xd_info[drive].control = 0; /* control byte */
xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]);
xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7);
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
printk("xd_dtc_init_drive: error setting step rate for drive %d\n",drive);
}
else
printk("xd_dtc_init_drive: error reading geometry for drive %d\n",drive);
}
static void xd_wd_init_controller (u_char *address)
{
switch ((u_long) address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xCA000: xd_iobase = 0x324; break;
case 0xCC000: xd_iobase = 0x328; break;
case 0xCE000: xd_iobase = 0x32C; break;
case 0xD0000: xd_iobase = 0x328; break;
case 0xD8000: xd_iobase = 0x32C; break;
default: printk("xd_wd_init_controller: unsupported BIOS address %p\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* don't know how to auto-detect this yet */
xd_dma = 3;
xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */
/* outb(0,XD_RESET); */ /* reset the controller */
}
static void xd_wd_init_drive (u_char drive)
{
u_char cmdblk[6],buf[0x200];
xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0);
if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) {
xd_info[drive].heads = buf[0x1AF]; /* heads */
xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */
xd_info[drive].sectors = 17; /* sectors */
#if 0
xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */
xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */
xd_info[drive].ecc = buf[0x1B4]; /* ecc length */
#endif /* 0 */
xd_info[drive].control = buf[0x1B5]; /* control byte */
xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]);
}
else
printk("xd_wd_init_drive: error reading geometry for drive %d\n",drive);
}
static void xd_seagate_init_controller (u_char *address)
{
switch ((u_long) address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xD0000: xd_iobase = 0x324; break;
case 0xD8000: xd_iobase = 0x328; break;
case 0xE0000: xd_iobase = 0x32C; break;
default: printk("xd_seagate_init_controller: unsupported BIOS address %p\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* the IRQ and DMA channel are fixed on the Seagate controllers */
xd_dma = 3;
xd_maxsectors = 0x40;
outb(0,XD_RESET); /* reset the controller */
}
static void xd_seagate_init_drive (u_char drive)
{
u_char cmdblk[6],buf[0x200];
xd_build(cmdblk,CMD_ST11GETGEOM,drive,0,0,0,1,0);
if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) {
xd_info[drive].heads = buf[0x04]; /* heads */
xd_info[drive].cylinders = (buf[0x02] << 8) | buf[0x03]; /* cylinders */
xd_info[drive].sectors = buf[0x05]; /* sectors */
xd_info[drive].control = 0; /* control byte */
}
else
printk("xd_seagate_init_drive: error reading geometry from drive %d\n",drive);
}
/* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads
etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */
static void xd_override_init_drive (u_char drive)
{
u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 };
u_char cmdblk[6],i;
for (i = 0; i < 3; i++) {
while (min[i] != max[i] - 1) {
test[i] = (min[i] + max[i]) / 2;
xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0);
if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
min[i] = test[i];
else
max[i] = test[i];
}
test[i] = min[i];
}
xd_info[drive].heads = (u_char) min[0] + 1;
xd_info[drive].cylinders = (u_short) min[1] + 1;
xd_info[drive].sectors = (u_char) min[2] + 1;
xd_info[drive].control = 0;
}
/* xd_setup: initialise from command line parameters */
void xd_setup (char *command,int *integers)
{
xd_override = 1;
xd_type = integers[1];
xd_irq = integers[2];
xd_iobase = integers[3];
xd_dma = integers[4];
xd_maxsectors = 0x01;
}
/* xd_setparam: set the drive characteristics */
static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc)
{
u_char cmdblk[14];
xd_build(cmdblk,command,drive,0,0,0,0,0);
cmdblk[6] = (u_char) (cylinders >> 8) & 0x03;
cmdblk[7] = (u_char) (cylinders & 0xFF);
cmdblk[8] = heads & 0x1F;
cmdblk[9] = (u_char) (rwrite >> 8) & 0x03;
cmdblk[10] = (u_char) (rwrite & 0xFF);
cmdblk[11] = (u_char) (wprecomp >> 8) & 0x03;
cmdblk[12] = (u_char) (wprecomp & 0xFF);
cmdblk[13] = ecc;
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
printk("xd_setparam: error setting characteristics for drive %d\n",drive);
}

View file

@ -0,0 +1,87 @@
#
# Makefile for the kernel character device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now inherited from the
# parent makes..
#
.c.s:
$(CC) $(CFLAGS) -S $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) -c $<
OBJS = tty_io.o console.o keyboard.o serial.o \
tty_ioctl.o pty.o vt.o mem.o \
defkeymap.o
SRCS = tty_io.c console.c keyboard.c serial.c \
tty_ioctl.c pty.c vt.c mem.c \
defkeymap.c
ifdef CONFIG_ATIXL_BUSMOUSE
M = y
OBJS := $(OBJS) atixlmouse.o
SRCS := $(SRCS) atixlmouse.c
endif
ifdef CONFIG_BUSMOUSE
M = y
OBJS := $(OBJS) busmouse.o
SRCS := $(SRCS) busmouse.c
endif
ifdef CONFIG_PRINTER
OBJS := $(OBJS) lp.o
SRCS := $(SRCS) lp.c
endif
ifdef CONFIG_MS_BUSMOUSE
M = y
OBJS := $(OBJS) msbusmouse.o
SRCS := $(SRCS) msbusmouse.c
endif
ifdef CONFIG_82C710_MOUSE
CONFIG_PSMOUSE = CONFIG_PSMOUSE
endif
ifdef CONFIG_PSMOUSE
M = y
OBJS := $(OBJS) psaux.o
SRCS := $(SRCS) psaux.c
endif
ifdef CONFIG_TAPE_QIC02
OBJS := $(OBJS) tpqic02.o
SRCS := $(SRCS) tpqic02.c
endif
ifdef M
OBJS := $(OBJS) mouse.o
SRCS := $(SRCS) mouse.c
endif
all: char.a
char.a: $(OBJS)
$(AR) rcs char.a $(OBJS)
sync
dep:
$(CPP) -M $(SRCS) > .depend
dummy:
#
# include a dependency file if one exists
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif

View file

@ -0,0 +1,198 @@
/*
* ATI XL Bus Mouse Driver for Linux
* by Bob Harris (rth@sparta.com)
*
* Uses VFS interface for linux 0.98 (01OCT92)
*
* Modified by Chris Colohan (colohan@eecg.toronto.edu)
*
* version 0.3
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
#define ATIXL_MOUSE_IRQ 5 /* H/W interrupt # set up on ATIXL board */
#define ATIXL_BUSMOUSE 3 /* Minor device # (mknod c 10 3 /dev/bm) */
/* ATI XL Inport Busmouse Definitions */
#define ATIXL_MSE_DATA_PORT 0x23d
#define ATIXL_MSE_SIGNATURE_PORT 0x23e
#define ATIXL_MSE_CONTROL_PORT 0x23c
#define ATIXL_MSE_READ_BUTTONS 0x00
#define ATIXL_MSE_READ_X 0x01
#define ATIXL_MSE_READ_Y 0x02
/* Some nice ATI XL macros */
/* Select IR7, HOLD UPDATES (INT ENABLED), save X,Y */
#define ATIXL_MSE_DISABLE_UPDATE() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0x20 | inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Select IR7, Enable updates (INT ENABLED) */
#define ATIXL_MSE_ENABLE_UPDATE() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0xdf & inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Select IR7 - Mode Register, NO INTERRUPTS */
#define ATIXL_MSE_INT_OFF() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0xe7 & inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Select IR7 - Mode Register, DATA INTERRUPTS ENABLED */
#define ATIXL_MSE_INT_ON() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \
outb( (0x08 | inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); }
/* Same general mouse structure */
static struct mouse_status {
char buttons;
char latch_buttons;
int dx;
int dy;
int present;
int ready;
int active;
struct wait_queue *wait;
} mouse;
void mouse_interrupt(int unused)
{
char dx, dy, buttons;
ATIXL_MSE_DISABLE_UPDATE(); /* Note that interrupts are still enabled */
outb(ATIXL_MSE_READ_X, ATIXL_MSE_CONTROL_PORT); /* Select IR1 - X movement */
dx = inb( ATIXL_MSE_DATA_PORT);
outb(ATIXL_MSE_READ_Y, ATIXL_MSE_CONTROL_PORT); /* Select IR2 - Y movement */
dy = inb( ATIXL_MSE_DATA_PORT);
outb(ATIXL_MSE_READ_BUTTONS, ATIXL_MSE_CONTROL_PORT); /* Select IR0 - Button Status */
buttons = inb( ATIXL_MSE_DATA_PORT);
if (dx != 0 || dy != 0 || buttons != mouse.latch_buttons) {
mouse.latch_buttons |= buttons;
mouse.dx += dx;
mouse.dy += dy;
mouse.ready = 1;
wake_up_interruptible(&mouse.wait);
}
ATIXL_MSE_ENABLE_UPDATE();
}
static void release_mouse(struct inode * inode, struct file * file)
{
ATIXL_MSE_INT_OFF(); /* Interrupts are really shut down here */
mouse.active = 0;
mouse.ready = 0;
free_irq(ATIXL_MOUSE_IRQ);
}
static int open_mouse(struct inode * inode, struct file * file)
{
if (!mouse.present)
return -EINVAL;
if (mouse.active)
return -EBUSY;
mouse.active = 1;
mouse.ready = 0;
mouse.dx = 0;
mouse.dy = 0;
mouse.buttons = mouse.latch_buttons = 0;
if (request_irq(ATIXL_MOUSE_IRQ, mouse_interrupt)) {
mouse.active = 0;
return -EBUSY;
}
ATIXL_MSE_INT_ON(); /* Interrupts are really enabled here */
return 0;
}
static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
return -EINVAL;
}
static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
int i;
if (count < 3)
return -EINVAL;
if (!mouse.ready)
return -EAGAIN;
ATIXL_MSE_DISABLE_UPDATE();
/* Allowed interrupts to occur during data gathering - shouldn't hurt */
put_fs_byte((char)(~mouse.latch_buttons&7) | 0x80 , buffer);
if (mouse.dx < -127)
mouse.dx = -127;
if (mouse.dx > 127)
mouse.dx = 127;
put_fs_byte((char)mouse.dx, buffer + 1);
if (mouse.dy < -127)
mouse.dy = -127;
if (mouse.dy > 127)
mouse.dy = 127;
put_fs_byte((char)-mouse.dy, buffer + 2);
for(i = 3; i < count; i++)
put_fs_byte(0x00, buffer + i);
mouse.dx = 0;
mouse.dy = 0;
mouse.latch_buttons = mouse.buttons;
mouse.ready = 0;
ATIXL_MSE_ENABLE_UPDATE();
return i; /* i data bytes returned */
}
static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
if (sel_type != SEL_IN)
return 0;
if (mouse.ready)
return 1;
select_wait(&mouse.wait,wait);
return 0;
}
struct file_operations atixl_busmouse_fops = {
NULL, /* mouse_seek */
read_mouse,
write_mouse,
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
NULL, /* mouse_mmap */
open_mouse,
release_mouse,
};
unsigned long atixl_busmouse_init(unsigned long kmem_start)
{
unsigned char a,b,c;
a = inb( ATIXL_MSE_SIGNATURE_PORT ); /* Get signature */
b = inb( ATIXL_MSE_SIGNATURE_PORT );
c = inb( ATIXL_MSE_SIGNATURE_PORT );
if (( a != b ) && ( a == c ))
printk("\nATI Inport ");
else{
mouse.present = 0;
return kmem_start;
}
outb(0x80, ATIXL_MSE_CONTROL_PORT); /* Reset the Inport device */
outb(0x07, ATIXL_MSE_CONTROL_PORT); /* Select Internal Register 7 */
outb(0x0a, ATIXL_MSE_DATA_PORT); /* Data Interrupts 8+, 1=30hz, 2=50hz, 3=100hz, 4=200hz rate */
mouse.present = 1;
mouse.active = 0;
mouse.ready = 0;
mouse.buttons = mouse.latch_buttons = 0;
mouse.dx = mouse.dy = 0;
mouse.wait = NULL;
printk("Bus mouse detected and installed.\n");
return kmem_start;
}

View file

@ -0,0 +1,239 @@
/*
* Logitech Bus Mouse Driver for Linux
* by James Banks
*
* Mods by Matthew Dillon
* calls verify_area()
* tracks better when X is busy or paging
*
* Heavily modified by David Giller
* changed from queue- to counter- driven
* hacked out a (probably incorrect) mouse_select
*
* Modified again by Nathan Laredo to interface with
* 0.96c-pl1 IRQ handling changes (13JUL92)
* didn't bother touching select code.
*
* Modified the select() code blindly to conform to the VFS
* requirements. 92.07.14 - Linus. Somebody should test it out.
*
* Modified by Johan Myreen to make room for other mice (9AUG92)
* removed assignment chr_fops[10] = &mouse_fops; see mouse.c
* renamed mouse_fops => bus_mouse_fops, made bus_mouse_fops public.
* renamed this file mouse.c => busmouse.c
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/busmouse.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
static struct mouse_status mouse;
static int mouse_irq = MOUSE_IRQ;
void bmouse_setup(char *str, int *ints)
{
if (ints[0] > 0)
mouse_irq=ints[1];
}
static void mouse_interrupt(int unused)
{
char dx, dy;
unsigned char buttons;
MSE_INT_OFF();
outb(MSE_READ_X_LOW, MSE_CONTROL_PORT);
dx = (inb(MSE_DATA_PORT) & 0xf);
outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT);
dx |= (inb(MSE_DATA_PORT) & 0xf) << 4;
outb(MSE_READ_Y_LOW, MSE_CONTROL_PORT );
dy = (inb(MSE_DATA_PORT) & 0xf);
outb(MSE_READ_Y_HIGH, MSE_CONTROL_PORT);
buttons = inb(MSE_DATA_PORT);
dy |= (buttons & 0xf) << 4;
buttons = ((buttons >> 5) & 0x07);
if (dx != 0 || dy != 0 || buttons != mouse.buttons) {
mouse.buttons = buttons;
mouse.dx += dx;
mouse.dy -= dy;
mouse.ready = 1;
wake_up_interruptible(&mouse.wait);
/*
* keep dx/dy reasonable, but still able to track when X (or
* whatever) must page or is busy (i.e. long waits between
* reads)
*/
if (mouse.dx < -2048)
mouse.dx = -2048;
if (mouse.dx > 2048)
mouse.dx = 2048;
if (mouse.dy < -2048)
mouse.dy = -2048;
if (mouse.dy > 2048)
mouse.dy = 2048;
}
MSE_INT_ON();
}
/*
* close access to the mouse (can deal with multiple
* opens if allowed in the future)
*/
static void close_mouse(struct inode * inode, struct file * file)
{
if (--mouse.active == 0) {
MSE_INT_OFF();
free_irq(mouse_irq);
}
}
/*
* open access to the mouse, currently only one open is
* allowed.
*/
static int open_mouse(struct inode * inode, struct file * file)
{
if (!mouse.present)
return -EINVAL;
if (mouse.active)
return -EBUSY;
mouse.ready = 0;
mouse.dx = 0;
mouse.dy = 0;
mouse.buttons = 0x87;
if (request_irq(mouse_irq, mouse_interrupt))
return -EBUSY;
mouse.active = 1;
MSE_INT_ON();
return 0;
}
/*
* writes are disallowed
*/
static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
return -EINVAL;
}
/*
* read mouse data. Currently never blocks.
*/
static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
int r;
int dx;
int dy;
unsigned char buttons;
if (count < 3)
return -EINVAL;
if ((r = verify_area(VERIFY_WRITE, buffer, count)))
return r;
if (!mouse.ready)
return -EAGAIN;
/*
* Obtain the current mouse parameters and limit as appropriate for
* the return data format. Interrupts are only disabled while
* obtaining the parameters, NOT during the puts_fs_byte() calls,
* so paging in put_fs_byte() does not effect mouse tracking.
*/
MSE_INT_OFF();
dx = mouse.dx;
dy = mouse.dy;
if (dx < -127)
dx = -127;
if (dx > 127)
dx = 127;
if (dy < -127)
dy = -127;
if (dy > 127)
dy = 127;
buttons = mouse.buttons;
mouse.dx -= dx;
mouse.dy -= dy;
mouse.ready = 0;
MSE_INT_ON();
put_fs_byte(buttons | 0x80, buffer);
put_fs_byte((char)dx, buffer + 1);
put_fs_byte((char)dy, buffer + 2);
for (r = 3; r < count; r++)
put_fs_byte(0x00, buffer + r);
return r;
}
/*
* select for mouse input, must disable the mouse interrupt while checking
* mouse.ready/select_wait() to avoid race condition (though in reality
* such a condition is not fatal to the proper operation of the mouse since
* multiple interrupts generally occur).
*/
static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
int r = 0;
if (sel_type == SEL_IN) {
MSE_INT_OFF();
if (mouse.ready) {
r = 1;
} else {
select_wait(&mouse.wait, wait);
}
MSE_INT_ON();
}
return(r);
}
struct file_operations bus_mouse_fops = {
NULL, /* mouse_seek */
read_mouse,
write_mouse,
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
NULL, /* mouse_mmap */
open_mouse,
close_mouse,
};
unsigned long bus_mouse_init(unsigned long kmem_start)
{
int i;
outb(MSE_CONFIG_BYTE, MSE_CONFIG_PORT);
outb(MSE_SIGNATURE_BYTE, MSE_SIGNATURE_PORT);
for (i = 0; i < 100000; i++)
/* busy loop */;
if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE) {
mouse.present = 0;
return kmem_start;
}
outb(MSE_DEFAULT_MODE, MSE_CONFIG_PORT);
MSE_INT_OFF();
mouse.present = 1;
mouse.active = 0;
mouse.ready = 0;
mouse.buttons = 0x87;
mouse.dx = 0;
mouse.dy = 0;
mouse.wait = NULL;
printk("Logitech Bus mouse detected and installed.\n");
return kmem_start;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,399 @@
/* Automatically generated by mktable */
/* Do not edit this file! */
#include <linux/types.h>
#include <linux/keyboard.h>
#include <linux/kd.h>
u_short key_map[NR_KEYMAPS][NR_KEYS] = {
{
0x0200, 0x001b, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036,
0x0037, 0x0038, 0x0039, 0x0030, 0x002d, 0x003d, 0x007f, 0x0009,
0x0b71, 0x0b77, 0x0b65, 0x0b72, 0x0b74, 0x0b79, 0x0b75, 0x0b69,
0x0b6f, 0x0b70, 0x005b, 0x005d, 0x0201, 0x0702, 0x0b61, 0x0b73,
0x0b64, 0x0b66, 0x0b67, 0x0b68, 0x0b6a, 0x0b6b, 0x0b6c, 0x003b,
0x0027, 0x0060, 0x0700, 0x005c, 0x0b7a, 0x0b78, 0x0b63, 0x0b76,
0x0b62, 0x0b6e, 0x0b6d, 0x002c, 0x002e, 0x002f, 0x0700, 0x030c,
0x0703, 0x0020, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104,
0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0209, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003c, 0x010a,
0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x001b, 0x0021, 0x0040, 0x0023, 0x0024, 0x0025, 0x005e,
0x0026, 0x002a, 0x0028, 0x0029, 0x005f, 0x002b, 0x007f, 0x0009,
0x0b51, 0x0b57, 0x0b45, 0x0b52, 0x0b54, 0x0b59, 0x0b55, 0x0b49,
0x0b4f, 0x0b50, 0x007b, 0x007d, 0x0201, 0x0702, 0x0b41, 0x0b53,
0x0b44, 0x0b46, 0x0b47, 0x0b48, 0x0b4a, 0x0b4b, 0x0b4c, 0x003a,
0x0022, 0x007e, 0x0700, 0x007c, 0x0b5a, 0x0b58, 0x0b43, 0x0b56,
0x0b42, 0x0b4e, 0x0b4d, 0x003c, 0x003e, 0x003f, 0x0700, 0x030c,
0x0703, 0x0020, 0x0207, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e,
0x010f, 0x0110, 0x0111, 0x0112, 0x0113, 0x0208, 0x0203, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003e, 0x010a,
0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x020b, 0x0601, 0x0602, 0x0117, 0x0600, 0x020a, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0040, 0x0200, 0x0024, 0x0200, 0x0200,
0x007b, 0x005b, 0x005d, 0x007d, 0x005c, 0x0200, 0x0200, 0x0200,
0x0b71, 0x0b77, 0x0b65, 0x0b72, 0x0b74, 0x0b79, 0x0b75, 0x0b69,
0x0b6f, 0x0b70, 0x0200, 0x007e, 0x0201, 0x0702, 0x0b61, 0x0b73,
0x0b64, 0x0b66, 0x0b67, 0x0b68, 0x0b6a, 0x0b6b, 0x0b6c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x0b7a, 0x0b78, 0x0b63, 0x0b76,
0x0b62, 0x0b6e, 0x0b6d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x050c, 0x050d, 0x050e, 0x050f, 0x0510,
0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0208, 0x0202, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x007c, 0x0516,
0x0517, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0b51, 0x0b57, 0x0b45, 0x0b52, 0x0b54, 0x0b59, 0x0b55, 0x0b49,
0x0b4f, 0x0b50, 0x0200, 0x0200, 0x0201, 0x0702, 0x0b41, 0x0b53,
0x0b44, 0x0b46, 0x0b47, 0x0b48, 0x0b4a, 0x0b4b, 0x0b4c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x0b5a, 0x0b58, 0x0b43, 0x0b56,
0x0b42, 0x0b4e, 0x0b4d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0000, 0x001b, 0x001c, 0x001d, 0x001e,
0x001f, 0x007f, 0x0200, 0x0200, 0x001f, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x001b, 0x001d, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0007, 0x0000, 0x0700, 0x001c, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x020e, 0x007f, 0x0700, 0x030c,
0x0703, 0x0000, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104,
0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0204, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x010a,
0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x001f, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013,
0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016,
0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x081b, 0x0831, 0x0832, 0x0833, 0x0834, 0x0835, 0x0836,
0x0837, 0x0838, 0x0839, 0x0830, 0x082d, 0x083d, 0x087f, 0x0809,
0x0871, 0x0877, 0x0865, 0x0872, 0x0874, 0x0879, 0x0875, 0x0869,
0x086f, 0x0870, 0x085b, 0x085d, 0x080d, 0x0702, 0x0861, 0x0873,
0x0864, 0x0866, 0x0867, 0x0868, 0x086a, 0x086b, 0x086c, 0x083b,
0x0827, 0x0860, 0x0700, 0x085c, 0x087a, 0x0878, 0x0863, 0x0876,
0x0862, 0x086e, 0x086d, 0x082c, 0x082e, 0x082f, 0x0700, 0x030c,
0x0703, 0x0820, 0x0207, 0x0500, 0x0501, 0x0502, 0x0503, 0x0504,
0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x0208, 0x0209, 0x0907,
0x0908, 0x0909, 0x030b, 0x0904, 0x0905, 0x0906, 0x030a, 0x0901,
0x0902, 0x0903, 0x0900, 0x0310, 0x0206, 0x0200, 0x083c, 0x050a,
0x050b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0851, 0x0857, 0x0845, 0x0852, 0x0854, 0x0859, 0x0855, 0x0849,
0x084f, 0x0850, 0x0200, 0x0200, 0x0201, 0x0702, 0x0841, 0x0853,
0x0844, 0x0846, 0x0847, 0x0848, 0x084a, 0x084b, 0x084c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x085a, 0x0858, 0x0843, 0x0856,
0x0842, 0x084e, 0x084d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0871, 0x0877, 0x0865, 0x0872, 0x0874, 0x0879, 0x0875, 0x0869,
0x086f, 0x0870, 0x0200, 0x0200, 0x0201, 0x0702, 0x0861, 0x0873,
0x0864, 0x0866, 0x0867, 0x0868, 0x086a, 0x086b, 0x086c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x087a, 0x0878, 0x0863, 0x0876,
0x0862, 0x086e, 0x086d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0851, 0x0857, 0x0845, 0x0852, 0x0854, 0x0859, 0x0855, 0x0849,
0x084f, 0x0850, 0x0200, 0x0200, 0x0201, 0x0702, 0x0841, 0x0853,
0x0844, 0x0846, 0x0847, 0x0848, 0x084a, 0x084b, 0x084c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x085a, 0x0858, 0x0843, 0x0856,
0x0842, 0x084e, 0x084d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
}, {
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809,
0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813,
0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200,
0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816,
0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
};
char func_buf[FUNC_BUFSIZE] = {
'\033', '[', '[', 'A', 0,
'\033', '[', '[', 'B', 0,
'\033', '[', '[', 'C', 0,
'\033', '[', '[', 'D', 0,
'\033', '[', '[', 'E', 0,
'\033', '[', '1', '7', '~', 0,
'\033', '[', '1', '8', '~', 0,
'\033', '[', '1', '9', '~', 0,
'\033', '[', '2', '0', '~', 0,
'\033', '[', '2', '1', '~', 0,
'\033', '[', '2', '3', '~', 0,
'\033', '[', '2', '4', '~', 0,
'\033', '[', '2', '5', '~', 0,
'\033', '[', '2', '6', '~', 0,
'\033', '[', '2', '8', '~', 0,
'\033', '[', '2', '9', '~', 0,
'\033', '[', '3', '1', '~', 0,
'\033', '[', '3', '2', '~', 0,
'\033', '[', '3', '3', '~', 0,
'\033', '[', '3', '4', '~', 0,
'\033', '[', '1', '~', 0,
'\033', '[', '2', '~', 0,
'\033', '[', '3', '~', 0,
'\033', '[', '4', '~', 0,
'\033', '[', '5', '~', 0,
'\033', '[', '6', '~', 0,
'\033', '[', 'M', 0,
0,
0,
'\033', '[', 'P', 0,
0,
0,
0,
0,
0,
0,
};
char *func_table[NR_FUNC] = {
func_buf + 0,
func_buf + 5,
func_buf + 10,
func_buf + 15,
func_buf + 20,
func_buf + 25,
func_buf + 31,
func_buf + 37,
func_buf + 43,
func_buf + 49,
func_buf + 55,
func_buf + 61,
func_buf + 67,
func_buf + 73,
func_buf + 79,
func_buf + 85,
func_buf + 91,
func_buf + 97,
func_buf + 103,
func_buf + 109,
func_buf + 115,
func_buf + 120,
func_buf + 125,
func_buf + 130,
func_buf + 135,
func_buf + 140,
func_buf + 145,
func_buf + 149,
func_buf + 150,
func_buf + 151,
func_buf + 155,
func_buf + 156,
func_buf + 157,
func_buf + 158,
func_buf + 159,
func_buf + 160,
};
struct kbdiacr accent_table[MAX_DIACR] = {
{'`', 'A', '\300'}, {'`', 'a', '\340'},
{'\'', 'A', '\301'}, {'\'', 'a', '\341'},
{'^', 'A', '\302'}, {'^', 'a', '\342'},
{'~', 'A', '\303'}, {'~', 'a', '\343'},
{'"', 'A', '\304'}, {'"', 'a', '\344'},
{'O', 'A', '\305'}, {'o', 'a', '\345'},
{'0', 'A', '\305'}, {'0', 'a', '\345'},
{'A', 'A', '\305'}, {'a', 'a', '\345'},
{'A', 'E', '\306'}, {'a', 'e', '\346'},
{',', 'C', '\307'}, {',', 'c', '\347'},
{'`', 'E', '\310'}, {'`', 'e', '\350'},
{'\'', 'E', '\311'}, {'\'', 'e', '\351'},
{'^', 'E', '\312'}, {'^', 'e', '\352'},
{'"', 'E', '\313'}, {'"', 'e', '\353'},
{'`', 'I', '\314'}, {'`', 'i', '\354'},
{'\'', 'I', '\315'}, {'\'', 'i', '\355'},
{'^', 'I', '\316'}, {'^', 'i', '\356'},
{'"', 'I', '\317'}, {'"', 'i', '\357'},
{'-', 'D', '\320'}, {'-', 'd', '\360'},
{'~', 'N', '\321'}, {'~', 'n', '\361'},
{'`', 'O', '\322'}, {'`', 'o', '\362'},
{'\'', 'O', '\323'}, {'\'', 'o', '\363'},
{'^', 'O', '\324'}, {'^', 'o', '\364'},
{'~', 'O', '\325'}, {'~', 'o', '\365'},
{'"', 'O', '\326'}, {'"', 'o', '\366'},
{'/', 'O', '\330'}, {'/', 'o', '\370'},
{'`', 'U', '\331'}, {'`', 'u', '\371'},
{'\'', 'U', '\332'}, {'\'', 'u', '\372'},
{'^', 'U', '\333'}, {'^', 'u', '\373'},
{'"', 'U', '\334'}, {'"', 'u', '\374'},
{'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
{'T', 'H', '\336'}, {'t', 'h', '\376'},
{'s', 's', '\337'}, {'"', 'y', '\377'},
{'s', 'z', '\337'}, {'i', 'j', '\377'},
};
unsigned int accent_table_size = 68;

View file

@ -0,0 +1,321 @@
keycode 0 =
keycode 1 = Escape Escape
alt keycode 1 = Meta_Escape
keycode 2 = one exclam
alt keycode 2 = Meta_one
keycode 3 = two at at
control keycode 3 = nul
shift control keycode 3 = nul
alt keycode 3 = Meta_two
keycode 4 = three numbersign
control keycode 4 = Escape
alt keycode 4 = Meta_three
keycode 5 = four dollar dollar
control keycode 5 = Control_backslash
alt keycode 5 = Meta_four
keycode 6 = five percent
control keycode 6 = Control_bracketright
alt keycode 6 = Meta_five
keycode 7 = six asciicircum
control keycode 7 = Control_asciicircum
alt keycode 7 = Meta_six
keycode 8 = seven ampersand braceleft
control keycode 8 = Control_underscore
alt keycode 8 = Meta_seven
keycode 9 = eight asterisk bracketleft
control keycode 9 = Delete
alt keycode 9 = Meta_eight
keycode 10 = nine parenleft bracketright
alt keycode 10 = Meta_nine
keycode 11 = zero parenright braceright
alt keycode 11 = Meta_zero
keycode 12 = minus underscore backslash
control keycode 12 = Control_underscore
shift control keycode 12 = Control_underscore
alt keycode 12 = Meta_minus
keycode 13 = equal plus
alt keycode 13 = Meta_equal
keycode 14 = Delete Delete
alt keycode 14 = Meta_Delete
keycode 15 = Tab Tab
alt keycode 15 = Meta_Tab
keycode 16 = q
keycode 17 = w
keycode 18 = e
keycode 19 = r
keycode 20 = t
keycode 21 = y
keycode 22 = u
keycode 23 = i
keycode 24 = o
keycode 25 = p
keycode 26 = bracketleft braceleft
control keycode 26 = Escape
alt keycode 26 = Meta_bracketleft
keycode 27 = bracketright braceright asciitilde
control keycode 27 = Control_bracketright
alt keycode 27 = Meta_bracketright
keycode 28 = Return
alt keycode 28 = Meta_Control_m
keycode 29 = Control
keycode 30 = a
keycode 31 = s
keycode 32 = d
keycode 33 = f
keycode 34 = g
keycode 35 = h
keycode 36 = j
keycode 37 = k
keycode 38 = l
keycode 39 = semicolon colon
alt keycode 39 = Meta_semicolon
keycode 40 = apostrophe quotedbl
control keycode 40 = Control_g
alt keycode 40 = Meta_apostrophe
keycode 41 = grave asciitilde
control keycode 41 = nul
alt keycode 41 = Meta_grave
keycode 42 = Shift
keycode 43 = backslash bar
control keycode 43 = Control_backslash
alt keycode 43 = Meta_backslash
keycode 44 = z
keycode 45 = x
keycode 46 = c
keycode 47 = v
keycode 48 = b
keycode 49 = n
keycode 50 = m
keycode 51 = comma less
alt keycode 51 = Meta_comma
keycode 52 = period greater
control keycode 52 = Compose
alt keycode 52 = Meta_period
keycode 53 = slash question
control keycode 53 = Delete
alt keycode 53 = Meta_slash
keycode 54 = Shift
keycode 55 = KP_Multiply
keycode 56 = Alt
keycode 57 = space space
control keycode 57 = nul
alt keycode 57 = Meta_space
keycode 58 = Caps_Lock
keycode 59 = F1 F11 Console_13
control keycode 59 = F1
alt keycode 59 = Console_1
keycode 60 = F2 F12 Console_14
control keycode 60 = F2
alt keycode 60 = Console_2
keycode 61 = F3 F13 Console_15
control keycode 61 = F3
alt keycode 61 = Console_3
keycode 62 = F4 F14 Console_16
control keycode 62 = F4
alt keycode 62 = Console_4
keycode 63 = F5 F15 Console_17
control keycode 63 = F5
alt keycode 63 = Console_5
keycode 64 = F6 F16 Console_18
control keycode 64 = F6
alt keycode 64 = Console_6
keycode 65 = F7 F17 Console_19
control keycode 65 = F7
alt keycode 65 = Console_7
keycode 66 = F8 F18 Console_20
control keycode 66 = F8
alt keycode 66 = Console_8
keycode 67 = F9 F19 Console_21
control keycode 67 = F9
alt keycode 67 = Console_9
keycode 68 = F10 F20 Console_22
control keycode 68 = F10
alt keycode 68 = Console_10
keycode 69 = Num_Lock
keycode 70 = Scroll_Lock Show_Memory Show_Registers
control keycode 70 = Show_State
alt keycode 70 = Scroll_Lock
keycode 71 = KP_7
alt keycode 71 = Ascii_7
keycode 72 = KP_8
alt keycode 72 = Ascii_8
keycode 73 = KP_9
alt keycode 73 = Ascii_9
keycode 74 = KP_Subtract
keycode 75 = KP_4
alt keycode 75 = Ascii_4
keycode 76 = KP_5
alt keycode 76 = Ascii_5
keycode 77 = KP_6
alt keycode 77 = Ascii_6
keycode 78 = KP_Add
keycode 79 = KP_1
alt keycode 79 = Ascii_1
keycode 80 = KP_2
alt keycode 80 = Ascii_2
keycode 81 = KP_3
alt keycode 81 = Ascii_3
keycode 82 = KP_0
alt keycode 82 = Ascii_0
keycode 83 = KP_Period
altgr control keycode 83 = Boot
control alt keycode 83 = Boot
keycode 84 = Last_Console
keycode 85 =
keycode 86 = less greater bar
alt keycode 86 = Meta_less
keycode 87 = F11 F11 Console_23
control keycode 87 = F11
alt keycode 87 = Console_11
keycode 88 = F12 F12 Console_24
control keycode 88 = F12
alt keycode 88 = Console_12
keycode 89 =
keycode 90 =
keycode 91 =
keycode 92 =
keycode 93 =
keycode 94 =
keycode 95 =
keycode 96 = KP_Enter
keycode 97 = Control
keycode 98 = KP_Divide
keycode 99 = Control_backslash
control keycode 99 = Control_backslash
alt keycode 99 = Control_backslash
keycode 100 = AltGr
keycode 101 = Break
keycode 102 = Find
keycode 103 = Up
keycode 104 = Prior
shift keycode 104 = Scroll_Backward
keycode 105 = Left
keycode 106 = Right
keycode 107 = Select
keycode 108 = Down
keycode 109 = Next
shift keycode 109 = Scroll_Forward
keycode 110 = Insert
keycode 111 = Remove
altgr control keycode 111 = Boot
control alt keycode 111 = Boot
keycode 112 = Macro
keycode 113 = F13
keycode 114 = F14
keycode 115 = Help
keycode 116 = Do
keycode 117 = F17
keycode 118 = KP_MinPlus
keycode 119 = Pause
keycode 120 =
keycode 121 =
keycode 122 =
keycode 123 =
keycode 124 =
keycode 125 =
keycode 126 =
keycode 127 =
string F1 = "\033[[A"
string F2 = "\033[[B"
string F3 = "\033[[C"
string F4 = "\033[[D"
string F5 = "\033[[E"
string F6 = "\033[17~"
string F7 = "\033[18~"
string F8 = "\033[19~"
string F9 = "\033[20~"
string F10 = "\033[21~"
string F11 = "\033[23~"
string F12 = "\033[24~"
string F13 = "\033[25~"
string F14 = "\033[26~"
string F15 = "\033[28~"
string F16 = "\033[29~"
string F17 = "\033[31~"
string F18 = "\033[32~"
string F19 = "\033[33~"
string F20 = "\033[34~"
string Find = "\033[1~"
string Insert = "\033[2~"
string Remove = "\033[3~"
string Select = "\033[4~"
string Prior = "\033[5~"
string Next = "\033[6~"
string Help = ""
string Do = ""
string Macro = "\033[M"
string Pause = "\033[P"
string F21 = ""
string F22 = ""
string F23 = ""
string F24 = ""
string F25 = ""
string F26 = ""
compose '`' 'A' to 'À'
compose '`' 'a' to 'à'
compose '\'' 'A' to 'Á'
compose '\'' 'a' to 'á'
compose '^' 'A' to 'Â'
compose '^' 'a' to 'â'
compose '~' 'A' to 'Ã'
compose '~' 'a' to 'ã'
compose '"' 'A' to 'Ä'
compose '"' 'a' to 'ä'
compose 'O' 'A' to 'Å'
compose 'o' 'a' to 'å'
compose '0' 'A' to 'Å'
compose '0' 'a' to 'å'
compose 'A' 'A' to 'Å'
compose 'a' 'a' to 'å'
compose 'A' 'E' to 'Æ'
compose 'a' 'e' to 'æ'
compose ',' 'C' to 'Ç'
compose ',' 'c' to 'ç'
compose '`' 'E' to 'È'
compose '`' 'e' to 'è'
compose '\'' 'E' to 'É'
compose '\'' 'e' to 'é'
compose '^' 'E' to 'Ê'
compose '^' 'e' to 'ê'
compose '"' 'E' to 'Ë'
compose '"' 'e' to 'ë'
compose '`' 'I' to 'Ì'
compose '`' 'i' to 'ì'
compose '\'' 'I' to 'Í'
compose '\'' 'i' to 'í'
compose '^' 'I' to 'Î'
compose '^' 'i' to 'î'
compose '"' 'I' to 'Ï'
compose '"' 'i' to 'ï'
compose '-' 'D' to 'Ð'
compose '-' 'd' to 'ð'
compose '~' 'N' to 'Ñ'
compose '~' 'n' to 'ñ'
compose '`' 'O' to 'Ò'
compose '`' 'o' to 'ò'
compose '\'' 'O' to 'Ó'
compose '\'' 'o' to 'ó'
compose '^' 'O' to 'Ô'
compose '^' 'o' to 'ô'
compose '~' 'O' to 'Õ'
compose '~' 'o' to 'õ'
compose '"' 'O' to 'Ö'
compose '"' 'o' to 'ö'
compose '/' 'O' to 'Ø'
compose '/' 'o' to 'ø'
compose '`' 'U' to 'Ù'
compose '`' 'u' to 'ù'
compose '\'' 'U' to 'Ú'
compose '\'' 'u' to 'ú'
compose '^' 'U' to 'Û'
compose '^' 'u' to 'û'
compose '"' 'U' to 'Ü'
compose '"' 'u' to 'ü'
compose '\'' 'Y' to 'Ý'
compose '\'' 'y' to 'ý'
compose 'T' 'H' to 'Þ'
compose 't' 'h' to 'þ'
compose 's' 's' to 'ß'
compose '"' 'y' to 'ÿ'
compose 's' 'z' to 'ß'
compose 'i' 'j' to 'ÿ'

View file

@ -0,0 +1,8 @@
#ifndef _DIACR_H
#define _DIACR_H
#include <linux/kd.h>
extern struct kbdiacr accent_table[];
extern unsigned int accent_table_size;
#endif /* _DIACR_H */

View file

@ -0,0 +1,107 @@
#ifndef _KBD_KERN_H
#define _KBD_KERN_H
#include <linux/interrupt.h>
#define set_leds() mark_bh(KEYBOARD_BH)
#include <linux/keyboard.h>
/*
* kbd->xxx contains the VC-local things (flag settings etc..)
*
* Note: externally visible are LED_SCR, LED_NUM, LED_CAP defined in kd.h
* The code in KDGETLED / KDSETLED depends on the internal and
* external order being the same.
*
* Note: lockstate is used as index in the array key_map.
*/
struct kbd_struct {
unsigned char ledstate; /* 3 bits */
unsigned char default_ledstate;
#define VC_SCROLLOCK 0 /* scroll-lock mode */
#define VC_NUMLOCK 1 /* numeric lock mode */
#define VC_CAPSLOCK 2 /* capslock mode */
unsigned char lockstate; /* 4 bits - must be in 0..15 */
#define VC_SHIFTLOCK KG_SHIFT /* shift lock mode */
#define VC_ALTGRLOCK KG_ALTGR /* altgr lock mode */
#define VC_CTRLLOCK KG_CTRL /* control lock mode */
#define VC_ALTLOCK KG_ALT /* alt lock mode */
unsigned char modeflags;
#define VC_APPLIC 0 /* application key mode */
#define VC_CKMODE 1 /* cursor key mode */
#define VC_REPEAT 2 /* keyboard repeat */
#define VC_CRLF 3 /* 0 - enter sends CR, 1 - enter sends CRLF */
#define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */
#define VC_PAUSE 5 /* pause key pressed - unused */
#define VC_RAW 6 /* raw (scancode) mode */
#define VC_MEDIUMRAW 7 /* medium raw (keycode) mode */
};
extern struct kbd_struct kbd_table[];
extern unsigned long kbd_init(unsigned long);
extern inline int vc_kbd_led(struct kbd_struct * kbd, int flag)
{
return ((kbd->ledstate >> flag) & 1);
}
extern inline int vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
return ((kbd->lockstate >> flag) & 1);
}
extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
return ((kbd->modeflags >> flag) & 1);
}
extern inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledstate |= 1 << flag;
}
extern inline void set_vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
kbd->lockstate |= 1 << flag;
}
extern inline void set_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags |= 1 << flag;
}
extern inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledstate &= ~(1 << flag);
}
extern inline void clr_vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
kbd->lockstate &= ~(1 << flag);
}
extern inline void clr_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags &= ~(1 << flag);
}
extern inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledstate ^= 1 << flag;
}
extern inline void chg_vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
kbd->lockstate ^= 1 << flag;
}
extern inline void chg_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags ^= 1 << flag;
}
#endif

View file

@ -0,0 +1,870 @@
/*
* linux/kernel/chr_drv/keyboard.c
*
* Keyboard driver for Linux v0.99 using Latin-1.
*
* Written for linux by Johan Myreen as a translation from
* the assembly version by Linus (with diacriticals added)
*
* Some additional features added by Christoph Niemann (ChN), March 1993
* Loadable keymaps by Risto Kankkunen, May 1993
* Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
*/
#define KEYBOARD_IRQ 1
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/config.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <asm/bitops.h>
#include "kbd_kern.h"
#include "diacr.h"
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
#ifndef KBD_DEFMODE
#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
#endif
#ifndef KBD_DEFLEDS
#define KBD_DEFLEDS (1 << VC_NUMLOCK)
#endif
#ifndef KBD_DEFLOCK
#define KBD_DEFLOCK 0
#endif
/*
* The default IO slowdown is doing 'inb()'s from 0x61, which should be
* safe. But as that is the keyboard controller chip address, we do our
* slowdowns here by doing short jumps: the keyboard controller should
* be able to keep up
*/
#define REALLY_SLOW_IO
#define SLOW_IO_BY_JUMPING
#include <asm/io.h>
#include <asm/system.h>
extern void do_keyboard_interrupt(void);
extern void ctrl_alt_del(void);
extern void change_console(unsigned int new_console);
extern void scrollback(int);
extern void scrollfront(int);
#define fake_keyboard_interrupt() \
__asm__ __volatile__("int $0x21")
unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */
/*
* global state includes the following, and various static variables
* in this module: prev_scancode, shift_state, diacr, npadch,
* dead_key_next, last_console
*/
/* shift state counters.. */
static unsigned char k_down[NR_SHIFT] = {0, };
/* keyboard key bitmap */
static unsigned long key_down[8] = { 0, };
static int want_console = -1;
static int last_console = 0; /* last used VC */
static int dead_key_next = 0;
static int shift_state = 0;
static int npadch = -1; /* -1 or number assembled on pad */
static unsigned char diacr = 0;
static char rep = 0; /* flag telling character repeat */
struct kbd_struct kbd_table[NR_CONSOLES];
static struct kbd_struct * kbd = kbd_table;
static struct tty_struct * tty = NULL;
/* used only by send_data - set by keyboard_interrupt */
static volatile unsigned char acknowledge = 0;
static volatile unsigned char resend = 0;
typedef void (*k_hand)(unsigned char value, char up_flag);
typedef void (k_handfn)(unsigned char value, char up_flag);
static k_handfn
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase;
static k_hand key_handler[] = {
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase
};
/* maximum values each key_handler can handle */
const int max_vals[] = {
255, NR_FUNC - 1, 14, 17, 4, 255, 3, NR_SHIFT,
255, 9, 3, 255
};
const int NR_TYPES = SIZE(max_vals);
static void put_queue(int);
static unsigned char handle_diacr(unsigned char);
/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
static struct pt_regs * pt_regs;
static int got_break = 0;
static inline void kb_wait(void)
{
int i;
for (i=0; i<0x10000; i++)
if ((inb_p(0x64) & 0x02) == 0)
break;
}
static inline void send_cmd(unsigned char c)
{
kb_wait();
outb(c,0x64);
}
/*
* Translation of escaped scancodes to keysyms.
* This should be user-settable.
*/
#define E0_BASE 96
#define E0_KPENTER (E0_BASE+0)
#define E0_RCTRL (E0_BASE+1)
#define E0_KPSLASH (E0_BASE+2)
#define E0_PRSCR (E0_BASE+3)
#define E0_RALT (E0_BASE+4)
#define E0_BREAK (E0_BASE+5) /* (control-pause) */
#define E0_HOME (E0_BASE+6)
#define E0_UP (E0_BASE+7)
#define E0_PGUP (E0_BASE+8)
#define E0_LEFT (E0_BASE+9)
#define E0_RIGHT (E0_BASE+10)
#define E0_END (E0_BASE+11)
#define E0_DOWN (E0_BASE+12)
#define E0_PGDN (E0_BASE+13)
#define E0_INS (E0_BASE+14)
#define E0_DEL (E0_BASE+15)
/* BTC */
#define E0_MACRO (E0_BASE+16)
/* LK450 */
#define E0_F13 (E0_BASE+17)
#define E0_F14 (E0_BASE+18)
#define E0_HELP (E0_BASE+19)
#define E0_DO (E0_BASE+20)
#define E0_F17 (E0_BASE+21)
#define E0_KPMINPLUS (E0_BASE+22)
#define E1_PAUSE (E0_BASE+23)
static unsigned char e0_keys[128] = {
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */
0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */
0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */
E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */
E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */
E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58-0x5f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */
};
static void keyboard_interrupt(int int_pt_regs)
{
unsigned char scancode;
static unsigned int prev_scancode = 0; /* remember E0, E1 */
char up_flag; /* 0 or 0200 */
char raw_mode;
pt_regs = (struct pt_regs *) int_pt_regs;
send_cmd(0xAD); /* disable keyboard */
kb_wait();
if ((inb_p(0x64) & kbd_read_mask) != 0x01)
goto end_kbd_intr;
scancode = inb(0x60);
mark_bh(KEYBOARD_BH);
if (scancode == 0xfa) {
acknowledge = 1;
goto end_kbd_intr;
} else if (scancode == 0xfe) {
resend = 1;
goto end_kbd_intr;
}
tty = TTY_TABLE(0);
kbd = kbd_table + fg_console;
if ((raw_mode = vc_kbd_mode(kbd,VC_RAW))) {
put_queue(scancode);
/* we do not return yet, because we want to maintain
the key_down array, so that we have the correct
values when finishing RAW mode or when changing VT's */
}
if (scancode == 0xe0 || scancode == 0xe1) {
prev_scancode = scancode;
goto end_kbd_intr;
}
/*
* Convert scancode to keysym, using prev_scancode.
*/
up_flag = (scancode & 0200);
scancode &= 0x7f;
if (prev_scancode) {
/*
* usually it will be 0xe0, but a Pause key generates
* e1 1d 45 e1 9d c5 when pressed, and nothing when released
*/
if (prev_scancode != 0xe0) {
if (prev_scancode == 0xe1 && scancode == 0x1d) {
prev_scancode = 0x100;
goto end_kbd_intr;
} else if (prev_scancode == 0x100 && scancode == 0x45) {
scancode = E1_PAUSE;
prev_scancode = 0;
} else {
printk("keyboard: unknown e1 escape sequence\n");
prev_scancode = 0;
goto end_kbd_intr;
}
} else {
prev_scancode = 0;
/*
* The keyboard maintains its own internal caps lock and
* num lock statuses. In caps lock mode E0 AA precedes make
* code and E0 2A follows break code. In num lock mode,
* E0 2A precedes make code and E0 AA follows break code.
* We do our own book-keeping, so we will just ignore these.
*/
/*
* For my keyboard there is no caps lock mode, but there are
* both Shift-L and Shift-R modes. The former mode generates
* E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
* So, we should also ignore the latter. - aeb@cwi.nl
*/
if (scancode == 0x2a || scancode == 0x36)
goto end_kbd_intr;
if (e0_keys[scancode])
scancode = e0_keys[scancode];
else if (!raw_mode) {
printk("keyboard: unknown scancode e0 %02x\n", scancode);
goto end_kbd_intr;
}
}
} else if (scancode >= E0_BASE && !raw_mode) {
printk("keyboard: scancode (%02x) not in range 00 - %2x\n",
scancode, E0_BASE - 1);
goto end_kbd_intr;
}
/*
* At this point the variable `scancode' contains the keysym.
* We keep track of the up/down status of the key, and
* return the keysym if in MEDIUMRAW mode.
* (Note: earlier kernels had a bug and did not pass the up/down
* bit to applications.)
*/
if (up_flag) {
clear_bit(scancode, key_down);
rep = 0;
} else
rep = set_bit(scancode, key_down);
if (raw_mode)
goto end_kbd_intr;
if (vc_kbd_mode(kbd, VC_MEDIUMRAW)) {
put_queue(scancode + up_flag);
goto end_kbd_intr;
}
/*
* Small change in philosophy: earlier we defined repetition by
* rep = scancode == prev_keysym;
* prev_keysym = scancode;
* but now by the fact that the depressed key was down already.
* Does this ever make a difference?
*/
/*
* Repeat a key only if the input buffers are empty or the
* characters get echoed locally. This makes key repeat usable
* with slow applications and under heavy loads.
*/
if (!rep ||
(vc_kbd_mode(kbd,VC_REPEAT) && tty &&
(L_ECHO(tty) || (EMPTY(&tty->secondary) && EMPTY(&tty->read_q)))))
{
u_short key_code;
u_char type;
/* the XOR below used to be an OR */
int shift_final = shift_state ^ kbd->lockstate;
key_code = key_map[shift_final][scancode];
type = KTYP(key_code);
if (type == KT_LETTER) {
type = KT_LATIN;
if (vc_kbd_led(kbd,VC_CAPSLOCK))
key_code = key_map[shift_final ^ (1<<KG_SHIFT)][scancode];
}
(*key_handler[type])(key_code & 0xff, up_flag);
}
end_kbd_intr:
send_cmd(0xAE); /* enable keyboard */
}
static void put_queue(int ch)
{
struct tty_queue *qp;
wake_up(&keypress_wait);
if (!tty)
return;
qp = &tty->read_q;
if (LEFT(qp)) {
qp->buf[qp->head] = ch;
INC(qp->head);
}
}
static void puts_queue(char *cp)
{
struct tty_queue *qp;
char ch;
/* why interruptible here, plain wake_up above? */
wake_up_interruptible(&keypress_wait);
if (!tty)
return;
qp = &tty->read_q;
while ((ch = *(cp++)) != 0) {
if (LEFT(qp)) {
qp->buf[qp->head] = ch;
INC(qp->head);
}
}
}
static void applkey(int key, char mode)
{
static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
buf[1] = (mode ? 'O' : '[');
buf[2] = key;
puts_queue(buf);
}
static void enter(void)
{
put_queue(13);
if (vc_kbd_mode(kbd,VC_CRLF))
put_queue(10);
}
static void caps_toggle(void)
{
if (rep)
return;
chg_vc_kbd_led(kbd,VC_CAPSLOCK);
}
static void caps_on(void)
{
if (rep)
return;
set_vc_kbd_led(kbd,VC_CAPSLOCK);
}
static void show_ptregs(void)
{
if (!pt_regs)
return;
printk("\n");
printk("EIP: %04x:%08lx",0xffff & pt_regs->cs,pt_regs->eip);
if (pt_regs->cs & 3)
printk(" ESP: %04x:%08lx",0xffff & pt_regs->ss,pt_regs->esp);
printk(" EFLAGS: %08lx\n",pt_regs->eflags);
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx);
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
pt_regs->esi, pt_regs->edi, pt_regs->ebp);
printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
0xffff & pt_regs->ds,0xffff & pt_regs->es,
0xffff & pt_regs->fs,0xffff & pt_regs->gs);
}
static void hold(void)
{
if (rep || !tty)
return;
/*
* Note: SCROLLOCK wil be set (cleared) by stop_tty (start_tty);
* these routines are also activated by ^S/^Q.
* (And SCROLLOCK can also be set by the ioctl KDSETLED.)
*/
if (tty->stopped)
start_tty(tty);
else
stop_tty(tty);
}
#if 0
/* unused at present - and the VC_PAUSE bit is not used anywhere either */
static void pause(void)
{
chg_vc_kbd_mode(kbd,VC_PAUSE);
}
#endif
static void num(void)
{
if (vc_kbd_mode(kbd,VC_APPLIC)) {
applkey('P', 1);
return;
}
if (!rep) /* no autorepeat for numlock, ChN */
chg_vc_kbd_led(kbd,VC_NUMLOCK);
}
static void lastcons(void)
{
/* pressing alt-printscreen switches to the last used console, ChN */
want_console = last_console;
}
static void send_intr(void)
{
got_break = 1;
}
static void scrll_forw(void)
{
scrollfront(0);
}
static void scrll_back(void)
{
scrollback(0);
}
static void boot_it(void)
{
ctrl_alt_del();
}
static void compose(void)
{
dead_key_next = 1;
}
static void do_spec(unsigned char value, char up_flag)
{
typedef void (*fnp)(void);
fnp fn_table[] = {
NULL, enter, show_ptregs, show_mem,
show_state, send_intr, lastcons, caps_toggle,
num, hold, scrll_forw, scrll_back,
boot_it, caps_on, compose
};
if (up_flag)
return;
if (value >= SIZE(fn_table))
return;
if (!fn_table[value])
return;
fn_table[value]();
}
static void do_lowercase(unsigned char value, char up_flag)
{
printk("keyboard.c: do_lowercase was called - impossible\n");
}
static void do_self(unsigned char value, char up_flag)
{
if (up_flag)
return; /* no action, if this is a key release */
if (diacr)
value = handle_diacr(value);
if (dead_key_next) {
dead_key_next = 0;
diacr = value;
return;
}
put_queue(value);
}
#define A_GRAVE '`'
#define A_ACUTE '\''
#define A_CFLEX '^'
#define A_TILDE '~'
#define A_DIAER '"'
static unsigned char ret_diacr[] =
{A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
/* If a dead key pressed twice, output a character corresponding to it, */
/* otherwise just remember the dead key. */
static void do_dead(unsigned char value, char up_flag)
{
if (up_flag)
return;
value = ret_diacr[value];
if (diacr == value) { /* pressed twice */
diacr = 0;
put_queue(value);
return;
}
diacr = value;
}
/* If space is pressed, return the character corresponding the pending */
/* dead key, otherwise try to combine the two. */
unsigned char handle_diacr(unsigned char ch)
{
int d = diacr;
int i;
diacr = 0;
if (ch == ' ')
return d;
for (i = 0; i < accent_table_size; i++)
if(accent_table[i].diacr == d && accent_table[i].base == ch)
return accent_table[i].result;
put_queue(d);
return ch;
}
static void do_cons(unsigned char value, char up_flag)
{
if (up_flag)
return;
want_console = value;
}
static void do_fn(unsigned char value, char up_flag)
{
if (up_flag)
return;
if (value < SIZE(func_table))
puts_queue(func_table[value]);
else
printk("do_fn called with value=%d\n", value);
}
static void do_pad(unsigned char value, char up_flag)
{
static char *pad_chars = "0123456789+-*/\015,.?";
static char *app_map = "pqrstuvwxylSRQMnn?";
if (up_flag)
return; /* no action, if this is a key release */
/* kludge... shift forces cursor/number keys */
if (vc_kbd_mode(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) {
applkey(app_map[value], 1);
return;
}
if (!vc_kbd_led(kbd,VC_NUMLOCK))
switch (value) {
case KVAL(K_PCOMMA):
case KVAL(K_PDOT):
do_fn(KVAL(K_REMOVE), 0);
return;
case KVAL(K_P0):
do_fn(KVAL(K_INSERT), 0);
return;
case KVAL(K_P1):
do_fn(KVAL(K_SELECT), 0);
return;
case KVAL(K_P2):
do_cur(KVAL(K_DOWN), 0);
return;
case KVAL(K_P3):
do_fn(KVAL(K_PGDN), 0);
return;
case KVAL(K_P4):
do_cur(KVAL(K_LEFT), 0);
return;
case KVAL(K_P6):
do_cur(KVAL(K_RIGHT), 0);
return;
case KVAL(K_P7):
do_fn(KVAL(K_FIND), 0);
return;
case KVAL(K_P8):
do_cur(KVAL(K_UP), 0);
return;
case KVAL(K_P9):
do_fn(KVAL(K_PGUP), 0);
return;
case KVAL(K_P5):
applkey('G', vc_kbd_mode(kbd, VC_APPLIC));
return;
}
put_queue(pad_chars[value]);
if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
put_queue(10);
}
static void do_cur(unsigned char value, char up_flag)
{
static char *cur_chars = "BDCA";
if (up_flag)
return;
applkey(cur_chars[value], vc_kbd_mode(kbd,VC_CKMODE));
}
static void do_shift(unsigned char value, char up_flag)
{
int old_state = shift_state;
if (rep)
return;
/* kludge... */
if (value == KVAL(K_CAPSSHIFT)) {
value = KVAL(K_SHIFT);
clr_vc_kbd_led(kbd, VC_CAPSLOCK);
}
if (up_flag) {
/* handle the case that two shift or control
keys are depressed simultaneously */
if (k_down[value])
k_down[value]--;
} else
k_down[value]++;
if (k_down[value])
shift_state |= (1 << value);
else
shift_state &= ~ (1 << value);
/* kludge */
if (up_flag && shift_state != old_state && npadch != -1) {
put_queue(npadch);
npadch = -1;
}
}
/* called after returning from RAW mode or when changing consoles -
recompute k_down[] and shift_state from key_down[] */
void compute_shiftstate(void)
{
int i, j, k, sym, val;
shift_state = 0;
for(i=0; i < SIZE(k_down); i++)
k_down[i] = 0;
for(i=0; i < SIZE(key_down); i++)
if(key_down[i]) { /* skip this word if not a single bit on */
k = (i<<5);
for(j=0; j<32; j++,k++)
if(test_bit(k, key_down)) {
sym = key_map[0][k];
if(KTYP(sym) == KT_SHIFT) {
val = KVAL(sym);
k_down[val]++;
shift_state |= (1<<val);
}
}
}
}
static void do_meta(unsigned char value, char up_flag)
{
if (up_flag)
return;
if (vc_kbd_mode(kbd, VC_META)) {
put_queue('\033');
put_queue(value);
} else
put_queue(value | 0x80);
}
static void do_ascii(unsigned char value, char up_flag)
{
if (up_flag)
return;
if (npadch == -1)
npadch = value;
else
npadch = (npadch * 10 + value) % 1000;
}
static void do_lock(unsigned char value, char up_flag)
{
if (up_flag || rep)
return;
chg_vc_kbd_lock(kbd, value);
}
/*
* send_data sends a character to the keyboard and waits
* for a acknowledge, possibly retrying if asked to. Returns
* the success status.
*/
static int send_data(unsigned char data)
{
int retries = 3;
int i;
do {
kb_wait();
acknowledge = 0;
resend = 0;
outb_p(data, 0x60);
for(i=0; i<0x20000; i++) {
inb_p(0x64); /* just as a delay */
if (acknowledge)
return 1;
if (resend)
break;
}
if (!resend)
return 0;
} while (retries-- > 0);
return 0;
}
/*
* This routine is the bottom half of the keyboard interrupt
* routine, and runs with all interrupts enabled. It does
* console changing, led setting and copy_to_cooked, which can
* take a reasonably long time.
*
* Aside from timing (which isn't really that important for
* keyboard interrupts as they happen often), using the software
* interrupt routines for this thing allows us to easily mask
* this when we don't want any of the above to happen. Not yet
* used, but this allows for easy and efficient race-condition
* prevention later on.
*/
static void kbd_bh(void * unused)
{
static unsigned char old_leds = 0xff;
unsigned char leds = kbd_table[fg_console].ledstate;
if (leds != old_leds) {
old_leds = leds;
if (!send_data(0xed) || !send_data(leds))
send_data(0xf4); /* re-enable kbd if any errors */
}
if (want_console >= 0) {
if (want_console != fg_console) {
last_console = fg_console;
change_console(want_console);
}
want_console = -1;
}
if (got_break) {
if (tty && !I_IGNBRK(tty)) {
if (I_BRKINT(tty)) {
flush_input(tty);
flush_output(tty);
if (tty->pgrp > 0)
kill_pg(tty->pgrp, SIGINT, 1);
} else {
cli();
if (LEFT(&tty->read_q) >= 2) {
set_bit(tty->read_q.head,
&tty->readq_flags);
put_queue(TTY_BREAK);
put_queue(0);
}
sti();
}
}
got_break = 0;
}
do_keyboard_interrupt();
cli();
if ((inb_p(0x64) & kbd_read_mask) == 0x01)
fake_keyboard_interrupt();
sti();
}
long no_idt[2] = {0, 0};
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low. We try that for a while,
* and if it doesn't work, we do some other stupid things.
*/
void hard_reset_now(void)
{
int i, j;
extern unsigned long pg0[1024];
sti();
/* rebooting needs to touch the page at absolute addr 0 */
pg0[0] = 7;
*((unsigned short *)0x472) = 0x1234;
for (;;) {
for (i=0; i<100; i++) {
kb_wait();
for(j = 0; j < 100000 ; j++)
/* nothing */;
outb(0xfe,0x64); /* pulse reset low */
}
__asm__("\tlidt _no_idt");
}
}
unsigned long kbd_init(unsigned long kmem_start)
{
int i;
struct kbd_struct * kbd;
kbd = kbd_table + 0;
for (i = 0 ; i < NR_CONSOLES ; i++,kbd++) {
kbd->ledstate = KBD_DEFLEDS;
kbd->default_ledstate = KBD_DEFLEDS;
kbd->lockstate = KBD_DEFLOCK;
kbd->modeflags = KBD_DEFMODE;
}
bh_base[KEYBOARD_BH].routine = kbd_bh;
request_irq(KEYBOARD_IRQ,keyboard_interrupt);
mark_bh(KEYBOARD_BH);
return kmem_start;
}

View file

@ -0,0 +1,461 @@
/*
* Copyright (C) 1992 by Jim Weigand and Linus Torvalds
* Copyright (C) 1992,1993 by Michael K. Johnson
* - Thanks much to Gunter Windau for pointing out to me where the error
* checking ought to be.
* Copyright (C) 1993 by Nigel Gamble (added interrupt code)
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/lp.h>
#include <linux/malloc.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
/*
* All my debugging code assumes that you debug with only one printer at
* a time. RWWH
*/
#undef LP_DEBUG
static int lp_reset(int minor)
{
int testvalue;
unsigned char command;
command = LP_PSELECP | LP_PINITP;
/* reset value */
outb_p(0, LP_C(minor));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
outb_p(command, LP_C(minor));
return LP_S(minor);
}
#ifdef LP_DEBUG
static int lp_max_count = 1;
#endif
static int lp_char_polled(char lpchar, int minor)
{
int status = 0, wait = 0;
unsigned long count = 0;
do {
status = LP_S(minor);
count ++;
if(need_resched)
schedule();
} while(!(status & LP_PBUSY) && count < LP_CHAR(minor));
if (count == LP_CHAR(minor)) {
return 0;
/* we timed out, and the character was /not/ printed */
}
#ifdef LP_DEBUG
if (count > lp_max_count) {
printk("lp success after %d counts.\n",count);
lp_max_count=count;
}
#endif
outb_p(lpchar, LP_B(minor));
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
return 1;
}
static int lp_char_interrupt(char lpchar, int minor)
{
int wait = 0;
unsigned char status;
if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
outb_p(lpchar, LP_B(minor));
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
return 1;
}
return 0;
}
#ifdef LP_DEBUG
unsigned int lp_total_chars = 0;
unsigned int lp_last_call = 0;
#endif
static void lp_interrupt(int irq)
{
struct lp_struct *lp = &lp_table[0];
struct lp_struct *lp_end = &lp_table[LP_NO];
while (irq != lp->irq) {
if (++lp >= lp_end)
return;
}
wake_up(&lp->lp_wait_q);
}
static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
{
unsigned int minor = MINOR(inode->i_rdev);
unsigned long copy_size;
unsigned long total_bytes_written = 0;
unsigned long bytes_written;
struct lp_struct *lp = &lp_table[minor];
unsigned char status;
do {
bytes_written = 0;
copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
memcpy_fromfs(lp->lp_buffer, buf, copy_size);
while (copy_size) {
if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
--copy_size;
++bytes_written;
} else {
if (!((status = LP_S(minor)) & LP_PERRORP)) {
int rc = total_bytes_written + bytes_written;
if ((status & LP_POUTPA)) {
printk("lp%d out of paper\n", minor);
if (!rc)
rc = -ENOSPC;
} else if (!(status & LP_PSELECD)) {
printk("lp%d off-line\n", minor);
if (!rc)
rc = -EIO;
} else {
printk("lp%d printer error\n", minor);
if (!rc)
rc = -EIO;
}
if(LP_F(minor) & LP_ABORT)
return rc;
}
cli();
outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
status = LP_S(minor);
if (!(status & LP_PACK) || (status & LP_PBUSY)) {
outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
sti();
continue;
}
current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
interruptible_sleep_on(&lp->lp_wait_q);
outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
if (current->signal & ~current->blocked) {
if (total_bytes_written + bytes_written)
return total_bytes_written + bytes_written;
else
return -EINTR;
}
}
}
total_bytes_written += bytes_written;
buf += bytes_written;
count -= bytes_written;
} while (count > 0);
return total_bytes_written;
}
static int lp_write_polled(struct inode * inode, struct file * file,
char * buf, int count)
{
int retval;
unsigned int minor = MINOR(inode->i_rdev);
char c, *temp = buf;
#ifdef LP_DEBUG
if (jiffies-lp_last_call > LP_TIME(minor)) {
lp_total_chars = 0;
lp_max_count = 1;
}
lp_last_call = jiffies;
#endif
temp = buf;
while (count > 0) {
c = get_fs_byte(temp);
retval = lp_char_polled(c, minor);
/* only update counting vars if character was printed */
if (retval) { count--; temp++;
#ifdef LP_DEBUG
lp_total_chars++;
#endif
}
if (!retval) { /* if printer timed out */
int status = LP_S(minor);
if (status & LP_POUTPA) {
printk("lp%d out of paper\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-ENOSPC;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
if (!(status & LP_PSELECD)) {
printk("lp%d off-line\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
/* not offline or out of paper. on fire? */
if (!(status & LP_PERRORP)) {
printk("lp%d on fire\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EFAULT;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
}
/* check for signals before going to sleep */
if (current->signal & ~current->blocked) {
if (temp != buf)
return temp-buf;
else
return -EINTR;
}
#ifdef LP_DEBUG
printk("lp sleeping at %d characters for %d jiffies\n",
lp_total_chars, LP_TIME(minor));
lp_total_chars=0;
#endif
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIME(minor);
schedule();
}
}
return temp-buf;
}
static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
{
if (LP_IRQ(MINOR(inode->i_rdev)))
return lp_write_interrupt(inode, file, buf, count);
else
return lp_write_polled(inode, file, buf, count);
}
static int lp_lseek(struct inode * inode, struct file * file,
off_t offset, int origin)
{
return -ESPIPE;
}
static int lp_open(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
int ret;
unsigned int irq;
struct sigaction sa;
if (minor >= LP_NO)
return -ENODEV;
if ((LP_F(minor) & LP_EXIST) == 0)
return -ENODEV;
if (LP_F(minor) & LP_BUSY)
return -EBUSY;
if ((irq = LP_IRQ(minor))) {
lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
if (!lp_table[minor].lp_buffer)
return -ENOMEM;
sa.sa_handler = lp_interrupt;
sa.sa_flags = SA_INTERRUPT;
sa.sa_mask = 0;
sa.sa_restorer = NULL;
ret = irqaction(irq, &sa);
if (ret) {
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
return ret;
}
}
LP_F(minor) |= LP_BUSY;
return 0;
}
static void lp_release(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
unsigned int irq;
if ((irq = LP_IRQ(minor))) {
free_irq(irq);
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
}
LP_F(minor) &= ~LP_BUSY;
}
static int lp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int minor = MINOR(inode->i_rdev);
int retval = 0;
#ifdef LP_DEBUG
printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
#endif
if (minor >= LP_NO)
return -ENODEV;
if ((LP_F(minor) & LP_EXIST) == 0)
return -ENODEV;
switch ( cmd ) {
case LPTIME:
LP_TIME(minor) = arg;
break;
case LPCHAR:
LP_CHAR(minor) = arg;
break;
case LPABORT:
if (arg)
LP_F(minor) |= LP_ABORT;
else
LP_F(minor) &= ~LP_ABORT;
break;
case LPWAIT:
LP_WAIT(minor) = arg;
break;
case LPSETIRQ: {
int oldirq;
int newirq = arg;
struct lp_struct *lp = &lp_table[minor];
struct sigaction sa;
if (!suser())
return -EPERM;
oldirq = LP_IRQ(minor);
/* Allocate buffer now if we are going to need it */
if (!oldirq && newirq) {
lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
if (!lp->lp_buffer)
return -ENOMEM;
}
if (oldirq) {
free_irq(oldirq);
}
if (newirq) {
/* Install new irq */
sa.sa_handler = lp_interrupt;
sa.sa_flags = SA_INTERRUPT;
sa.sa_mask = 0;
sa.sa_restorer = NULL;
if ((retval = irqaction(newirq, &sa))) {
if (oldirq) {
/* restore old irq */
irqaction(oldirq, &sa);
} else {
/* We don't need the buffer */
kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
lp->lp_buffer = NULL;
}
return retval;
}
}
if (oldirq && !newirq) {
/* We don't need the buffer */
kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
lp->lp_buffer = NULL;
}
LP_IRQ(minor) = newirq;
lp_reset(minor);
break;
}
case LPGETIRQ:
retval = LP_IRQ(minor);
break;
default:
retval = -EINVAL;
}
return retval;
}
static struct file_operations lp_fops = {
lp_lseek,
NULL, /* lp_read */
lp_write,
NULL, /* lp_readdir */
NULL, /* lp_select */
lp_ioctl,
NULL, /* lp_mmap */
lp_open,
lp_release
};
long lp_init(long kmem_start)
{
int offset = 0;
unsigned int testvalue = 0;
int count = 0;
if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
printk("unable to get major %d for line printer\n", LP_MAJOR);
return kmem_start;
}
/* take on all known port values */
for (offset = 0; offset < LP_NO; offset++) {
/* write to port & read back to check */
outb_p( LP_DUMMY, LP_B(offset));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
testvalue = inb_p(LP_B(offset));
if (testvalue != 255) {
LP_F(offset) |= LP_EXIST;
lp_reset(offset);
printk("lp_init: lp%d exists (%d), ", offset, testvalue);
if (LP_IRQ(offset))
printk("using IRQ%d\n", LP_IRQ(offset));
else
printk("using polling driver\n");
count++;
}
}
if (count == 0)
printk("lp_init: no lp devices found\n");
return kmem_start;
}

View file

@ -0,0 +1,426 @@
/*
* linux/kernel/chr_drv/mem.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/tty.h>
#include <linux/mouse.h>
#include <linux/tpqic02.h>
#include <linux/malloc.h>
#include <linux/mman.h>
#include <asm/segment.h>
#include <asm/io.h>
#ifdef CONFIG_SOUND
extern long soundcard_init(long mem_start);
#endif
static int read_ram(struct inode * inode, struct file * file,char * buf, int count)
{
return -EIO;
}
static int write_ram(struct inode * inode, struct file * file,char * buf, int count)
{
return -EIO;
}
static int read_mem(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned long p = file->f_pos;
int read;
if (count < 0)
return -EINVAL;
if (p >= high_memory)
return 0;
if (count > high_memory - p)
count = high_memory - p;
read = 0;
while (p < PAGE_SIZE && count > 0) {
put_fs_byte(0,buf);
buf++;
p++;
count--;
read++;
}
memcpy_tofs(buf,(void *) p,count);
read += count;
file->f_pos += read;
return read;
}
static int write_mem(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned long p = file->f_pos;
int written;
if (count < 0)
return -EINVAL;
if (p >= high_memory)
return 0;
if (count > high_memory - p)
count = high_memory - p;
written = 0;
while (p < PAGE_SIZE && count > 0) {
/* Hmm. Do something? */
buf++;
p++;
count--;
written++;
}
memcpy_fromfs((void *) p,buf,count);
written += count;
file->f_pos += written;
return count;
}
static int mmap_mem(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct * mpnt;
if (off & 0xfff || off + len < off)
return -ENXIO;
if (x86 > 3 && off >= high_memory)
prot |= PAGE_PCD;
if (remap_page_range(addr, off, len, prot))
return -EAGAIN;
/* try to create a dummy vmm-structure so that the rest of the kernel knows we are here */
mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!mpnt)
return 0;
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = inode;
inode->i_count++;
mpnt->vm_offset = off;
mpnt->vm_ops = NULL;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, NULL, NULL);
return 0;
}
static int read_kmem(struct inode *inode, struct file *file, char *buf, int count)
{
int read1, read2;
read1 = read_mem(inode, file, buf, count);
if (read1 < 0)
return read1;
read2 = vread(buf + read1, (char *) file->f_pos, count - read1);
if (read2 < 0)
return read2;
file->f_pos += read2;
return read1 + read2;
}
static int read_port(struct inode * inode,struct file * file,char * buf, int count)
{
unsigned int i = file->f_pos;
char * tmp = buf;
while (count-- > 0 && i < 65536) {
put_fs_byte(inb(i),tmp);
i++;
tmp++;
}
file->f_pos = i;
return tmp-buf;
}
static int write_port(struct inode * inode,struct file * file,char * buf, int count)
{
unsigned int i = file->f_pos;
char * tmp = buf;
while (count-- > 0 && i < 65536) {
outb(get_fs_byte(tmp),i);
i++;
tmp++;
}
file->f_pos = i;
return tmp-buf;
}
static int read_null(struct inode * node,struct file * file,char * buf,int count)
{
return 0;
}
static int write_null(struct inode * inode,struct file * file,char * buf, int count)
{
return count;
}
static int read_zero(struct inode * node,struct file * file,char * buf,int count)
{
int left;
for (left = count; left > 0; left--) {
put_fs_byte(0,buf);
buf++;
}
return count;
}
static int mmap_zero(struct inode * inode, struct file * file,
unsigned long addr, size_t len, int prot, unsigned long off)
{
struct vm_area_struct *mpnt;
if (prot & PAGE_RW)
return -EINVAL;
if (zeromap_page_range(addr, len, prot))
return -EAGAIN;
/*
* try to create a dummy vmm-structure so that the
* rest of the kernel knows we are here
*/
mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
if (!mpnt)
return 0;
mpnt->vm_task = current;
mpnt->vm_start = addr;
mpnt->vm_end = addr + len;
mpnt->vm_page_prot = prot;
mpnt->vm_share = NULL;
mpnt->vm_inode = NULL;
mpnt->vm_offset = off;
mpnt->vm_ops = NULL;
insert_vm_struct(current, mpnt);
merge_segments(current->mmap, ignoff_mergep, inode);
return 0;
}
static int read_full(struct inode * node,struct file * file,char * buf,int count)
{
return count;
}
static int write_full(struct inode * inode,struct file * file,char * buf, int count)
{
return -ENOSPC;
}
/*
* Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
* both devices with "a" now. This was previously impossible. SRB.
*/
static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
return file->f_pos=0;
}
/*
* The memory devices use the full 32 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
* though, in that case (0).
*
* also note that seeking relative to the "end of file" isn't supported:
* it has no meaning, so it returns -EINVAL.
*/
static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
switch (orig) {
case 0:
file->f_pos = offset;
return file->f_pos;
case 1:
file->f_pos += offset;
return file->f_pos;
default:
return -EINVAL;
}
if (file->f_pos < 0)
return 0;
return file->f_pos;
}
#define write_kmem write_mem
#define mmap_kmem mmap_mem
#define zero_lseek null_lseek
#define write_zero write_null
static struct file_operations ram_fops = {
memory_lseek,
read_ram,
write_ram,
NULL, /* ram_readdir */
NULL, /* ram_select */
NULL, /* ram_ioctl */
NULL, /* ram_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations mem_fops = {
memory_lseek,
read_mem,
write_mem,
NULL, /* mem_readdir */
NULL, /* mem_select */
NULL, /* mem_ioctl */
mmap_mem,
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations kmem_fops = {
memory_lseek,
read_kmem,
write_kmem,
NULL, /* kmem_readdir */
NULL, /* kmem_select */
NULL, /* kmem_ioctl */
mmap_kmem,
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations null_fops = {
null_lseek,
read_null,
write_null,
NULL, /* null_readdir */
NULL, /* null_select */
NULL, /* null_ioctl */
NULL, /* null_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations port_fops = {
memory_lseek,
read_port,
write_port,
NULL, /* port_readdir */
NULL, /* port_select */
NULL, /* port_ioctl */
NULL, /* port_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
static struct file_operations zero_fops = {
zero_lseek,
read_zero,
write_zero,
NULL, /* zero_readdir */
NULL, /* zero_select */
NULL, /* zero_ioctl */
mmap_zero,
NULL, /* no special open code */
NULL /* no special release code */
};
static struct file_operations full_fops = {
memory_lseek,
read_full,
write_full,
NULL, /* full_readdir */
NULL, /* full_select */
NULL, /* full_ioctl */
NULL, /* full_mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
static int memory_open(struct inode * inode, struct file * filp)
{
switch (MINOR(inode->i_rdev)) {
case 0:
filp->f_op = &ram_fops;
break;
case 1:
filp->f_op = &mem_fops;
break;
case 2:
filp->f_op = &kmem_fops;
break;
case 3:
filp->f_op = &null_fops;
break;
case 4:
filp->f_op = &port_fops;
break;
case 5:
filp->f_op = &zero_fops;
break;
case 7:
filp->f_op = &full_fops;
break;
default:
return -ENODEV;
}
if (filp->f_op && filp->f_op->open)
return filp->f_op->open(inode,filp);
return 0;
}
static struct file_operations memory_fops = {
NULL, /* lseek */
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
memory_open, /* just a selector for the real open */
NULL, /* release */
NULL /* fsync */
};
#ifdef CONFIG_FTAPE
char* ftape_big_buffer;
#endif
long chr_dev_init(long mem_start, long mem_end)
{
if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
printk("unable to get major %d for memory devs\n", MEM_MAJOR);
mem_start = tty_init(mem_start);
#ifdef CONFIG_PRINTER
mem_start = lp_init(mem_start);
#endif
#if defined (CONFIG_BUSMOUSE) || defined (CONFIG_82C710_MOUSE) || \
defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
defined (CONFIG_ATIXL_BUSMOUSE)
mem_start = mouse_init(mem_start);
#endif
#ifdef CONFIG_SOUND
mem_start = soundcard_init(mem_start);
#endif
#if CONFIG_TAPE_QIC02
mem_start = tape_qic02_init(mem_start);
#endif
/*
* Rude way to allocate kernel memory buffer for tape device
*/
#ifdef CONFIG_FTAPE
/* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */
ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff);
printk( "ftape: allocated %d buffers alligned at: %p\n",
NR_FTAPE_BUFFERS, ftape_big_buffer);
mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000;
#endif
return mem_start;
}

View file

@ -0,0 +1,99 @@
/*
* linux/kernel/chr_drv/mouse.c
*
* Generic mouse open routine by Johan Myreen
*
* Based on code from Linus
*
* Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
* changes incorporated into 0.97pl4
* by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
* See busmouse.c for particulars.
*
* Made things a lot mode modular - easy to compile in just one or two
* of the mouse drivers, as they are now completely independent. Linus.
*/
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mouse.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
/*
* note that you can remove any or all of the drivers by undefining
* the minor values in <linux/mouse.h>
*/
extern struct file_operations bus_mouse_fops;
extern struct file_operations psaux_fops;
extern struct file_operations ms_bus_mouse_fops;
extern struct file_operations atixl_busmouse_fops;
extern unsigned long bus_mouse_init(unsigned long);
extern unsigned long psaux_init(unsigned long);
extern unsigned long ms_bus_mouse_init(unsigned long);
extern unsigned long atixl_busmouse_init(unsigned long);
static int mouse_open(struct inode * inode, struct file * file)
{
int minor = MINOR(inode->i_rdev);
switch (minor) {
#ifdef CONFIG_BUSMOUSE
case BUSMOUSE_MINOR:
file->f_op = &bus_mouse_fops;
break;
#endif
#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE
case PSMOUSE_MINOR:
file->f_op = &psaux_fops;
break;
#endif
#ifdef CONFIG_MS_BUSMOUSE
case MS_BUSMOUSE_MINOR:
file->f_op = &ms_bus_mouse_fops;
break;
#endif
#ifdef CONFIG_ATIXL_BUSMOUSE
case ATIXL_BUSMOUSE_MINOR:
file->f_op = &atixl_busmouse_fops;
break;
#endif
default:
return -ENODEV;
}
return file->f_op->open(inode,file);
}
static struct file_operations mouse_fops = {
NULL, /* seek */
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
mouse_open,
NULL /* release */
};
unsigned long mouse_init(unsigned long kmem_start)
{
#ifdef CONFIG_BUSMOUSE
kmem_start = bus_mouse_init(kmem_start);
#endif
#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE
kmem_start = psaux_init(kmem_start);
#endif
#ifdef CONFIG_MS_BUSMOUSE
kmem_start = ms_bus_mouse_init(kmem_start);
#endif
#ifdef CONFIG_ATIXL_BUSMOUSE
kmem_start = atixl_busmouse_init(kmem_start);
#endif
if (register_chrdev(MOUSE_MAJOR,"mouse",&mouse_fops))
printk("unable to get major %d for mouse devices\n",
MOUSE_MAJOR);
return kmem_start;
}

View file

@ -0,0 +1,175 @@
/*
* Microsoft busmouse driver based on Logitech driver (see busmouse.c)
*
* Microsoft BusMouse support by Teemu Rantanen (tvr@cs.hut.fi) (02AUG92)
*
* Microsoft Bus Mouse support modified by Derrick Cole (cole@concert.net)
* 8/28/92
*
* Microsoft Bus Mouse support folded into 0.97pl4 code
* by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
* Changes: Logitech and Microsoft support in the same kernel.
* Defined new constants in busmouse.h for MS mice.
* Added int mse_busmouse_type to distinguish busmouse types
* Added a couple of new functions to handle differences in using
* MS vs. Logitech (where the int variable wasn't appropriate).
*
* Modified by Peter Cervasio (address above) (26SEP92)
* Changes: Included code to (properly?) detect when a Microsoft mouse is
* really attached to the machine. Don't know what this does to
* Logitech bus mice, but all it does is read ports.
*
* Modified by Christoph Niemann (niemann@rubdv15.etdv.ruhr-uni-bochum.de)
* Changes: Better interrupt-handler (like in busmouse.c).
* Some changes to reduce code-size.
* Changed dectection code to use inb_p() instead of doing empty
* loops to delay i/o.
*
* version 0.3a
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/busmouse.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
static struct mouse_status mouse;
static void ms_mouse_interrupt(int unused)
{
char dx, dy;
unsigned char buttons;
outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT);
outb((inb(MS_MSE_DATA_PORT) | 0x20), MS_MSE_DATA_PORT);
outb(MS_MSE_READ_X, MS_MSE_CONTROL_PORT);
dx = inb(MS_MSE_DATA_PORT);
outb(MS_MSE_READ_Y, MS_MSE_CONTROL_PORT);
dy = inb(MS_MSE_DATA_PORT);
outb(MS_MSE_READ_BUTTONS, MS_MSE_CONTROL_PORT);
buttons = ~(inb(MS_MSE_DATA_PORT)) & 0x07;
outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT);
outb((inb(MS_MSE_DATA_PORT) & 0xdf), MS_MSE_DATA_PORT);
if (dx != 0 || dy != 0 || buttons != mouse.buttons || ((~buttons) & 0x07)) {
mouse.buttons = buttons;
mouse.dx += dx;
mouse.dy += dy;
mouse.ready = 1;
wake_up_interruptible(&mouse.wait);
}
}
static void release_mouse(struct inode * inode, struct file * file)
{
MS_MSE_INT_OFF();
mouse.active = mouse.ready = 0;
free_irq(MOUSE_IRQ);
}
static int open_mouse(struct inode * inode, struct file * file)
{
if (!mouse.present)
return -EINVAL;
if (mouse.active)
return -EBUSY;
mouse.active = 1;
mouse.ready = mouse.dx = mouse.dy = 0;
mouse.buttons = 0x80;
if (request_irq(MOUSE_IRQ, ms_mouse_interrupt)) {
mouse.active = 0;
return -EBUSY;
}
outb(MS_MSE_START, MS_MSE_CONTROL_PORT);
MS_MSE_INT_ON();
return 0;
}
static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
return -EINVAL;
}
static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
int i, dx, dy;
if (count < 3)
return -EINVAL;
if (!mouse.ready)
return -EAGAIN;
put_fs_byte(mouse.buttons | 0x80, buffer);
dx = mouse.dx < -127 ? -127 : mouse.dx > 127 ? 127 : mouse.dx;
dy = mouse.dy < -127 ? 127 : mouse.dy > 127 ? -127 : -mouse.dy;
put_fs_byte((char)dx, buffer + 1);
put_fs_byte((char)dy, buffer + 2);
for (i = 3; i < count; i++)
put_fs_byte(0x00, buffer + i);
mouse.dx -= dx;
mouse.dy += dy;
mouse.ready = 0;
return i;
}
static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
if (sel_type != SEL_IN)
return 0;
if (mouse.ready)
return 1;
select_wait(&mouse.wait,wait);
return 0;
}
struct file_operations ms_bus_mouse_fops = {
NULL, /* mouse_seek */
read_mouse,
write_mouse,
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
NULL, /* mouse_mmap */
open_mouse,
release_mouse,
};
unsigned long ms_bus_mouse_init(unsigned long kmem_start)
{
int mse_byte, i;
mouse.present = mouse.active = mouse.ready = 0;
mouse.buttons = 0x80;
mouse.dx = mouse.dy = 0;
mouse.wait = NULL;
if (inb_p(MS_MSE_SIGNATURE_PORT) == 0xde) {
mse_byte = inb_p(MS_MSE_SIGNATURE_PORT);
for (i = 0; i < 4; i++) {
if (inb_p(MS_MSE_SIGNATURE_PORT) == 0xde) {
if (inb_p(MS_MSE_SIGNATURE_PORT) == mse_byte)
mouse.present = 1;
else
mouse.present = 0;
} else
mouse.present = 0;
}
}
if (mouse.present == 0) {
return kmem_start;
}
MS_MSE_INT_OFF();
printk("Microsoft BusMouse detected and installed.\n");
return kmem_start;
}

View file

@ -0,0 +1,552 @@
/*
* linux/kernel/chr_drv/psaux.c
*
* Driver for PS/2 type mouse by Johan Myreen.
*
* Supports pointing devices attached to a PS/2 type
* Keyboard and Auxiliary Device Controller.
*
* Corrections in device setup for some laptop mice & trackballs.
* 02Feb93 (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
*
* Changed to prevent keyboard lockups on AST Power Exec.
* 28Jul93 Brad Bosch - brad@lachman.com
*
* Modified by Johan Myreen (jem@cs.hut.fi) 04Aug93
* to include support for QuickPort mouse.
*
* Changed references to "QuickPort" with "82C710" since "QuickPort"
* is not what this driver is all about -- QuickPort is just a
* connector type, and this driver is for the mouse port on the Chips
* & Technologies 82C710 interface chip. 15Nov93 jem@cs.hut.fi
*/
/* Uncomment the following line if your mouse needs initialization. */
/* #define INITIALIZE_DEVICE */
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/config.h>
/* aux controller ports */
#define AUX_INPUT_PORT 0x60 /* Aux device output buffer */
#define AUX_OUTPUT_PORT 0x60 /* Aux device input buffer */
#define AUX_COMMAND 0x64 /* Aux device command buffer */
#define AUX_STATUS 0x64 /* Aux device status reg */
/* aux controller status bits */
#define AUX_OBUF_FULL 0x21 /* output buffer (from device) full */
#define AUX_IBUF_FULL 0x02 /* input buffer (to device) full */
/* aux controller commands */
#define AUX_CMD_WRITE 0x60 /* value to write to controller */
#define AUX_MAGIC_WRITE 0xd4 /* value to send aux device data */
#define AUX_INTS_ON 0x47 /* enable controller interrupts */
#define AUX_INTS_OFF 0x65 /* disable controller interrupts */
#define AUX_DISABLE 0xa7 /* disable aux */
#define AUX_ENABLE 0xa8 /* enable aux */
/* aux device commands */
#define AUX_SET_RES 0xe8 /* set resolution */
#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */
#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */
#define AUX_GET_SCALE 0xe9 /* get scaling factor */
#define AUX_SET_STREAM 0xea /* set stream mode */
#define AUX_SET_SAMPLE 0xf3 /* set sample rate */
#define AUX_ENABLE_DEV 0xf4 /* enable aux device */
#define AUX_DISABLE_DEV 0xf5 /* disable aux device */
#define AUX_RESET 0xff /* reset aux device */
#define MAX_RETRIES 60 /* some aux operations take long time*/
#define AUX_IRQ 12
#define AUX_BUF_SIZE 2048
/* 82C710 definitions */
#define QP_DATA 0x310 /* Data Port I/O Address */
#define QP_STATUS 0x311 /* Status Port I/O Address */
#define QP_DEV_IDLE 0x01 /* Device Idle */
#define QP_RX_FULL 0x02 /* Device Char received */
#define QP_TX_IDLE 0x04 /* Device XMIT Idle */
#define QP_RESET 0x08 /* Device Reset */
#define QP_INTS_ON 0x10 /* Device Interrupt On */
#define QP_ERROR_FLAG 0x20 /* Device Error */
#define QP_CLEAR 0x40 /* Device Clear */
#define QP_ENABLE 0x80 /* Device Enable */
#define QP_IRQ 12
extern unsigned char aux_device_present;
extern unsigned char kbd_read_mask; /* from keyboard.c */
struct aux_queue {
unsigned long head;
unsigned long tail;
struct wait_queue *proc_list;
unsigned char buf[AUX_BUF_SIZE];
};
static struct aux_queue *queue;
static int aux_ready = 0;
static int aux_busy = 0;
static int aux_present = 0;
static int poll_aux_status(void);
#ifdef CONFIG_82C710_MOUSE
static int qp_present = 0;
static int qp_busy = 0;
static int qp_data = QP_DATA;
static int qp_status = QP_STATUS;
static int poll_qp_status(void);
static int probe_qp(void);
#endif
/*
* Write to aux device
*/
static void aux_write_dev(int val)
{
poll_aux_status();
outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); /* write magic cookie */
poll_aux_status();
outb_p(val,AUX_OUTPUT_PORT); /* write data */
}
/*
* Write to device & handle returned ack
*/
#if defined INITIALIZE_DEVICE
static int aux_write_ack(int val)
{
int retries = 0;
aux_write_dev(val); /* write the value to the device */
while ((inb(AUX_STATUS) & AUX_OBUF_FULL) != AUX_OBUF_FULL
&& retries < MAX_RETRIES) { /* wait for ack */
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 5;
schedule();
retries++;
}
if ((inb(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL)
{
return (inb(AUX_INPUT_PORT));
}
return 0;
}
#endif /* INITIALIZE_DEVICE */
/*
* Write aux device command
*/
static void aux_write_cmd(int val)
{
poll_aux_status();
outb_p(AUX_CMD_WRITE,AUX_COMMAND);
poll_aux_status();
outb_p(val,AUX_OUTPUT_PORT);
}
static unsigned int get_from_queue(void)
{
unsigned int result;
unsigned long flags;
save_flags(flags);
cli();
result = queue->buf[queue->tail];
queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
restore_flags(flags);
return result;
}
static inline int queue_empty(void)
{
return queue->head == queue->tail;
}
/*
* Interrupt from the auxiliary device: a character
* is waiting in the keyboard/aux controller.
*/
static void aux_interrupt(int cpl)
{
int head = queue->head;
int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
queue->buf[head] = inb(AUX_INPUT_PORT);
if (head != maxhead) {
head++;
head &= AUX_BUF_SIZE-1;
}
queue->head = head;
aux_ready = 1;
wake_up_interruptible(&queue->proc_list);
}
/*
* Interrupt handler for the 82C710 mouse port. A character
* is waiting in the 82C710.
*/
#ifdef CONFIG_82C710_MOUSE
static void qp_interrupt(int cpl)
{
int head = queue->head;
int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
queue->buf[head] = inb(qp_data);
if (head != maxhead) {
head++;
head &= AUX_BUF_SIZE-1;
}
queue->head = head;
aux_ready = 1;
wake_up_interruptible(&queue->proc_list);
}
#endif
static void release_aux(struct inode * inode, struct file * file)
{
aux_write_dev(AUX_DISABLE_DEV); /* disable aux device */
aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
poll_aux_status();
outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
poll_aux_status();
free_irq(AUX_IRQ);
aux_busy = 0;
}
#ifdef CONFIG_82C710_MOUSE
static void release_qp(struct inode * inode, struct file * file)
{
unsigned char status;
if (!poll_qp_status())
printk("Warning: Mouse device busy in release_qp()\n");
status = inb_p(qp_status);
outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
if (!poll_qp_status())
printk("Warning: Mouse device busy in release_qp()\n");
free_irq(QP_IRQ);
qp_busy = 0;
}
#endif
/*
* Install interrupt handler.
* Enable auxiliary device.
*/
static int open_aux(struct inode * inode, struct file * file)
{
if (!aux_present)
return -EINVAL;
if (aux_busy)
return -EBUSY;
if (!poll_aux_status())
return -EBUSY;
aux_busy = 1;
queue->head = queue->tail = 0; /* Flush input queue */
if (request_irq(AUX_IRQ, aux_interrupt)) {
aux_busy = 0;
return -EBUSY;
}
poll_aux_status();
outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */
aux_write_dev(AUX_ENABLE_DEV); /* enable aux device */
aux_write_cmd(AUX_INTS_ON); /* enable controller ints */
poll_aux_status();
aux_ready = 0;
return 0;
}
#ifdef CONFIG_82C710_MOUSE
/*
* Install interrupt handler.
* Enable the device, enable interrupts. Set qp_busy
* (allow only one opener at a time.)
*/
static int open_qp(struct inode * inode, struct file * file)
{
unsigned char status;
if (!qp_present)
return -EINVAL;
if (qp_busy)
return -EBUSY;
if (request_irq(QP_IRQ, qp_interrupt))
return -EBUSY;
qp_busy = 1;
status = inb_p(qp_status);
status |= (QP_ENABLE|QP_RESET);
outb_p(status, qp_status);
status &= ~(QP_RESET);
outb_p(status, qp_status);
queue->head = queue->tail = 0; /* Flush input queue */
status |= QP_INTS_ON;
outb_p(status, qp_status); /* Enable interrupts */
while (!poll_qp_status()) {
printk("Error: Mouse device busy in open_qp()\n");
return -EBUSY;
}
outb_p(AUX_ENABLE_DEV, qp_data); /* Wake up mouse */
return 0;
}
#endif
/*
* Write to the aux device.
*/
static int write_aux(struct inode * inode, struct file * file, char * buffer, int count)
{
int i = count;
while (i--) {
if (!poll_aux_status())
return -EIO;
outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
if (!poll_aux_status())
return -EIO;
outb_p(get_fs_byte(buffer++),AUX_OUTPUT_PORT);
}
inode->i_mtime = CURRENT_TIME;
return count;
}
#ifdef CONFIG_82C710_MOUSE
/*
* Write to the 82C710 mouse device.
*/
static int write_qp(struct inode * inode, struct file * file, char * buffer, int count)
{
int i = count;
while (i--) {
if (!poll_qp_status())
return -EIO;
outb_p(get_fs_byte(buffer++), qp_data);
}
inode->i_mtime = CURRENT_TIME;
return count;
}
#endif
/*
* Put bytes from input queue to buffer.
*/
static int read_aux(struct inode * inode, struct file * file, char * buffer, int count)
{
struct wait_queue wait = { current, NULL };
int i = count;
unsigned char c;
if (queue_empty()) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
add_wait_queue(&queue->proc_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
if (queue_empty() && !(current->signal & ~current->blocked)) {
schedule();
goto repeat;
}
current->state = TASK_RUNNING;
remove_wait_queue(&queue->proc_list, &wait);
}
while (i > 0 && !queue_empty()) {
c = get_from_queue();
put_fs_byte(c, buffer++);
i--;
}
aux_ready = !queue_empty();
if (count-i) {
inode->i_atime = CURRENT_TIME;
return count-i;
}
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
return 0;
}
static int aux_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
if (sel_type != SEL_IN)
return 0;
if (aux_ready)
return 1;
select_wait(&queue->proc_list, wait);
return 0;
}
struct file_operations psaux_fops = {
NULL, /* seek */
read_aux,
write_aux,
NULL, /* readdir */
aux_select,
NULL, /* ioctl */
NULL, /* mmap */
open_aux,
release_aux,
};
/*
* Initialize driver. First check for a 82C710 chip; if found
* forget about the Aux port and use the *_qp functions.
*/
unsigned long psaux_init(unsigned long kmem_start)
{
int qp_found = 0;
#ifdef CONFIG_82C710_MOUSE
printk("Probing 82C710 mouse port device.\n");
if ((qp_found = probe_qp())) {
printk("82C710 type pointing device detected -- driver installed.\n");
/* printk("82C710 address = %x (should be 0x310)\n", qp_data); */
qp_present = 1;
psaux_fops.write = write_qp;
psaux_fops.open = open_qp;
psaux_fops.release = release_qp;
poll_qp_status();
} else
#endif
if (aux_device_present == 0xaa) {
printk("PS/2 auxiliary pointing device detected -- driver installed.\n");
aux_present = 1;
kbd_read_mask = AUX_OBUF_FULL;
poll_aux_status();
} else {
return kmem_start; /* No mouse at all */
}
queue = (struct aux_queue *) kmem_start;
kmem_start += sizeof (struct aux_queue);
queue->head = queue->tail = 0;
queue->proc_list = NULL;
if (!qp_found) {
#if defined INITIALIZE_DEVICE
outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */
aux_write_ack(AUX_SET_SAMPLE);
aux_write_ack(100); /* 100 samples/sec */
aux_write_ack(AUX_SET_RES);
aux_write_ack(3); /* 8 counts per mm */
aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
poll_aux_status();
#endif /* INITIALIZE_DEVICE */
outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
poll_aux_status();
}
return kmem_start;
}
static int poll_aux_status(void)
{
int retries=0;
while ((inb(AUX_STATUS)&0x03) && retries < MAX_RETRIES) {
if (inb_p(AUX_STATUS) & AUX_OBUF_FULL == AUX_OBUF_FULL)
inb_p(AUX_INPUT_PORT);
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 5;
schedule();
retries++;
}
return !(retries==MAX_RETRIES);
}
#ifdef CONFIG_82C710_MOUSE
/*
* Wait for device to send output char and flush any input char.
*/
static int poll_qp_status(void)
{
int retries=0;
while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
!= (QP_DEV_IDLE|QP_TX_IDLE)
&& retries < MAX_RETRIES) {
if (inb_p(qp_status)&(QP_RX_FULL))
inb_p(qp_data);
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 5;
schedule();
retries++;
}
return !(retries==MAX_RETRIES);
}
/*
* Function to read register in 82C710.
*/
static inline unsigned char read_710(unsigned char index)
{
outb_p(index, 0x390); /* Write index */
return inb_p(0x391); /* Read the data */
}
/*
* See if we can find a 82C710 device. Read mouse address.
*/
static int probe_qp(void)
{
outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */
outb_p(0xaa, 0x3fa); /* Inverse of 55 */
outb_p(0x36, 0x3fa); /* Address the chip */
outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */
outb_p(0x1b, 0x2fa); /* Inverse of e4 */
if (read_710(0x0f) != 0xe4) /* Config address found? */
return 0; /* No: no 82C710 here */
qp_data = read_710(0x0d)*4; /* Get mouse I/O address */
qp_status = qp_data+1;
outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */
return 1;
}
#endif

Some files were not shown because too many files have changed in this diff Show more