mouse support that requires polling

This commit is contained in:
Kartik K. Agaram 2021-03-23 21:14:12 -07:00
parent e0f6dd5240
commit 49a9938333
5 changed files with 271 additions and 15 deletions

3
400.mu
View File

@ -12,6 +12,9 @@ sig read-key kbd: (addr keyboard) -> _/eax: byte
sig load-first-sector-from-primary-bus-secondary-drive out: (addr stream byte)
sig store-first-sector-to-primary-bus-secondary-drive out: (addr stream byte)
# mouse
sig read-mouse-event -> _/eax: int, _/ecx: int
# tests
sig count-test-failure
sig num-test-failures -> _/eax: int

View File

@ -100,24 +100,24 @@ $ ./translate_emulated ex2.mu # ~2 mins to emit disk.img
Mu programs can be written for two very different environments:
* At the top-level, Mu programs emit a bootable image that runs without an OS
(under emulation; I haven't tested on native hardware yet). There's just a
screen and a keyboard, and that's it. No mouse, no hardware acceleration, no
virtual memory, no process separation, no multi-tasking, no persistent
storage, no network. All tests run on boot. `main` only runs if all tests
pass.
(under emulation; I haven't tested on native hardware yet). There's rudimentary
support for some core peripherals: a 1024x768 screen, a keyboard with some
key-combinations, a PS/2 mouse that must be polled, a slow ATA disk drive.
No hardware acceleration, no virtual memory, no process separation, no
multi-tasking, no network. Boot always runs all tests, and only gets to
`main` if all tests pass.
* The top-level is built using tools created under the `linux/` sub-directory.
This sub-directory contains an entirely separate set of standard libraries
intended for building programs that run with just a Linux kernel, reading
from stdin and writing to stdout. The Mu compiler is such a program, at
`linux/mu.subx`. Individual programs typically run tests if given some
commandline argument like `test`.
This sub-directory contains an entirely separate set of libraries intended
for building programs that run with just a Linux kernel, reading from stdin
and writing to stdout. The Mu compiler is such a program, at `linux/mu.subx`.
Individual programs typically run tests if given some commandline argument
like `test`.
While I currently focus on programs without an OS, the `linux/` sub-directory
is fairly ergonomic. There's a couple of dozen example programs to try out
there. It is likely to be the option for a network stack in the foreseeable
future; I have no idea how to write to disk or interact on the network without
Linux.
future; I have no idea how to interact on the network without Linux.
## Syntax

View File

@ -11,5 +11,5 @@ display_library: sdl2
ata0-master: type=disk, path="disk.img", mode=flat, cylinders=20, heads=16, spt=63 # 10MB, 512 bytes per sector
boot: disk
# PS/2 mouse requires black magic that I don't know how to explain.
mouse: enabled=1, toggle=ctrl+f10
log: -

215
boot.subx
View File

@ -219,6 +219,8 @@ initialize_32bit_mode:
fb/enable-interrupts
(initialize-mouse)
## enable floating point
db/floating-point-coprocessor e3/initialize
# eax <- cr4
@ -805,14 +807,15 @@ Font:
== code
# Use 28-bit PIO mode to read the first sector (512 bytes) from an IDE (ATA)
# disk drive.
## Controlling IDE (ATA) hard disks
# Uses 28-bit PIO mode.
# Inspired by https://colorforth.github.io/ide.html
#
# Resources:
# https://wiki.osdev.org/ATA_PIO_Mode
# https://forum.osdev.org/viewtopic.php?f=1&p=167798
# read-sector, according to https://www.scs.stanford.edu/11wi-cs140/pintos/specs/ata-3-std.pdf
load-first-sector-from-primary-bus-secondary-drive: # out: (addr stream byte)
# . prologue
55/push-ebp
@ -1183,4 +1186,212 @@ until-ata-ready-for-data:
(until-ata-data-available)
c3/return
## Controlling a PS/2 mouse
# Uses no IRQs, just polling.
# Thanks Dave Long: https://github.com/jtauber/cleese/blob/master/necco/kernel/bochs/py8042.py
#
# Resources:
# https://wiki.osdev.org/Mouse_Input
# results x/eax, y/ecx range from -256 to +255
# See https://wiki.osdev.org/index.php?title=Mouse_Input&oldid=25663#Format_of_First_3_Packet_Bytes
read-mouse-event: # -> _/eax: int, _/ecx: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
52/push-edx
53/push-ebx
# if no event, return 0, 0
b8/copy-to-eax 0/imm32
b9/copy-to-ecx 0/imm32
(any-mouse-event?) # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= $read-mouse-event:end/disp8
# var f1/edx: byte = inb(0x60)
31/xor %eax 0/r32/eax
e4/read-port-into-al 0x60/imm8
89/<- %edx 0/r32/eax
(wait-for-mouse-event)
# var dx/ebx: byte = inb(0x60)
31/xor %eax 0/r32/eax
e4/read-port-into-al 0x60/imm8
89/<- %ebx 0/r32/eax
(wait-for-mouse-event)
# var dy/ecx: byte = inb(0x60)
31/xor %eax 0/r32/eax
e4/read-port-into-al 0x60/imm8
89/<- %ecx 0/r32/eax
# eax = dx
89/<- %eax 3/r32/ebx
# if (f1 & 0x10) dx = -dx
{
f6 0/subop/test-bits %dl 0x10/imm8
74/jump-if-zero break/disp8
0d/or-eax-with 0xffffff00/imm32
}
# if (f1 & 0x20) dy = -dy
{
f6 0/subop/test-bits %dl 0x20/imm8
74/jump-if-zero break/disp8
81 1/subop/or %ecx 0xffffff00/imm32
}
$read-mouse-event:end:
# . restore registers
5b/pop-to-ebx
5a/pop-to-edx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
wait-for-mouse-event:
# . save registers
50/push-eax
#
{
(any-mouse-event?) # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= loop/disp8
}
$wait-for-mouse-event:end:
# . restore registers
58/pop-to-eax
# .
c3/return
any-mouse-event?: # -> _/eax: boolean
31/xor %eax 0/r32/eax
# 0x1 bit: there's data from the keyboard controller
# 0x20 bit: it's data from the aux port (the mouse)
e4/read-port-into-al 0x60/imm8
24/and-al-with 0x21/imm8
3c/compare-al-with 0x21/imm8
0f 94/set-byte-if-= %al
c3/return
initialize-mouse:
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "A" 7 0)
(enable-keyboard-controller-aux-device)
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "B" 7 0)
# tell mouse to use default settings
(send-mouse-command 0xf6)
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "P" 7 0)
# enable mouse
(send-mouse-command 0xf4)
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "Z" 7 0)
c3/return
enable-keyboard-controller-aux-device:
(command-keyboard-controller 0xa8)
c3/return
send-mouse-command: # command: byte
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(command-keyboard-controller 0xd4)
(send-keyboard-controller-data *(ebp+8))
(wait-for-ack-from-mouse)
$send-mouse-command:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
wait-for-ack-from-mouse:
# . save registers
50/push-eax
{
(read-keyboard-controller-data) # => eax
(draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 %eax 7 0) # screen n fg bg
81 7/subop/compare %eax 0xfa/imm32
75/jump-if-!= loop/disp8
}
$wait-for-ack-from-mouse:end:
# . restore registers
58/pop-eax
c3/return
command-keyboard-controller: # command: byte
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
#
(poll-keyboard-controller-to-write)
8b/-> *(ebp+8) 0/r32/eax
e6/write-al-into-port 0x64/imm8
$command-keyboard-controller:end:
# . restore registers
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
send-keyboard-controller-data: # data: byte
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
#
(poll-keyboard-controller-to-write)
8b/-> *(ebp+8) 0/r32/eax
e6/write-al-into-port 0x60/imm8
$send-keyboard-controller-data:end:
# . restore registers
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
read-keyboard-controller-data: # -> _/eax: byte
(poll-keyboard-controller-to-read-data-port)
31/xor %eax 0/r32/eax
e4/read-port-into-al 0x60/imm8
c3/return
poll-keyboard-controller-to-write:
# . save registers
50/push-eax
# "All output to port 0x60 or 0x64 must be preceded by waiting for bit 1
# (value=2) of port 0x64 to become clear."
# https://wiki.osdev.org/index.php?title=Mouse_Input&oldid=25663#Waiting_to_Send_Bytes_to_Port_0x60_and_0x64
{
e4/read-port-into-al 0x64/imm8
a8/test-bits-in-al 2/imm8 # set zf if bit 1 (second-least significant) is not set
75/jump-if-zf-not-set-and-bit-1-set loop/disp8
}
$poll-keyboard-controller-to-write:end:
# . restore registers
58/pop-to-eax
# . epilogue
c3/return
poll-keyboard-controller-to-read-data-port:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
# "Bytes cannot be read from port 0x60 until bit 0 (value=1) of port 0x64 is set."
# https://wiki.osdev.org/index.php?title=Mouse_Input&oldid=25663#Waiting_to_Send_Bytes_to_Port_0x60_and_0x64
{
e4/read-port-into-al 0x64/imm8
a8/test-bits-in-al 1/imm8 # set zf if bit 0 (least significant) is not set
74/jump-if-zf-set-and-bit-0-not-set loop/disp8
}
$poll-keyboard-controller-to-read-data-port:end:
# . restore registers
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# vim:ft=subx

42
ex10.mu Normal file
View File

@ -0,0 +1,42 @@
# Demo of mouse, showing deltas in x and y position for every event.
#
# To build a disk image:
# ./translate ex10.mu # emits disk.img
# To run:
# qemu-system-i386 disk.img
# Or:
# bochs -f bochsrc # bochsrc loads disk.img
#
# Expected output:
# Values between -256 and +255 as you move the mouse over the window.
# You might need to click on the window once.
fn main {
# repeatedly print out mouse driver results if non-zero
$main:event-loop: {
var dx/eax: int <- copy 0
var dy/ecx: int <- copy 0
dx, dy <- read-mouse-event
{
compare dx, 0
break-if-!=
compare dy, 0
break-if-!=
loop $main:event-loop
}
{
var dummy1/eax: int <- copy 0
var dummy2/ecx: int <- copy 0
dummy1, dummy2 <- draw-text-wrapping-right-then-down-over-full-screen 0/screen, " ", 0/x, 0x10/y, 0x31/fg, 0/bg
}
{
var dummy/ecx: int <- copy 0
dx, dummy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen 0/screen, dx, 0/x, 0x10/y, 0x31/fg, 0/bg
}
{
var dummy/eax: int <- copy 0
dummy, dy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen 0/screen, dy, 5/x, 0x10/y, 0x31/fg, 0/bg
}
loop
}
}