86 Commits
mip2 ... master

Author SHA1 Message Date
212431a9f2 bitrot: no trailing ';' in macros when eval'd in expr position
https://github.com/rust-lang/rust/issues/79813

At least some of these don't initially appear to be in expr position,
but the end of a block is always an expression, so these being wrapped
in `{}` don't actually help. Seems weird but whatever, easy enough to
fix!
2021-10-27 17:36:30 -07:00
054d40aa95 emu: comment remaining opcodes as todo/unimplemented 2021-04-27 02:51:48 -07:00
27aa49e249 emu: CBW/CWD sign extension operations 2021-04-27 02:29:16 -07:00
d42603501f emu: formatting 2021-04-27 01:16:01 -07:00
bd00e9ca5f emu: ADD/ADC/SUB/SBB operations 2021-04-27 01:15:30 -07:00
940f2d1d1e emu: don't branch from each modrm calculation
modrm calculation still done inline on each opcode, but later op
directives don't branch from each modrm outcome anymore. Speeds
compilation by about 30%, and decreases release binary size by about
300K currently (and presumably more when the rest of the opcodes are done)
2021-04-24 00:52:35 -07:00
19d686e7ad emu: make $name optional in step!-internal calls
Gearing up to change modrm code to no longer push its directives onto
the main processing stack. This also necessitates making the
function's $form/width/tyargs-thingy a child of $name.

It might be nice to revisit this at some point if we want to merge all
the different form=word[0,1,2,3] arities together. I had hoped that
FarPtr could become parameterized on its pointee width (u8 or u16) and
that would obviate the need for turbofishing the op function
directly. Unfortunately, the string operations need disambiguation
regardless as they construct their pointers internally. But perhaps
that could've been solved with some PhantomData. Regardless, this kind
of approach becomes more difficult after this commit as the width is
now tied to the function name.
2021-04-18 00:26:50 -07:00
c66feda360 emu: RegHalf dispatcher to re-merge @r8 op directive
Keeps the macro recursion from branching. In effect this just moves a
conditional from @r8's output to the RegHalf wrapper, so this is
usually a wash except with less code generated.
2021-04-17 23:29:34 -07:00
7b00a8e708 emu: don't macro recur on every @r8/r16 value
Avoids O(M*N) codegen blowup when paired with a modrm arg.
Probably want to do the same thing to the modrm op directives but
those cases are little more tricky and this alone gets rid of much of
the pain.
2021-04-13 14:39:10 -07:00
663fa40137 emu: AND/OR/XOR/TEST ops 2021-04-09 00:38:21 -07:00
eca60fbe45 emu: LOOP/LOOPZ/LOOPNZ ops 2021-04-08 01:18:47 -07:00
96c869f711 emu: NOT/NEG ops 2021-04-08 00:52:12 -07:00
7c2ca230ff emu: XCHG op 2021-04-07 02:06:03 -07:00
49e0132b2e emu: PUSHF/POPF ops 2021-04-07 00:38:10 -07:00
4833b44074 emu: RCL/RCR ops.
I even did some testing this time...
2021-04-06 00:53:58 -07:00
647841469d emu: CMC/CLC/STC/CLD/STC instructions. POPF flags differ from Eager 2021-04-04 00:53:05 -07:00
67b5b7a615 emu: modrm8 r8 decode had HI/LO registers backwards 2021-04-03 01:14:51 -07:00
e1c7370aa2 emu: MOV r/m, imm. Mark MOVs with [addr] as defaulting to data seg 2021-04-03 01:03:06 -07:00
a4e5d03e22 emu: use mask, not mod for ROL/ROR CF calc. No flags when SHL/SHR=0 2021-03-31 01:39:14 -07:00
a789b7e87d emu: flags: make CF & OF always lazy. Remove Flags::update_of()
Rotate ops now backup flags manually into their enum data instead of
converting everything to eager mode
2021-03-30 01:58:08 -07:00
984ff891c0 emu: Make FlagOp enum algebraic to carry op-specific data
Avoids writing operand data for lazy flags that won't be read
2021-03-30 01:35:35 -07:00
d8600151d6 emu: ROL/ROR operations 2021-03-30 01:35:29 -07:00
f5ab9cfad2 emu: terser names for form= directives 2021-03-29 00:33:53 -07:00
40fed58ecf emu: remove unsafe from {push,pop}_modrm operations 2021-03-29 00:22:56 -07:00
7c7f74a65b emu: make string ops generic over operand size
After many attempts, it turns out this is as easy as remembering that
where clauses can bound non-parameter types
2021-03-28 14:46:35 -07:00
a183a5ad38 emu: SHL/SHR/SAR opcodes (BARELY TESTED!), const Op Directive
Carry Flag as yet untested and likely to contain bugs
2021-03-28 02:08:23 -07:00
874fc6adbc emu: Make flags lazy, implement parity flag & JPE/JPO ops 2021-03-27 01:00:29 -07:00
e15fb6ae27 Squelch warning about intersperse() entering stdlib.
Presumably we'll want to switch to it when it standardizes, so we want
the build to break when it's time to move over.
2021-03-27 00:59:04 -07:00
4b5d85352a emu: move cpu Flags into own module 2021-03-27 00:58:51 -07:00
5d2fbcd01d emu: Remove LValue implementation for u16
I think this was from when registers were passed by mut ref and this
could update them in place. Now they are wrapped and those wrappers
are LValues instead.
2021-03-24 20:37:55 -07:00
8c359d2c70 emu: Use type arguments instead of PhantomData to disambiguate
Allows elision of op directive when operands are of unambiguous width
2021-03-24 20:34:08 -07:00
88166c5a85 emu: remove duplicate @rep arg decoder rule 2021-03-24 01:38:11 -07:00
017eaf2955 emu: Hack in push/pop support for ModR/M. UNSAFE, and maybe UNSOUND. 2021-03-24 01:12:32 -07:00
cf9d2dd15d emu: Pass [LR]Values by value instead of by &mut
I think this made sense originally because registers were being passed
as borrows directly out of the cpu struct. Now they have wrappers
around Cell refs, and those wrappers can be passed directly.
2021-03-22 22:21:03 -07:00
9b0b4efc40 emu: update si in cmps[bw] string loop [bugfix] 2021-03-22 22:19:48 -07:00
ff7e8a7fb9 emu: INC/DEC opcodes. width={byte,word} argtypes for disambig
Also made all LValues also RValues for brevity (so INC/DEC don't have
to bound their destination arguments by both, but this is not required
if it causes problems for some reason down the road).
2021-03-19 00:07:49 -07:00
c8d6a3312a emu: near/short JUMP instructions. Use u16+wrapping_add for all rels 2021-03-18 02:16:15 -07:00
d6e645cae7 emu: use wrapping_add in displacement address as operand can be neg 2021-03-18 01:43:22 -07:00
73c537183e emu: Rename "Addr" to "FarPtr"
It was a little confusing with the "Address" trait. And FarPtr carries
with it a segment.
2021-03-18 00:38:06 -07:00
3b746b75ec emu: PUSH/POP opcodes, reimpl RET as just "POP IP"
This also promotes CALL/RET to actually using the bus for
addressing (because it goes through the generic PUSH/POP code), which
is probably more correct anyway.
2021-03-18 00:32:08 -07:00
dcf96ec5b3 emu: Operand trait over u8/u16, d8_as_d16 argtype 2021-03-17 21:50:02 -07:00
ae1f3da4b2 emu: UNTESTED string ops, CMP instruction
Likely to be a bugfest.
There's a flags macro for now but unclear how useful it will turn out
to be in practice. Flags in particular need testing.

Also disappointing that I couldn't make instructions generic across
u8/u16, especially for ordinary, non-string CMP. Need to look into
this more to see if it can be done with num-traits or some other
trickery.
2021-03-17 21:37:03 -07:00
04348a816e emu: Fake opcode ASSERT for testing 2021-03-12 22:19:45 -08:00
0c55ff9b36 emu: modrm displacement, LEA instruction 2021-03-12 08:01:28 -08:00
adb7e672d9 emu: accept cycle cost for modrm spec (and ignore for now) 2021-03-12 04:10:15 -08:00
c372a96b7c emu: all MOV opcodes (no modrm base/index/displacement modes yet) 2021-03-12 04:02:27 -08:00
0a4b240974 emu: fix copypasta where write_lo actually did write_hi 2021-03-12 02:54:44 -08:00
ac7b3b16a1 emu: factor out utils into own module 2021-03-12 02:27:00 -08:00
27bc36f9d2 emu: factor out operations and operands into own module 2021-03-11 22:55:51 -08:00
b950a56d26 emu: segment prefix, addr Arg decoder, Addr arg trait, NOP opcode 2021-03-11 05:55:07 -08:00
71bcec1574 emu: wrap cpu regs in Cell. r16 addr mode.
r16 + modrm16 allowed cpu regs to alias. Wrap them in Cell to allow
this. We also create a Reg wrapper when passing regs so that we can
pass as &mut while simultaneously having other references to the same
reg.
2021-03-10 07:16:08 -08:00
49789c74f9 emu: show/peek debug instructions 2021-03-09 06:42:12 -08:00
20bfc45dae emu: modrm16 arg decoder 2021-03-08 06:20:20 -08:00
3f2c12a83c emu: machinery for ModRM propegation 2021-03-08 06:17:45 -08:00
eeaf713470 emu: dos exit service.
Just calls process::exit() for now
2021-03-06 23:48:00 -08:00
e1a6feb6f5 emu: Factor out hi/lo register access 2021-03-06 23:47:56 -08:00
1e32036bf7 emu: INT instruction, standalone dos function for now 2021-03-06 05:26:40 -08:00
0f4e52697a emu: reghi & d8 address modes. make next_ip assoc instead of member
next_ip would borrow all of the cpu, now we just need IP and CS.
2021-03-06 04:52:09 -08:00
b2aa3fd0e0 emu: MOV generic. Drop Reg16 register wrapper type 2021-03-06 04:18:48 -08:00
be7e926842 emu: dump register file on unimplemented opcode 2021-03-06 02:10:25 -08:00
fa49d19ae0 emu: arg=rhs parsing. d16, reg, regval argtypes. non-generic MOV. 2021-03-06 01:09:17 -08:00
5c39f3b5af emu ops can get multiple args, mem no longer implicit, implement RET 2021-03-06 01:09:07 -08:00
639e05edac basic emu skeleton and loop, CALL instruction & rel16 addr mode 2021-03-06 01:08:19 -08:00
1c15695c4b Explicitly name window size constant
separating out and committing pieces in an old WIP. Don't know what
specifically prompted this change.
2021-02-11 06:33:03 -08:00
a91ee62190 cleanup warnings/weird code 2021-02-11 01:31:04 -08:00
ede85ecb5d bitrot: update for nalgebra deprecations 2021-02-11 01:30:51 -08:00
82c53bb761 bitrot: update for lang/stdlib deprecations 2021-02-11 01:30:05 -08:00
9dd48d359c bitrot: replace bare traits object syntax with dyn traits 2021-02-11 01:30:01 -08:00
dea54e4883 update bitrotted dep 2021-02-11 01:29:40 -08:00
bc0e916179 feature(iterator_flatten) is now stable 2021-02-11 01:28:16 -08:00
5e18d452cb mouselook 2021-02-11 01:27:49 -08:00
f40786931e Return local error type on VR init 2021-02-11 01:26:52 -08:00
7ac96862eb remove bitrot, update and set versions of all deps, notably gfx & nalgebra 2021-02-11 01:25:42 -08:00
493563b7cc lto/no-unwind in release builds 2016-12-29 10:27:12 -08:00
b955d7f1b6 update to gfx 0.13 2016-12-26 15:42:03 -08:00
3a8c0148c8 read rle .EGA files 2016-11-01 04:08:55 -07:00
ddc135e09a read lzw .EGA files 2016-11-01 03:41:12 -07:00
da0d5a26a3 slightly bump up tile anim speed 2016-10-28 00:33:10 -07:00
9db44c212c consolidate imports 2016-10-26 22:52:42 -07:00
f6b0068ec2 haze 2016-10-26 19:42:22 -07:00
d71967565d no supersampling for now 2016-10-21 19:02:26 -07:00
11a16e092f hotkeys for torus radius, camera reset 2016-10-21 18:59:34 -07:00
73ac584ca4 try! -> ? 2016-10-20 12:45:15 -07:00
a66cd3ae64 parse NO_VR=0 2016-10-19 17:34:12 -07:00
2583de70b3 walk around reorienting world torus 2016-10-19 15:14:57 -07:00
f2a645730c NO_VR env var for monitor only mode 2016-10-13 01:50:07 -07:00
25 changed files with 2781 additions and 295 deletions

View File

@@ -1,21 +1,26 @@
[package] [package]
name = "vrtue" name = "vrtue"
version = "0.1.0" version = "0.1.0"
authors = ["Jared Roberts <jaredr@gmail.com>"] authors = ["Jared Burce <jaredr@gmail.com>"]
[dependencies] [dependencies]
byteorder = "1.4.2"
env_logger = "0.3" env_logger = "0.3"
itertools = ">=0.4" itertools = ">=0.4"
gl = "0.10"
gfx = "0.17"
gfx_device_gl = "0.15"
image = "0.19"
log = "0.3" log = "0.3"
lzw = "0.10"
memmap = "~0.2" memmap = "~0.2"
nalgebra = "0.16"
num-traits = "0.2.14"
openvr = "0.6.0"
openvr_sys = "2"
piston = "0.37"
piston_window = "0.80"
gl = "*" [profile.release]
gfx = "*" lto = true
gfx_device_gl = "*" panic = "abort"
image = "*"
nalgebra = "*"
num-traits = "*"
openvr = { git = "https://github.com/rust-openvr/rust-openvr" }
openvr_sys = "*"
piston = "*"
piston_window = "*"

View File

@@ -35,13 +35,13 @@ impl world::Map for Field {
} }
impl world::HasMap for Arena { impl world::HasMap for Arena {
fn map(&self) -> &world::Map { fn map(&self) -> &dyn world::Map {
&self.map &self.map
} }
} }
impl world::HasMap for Shrine { impl world::HasMap for Shrine {
fn map(&self) -> &world::Map { fn map(&self) -> &dyn world::Map {
&self.map &self.map
} }
} }

View File

@@ -4,7 +4,6 @@ use vrtue::vr;
extern crate env_logger; extern crate env_logger;
extern crate gfx; extern crate gfx;
#[macro_use] extern crate log; #[macro_use] extern crate log;
extern crate openvr_sys;
extern crate piston_window; extern crate piston_window;
use self::piston_window::{PistonWindow, Window, WindowSettings}; use self::piston_window::{PistonWindow, Window, WindowSettings};
@@ -22,9 +21,9 @@ pub fn main() {
.build().expect("Building Window"); .build().expect("Building Window");
let render_size = vr.recommended_render_target_size(); let render_size = vr.recommended_render_target_size();
let left: vr::EyeBuffer<ColorFormat, DepthFormat> = vr::create_eyebuffer(&mut window.factory, render_size) let left: vr::EyeBuffer<ColorFormat, DepthFormat> = vr::create_eyebuffer(&mut window.factory, render_size.0, render_size.1)
.expect("create left renderbuffer"); .expect("create left renderbuffer");
let right: vr::EyeBuffer<ColorFormat, DepthFormat> = vr::create_eyebuffer(&mut window.factory, render_size) let right: vr::EyeBuffer<ColorFormat, DepthFormat> = vr::create_eyebuffer(&mut window.factory, render_size.0, render_size.1)
.expect("create right renderbuffer"); .expect("create right renderbuffer");
window.encoder.clear(&left.target, [1.0, 0.0, 0.0, 1.0]); window.encoder.clear(&left.target, [1.0, 0.0, 0.0, 1.0]);
window.encoder.clear_depth(&left.depth, 1.0); window.encoder.clear_depth(&left.depth, 1.0);
@@ -32,31 +31,31 @@ pub fn main() {
window.encoder.clear_depth(&right.depth, 1.0); window.encoder.clear_depth(&right.depth, 1.0);
window.encoder.flush(&mut window.device); window.encoder.flush(&mut window.device);
let mut pads = ::std::collections::BTreeMap::<_, Option<openvr_sys::VRControllerState_t>>::new(); let mut pads = ::std::collections::BTreeMap::<_, Option<vr::ControllerState>>::new();
'main: loop { 'main: loop {
let _poses = vr.poses(); let _poses = vr.poses();
vr.submit(vr::Eye::Left, &left.tex); vr.submit(vr::Eye::Left, &left.tex).expect("submit left eye");
vr.submit(vr::Eye::Right, &right.tex); vr.submit(vr::Eye::Right, &right.tex).expect("submit right eye");
while let Some(ev) = vr.poll_next_event() { while let Some(ev) = vr.poll_next_event() {
match ev { match ev {
vr::Event::Press { dev_idx, controller } => { vr::Event::Press { dev_idx, button } => {
println!("Press event on #{}: {:?}", dev_idx, controller); println!("Press event on #{}: {}", dev_idx, button);
}, },
vr::Event::Unpress { dev_idx, controller } => { vr::Event::Unpress { dev_idx, button } => {
println!("Unpress event on #{}: {:?}", dev_idx, controller); println!("Unpress event on #{}: {}", dev_idx, button);
}, },
vr::Event::Touch { dev_idx, controller } => { vr::Event::Touch { dev_idx, button } => {
if controller.button == openvr_sys::EVRButtonId_k_EButton_SteamVR_Touchpad as u32 { if button == vr::button_id::STEAM_VR_TOUCHPAD as u32 {
pads.insert(dev_idx, None); pads.insert(dev_idx, None);
} }
println!("Touch event on #{}: {:?}", dev_idx, controller); println!("Touch event on #{}: {}", dev_idx, button);
}, },
vr::Event::Untouch { dev_idx, controller } => { vr::Event::Untouch { dev_idx, button } => {
if controller.button == openvr_sys::EVRButtonId_k_EButton_SteamVR_Touchpad as u32 { if button == vr::button_id::STEAM_VR_TOUCHPAD as u32 {
pads.remove(&dev_idx); pads.remove(&dev_idx);
} }
println!("Untouch event on #{}: {:?}", dev_idx, controller); println!("Untouch event on #{}: {}", dev_idx, button);
}, },
/* /*
t if t == openvr_sys::EVREventType::EVREventType_VREvent_TouchPadMove as u32 => { t if t == openvr_sys::EVREventType::EVREventType_VREvent_TouchPadMove as u32 => {
@@ -74,7 +73,7 @@ pub fn main() {
for (pad, old) in pads.iter_mut() { for (pad, old) in pads.iter_mut() {
if let Some(state) = vr.get_controller_state(*pad) { if let Some(state) = vr.get_controller_state(*pad) {
if let Some(old_state) = *old { if let Some(old_state) = *old {
if state.unPacketNum == old_state.unPacketNum { if state.packet_num == old_state.packet_num {
continue; continue;
} }
} }

View File

@@ -11,7 +11,7 @@ use itertools::Itertools;
use memmap::{Mmap, Protection}; use memmap::{Mmap, Protection};
fn mmap_to_rows<'a, M: world::HasMap>(mmap: &memmap::Mmap) -> &'a world::HasMap fn mmap_to_rows<'a, M: world::HasMap>(mmap: &memmap::Mmap) -> &'a dyn world::HasMap
where M: Copy + 'a where M: Copy + 'a
{ {
assert_eq!(std::mem::size_of::<M>(), mmap.len()); assert_eq!(std::mem::size_of::<M>(), mmap.len());
@@ -40,6 +40,7 @@ fn main() {
print_rows(world.map().rows()); print_rows(world.map().rows());
} }
#[allow(unstable_name_collisions)]
fn print_rows<'a, T: ?Sized, U: ?Sized>(rows: Box<T>) fn print_rows<'a, T: ?Sized, U: ?Sized>(rows: Box<T>)
where T: Iterator<Item = Box<U>> + 'a, where T: Iterator<Item = Box<U>> + 'a,
U: Iterator<Item = &'a tile::Tile> + 'a U: Iterator<Item = &'a tile::Tile> + 'a

14
src/bin/run88.rs Normal file
View File

@@ -0,0 +1,14 @@
use std::io::Read;
extern crate vrtue;
use vrtue::emu::pc::PC;
fn main() -> Result<(), std::io::Error> {
let filename = std::env::args().nth(1).expect("Need filename argument");
let mut file = Vec::new();
std::fs::File::open(filename)?.read_to_end(&mut file)?;
let mut pc = PC::new_with_com_file(&file);
pc.run();
println!("{:?}", pc);
Ok(())
}

View File

@@ -5,24 +5,31 @@ use vrtue::scene::{Event, Scene};
extern crate env_logger; extern crate env_logger;
extern crate gfx_device_gl; extern crate gfx_device_gl;
#[macro_use] extern crate log; #[macro_use] extern crate log;
extern crate piston;
extern crate piston_window; extern crate piston_window;
use self::piston_window::{PistonWindow, Window, WindowSettings}; use self::piston::input::{Button, ButtonArgs, Input, Key};
use self::piston_window::{PistonWindow, Size, Window, WindowSettings};
use std::env;
const WINDOW_SIZE: Size = Size { width: 1024, height: 1024 };
pub fn main() { pub fn main() {
env_logger::init().expect("env logger"); env_logger::init().expect("env logger");
let mut vr = vr::VR::new().expect("VR init"); let mut vr = match env::var("NO_VR") {
Ok(ref no_vr) if no_vr != "0" => None,
_ => Some(vr::VR::new().expect("VR init"))
};
let mut window: PistonWindow = let mut window: PistonWindow =
WindowSettings::new("Hello, Britannia!", [1024; 2]) WindowSettings::new("Hello, Britannia!", WINDOW_SIZE)
.exit_on_esc(true) .exit_on_esc(true)
.vsync(false) .vsync(vr.is_none()) // Let VR throttle framerate, if available
.build().expect("Building Window"); .build().expect("Building Window");
let mut aux_command = window.factory.create_command_buffer(); let mut aux_command = window.factory.create_command_buffer();
let mut scene = scenes::world::WorldScene::new(&mut window.device, let mut scene = scenes::world::WorldScene::new(&mut window.device,
&mut window.factory, &mut window.factory,
&mut window.encoder,
&mut aux_command); &mut aux_command);
let view = view::ViewRoot::<gfx_device_gl::Device, view::ColorFormat, view::DepthFormat> let view = view::ViewRoot::<gfx_device_gl::Device, view::ColorFormat, view::DepthFormat>
::create_view(&mut window, &mut vr); ::create_view(&mut window, &mut vr);
@@ -31,12 +38,13 @@ pub fn main() {
//while let Some(_) = window.next() { //while let Some(_) = window.next() {
loop { loop {
scene.update(&mut vr, &mut window.encoder); scene.update(&mut vr, &mut window.encoder);
view.draw(&mut window, &mut vr, &scene); view.draw(&mut window, &mut vr, &scene).expect("main draw");
// handle window events // handle window events
while let Some(ev) = window.poll_event() { while let Some(ev) = window.poll_event() {
match ev { match ev {
piston_window::Input::Text(_) => break 'main, Input::Button(ButtonArgs { button: Button::Keyboard(Key::Space), .. }) |
Input::Button(ButtonArgs { button: Button::Keyboard(Key::Escape), .. }) => break 'main,
_ => debug!("\t{:?}", ev) _ => debug!("\t{:?}", ev)
} }
@@ -44,7 +52,7 @@ pub fn main() {
} }
// handle VR events // handle VR events
while let Some(ev) = vr.poll_next_event() { while let Some(ev) = vr.as_mut().and_then(|vr| vr.poll_next_event()) {
scene.event(Event::Vr(ev)); scene.event(Event::Vr(ev));
} }
} }

View File

@@ -1,19 +1,25 @@
static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0x00], extern crate lzw;
[0x00, 0x00, 0xAA, 0x00],
[0x00, 0xAA, 0x00, 0x00], use std::cell::RefCell;
[0x00, 0xAA, 0xAA, 0x00],
[0xAA, 0x00, 0x00, 0x00], use self::lzw::BitReader;
[0xAA, 0x00, 0xAA, 0x00],
[0xAA, 0x55, 0x00, 0x00], static EGA_PALETTE: [[u8; 4]; 16] = [[0x00, 0x00, 0x00, 0xFF],
[0xAA, 0xAA, 0xAA, 0x00], [0x00, 0x00, 0xAA, 0xFF],
[0x55, 0x55, 0x55, 0x00], [0x00, 0xAA, 0x00, 0xFF],
[0x55, 0x55, 0xFF, 0x00], [0x00, 0xAA, 0xAA, 0xFF],
[0x55, 0xFF, 0x55, 0x00], [0xAA, 0x00, 0x00, 0xFF],
[0x55, 0xFF, 0xFF, 0x00], [0xAA, 0x00, 0xAA, 0xFF],
[0xFF, 0x55, 0x55, 0x00], [0xAA, 0x55, 0x00, 0xFF],
[0xFF, 0x55, 0xFF, 0x00], [0xAA, 0xAA, 0xAA, 0xFF],
[0xFF, 0xFF, 0x55, 0x00], [0x55, 0x55, 0x55, 0xFF],
[0xFF, 0xFF, 0xFF, 0x00]]; [0x55, 0x55, 0xFF, 0xFF],
[0x55, 0xFF, 0x55, 0xFF],
[0x55, 0xFF, 0xFF, 0xFF],
[0xFF, 0x55, 0x55, 0xFF],
[0xFF, 0x55, 0xFF, 0xFF],
[0xFF, 0xFF, 0x55, 0xFF],
[0xFF, 0xFF, 0xFF, 0xFF]];
pub enum Compression { pub enum Compression {
Uncompressed, Uncompressed,
@@ -98,16 +104,14 @@ pub fn decode<'a>(buf: &[u8], compression: Compression, tiling: Tiling)
let out: Vec<u8>; let out: Vec<u8>;
out = match compression { out = match compression {
Compression::Uncompressed => buf.iter() Compression::Uncompressed => decode_uncompressed(buf),
.flat_map(|tile_byte| { Compression::Rle => {
EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize] decode_uncompressed(&decode_rle(buf))
.into_iter() },
.chain(EGA_PALETTE[(tile_byte & 0xF) as usize] Compression::Lzw => {
.into_iter()) let mut decoder = U4Lzw::new();
}) decode_uncompressed(&decoder.decode(buf))
.map(|x| *x) },
.collect(),
_ => unimplemented!()
}; };
let dim = match tiling { let dim = match tiling {
Tiling::Tiled(tiledim) => tiledim as usize, Tiling::Tiled(tiledim) => tiledim as usize,
@@ -115,3 +119,142 @@ pub fn decode<'a>(buf: &[u8], compression: Compression, tiling: Tiling)
}; };
EgaPage { data: out, dim: dim} EgaPage { data: out, dim: dim}
} }
fn decode_uncompressed(buf: &[u8]) -> Vec<u8> {
buf.iter()
.flat_map(|tile_byte| {
EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize]
.iter()
.chain(EGA_PALETTE[(tile_byte & 0xF) as usize]
.iter())
})
.map(|x| *x)
.collect()
}
fn decode_rle(bytes: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
let mut iter = bytes.iter();
while let Some(cmd) = iter.next() {
match cmd {
&0x02 => {
let len = *iter.next().expect("rle missing run length") as usize;
out.extend(::std::iter::repeat(*iter.next().expect("rle missing run value")).take(len));
},
val => out.push(*val),
}
}
out
}
struct U4Lzw {
table: Vec<RefCell<Vec<u8>>>,
load: u16,
}
const U4_CODE_SIZE: u8 = 12;
impl U4Lzw {
fn new() -> U4Lzw {
let mut out = U4Lzw {
table: vec![RefCell::new(Vec::new()); 1 << U4_CODE_SIZE],
load: 0
};
for (i, entry) in out.table.iter_mut().take(256).enumerate() {
entry.borrow_mut().push(i as u8)
}
out
}
fn reset(&mut self) {
for i in 256..(1 << U4_CODE_SIZE) {
self.table[i].borrow_mut().clear();
}
self.load = 0;
}
fn decode(&mut self, bytes: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
let mut iter = CodeIter::new(bytes);
let mut prev = iter.next().expect("empty lzw");
let mut ch = prev as u8;
out.push(ch);
for code in iter {
{
let mut new;
let mut seq = &*self.table[code as usize].borrow();
if seq.is_empty() {
new = self.table[prev as usize].borrow().clone();
new.push(ch);
seq = &new;
};
out.extend(seq.iter());
ch = seq[0];
}
let hash = self.hash(ch, prev);
{
let old = self.table[prev as usize].borrow();
let mut entry = self.table[hash].borrow_mut();
entry.extend(old.iter());
entry.push(ch);
}
prev = code;
self.load += 1;
if self.load > 0xccd { self.reset(); }
}
out
}
fn hash(&self, root: u8, code: u16) -> usize {
let hash = Self::hash_primary(root, code);
if self.table[hash].borrow().is_empty() { return hash; }
let mut hash = Self::hash_secondary(root, code);
while !self.table[hash].borrow().is_empty() {
hash = (hash + 509) & 0xfff
}
hash
}
fn hash_primary(root: u8, code: u16) -> usize {
(((root as u16) << 4) ^ code) as usize
}
fn hash_secondary(root: u8, code: u16) -> usize {
let base = ((((root as u16) << 1) + code) | 0x800) as u32;
let squared = base * base;
((squared & 0x0003ffc0) >> 6) as usize
}
}
struct CodeIter<'a> {
pos: usize,
bytes: &'a [u8],
reader: lzw::MsbReader,
}
impl<'a> CodeIter<'a> {
fn new(bytes: &[u8]) -> CodeIter {
CodeIter { pos: 0, bytes: bytes, reader: lzw::MsbReader::new() }
}
}
impl<'a> Iterator for CodeIter<'a> {
type Item = u16;
fn next(&mut self) -> Option<u16> {
match self.reader.read_bits(&self.bytes[self.pos..], U4_CODE_SIZE) {
lzw::Bits::Some(used, code) => {
self.pos += used;
Some(code)
},
lzw::Bits::None(used) => {
self.pos += used;
None
}
}
}
}

24
src/emu/dos.rs Normal file
View File

@@ -0,0 +1,24 @@
use emu::pc::Bus;
use emu::i8088::i8088;
use emu::util::{read_hi, read_lo, segoff_to_addr};
pub fn interrupt(cpu: &mut i8088, bus: &mut Bus) {
let svc = read_hi(&cpu.a);
match svc {
0x09 => print_string(cpu, bus),
0x4C => exit(read_lo(&cpu.a)),
_ => unimplemented!("dos service: AH={:02X}h\ncpu: {:#X?}", svc, cpu)
}
}
fn print_string(cpu: &i8088, bus: &Bus) {
let addr = segoff_to_addr(cpu.ds.get(), cpu.d.get());
for byte in bus.ram[addr..].iter().take_while(|byte| **byte as char != '$') {
print!("{}", *byte as char);
}
}
fn exit(code: u8) {
println!("");
std::process::exit(code as i32);
}

252
src/emu/flags.rs Normal file
View File

@@ -0,0 +1,252 @@
use std::fmt::{Debug, Formatter};
use emu::operands::{LValue, RValue};
const CF_BIT: u8 = 0; // Carry Flag
const PF_BIT: u8 = 2; // Parity Flag
const AF_BIT: u8 = 4; // Adjust Flag
const ZF_BIT: u8 = 6; // Zero Flag
const SF_BIT: u8 = 7; // Sign Flag
const TF_BIT: u8 = 8; // Trap Flag
const IF_BIT: u8 = 9; // Interrupt Enable
const DF_BIT: u8 = 10; // Direction Flag
const OF_BIT: u8 = 11; // Overflow Flag
#[derive(Clone, Copy, Default)]
pub struct Flags {
// 0: Carry Flag: 1=CY(Carry), 0=NC(No Carry)
// 1: Reserved
// 2: LAZY Parity Flag: 1=PE(Even), 0=PO(Odd)
// 3: Reserved
// 4: LAZY Adjust Flag: 1=AC(Aux Carry), 0=NA(No Aux Carry)
// 5: Reserved
// 6: LAZY Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero)
// 7: LAZY Sign Flag: 1=NG(Negative), 0=PL(Positive)
pub tf: bool, // 8: Trap Flag
pub ie: bool, // 9: (Real name "IF") Interrupt Enable: 1=EI(Enable Interrupt), 0=DI(Disable Interrupt)
pub df: bool, // 10: Direction Flag: 1=DN(Down), 0=UP(Up)
// 11: LAZY Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow)
// bits 12-15 always 1
// ALU state for lazy flag evaluation
flag_op: FlagOp,
res: u16,
sign_mask: u16,
}
impl Flags {
pub fn cf(&self) -> bool {
match self.flag_op {
FlagOp::Eager { cf, .. } => cf,
FlagOp::ADD { cf, .. } => cf,
FlagOp::DEC { cf } => cf,
FlagOp::INC { cf } => cf,
FlagOp::LOGIC => false,
FlagOp::NEG { .. } => { self.res != 0 },
FlagOp::POPF => { self.res & 1 << CF_BIT != 0 },
FlagOp::RCL { dst, shamt, bits, old_cf, .. } => {
// for carry-rotate shamt bas been modded to have range [0,bits].
let full_dst = dst as u32
| (old_cf as u32) << bits; // dst w/ carry as new hi-bit
let hi_bit = 1u32 << bits;
full_dst & (hi_bit >> shamt) != 0
},
FlagOp::RCR { dst, shamt, old_cf, .. } => {
// for carry-rotate shamt bas been modded to have range [0,bits].
let full_dst = (dst as u32) << 1
| old_cf as u32; // dst w/ carry as new lo-bit
full_dst & (1 << shamt) != 0
},
// Rotate without carry only preserves previous CF on shamt=0,
// which is an early-return in the op and doesn't touch flags,
// so it doesn't need to be considered here.
FlagOp::ROL { dst, shamt, rot_mask, .. } => {
dst & (self.sign_mask >> ((shamt.wrapping_sub(1)) & rot_mask as u16)) != 0
},
FlagOp::ROR { dst, shamt, rot_mask, .. } => {
dst & (1 << ((shamt.wrapping_sub(1)) & rot_mask as u16)) != 0
},
FlagOp::SAR { dst, src, sign_bit } => {
match 1u16.checked_shl(src as u32 - 1) {
Some(carrymask) => dst & carrymask != 0,
None => sign_bit
}
},
FlagOp::SHL { dst, src } => {
match self.sign_mask.checked_shr(src as u32 - 1) {
Some(carrymask) => dst & carrymask != 0,
None => false
}
},
FlagOp::SHR { dst, src } => {
match 1u16.checked_shl(src as u32 - 1) {
Some(carrymask) => dst & carrymask != 0,
None => false
}
},
FlagOp::SUB { cf, .. } => cf,
}
}
pub fn pf(&self) -> bool {
match self.flag_op {
FlagOp::Eager { pf, .. } => pf,
FlagOp::POPF => { self.res & 1 << PF_BIT != 0 },
FlagOp::RCL { pf, .. }
| FlagOp::RCR { pf, .. }
| FlagOp::ROL { pf, .. }
| FlagOp::ROR { pf, .. } => pf,
_ => { self.res.count_ones() & 1 == 0 },
}
}
pub fn af(&self) -> bool {
match self.flag_op {
FlagOp::Eager { af, .. } => af,
FlagOp::LOGIC => false, // undefined
FlagOp::POPF => { self.res & 1 << AF_BIT != 0 },
FlagOp::RCL { af, .. }
| FlagOp::RCR { af, .. }
| FlagOp::ROL { af, .. }
| FlagOp::ROR { af, .. } => af,
_ => { false /* XXX: unimplemented! */ },
}
}
pub fn zf(&self) -> bool {
match self.flag_op {
FlagOp::Eager { zf, .. } => zf,
FlagOp::POPF => { self.res & 1 << ZF_BIT != 0 },
FlagOp::RCL { zf, .. }
| FlagOp::RCR { zf, .. }
| FlagOp::ROL { zf, .. }
| FlagOp::ROR { zf, .. } => zf,
_ => { self.res == 0 },
}
}
pub fn sf(&self) -> bool {
match self.flag_op {
FlagOp::Eager { sf, .. } => sf,
FlagOp::POPF => { self.res & 1 << SF_BIT != 0 },
FlagOp::RCL { sf, .. }
| FlagOp::RCR { sf, .. }
| FlagOp::ROL { sf, .. }
| FlagOp::ROR { sf, .. } => sf,
_ => { self.res & self.sign_mask != 0 },
}
}
pub fn of(&self) -> bool {
match self.flag_op {
FlagOp::Eager { of, .. } => of,
FlagOp::ADD { dst, src, .. } => { 0 != self.sign_mask & // In the (maybe) sign bit...
(dst ^ src ^ self.sign_mask) & // ...operands have same sign...
(dst ^ self.res) }, // ...and result sign-bit changed
FlagOp::DEC { .. } => { self.res == self.sign_mask - 1 },
FlagOp::INC { .. } => { self.res == self.sign_mask },
FlagOp::LOGIC => false,
FlagOp::NEG { dst } => { self.res == dst && dst != 0 },
FlagOp::POPF => { self.res & 1 << OF_BIT != 0 },
FlagOp::RCL { dst, .. }
| FlagOp::RCR { dst, .. }
| FlagOp::ROL { dst, .. }
| FlagOp::ROR { dst, .. } => { self.sign_mask & (dst ^ self.res) != 0 },
FlagOp::SAR { dst, .. }
| FlagOp::SHL { dst, .. }
| FlagOp::SHR { dst, .. } => { 0 != self.sign_mask & (dst ^ self.res) },
FlagOp::SUB { dst, src, .. } => { 0 != self.sign_mask & // In the (maybe) sign bit...
(dst ^ src) & // ...operands have different signs...
(dst ^ self.res) }, // ...and result sign-bit changed
}
}
pub fn update(&mut self, op: FlagOp, res: u16, sign_mask: u16) {
self.flag_op = op;
self.res = res;
self.sign_mask = sign_mask;
}
}
#[derive(Clone, Copy)]
pub enum FlagOp {
Eager { cf: bool, pf: bool, af: bool, zf: bool, sf: bool, of: bool }, // all flags precomputed
ADD { dst: u16, src: u16, cf: bool },
DEC { cf: bool },
INC { cf: bool },
LOGIC,
NEG { dst: u16 },
POPF, // flags encoded in result
RCL { dst: u16, shamt: u16, bits: u16, old_cf: bool, pf: bool, af: bool, zf: bool, sf: bool },
RCR { dst: u16, shamt: u16, old_cf: bool, pf: bool, af: bool, zf: bool, sf: bool },
ROL { dst: u16, shamt: u16, rot_mask: u16, pf: bool, af: bool, zf: bool, sf: bool },
ROR { dst: u16, shamt: u16, rot_mask: u16, pf: bool, af: bool, zf: bool, sf: bool },
SAR { dst: u16, src: u16, sign_bit: bool },
SHL { dst: u16, src: u16 },
SHR { dst: u16, src: u16 },
SUB { dst: u16, src: u16, cf: bool },
}
impl Default for FlagOp {
fn default() -> Self {
FlagOp::Eager { cf: false,
pf: false,
af: false,
zf: false,
sf: false,
of: false }
}
}
impl LValue<u16> for Flags {
fn write(&mut self, flags: u16) {
self.tf = flags & 1 << TF_BIT != 0;
self.ie = flags & 1 << IF_BIT != 0;
self.df = flags & 1 << DF_BIT != 0;
self.flag_op = FlagOp::POPF;
self.res = flags;
self.sign_mask = 0;
}
}
impl RValue<u16> for Flags {
fn read(&self) -> u16 {
(*self).into()
}
}
impl From<Flags> for u16 {
fn from(flags: Flags) -> Self {
0b1111_0000_0010_1010 // Not sure what all reserved bits should be, but it shouldn't matter
| (flags.cf() as u16)
| (flags.pf() as u16) << PF_BIT
| (flags.af() as u16) << AF_BIT
| (flags.zf() as u16) << ZF_BIT
| (flags.sf() as u16) << SF_BIT
| (flags.tf as u16) << TF_BIT
| (flags.ie as u16) << IF_BIT
| (flags.df as u16) << DF_BIT
| (flags.of() as u16) << OF_BIT
}
}
impl Debug for Flags {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> {
use std::fmt::Write;
fmt.write_str("[ ")?;
for flag in [ (self.cf(), "CF "),
(self.pf(), "PF "),
(self.af(), "AF "),
(self.zf(), "ZF "),
(self.sf(), "SF "),
(self.tf, "TF "),
(self.ie, "IF "),
(self.df, "DF "),
(self.of(), "OF ") ].iter() {
if flag.0 { fmt.write_str(flag.1)? };
}
fmt.write_char(']')?;
Ok(())
}
}

800
src/emu/i8088.rs Normal file
View File

@@ -0,0 +1,800 @@
use std::cell::Cell;
use std::fmt::Debug;
use super::byteorder::{ByteOrder, LittleEndian};
use emu::operands::{FarPtr, Reg, RegHalf, RegHi, RegLo};
use emu::operations as ops;
use emu::flags::Flags;
use emu::pc::Bus;
use emu::util::segoff_to_addr;
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, Default)]
pub struct i8088 {
// Data Registers
pub a: Cell<u16>,
pub b: Cell<u16>,
pub c: Cell<u16>,
pub d: Cell<u16>,
// Index Registers
pub si: Cell<u16>, // Source Index
pub di: Cell<u16>, // Dest Index
pub bp: Cell<u16>, // Base Pointer
pub sp: Cell<u16>, // Stack Pointer
// Segment Registers
pub cs: Cell<u16>, // Code Segment
pub ds: Cell<u16>, // Data Segment
pub es: Cell<u16>, // Extra Segment
pub ss: Cell<u16>, // Stack Segment
// Pointer Register
pub ip: Cell<u16>,
// Status Register
pub flags: Flags,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RepPrefix {
None, // No Prefix
Equal, // REP/REPE/REPZ
NotEqual // REPNE/REPNZ
}
macro_rules! step {
// Base case: all args processed and ready to call op
(@code ( $($done:tt)* ),
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt,
$(name=$name:ident$(<$($tyargs:tt),*>)?,)? $cycles:literal,
()) => {
$(ops::$name$(::<$($tyargs),*>)?)?($($done),*)
};
// Inductive case: decode next arg to be placed in list
(@code ( $($done:tt)* ),
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt,
$(name=$name:ident$(<$($tyargs:tt),*>)?,)? $cycles:literal,
($next:ident $(= $nextrhs:tt)? $($rest:ident $(= $restrhs:tt)?)*)) => {
step!(@$next$(= $nextrhs)?
// "cookie" tt to be passed thru to @arg so we have all the args for the recursive step
( ($($done)*), $cpu, $bus, $prefix, $modrm, $(name=$name$(<$($tyargs),*>)?,)? $cycles, ($($rest $(= $restrhs)?)*) ),
$cpu, $bus, $prefix, $modrm)
};
// accept an argument from a decoder and recur to look for next arg
(@arg ( ($($done:tt)*),
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt,
$(name=$name:ident$(<$($tyargs:tt),*>)?,)? $cycles:literal,
$rest:tt )
$(, $arg:expr)?) => {
step!(@code ($($done)* $($arg)?), $cpu, $bus, $prefix, $modrm, $(name=$name$(<$($tyargs),*>)?,)? $cycles, $rest)
};
// directive contains sub-directives, process them separately
(@sub ( ($($_done:tt)*),
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt,
$(name=$_name:ident$(<$($_tyargs:tt),*>)?,)? $cycles:literal,
( $($_rest:tt)* ) ),
( $($dirs:tt)* )) => {
step!(@code (/* omit done */), $cpu, $bus, $prefix, $modrm, /* omit name */ $cycles, ( $($dirs)* ))
};
// Set the type arguments for operations that cannot infer width
(@form ( $done:tt,
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, name=$name:ident, $cycles:literal,
$rest:tt ),
form=<$($tyargs:tt),*> ) => {
step!(@code $done, $cpu, $bus, $prefix, $modrm, name=$name<$($tyargs),*>, $cycles, $rest)
};
// Adds tokens to the last argument. Used by @convert to append ".into()" to the last arg
(@append-to-last ($($done:tt)*)
( ($last:tt),
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt,
$(name=$name:ident$(<$($tyargs:tt),*>)?,)? $cycles:literal,
$rest:tt ) { $($conv:tt)+ } ) => {
step!(@code ($($done)* ($last$($conv)+)),
$cpu, $bus, $prefix, $modrm, $(name=$name$(<$($tyargs),*>)?,)? $cycles,
$rest )
};
// Recursive case for @append-to-last, walks through already
// processed args to find the last one for appending
(@append-to-last ($($done:tt)*)
( ($next:tt $($restdone:tt)+),
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt,
$(name=$name:ident$(<$($tyargs:tt),*>)?,)? $cycles:literal,
$rest:tt ) { $($conv:tt)+ } ) => {
step!(@append-to-last ($($done)* $next)
( ($($restdone)+),
$cpu, $bus, $prefix, $modrm, $(name=$name$(<$($tyargs),*>)?,)? $cycles,
$rest ) { $($conv)+ } )
};
// === OP DIRECTIVES ===
(@addr $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => { {
let a16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
step!(@arg $cookie, FarPtr { bus: $bus, segment: $segment.unwrap(), offset: a16 } )
} };
(@bus $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, $bus)
};
(@const=$val:literal $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, $val)
};
// Adds ".into()" onto the last argument
(@convert $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@append-to-last () $cookie { .into() } )
};
(@cpu $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, $cpu)
};
(@d8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
step!(@arg $cookie, d8)
} };
(@d8_as_d16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
step!(@arg $cookie, d8 as u16)
} };
(@d16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
let d16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
step!(@arg $cookie, d16)
} };
(@displace0=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => {
step!(@arg $cookie, ($segment.unwrap(), $cpu.$reg1.get() $(+ $cpu.$reg2.get())?) )
};
(@displace8=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => { {
let rel8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus) as i8 as u16;
step!(@arg $cookie, ($segment.unwrap(),
($cpu.$reg1.get() $(+ $cpu.$reg2.get())?).wrapping_add(rel8)) )
} };
(@displace16=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => { {
let rel16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
step!(@arg $cookie, ($segment.unwrap(),
($cpu.$reg1.get() $(+ $cpu.$reg2.get())?).wrapping_add(rel16)) )
} };
(@flags $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, &mut $cpu.flags)
};
(@form=byte0 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@form $cookie, form=<u8>)
};
(@form=byte1 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@form $cookie, form=<u8, _>)
};
(@form=byte3 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@form $cookie, form=<u8, _, _, _>)
};
(@form=word0 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@form $cookie, form=<u16>)
};
(@form=word1 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@form $cookie, form=<u16, _>)
};
(@form=word3 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@form $cookie, form=<u16, _, _, _>)
};
(@mem $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, &mut $bus.ram)
};
(@modrm $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt,
($modrm_val:ident,
{ $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? },
$modrm16:tt,
$modrm8:tt
)) => { {
let modrm_val = $modrm_val & !0x38;
let (seg, addr) = match modrm_val {
$( $val => step!(@sub $cookie, ($($args $(= $argrhs)?)* ) ) ),*,
_ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
};
step!(@arg $cookie, FarPtr { bus: $bus, segment: seg, offset: addr })
} };
(@modrm16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt,
($modrm_val:ident,
{ $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? },
{ $($val16:literal => $($args16:ident $(= $argrhs16:tt)?),* / $mode16:ident $cycles16:literal),*$(,)? },
$modrm8:tt
)) => { {
let modrm_val = $modrm_val & !0x38;
if modrm_val & 0xC0 != 0xC0 {
// argument is a FarPtr
let (seg, addr) = match modrm_val {
$( $val => step!(@sub $cookie, ($($args $(= $argrhs)?)* ) ) ),*,
_ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
};
step!(@arg $cookie, FarPtr { bus: $bus, segment: seg, offset: addr })
} else {
// mod=11, argument is a register
let reg = match modrm_val {
$( $val16 => step!(@sub $cookie, ($($args16 $(= $argrhs16)?)* ) ) ),*,
_ => unimplemented!("modrm mod=11: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
};
step!(@arg $cookie, reg)
}
} };
(@modrm8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt,
($modrm_val:ident,
{ $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? },
$modrm16:tt,
{ $($val8:literal => $($args8:ident $(= $argrhs8:tt)?),* / $mode8:ident $cycles8:literal),*$(,)? }
)) => { {
let modrm_val = $modrm_val & !0x38;
if modrm_val & 0xC0 != 0xC0 {
// argument is a FarPtr
let (seg, addr) = match modrm_val {
$( $val => step!(@sub $cookie, ($($args $(= $argrhs)?)* ) ) ),*,
_ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
};
step!(@arg $cookie, FarPtr { bus: $bus, segment: seg, offset: addr })
} else {
// mod=11, argument is a register
let reg: RegHalf = match modrm_val {
$( $val8 => step!(@sub $cookie, ($($args8 $(= $argrhs8)?)* ) ) ),*,
_ => unimplemented!("modrm mod=11: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
};
step!(@arg $cookie, reg)
}
} };
(@prefix $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => {
continue $prefix_loop
};
(@r16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, $($modrm_rest:tt),*)) => { {
// TODO: Should these also be passed into the macro like the modrm specs?
let reg = match $modrm_val >> 3 & 0x7 {
0 => Reg { reg: &$cpu.a },
1 => Reg { reg: &$cpu.c },
2 => Reg { reg: &$cpu.d },
3 => Reg { reg: &$cpu.b },
4 => Reg { reg: &$cpu.sp },
5 => Reg { reg: &$cpu.bp },
6 => Reg { reg: &$cpu.si },
7 => Reg { reg: &$cpu.di },
_ => unreachable!()
};
step!(@arg $cookie, reg)
} };
(@r8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, $($modrm_rest:tt),*)) => { {
// TODO: Should these also be passed into the macro like the modrm specs?
let reg = match $modrm_val >> 3 & 0x7 {
0 => RegHalf::Lo(RegLo { reg: &$cpu.a }),
1 => RegHalf::Lo(RegLo { reg: &$cpu.c }),
2 => RegHalf::Lo(RegLo { reg: &$cpu.d }),
3 => RegHalf::Lo(RegLo { reg: &$cpu.b }),
4 => RegHalf::Hi(RegHi { reg: &$cpu.a }),
5 => RegHalf::Hi(RegHi { reg: &$cpu.c }),
6 => RegHalf::Hi(RegHi { reg: &$cpu.d }),
7 => RegHalf::Hi(RegHi { reg: &$cpu.b }),
_ => unreachable!()
};
step!(@arg $cookie, reg)
} };
(@reg=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, Reg { reg: &$cpu.$reg })
};
(@reghi=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, RegHi { reg: &$cpu.$reg })
};
(@reglo=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, RegLo { reg: &$cpu.$reg })
};
(@regval=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
step!(@arg $cookie, $cpu.$reg.get())
};
(@rel8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
let rel8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus) as i8;
step!(@arg $cookie, rel8 as u16)
} };
(@rel16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
let mut buf = [0; 2];
buf[0] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
buf[1] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
step!(@arg $cookie, LittleEndian::read_i16(&buf) as u16)
} };
(@rep=$mode:ident $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => { {
$repeat = RepPrefix::$mode;
step!(@arg $cookie)
} };
(@rep $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => { {
step!(@arg $cookie, $repeat)
} };
(@seg=$seg:ident $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => { {
$segment = $segment.or(Some($cpu.$seg.get()));
step!(@arg $cookie)
} };
(@seg $cookie:tt, $cpu:expr, $bus:expr,
($segment:ident, $repeat:ident, $prefix_loop:lifetime),
$modrm:tt) => { {
step!(@arg $cookie, $segment.unwrap())
} };
// Group Decoder
(@group $cpu:expr, $bus:expr, $prefix:tt,
($modrm_val:ident, $modrm:tt, $modrm16:tt, $modrm8:tt),
$code:literal,
{ $( $subcode:literal =>
$name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal),*$(,)? } ) => { {
let subcode = $modrm_val & 0x38;
match subcode {
$( $subcode => step!(@code (), $cpu, $bus, $prefix, ($modrm_val, $modrm, $modrm16, $modrm8), name=$name, $cycles, ($($args $(= $argrhs)?)*)) ),*,
_ => unimplemented!("opcode: {:02X} {:02X}({:02X})\ncpu: {:#X?}", $code, $modrm_val, subcode, $cpu)
} }
};
// Entry Point
(($cpu:expr, $bus:expr) =>
opcodes: { $(
$code:literal
// Non-group opcodes
$( $($ext:pat)? =>
$name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal
)?
// Group opcodes
$( : $subcodes:tt )?
),*
$(,)? },
modrm: $modrm:tt,
modrm16: $modrm16:tt,
modrm8: $modrm8:tt
) => {
{
let mut segment = None;
let mut repeat = RepPrefix::None;
let modrm_val: u8; // Type ascription unnecessary but gives better err messages when missing $ext
'prefix_loop: loop {
let opcode = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
match(opcode) {
$( $( $code => {
$( let $ext = (); /* No-op just to trigger expansion. */
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); )?
step!(@code (), $cpu, $bus, (segment, repeat, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), name=$name, $cycles, ($($args $(= $argrhs)?)*))
}
)?
$( $code => {
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
step!(@group $cpu, $bus, (segment, repeat, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $code, $subcodes)
}
)?
),*,
_ => unimplemented!("opcode: {:02X}\ncpu: {:#X?}", opcode, $cpu)
}
// Only prefixes loop and they do so with an explicit continue statement
break;
}
}
}
}
impl i8088 {
pub fn run(&mut self, bus: &mut Bus) {
loop {
step!((self, bus) =>
opcodes: {
0x00 _ => add[flags, modrm8, r8] / "3/24+", // ADD r/m8, r8
0x01 _ => add[flags, modrm16, r16] / "3/24+", // ADD r/m16, r16
0x02 _ => add[flags, r8, modrm8] / "3/13+", // ADD r8, r/m8
0x03 _ => add[flags, r16, modrm16] / "3/13+", // ADD r16, r/m16
0x04 => add[flags, reglo=a, d8] / 4, // ADD al, d8
0x05 => add[flags, reg=a, d16] / 4, // ADD ax, d16
0x06 => push[bus, regval=ss, reg=sp, regval=es] / 14,
0x07 => pop[bus, regval=ss, reg=sp, reg=es] / 12,
0x08 _ => or[flags, modrm8, r8] / "3/24+", // OR r/m8, r8
0x09 _ => or[flags, modrm16, r16] / "3/24+", // OR r/m16, r16
0x0A _ => or[flags, r8, modrm8] / "3/13+", // OR r8, r/m8
0x0B _ => or[flags, r16, modrm16] / "3/13+", // OR r16, r/m16
0x0C => or[flags, reglo=a, d8] / 4, // OR al, d8
0x0D => or[flags, reg=a, d16] / 4, // OR ax, d16
0x0E => push[bus, regval=ss, reg=sp, regval=cs] / 14,
0x10 _ => adc[flags, modrm8, r8] / "3/24+", // ADC r/m8, r8
0x11 _ => adc[flags, modrm16, r16] / "3/24+", // ADC r/m16, r16
0x12 _ => adc[flags, r8, modrm8] / "3/13+", // ADC r8, r/m8
0x13 _ => adc[flags, r16, modrm16] / "3/13+", // ADC r16, r/m16
0x14 => adc[flags, reglo=a, d8] / 4, // ADC al, d8
0x15 => adc[flags, reg=a, d16] / 4, // ADC ax, d16
0x16 => push[bus, regval=ss, reg=sp, regval=ss] / 14,
0x17 => pop[bus, regval=ss, reg=sp, reg=ss] / 12,
0x18 _ => sbb[flags, modrm8, r8] / "3/24+", // SBB r/m8, r8
0x19 _ => sbb[flags, modrm16, r16] / "3/24+", // SBB r/m16, r16
0x1A _ => sbb[flags, r8, modrm8] / "3/13+", // SBB r8, r/m8
0x1B _ => sbb[flags, r16, modrm16] / "3/13+", // SBB r16, r/m16
0x1C => sbb[flags, reglo=a, d8] / 4, // SBB al, d8
0x1D => sbb[flags, reg=a, d16] / 4, // SBB ax, d16
0x1E => push[bus, regval=ss, reg=sp, regval=ds] / 14,
0x1F => pop[bus, regval=ss, reg=sp, reg=ds] / 12,
0x20 _ => and[flags, modrm8, r8] / "3/24+", // AND r/m8, r8
0x21 _ => and[flags, modrm16, r16] / "3/24+", // AND r/m16, r16
0x22 _ => and[flags, r8, modrm8] / "3/13+", // AND r8, r/m8
0x23 _ => and[flags, r16, modrm16] / "3/13+", // AND r16, r/m16
0x24 => and[flags, reglo=a, d8] / 4, // AND al, d8
0x25 => and[flags, reg=a, d16] / 4, // AND ax, d16
0x26 => nop[seg=es, prefix] / 2,
// 0x27 DAA not implemented
0x28 _ => sub[flags, modrm8, r8] / "3/24+", // SUB r/m8, r8
0x29 _ => sub[flags, modrm16, r16] / "3/24+", // SUB r/m16, r16
0x2A _ => sub[flags, r8, modrm8] / "3/13+", // SUB r8, r/m8
0x2B _ => sub[flags, r16, modrm16] / "3/13+", // SUB r16, r/m16
0x2C => sub[flags, reglo=a, d8] / 4, // SUB al, d8
0x2D => sub[flags, reg=a, d16] / 4, // SUB ax, d16
0x2E => nop[seg=cs, prefix] / 2,
// 0x2F DAS not implemented
0x30 _ => xor[flags, modrm8, r8] / "3/24+", // XOR r/m8, r8
0x31 _ => xor[flags, modrm16, r16] / "3/24+", // XOR r/m16, r16
0x32 _ => xor[flags, r8, modrm8] / "3/13+", // XOR r8, r/m8
0x33 _ => xor[flags, r16, modrm16] / "3/13+", // XOR r16, r/m16
0x34 => xor[flags, reglo=a, d8] / 4, // XOR al, d8
0x35 => xor[flags, reg=a, d16] / 4, // XOR ax, d16
0x36 => nop[seg=ss, prefix] / 2,
// 0x37 AAA not implemented
0x38 _ => cmp[flags, modrm8, r8] / "3/24+",
0x39 _ => cmp[flags, modrm16, r16] / "3/24+",
0x3A _ => cmp[flags, r8, modrm8] / "3/24+",
0x3B _ => cmp[flags, r16, modrm16] / "3/24+",
0x3C => cmp[flags, reglo=a, d8] / "3/24+",
0x3D => cmp[flags, reg=a, d16] / "3/24+",
0x3E => nop[seg=ds, prefix] / 2,
// 0x3F AAS not implemented
0x40 => inc[flags, reg=a] / 3,
0x41 => inc[flags, reg=c] / 3,
0x42 => inc[flags, reg=d] / 3,
0x43 => inc[flags, reg=b] / 3,
0x44 => inc[flags, reg=sp] / 3,
0x45 => inc[flags, reg=bp] / 3,
0x46 => inc[flags, reg=si] / 3,
0x47 => inc[flags, reg=di] / 3,
0x48 => dec[flags, reg=a] / 3,
0x49 => dec[flags, reg=c] / 3,
0x4A => dec[flags, reg=d] / 3,
0x4B => dec[flags, reg=b] / 3,
0x4C => dec[flags, reg=sp] / 3,
0x4D => dec[flags, reg=bp] / 3,
0x4E => dec[flags, reg=si] / 3,
0x4F => dec[flags, reg=di] / 3,
0x50 => push[bus, regval=ss, reg=sp, regval=a] / 15,
0x51 => push[bus, regval=ss, reg=sp, regval=c] / 15,
0x52 => push[bus, regval=ss, reg=sp, regval=d] / 15,
0x53 => push[bus, regval=ss, reg=sp, regval=b] / 15,
0x54 => push[bus, regval=ss, reg=sp, regval=sp] / 15,
0x55 => push[bus, regval=ss, reg=sp, regval=bp] / 15,
0x56 => push[bus, regval=ss, reg=sp, regval=si] / 15,
0x57 => push[bus, regval=ss, reg=sp, regval=di] / 15,
0x58 => pop[bus, regval=ss, reg=sp, reg=a] / 12,
0x59 => pop[bus, regval=ss, reg=sp, reg=c] / 12,
0x5A => pop[bus, regval=ss, reg=sp, reg=d] / 12,
0x5B => pop[bus, regval=ss, reg=sp, reg=b] / 12,
0x5C => pop[bus, regval=ss, reg=sp, reg=sp] / 12,
0x5D => pop[bus, regval=ss, reg=sp, reg=bp] / 12,
0x5E => pop[bus, regval=ss, reg=sp, reg=si] / 12,
0x5F => pop[bus, regval=ss, reg=sp, reg=di] / 12,
0x60 => show[cpu] / 0, // Fake opcode for debugging
0x61 => peek[seg=ds, addr] / 0, // Fake opcode for debugging
0x62 _ => assert[modrm8, d8] / 0, // Fake opcode for debugging
0x63 _ => assert[modrm16, d16] / 0, // Fake opcode for debugging
0x70 => jo[flags, reg=ip, rel8] / "16/4", // JO rel8
0x71 => jno[flags, reg=ip, rel8] / "16/4", // JNO rel8
0x72 => jb[flags, reg=ip, rel8] / "16/4", // JB/JNAE/JC rel8
0x73 => jae[flags, reg=ip, rel8] / "16/4", // JNB/JAE/JNC rel8
0x74 => jz[flags, reg=ip, rel8] / "16/4", // JE/JZ rel8
0x75 => jnz[flags, reg=ip, rel8] / "16/4", // JNE/JNZ rel8
0x76 => jbe[flags, reg=ip, rel8] / "16/4", // JBE/JNA rel8
0x77 => ja[flags, reg=ip, rel8] / "16/4", // JNBE/JA rel8
0x78 => js[flags, reg=ip, rel8] / "16/4", // JS rel8
0x79 => jns[flags, reg=ip, rel8] / "16/4", // JNS rel8
0x7A => jpe[flags, reg=ip, rel8] / "16/4", // JP/JPE rel8
0x7B => jpo[flags, reg=ip, rel8] / "16/4", // JNP/JPO rel8
0x7C => jl[flags, reg=ip, rel8] / "16/4", // JL/JNGE rel8
0x7D => jge[flags, reg=ip, rel8] / "16/4", // JNL/JGE rel8
0x7E => jle[flags, reg=ip, rel8] / "16/4", // JLE/JNG rel8
0x7F => jg[flags, reg=ip, rel8] / "16/4", // JNLE/JG rel8
0x80: { 0x00 => add[flags, modrm8, d8] / "4/23+", // ADD r/m8, d8
0x08 => or[flags, modrm8, d8] / "4/23+", // OR r/m8, d8
0x10 => adc[flags, modrm8, d8] / "4/23+", // ADC r/m8, d8
0x18 => sbb[flags, modrm8, d8] / "4/23+", // SBB r/m8, d8
0x20 => and[flags, modrm8, d8] / "4/23+", // AND r/m8, d8
0x28 => sub[flags, modrm8, d8] / "4/23+", // SUB r/m8, d8
0x30 => xor[flags, modrm8, d8] / "4/23+", // XOR r/m8, d8
0x38 => cmp[flags, modrm8, d8] / "4/23+", }, // CMP r/m8, d8
0x81: { 0x00 => add[flags, modrm16, d16] / "4/23+", // ADD r/m16, d16
0x08 => or[flags, modrm16, d16] / "4/23+", // OR r/m16, d16
0x10 => adc[flags, modrm16, d16] / "4/23+", // ADC r/m16, d16
0x18 => sbb[flags, modrm16, d16] / "4/23+", // SBB r/m16, d16
0x20 => and[flags, modrm16, d16] / "4/23+", // AND r/m16, d16
0x28 => sub[flags, modrm16, d16] / "4/23+", // SUB r/m16, d16
0x30 => xor[flags, modrm16, d16] / "4/23+", // XOR r/m16, d16
0x38 => cmp[flags, modrm16, d16] / "4/23+", }, // CMP r/m16, d16
0x83: { 0x00 => add[flags, modrm16, d8_as_d16] / "4/23+", // ADD r/m16, d8
0x08 => or[flags, modrm16, d8_as_d16] / "4/23+", // OR r/m16, d8
0x10 => adc[flags, modrm16, d8_as_d16] / "4/23+", // ADC r/m16, d8
0x18 => sbb[flags, modrm16, d8_as_d16] / "4/23+", // SBB r/m16, d8
0x20 => and[flags, modrm16, d8_as_d16] / "4/23+", // AND r/m16, d8
0x28 => sub[flags, modrm16, d8_as_d16] / "4/23+", // SUB r/m16, d8
0x30 => xor[flags, modrm16, d8_as_d16] / "4/23+", // XOR r/m16, d8
0x38 => cmp[flags, modrm16, d8_as_d16] / "4/23+", }, // CMP r/m16, d8
0x84 _ => test[flags, modrm8, r8] / "3/13+", // TEST r/m8, r8
0x85 _ => test[flags, modrm16, r16] / "3/13+", // TEST r/m16, r16
0x86 _ => xchg[r8, modrm8] / "4/25+", // XCHG r8, r/m8
0x87 _ => xchg[r16, modrm16] / "4/25+", // XCHG r16, r/m16
0x88 _ => mov[modrm8, r8] / "2/13+", // MOV r/m8, r8
0x89 _ => mov[modrm16, r16] / "2/13+", // MOV r/m16, r16
0x8A _ => mov[r8, modrm8] / "2/12+", // MOV r8, r/m8
0x8B _ => mov[r16, modrm16] / "2/12+", // MOV r16, r/m16
0x8C: { 0x00 => mov[modrm16, reg=es] / "2/13+", // MOV r/m16, es
0x08 => mov[modrm16, reg=cs] / "2/13+", // MOV r/m16, cs
0x10 => mov[modrm16, reg=ss] / "2/13+", // MOV r/m16, ss
0x18 => mov[modrm16, reg=ds] / "2/13+", }, // MOV r/m16, ds
0x8D _ => lea[r16, modrm] / "2+",
0x8E: { 0x00 => mov[reg=es, modrm16] / "2/12+", // MOV es, r/m16
0x08 => mov[reg=cs, modrm16] / "2/12+", // MOV cs, r/m16
0x10 => mov[reg=ss, modrm16] / "2/12+", // MOV ss, r/m16
0x18 => mov[reg=ds, modrm16] / "2/12+", }, // MOV ds, r/m16
0x8F: { 0x00 => pop_modrm[regval=ss, reg=sp, modrm16, convert, bus] / "12/25+" }, // POP r/m16
0x90 => nop[] / 3, // NOP
0x91 => xchg[reg=a, reg=c] / 3, // XCHG ax, cx
0x92 => xchg[reg=a, reg=d] / 3, // XCHG ax, dx
0x93 => xchg[reg=a, reg=b] / 3, // XCHG ax, bx
0x94 => xchg[reg=a, reg=sp] / 3, // XCHG ax, sp
0x95 => xchg[reg=a, reg=bp] / 3, // XCHG ax, bp
0x96 => xchg[reg=a, reg=si] / 3, // XCHG ax, si
0x97 => xchg[reg=a, reg=di] / 3, // XCHG ax, di
0x98 => cbw[reg=a, reglo=a] / 2, // CBW
0x99 => cwd[reg=d, reg=a] / 5, // CWD
// 0x9A CALL seg:a16 todo
// 0x9B WAIT not implemented
0x9C => push[bus, regval=ss, reg=sp, flags] / 14, // PUSHF
0x9D => pop[bus, regval=ss, reg=sp, flags] / 12, // POPF
// 0x9E SAHF not implemented
// 0x9F LAHF not implemented
0xA0 => mov[seg=ds, reglo=a, addr] / 14, // MOV al, [addr]
0xA1 => mov[seg=ds, reg=a, addr] / 14, // MOV ax, [addr]
0xA2 => mov[seg=ds, addr, reglo=a] / 14, // MOV [addr], al
0xA3 => mov[seg=ds, addr, reg=a] / 14, // MOV [addr], ax
0xA4 => movs[form=byte0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "18/9+17n",
0xA5 => movs[form=word0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "26/9+25n",
0xA6 => cmps[form=byte0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "22/9+22n",
0xA7 => cmps[form=word0, flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=es, reg=di] / "30/9+30n",
0xA8 => test[flags, reglo=a, d8] / 4, // TEST al, d8
0xA9 => test[flags, reg=a, d16] / 4, // TEST ax, d16
0xAA => stos[flags, bus, rep, reg=c, reg=es, reg=di, reglo=a] / "11/9+10n",
0xAB => stos[flags, bus, rep, reg=c, reg=es, reg=di, reg=a] / "15/9+14n",
0xAC => lods[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reglo=a] / "12/9+13n",
0xAD => lods[flags, bus, rep, reg=c, seg=ds, seg, reg=si, reg=a] / "16/9+17n",
0xAE => scas[flags, bus, rep, reg=c, reg=es, reg=di, reglo=a] / "15/9+15n",
0xAF => scas[flags, bus, rep, reg=c, reg=es, reg=di, reg=a] / "19/9+19n",
0xB0 => mov[reglo=a, d8] / 4, // MOV al, d8
0xB1 => mov[reglo=c, d8] / 4, // MOV cl, d8
0xB2 => mov[reglo=d, d8] / 4, // MOV dl, d8
0xB3 => mov[reglo=b, d8] / 4, // MOV bl, d8
0xB4 => mov[reghi=a, d8] / 4, // MOV ah, d8
0xB5 => mov[reghi=c, d8] / 4, // MOV ch, d8
0xB6 => mov[reghi=d, d8] / 4, // MOV dh, d8
0xB7 => mov[reghi=b, d8] / 4, // MOV bh, d8
0xB8 => mov[reg=a, d16] / 4, // MOV ax, d16
0xB9 => mov[reg=c, d16] / 4, // MOV cx, d16
0xBA => mov[reg=d, d16] / 4, // MOV dx, d16
0xBB => mov[reg=b, d16] / 4, // MOV bx, d16
0xBC => mov[reg=sp, d16] / 4, // MOV sp, d16
0xBD => mov[reg=bp, d16] / 4, // MOV bp, d16
0xBE => mov[reg=si, d16] / 4, // MOV si, d16
0xBF => mov[reg=di, d16] / 4, // MOV di, d16
// 0xC2 RET d16 todo
0xC3 => pop[bus, regval=ss, reg=sp, reg=ip] / 20, // RET
// 0xC4 LES todo
// 0xC5 LDS todo
0xC6: { 0x00 => mov[modrm8, d8] / "4/14+" }, // MOV r/m8, d8
0xC7: { 0x00 => mov[modrm16, d16] / "4/14+" }, // MOV r/m16, d16
// 0xCA RETF d16 todo
// 0xCB RETF todo
// 0xCC INT3/bend todo
0xCD => int[cpu, bus, d8] / 71,
// 0xCE INTO todo?
// 0xCF IRET todo
0xD0: { 0x00 => rol[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROL r/m16, 1
0x08 => ror[form=byte3, flags, modrm8, const=1u8] / "2/23+", // ROR r/m16, 1
0x10 => rcl[form=byte3, flags, modrm8, const=1u8] / "2/23+", // RCL r/m16, 1
0x18 => rcr[form=byte3, flags, modrm8, const=1u8] / "2/23+", // RCR r/m16, 1
0x20 => shl[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHL r/m8, 1
0x28 => shr[form=byte3, flags, modrm8, const=1u8] / "2/23+", // SHR r/m8, 1
0x38 => sar[form=byte3, flags, modrm8, const=1u8] / "2/23+" }, // SAR r/m8, 1
0xD1: { 0x00 => rol[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROL r/m16, 1
0x08 => ror[form=word3, flags, modrm16, const=1u16] / "2/23+", // ROR r/m16, 1
0x10 => rcl[form=word3, flags, modrm16, const=1u16] / "2/23+", // RCL r/m16, 1
0x18 => rcr[form=word3, flags, modrm16, const=1u16] / "2/23+", // RCR r/m16, 1
0x20 => shl[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHL r/m16, 1
0x28 => shr[form=word3, flags, modrm16, const=1u16] / "2/23+", // SHR r/m16, 1
0x38 => sar[form=word3, flags, modrm16, const=1u16] / "2/23+" }, // SAR r/m16, 1
0xD2: { 0x00 => rol[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROL r/m8, CL
0x08 => ror[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // ROR r/m8, CL
0x10 => rcl[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // RCL r/m8, CL
0x18 => rcr[form=byte3, flags, modrm8, reglo=c] / "8/28++4n", // RCR r/m8, CL
0x20 => shl[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHL r/m8, CL
0x28 => shr[form=byte3, flags, modrm8, reglo=c] / "2/23+", // SHR r/m8, CL
0x38 => sar[form=byte3, flags, modrm8, reglo=c] / "2/23+" }, // SAR r/m8, CL
0xD3: { 0x00 => rol[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROL r/m16, CL
0x08 => ror[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // ROR r/m16, CL
0x10 => rcl[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // RCL r/m16, CL
0x18 => rcr[form=word3, flags, modrm16, reglo=c] / "8/28++4n", // RCR r/m16, CL
0x20 => shl[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHL r/m16, CL
0x28 => shr[form=word3, flags, modrm16, reglo=c] / "2/23+", // SHR r/m16, CL
0x38 => sar[form=word3, flags, modrm16, reglo=c] / "2/23+" }, // SAR r/m16, CL
// 0xD4 AAM not implemented
// 0xD5 AAD not implemented
// 0xD6 *SALC todo? (unneeded but looks easy)
// 0xD7 XLAT todo
// 0xD8-0xDF ESC0-ESC7 not implemented
0xE0 => loopnz[flags, reg=ip, reg=c, rel8] / "18/6", // LOOPNZ/LOOPNE rel8
0xE1 => loopz[flags, reg=ip, reg=c, rel8] / "18/6", // LOOPZ/LOOPE rel8
0xE2 => looop[reg=ip, reg=c, rel8] / "17/5", // LOOP rel8
0xE3 => jcxz[reg=ip, reg=c, rel8] / "18/6", // JCXZ rel8
// 0xE4 IN AL,[d8] todo
// 0xE5 IN AX,[d16] todo
// 0xE6 OUT [d8],AL todo
// 0xE7 OUT [d16],AX todo
0xE8 => call[reg=ip, bus, regval=ss, reg=sp, rel16] / 23, // CALL rel16
0xE9 => jmp[reg=ip, rel16] / 15, // JMP rel16
// 0xEA JMP seg:a16 todo
0xEB => jmp[reg=ip, rel8] / 15, // JMP rel8
// 0xEC IN AL,[DX] todo
// 0xED IN AX,[DX] todo
// 0xEE OUT [DX],AL todo
// 0xEF OUT [DX],AX todo
// 0xF0 not implemented
0xF2 => nop[rep=NotEqual, prefix] / 0, // REPNE/REPNZ
0xF3 => nop[rep=Equal, prefix] / 0, // REP/REPE/REPZ
// 0xF4 HLT not implemented (consider making this todo)
0xF5 => cmc[flags] / 2, // CMC (Complement Carry Flag)
0xF6: { 0x00 => test[flags, modrm8, d8] / "5/11+", // TEST r/m8, d8
0x10 => not[form=byte1, modrm8] / "3/24+", // NOT r/m8
0x18 => neg[form=byte1, flags, modrm8] / "3/24+" // NEG r/m8
// 0x20 MUL r/m8 todo
// 0x28 IMUL r/m8 todo
// 0x30 DIV r/m8 todo
/* 0x38 IDIV r/m8 todo */ },
0xF7: { 0x00 => test[flags, modrm16, d16] / "5/11+", // TEST r/m16, d16
0x10 => not[form=word1, modrm16] / "3/24+", // NOT r/m16
0x18 => neg[form=word1, flags, modrm16] / "3/24+" // NEG r/m16
// 0x20 MUL r/m16 todo
// 0x28 IMUL r/m16 todo
// 0x30 DIV r/m16 todo
/* 0x38 IDIV r/m16 todo */ },
0xF8 => clc[flags] / 2, // CLC (Clear Carry Flag)
0xF9 => stc[flags] / 2, // STC (Set Carry Flag)
// 0xFA CLI todo
// 0xFB STI todo
0xFC => cld[flags] / 2, // CLD (Clear Direction Flag)
0xFD => std[flags] / 2, // STD (Set Direction Flag)
0xFE: { 0x00 => inc[form=byte1, flags, modrm8] / "3/23+", // INC r/m8
0x08 => dec[form=byte1, flags, modrm8] / "3/23+", }, // DEC r/m8
0xFF: { 0x00 => inc[form=word1, flags, modrm16] / "3/23+", // INC r/m16
0x08 => dec[form=word1, flags, modrm16] / "3/23+", // DEC r/m16
// 0x10 CALL r/m16 todo
// 0x18 CALL m32 todo
// 0x20 JMP r/m16 todo
// 0x28 JMP m32 todo
0x30 => push_modrm[regval=ss, reg=sp, modrm16, convert, bus] / "15/24+" },
},
modrm: {
0x00 => seg=ds, displace0=(b+si) / M 7,
0x01 => seg=ds, displace0=(b+di) / M 8,
0x02 => seg=ss, displace0=(bp+si) / M 8,
0x03 => seg=ss, displace0=(bp+di) / M 7,
0x04 => seg=ds, displace0=(si) / M 5,
0x05 => seg=ds, displace0=(di) / M 5,
0x06 => seg=ds, seg, d16 / M 6,
0x07 => seg=ds, displace0=(b) / M 5,
0x40 => seg=ds, displace8=(b+si) / M 11,
0x41 => seg=ds, displace8=(b+di) / M 12,
0x42 => seg=ss, displace8=(bp+si) / M 12,
0x43 => seg=ss, displace8=(bp+di) / M 11,
0x44 => seg=ds, displace8=(si) / M 9,
0x45 => seg=ds, displace8=(di) / M 9,
0x46 => seg=ss, displace8=(bp) / M 9,
0x47 => seg=ds, displace8=(b) / M 9,
0x80 => seg=ds, displace16=(b+si) / M 11,
0x81 => seg=ds, displace16=(b+di) / M 12,
0x82 => seg=ss, displace16=(bp+si) / M 12,
0x83 => seg=ss, displace16=(bp+di) / M 11,
0x84 => seg=ds, displace16=(si) / M 9,
0x85 => seg=ds, displace16=(di) / M 9,
0x86 => seg=ss, displace16=(bp) / M 9,
0x87 => seg=ds, displace16=(b) / M 9,
},
modrm16: {
0xC0 => reg=a / R 0,
0xC1 => reg=c / R 0,
0xC2 => reg=d / R 0,
0xC3 => reg=b / R 0,
0xC4 => reg=sp / R 0,
0xC5 => reg=bp / R 0,
0xC6 => reg=si / R 0,
0xC7 => reg=di / R 0,
},
modrm8: {
0xC0 => reglo=a, convert / R 0,
0xC1 => reglo=c, convert / R 0,
0xC2 => reglo=d, convert / R 0,
0xC3 => reglo=b, convert / R 0,
0xC4 => reghi=a, convert / R 0,
0xC5 => reghi=c, convert / R 0,
0xC6 => reghi=d, convert / R 0,
0xC7 => reghi=b, convert / R 0,
});
}
}
fn next_ip<'a>(cs: u16, ip: &mut Cell<u16>, bus: &Bus) -> u8 {
let eip = segoff_to_addr(cs, ip.get());
ip.set(ip.get() + 1);
// We'll assume cpu is always executing in RAM. Also assume the
// IP doesn't reach the end of the segment (My guess is that it
// should loop within the segment if it did)
bus.ram[eip - Bus::RAM_LOCATION]
}
fn next_ip16<'a>(cs: u16, ip: &mut Cell<u16>, bus: &Bus) -> u16 {
let eip = segoff_to_addr(cs, ip.get());
ip.set(ip.get() + 2);
// We'll assume cpu is always executing in RAM. Also assume the
// IP doesn't reach the end of the segment (My guess is that it
// should loop within the segment if it did)
let buf = &bus.ram[eip - Bus::RAM_LOCATION .. eip + 2 - Bus::RAM_LOCATION];
LittleEndian::read_u16(buf)
}
}

10
src/emu/mod.rs Normal file
View File

@@ -0,0 +1,10 @@
extern crate byteorder;
extern crate num_traits;
pub mod dos;
mod flags;
pub mod i8088;
mod operands;
mod operations;
pub mod pc;
mod util;

243
src/emu/operands.rs Normal file
View File

@@ -0,0 +1,243 @@
use std::cell::Cell;
use std::convert::From;
use emu::num_traits as nt;
use emu::util::{read_hi, read_lo, segoff_to_addr, write_hi, write_lo};
use emu::pc::Bus;
pub trait Operand: Copy + 'static +
std::convert::Into<u16> +
std::fmt::Display +
nt::int::PrimInt +
nt::ops::checked::CheckedShl<Output = Self> +
nt::ops::checked::CheckedShr<Output = Self> +
nt::ops::overflowing::OverflowingAdd +
nt::ops::overflowing::OverflowingSub +
nt::ops::wrapping::WrappingAdd +
nt::ops::wrapping::WrappingNeg +
nt::ops::wrapping::WrappingSub +
std::ops::Shl<Output = Self> +
std::ops::Shr<Output = Self>
{
type Signed: nt::ops::checked::CheckedShr<Output = Self::Signed>;
const HI_BIT_MASK: Self;
const BITS: u16 = std::mem::size_of::<Self>() as u16 * 8;
const ROTATE_MASK: u16 = Self::BITS - 1;
fn hi_bit(self) -> bool;
fn as_signed(self) -> Self::Signed;
fn from_signed(src: Self::Signed) -> Self;
}
impl Operand for u8 {
type Signed = i8;
const HI_BIT_MASK: u8 = 0x80;
fn hi_bit(self) -> bool {
self >> 7 == 1
}
fn as_signed(self) -> Self::Signed {
self as Self::Signed
}
fn from_signed(src: Self::Signed) -> Self {
src as Self
}
}
impl Operand for u16 {
type Signed = i16;
const HI_BIT_MASK: u16 = 0x8000;
fn hi_bit(self) -> bool {
self >> 15 == 1
}
fn as_signed(self) -> Self::Signed {
self as Self::Signed
}
fn from_signed(src: Self::Signed) -> Self {
src as Self
}
}
pub trait LValue<T>: RValue<T> {
fn write(&mut self, val: T);
}
pub trait RValue<T> {
fn read(&self) -> T;
}
pub trait Address {
fn addr(&self) -> usize;
}
impl RValue<u8> for u8 {
fn read(&self) -> u8 {
*self
}
}
impl RValue<u16> for u16 {
fn read(&self) -> u16 {
*self
}
}
pub struct FarPtr<'a> {
pub bus: &'a mut Bus,
pub segment: u16,
pub offset: u16
}
impl Address for FarPtr<'_> {
fn addr(&self) -> usize {
segoff_to_addr(self.segment, self.offset)
}
}
impl LValue<u8> for FarPtr<'_> {
fn write(&mut self, val: u8) {
self.bus.write(self.addr(), val);
}
}
impl RValue<u8> for FarPtr<'_> {
fn read(&self) -> u8 {
self.bus.read(self.addr())
}
}
impl LValue<u16> for FarPtr<'_> {
fn write(&mut self, val: u16) {
let buf = val.to_le_bytes();
self.bus.write(self.addr(), buf[0]);
self.bus.write(self.addr() + 1, buf[1]);
}
}
impl RValue<u16> for FarPtr<'_> {
fn read(&self) -> u16 {
let addr = self.addr();
let mut buf = [0u8; 2];
buf[0] = self.bus.read(addr);
buf[1] = self.bus.read(addr + 1);
u16::from_le_bytes(buf)
}
}
pub struct Reg<'a> {
pub reg: &'a Cell<u16>
}
impl LValue<u16> for Reg<'_> {
fn write(&mut self, val: u16) {
self.reg.set(val);
}
}
impl RValue<u16> for Reg<'_> {
fn read(&self) -> u16 {
self.reg.get()
}
}
pub struct RegHi<'a> {
pub reg: &'a Cell<u16>
}
impl LValue<u8> for RegHi<'_> {
fn write(&mut self, val: u8) {
write_hi(&mut self.reg, val);
}
}
impl RValue<u8> for RegHi<'_> {
fn read(&self) -> u8 {
read_hi(self.reg)
}
}
pub struct RegLo<'a> {
pub reg: &'a Cell<u16>
}
impl LValue<u8> for RegLo<'_> {
fn write(&mut self, val: u8) {
write_lo(&mut self.reg, val);
}
}
impl RValue<u8> for RegLo<'_> {
fn read(&self) -> u8 {
read_lo(self.reg)
}
}
pub enum RegHalf<'a> {
Lo(RegLo<'a>),
Hi(RegHi<'a>)
}
impl LValue<u8> for RegHalf<'_> {
fn write(&mut self, val: u8) {
match self {
RegHalf::Lo(lo) => lo.write(val),
RegHalf::Hi(hi) => hi.write(val)
}
}
}
impl RValue<u8> for RegHalf<'_> {
fn read(&self) -> u8 {
match self {
RegHalf::Lo(lo) => lo.read(),
RegHalf::Hi(hi) => hi.read()
}
}
}
impl<'a> From<RegHi<'a>> for RegHalf<'a> {
fn from(reg: RegHi) -> RegHalf {
RegHalf::Hi(reg)
}
}
impl<'a> From<RegLo<'a>> for RegHalf<'a> {
fn from(reg: RegLo) -> RegHalf {
RegHalf::Lo(reg)
}
}
pub enum DynLValue<'a> {
Reg(Reg<'a>),
FarPtr { segment: u16, offset: u16 },
}
impl<'a> From<Reg<'a>> for DynLValue<'a> {
fn from(reg: Reg<'a>) -> DynLValue<'a> {
DynLValue::Reg(reg)
}
}
impl<'a> From<FarPtr<'_>> for DynLValue<'a> {
fn from(farptr: FarPtr<'_>) -> DynLValue<'a> {
DynLValue::FarPtr { segment: farptr.segment, offset: farptr.offset }
}
}
impl<LVal: LValue<T>, T> LValue<T> for &mut LVal {
fn write(&mut self, val: T) {
(**self).write(val);
}
}
impl<RVal: RValue<T>, T> RValue<T> for &mut RVal {
fn read(&self) -> T {
(**self).read()
}
}

742
src/emu/operations.rs Normal file
View File

@@ -0,0 +1,742 @@
use std::fmt::Debug;
use emu::num_traits::AsPrimitive;
use emu::num_traits::CheckedShr;
use emu::dos;
use emu::flags::{FlagOp, Flags};
use emu::i8088::{RepPrefix, i8088};
use emu::operands::{Address, DynLValue, FarPtr, LValue, Operand, Reg, RegLo, RValue};
use emu::pc::Bus;
macro_rules! string_op {
( ($type:ty,
$flags:ident,
$rep:ident,
$cx:ident
$(, si=$si:expr)?
$(, di=$di:expr)?
$(, zf=$zf:expr)? ),
$code:tt
) => {
let stride = std::mem::size_of::<$type>() as u16;
if $rep == RepPrefix::None {
$code;
if $flags.df {
$($si.write($si.read() - stride);)?
$($di.write($di.read() - stride);)?
} else {
$($si.write($si.read() + stride);)?
$($di.write($di.read() + stride);)?
}
} else {
while $cx.read() != 0 {
$cx.write($cx.read() - 1);
$code;
if $flags.df {
$($si.write($si.read() - stride);)?
$($di.write($di.read() - stride);)?
} else {
$($si.write($si.read() + stride);)?
$($di.write($di.read() + stride);)?
}
$( match ($rep, $zf) {
(RepPrefix::Equal, true) => (),
(RepPrefix::Equal, false) => break,
(RepPrefix::NotEqual, true) => break,
(RepPrefix::NotEqual, false) => (),
_ => (),
} )?
}
}
}
}
pub fn assert<T: Operand + Debug>(loc: impl RValue<T>, val: impl RValue<T>) {
assert_eq!(loc.read(), val.read(),
"ASSERT instruction failed: {:#2X?} != {:#2X?}", loc.read(), val.read());
println!("ASSERT pass: {:#2X?} == {:#2X?}", loc.read(), val.read());
}
pub fn show(cpu: &mut i8088) {
println!("{:#X?}", cpu);
}
pub fn peek(addr: impl Address + RValue<u8>) {
println!("PEEK: @{:#X} = {:#X} ({})", addr.addr(), addr.read(), addr.read());
}
pub fn adc<T: Operand>(flags: &mut Flags, mut dst: impl LValue<T>, src: impl RValue<T>)
where bool: Into<T>
{
let dst_before = dst.read();
let src_before = src.read(); // may alias dst
let (res_tmp, carry) = dst_before.overflowing_add(&src_before);
let (res, carry2) = res_tmp.overflowing_add(&flags.cf().into());
dst.write(res);
flags.update(FlagOp::ADD { dst: dst_before.into(), src: src_before.into(), cf: carry | carry2 },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn add<T: Operand>(flags: &mut Flags, mut dst: impl LValue<T>, src: impl RValue<T>) {
let dst_before = dst.read();
let src_before = src.read(); // may alias dst
let (res, carry) = dst_before.overflowing_add(&src_before);
dst.write(res);
flags.update(FlagOp::ADD { dst: dst_before.into(), src: src_before.into(), cf: carry },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn and<T: Operand>(flags: &mut Flags, mut dst: impl LValue<T>, src: impl RValue<T>) {
let res = dst.read() & src.read();
dst.write(res);
flags.update(FlagOp::LOGIC, res.into(), T::HI_BIT_MASK.into());
}
pub fn call(mut ip: Reg, bus: &mut Bus, ss: u16, sp: Reg, rel16: u16) {
let target = ip.read().wrapping_add(rel16);
push(bus, ss, sp, ip.read());
ip.write(target);
}
pub fn cbw(mut ax: Reg, al: RegLo) {
ax.write(al.read() as i8 as u16);
}
pub fn clc(flags: &mut Flags) {
flags.update(FlagOp::Eager { cf: false,
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf(),
of: flags.of() },
0, 0);
}
pub fn cld(flags: &mut Flags) {
flags.df = false;
}
pub fn cmc(flags: &mut Flags) {
flags.update(FlagOp::Eager { cf: !flags.cf(),
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf(),
of: flags.of() },
0, 0);
}
pub fn cmp<T: Operand>(flags: &mut Flags, dst: impl RValue<T>, src: impl RValue<T>) {
let (dst, src) = (dst.read(), src.read());
let (res, carry) = dst.overflowing_sub(&src);
flags.update(FlagOp::SUB { dst: dst.into(), src: src.into(), cf: carry },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn cmps<T>(flags: &mut Flags,
bus: &mut Bus,
rep: RepPrefix,
mut cx: Reg,
seg: u16,
mut si: Reg,
es: Reg,
mut di: Reg)
where T: Operand + RValue<T>,
for<'a> FarPtr<'a>: RValue<T>
{
string_op!((T, flags, rep, cx, si=si, di=di, zf=flags.zf()), {
let src = <FarPtr as RValue<T>>::read(&FarPtr { bus: bus, segment: seg, offset: si.read() });
let dst = <FarPtr as RValue<T>>::read(&FarPtr { bus: bus, segment: es.read(), offset: di.read() });
cmp(flags, dst, src);
});
}
pub fn cwd(mut dx: Reg, ax: Reg) {
dx.write(if ax.read() < 0x8000 { 0x0000 } else { 0xFFFF });
}
pub fn dec<T: Operand, LVal: LValue<T>>(flags: &mut Flags, mut dst: LVal) {
let dst_before = dst.read();
let res = dst_before.wrapping_sub(&T::one());
dst.write(res);
let cf = flags.cf(); // we'll store current CF so we don't clobber it
flags.update(FlagOp::DEC { cf }, res.into(), T::HI_BIT_MASK.into());
}
pub fn inc<T: Operand, LVal: LValue<T>>(flags: &mut Flags, mut dst: LVal) {
let dst_before = dst.read();
let res = dst_before.wrapping_add(&T::one());
dst.write(res);
let cf = flags.cf(); // we'll store current CF so we don't clobber it
flags.update(FlagOp::INC { cf }, res.into(), T::HI_BIT_MASK.into());
}
pub fn int(cpu: &mut i8088, bus: &mut Bus, num: u8) {
match num {
0x21 => dos::interrupt(cpu, bus),
_ => unimplemented!("interrupt: {:02X}\ncpu: {:#X?}", num, cpu)
}
}
pub fn ja(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.cf() && !flags.zf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jae(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.cf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jb(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.cf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jbe(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.cf() || flags.zf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jcxz(mut ip: Reg, cx: Reg, rel8: u16) {
if cx.read() == 0 {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jg(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.zf() && flags.sf() == flags.of() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jge(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.sf() == flags.of() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jl(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.sf() != flags.of() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jle(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.zf() || flags.sf() != flags.of() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jmp(mut ip: Reg, rel: u16) {
ip.write(ip.read().wrapping_add(rel));
}
pub fn jno(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.of() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jns(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.sf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jnz(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.zf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jo(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.of() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jpe(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.pf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jpo(flags: &Flags, mut ip: Reg, rel8: u16) {
if !flags.pf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn js(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.sf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn jz(flags: &Flags, mut ip: Reg, rel8: u16) {
if flags.zf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn lea(mut dst: Reg, addr: FarPtr) {
dst.write(addr.offset)
}
pub fn lods<T>(flags: &Flags,
bus: &mut Bus,
rep: RepPrefix,
mut cx: Reg,
seg: u16,
mut si: Reg,
mut ax: impl LValue<T>)
where for<'a> FarPtr<'a>: RValue<T>
{
string_op!((T, flags, rep, cx, si=si), {
ax.write(FarPtr { bus: bus, segment: seg, offset: si.read() }.read());
});
}
// loop is a rust keyword and I don't wanna put r# everywhere
pub fn looop(mut ip: Reg, mut cx: Reg, rel8: u16) {
let count = cx.read().wrapping_sub(1);
cx.write(count);
if count != 0 {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn loopnz(flags: &mut Flags, mut ip: Reg, mut cx: Reg, rel8: u16) {
let count = cx.read().wrapping_sub(1);
cx.write(count);
if count != 0 && !flags.zf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn loopz(flags: &mut Flags, mut ip: Reg, mut cx: Reg, rel8: u16) {
let count = cx.read().wrapping_sub(1);
cx.write(count);
if count != 0 && flags.zf() {
ip.write(ip.read().wrapping_add(rel8));
}
}
pub fn mov<T>(mut dst: impl LValue<T>, src: impl RValue<T>) {
dst.write(src.read());
}
pub fn movs<T>(flags: &Flags,
bus: &mut Bus,
rep: RepPrefix,
mut cx: Reg,
seg: u16,
mut si: Reg,
es: Reg,
mut di: Reg)
where for<'a> FarPtr<'a>: RValue<T> + LValue<T>
{
string_op!((T, flags, rep, cx, si=si, di=di), {
let src = <FarPtr as RValue<T>>::read(&FarPtr { bus: bus, segment: seg, offset: si.read() });
let mut dst = FarPtr { bus: bus, segment: es.read(), offset: di.read() };
<FarPtr as LValue<T>>::write(&mut dst, src);
});
}
pub fn nop() {}
pub fn not<T: Operand, LVal: LValue<T>>(mut dst: LVal) {
dst.write(!dst.read());
}
pub fn neg<T: Operand, LVal: LValue<T>>(flags: &mut Flags, mut dst: LVal) {
let dst_before = dst.read();
let res = dst_before.wrapping_neg();
dst.write(res);
flags.update(FlagOp::NEG { dst: dst_before.into() },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn or<T: Operand>(flags: &mut Flags, mut dst: impl LValue<T>, src: impl RValue<T>) {
let res = dst.read() | src.read();
dst.write(res);
flags.update(FlagOp::LOGIC, res.into(), T::HI_BIT_MASK.into());
}
pub fn pop<LVal: LValue<u16>>(bus: &mut Bus, ss: u16, mut sp: Reg, mut dst: LVal) {
let ptr = FarPtr { bus: bus, segment: ss, offset: sp.read() };
dst.write(ptr.read());
// XXX: Not checking for stack faults or anything
sp.write(sp.read() + 2);
}
// Ugly hack for the ModR/M case. The ModR/M byte may select for a
// memory operand, in which case we are supplied with a FarPtr for our
// operand which contains a mutable pointer to the Bus. Unfortunately,
// we also want a mutable Bus pointer for accessing the stack. In the
// normal pop() function, this is passed in as an argument. Doing this
// when ModR/M selects for a memory location though would mean two
// mutable references to the Bus and an angry borrow checker.
//
// We solve this with a wrapper type for dynamic dispatch. If ModR/M
// selects for a register (which is kind of silly since single-byte
// opcodes exist for these cases), then we just defer to normal
// pop(). If a memory location is selected for however, we don't wrap
// a full FarPtr but a degraded form that doesn't contain a Bus
// pointer (and thus isn't a valid [LR]Value). We can promote it back
// to a full FarPtr using the provided Bus.
//
// The Bus argument is also in an unusual position so that it is not
// being held during the construction of the memory argument which
// temporarily requires the Bus before downgrading to its dynamic
// form. (This limitation is not essential, but it allows for reuse of
// standard [LR]Value ModR/M code)
pub fn pop_modrm<'a>(ss: u16, mut sp: Reg, dst: DynLValue<'a>, bus: &mut Bus) {
match dst {
DynLValue::Reg(reg) => pop(bus, ss, sp, reg),
DynLValue::FarPtr { segment: dst_segment, offset: dst_offset } => {
let val = <FarPtr as RValue<u16>>::read(&FarPtr { bus: bus, segment: ss, offset: sp.read() });
<FarPtr as LValue<u16>>::write(
&mut FarPtr { bus: bus, segment: dst_segment, offset: dst_offset },
val);
// XXX: Not checking for stack faults or anything
sp.write(sp.read() + 2);
}
}
}
pub fn push<RVal: RValue<u16>>(bus: &mut Bus, ss: u16, mut sp: Reg, val: RVal) {
// XXX: Not checking for stack faults or anything
sp.write(sp.read() - 2);
let mut ptr = FarPtr { bus: bus, segment: ss, offset: sp.read() };
ptr.write(val.read());
}
// Ugly hack for the ModR/M case. See the comments on pop_modrm.
pub fn push_modrm<'a>(ss: u16, sp: Reg, src: DynLValue<'a>, bus: &mut Bus) {
match src {
DynLValue::Reg(reg) => push(bus, ss, sp, reg.read()),
DynLValue::FarPtr { segment, offset } => {
let val = <FarPtr as RValue<u16>>::read(&FarPtr { bus: bus, segment: segment, offset: offset });
push(bus, ss, sp, val);
}
}
}
pub fn rcl<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand + Into<u32>,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>,
u32: AsPrimitive<T>
{
// Rust shifts overflow if shamt >= bit-size of the
// operand. Unfortunately, because we're looping-in the carry flag,
// our operand size is effectively 1 bit larger and so we might want
// a shift as large as the base operand size. We'll use u32 instead
// so that we always have adequate range on shifting operations.
//
// TODO-ish: Rust doesn't have static asserts, but it'd be nice to
// verify that u32 is larger than our operand. This technique is
// also used in the CF flag calculation.
let dst_before: u32 = dst.read().into();
let src_before = src.read().into(); // may alias dst
let shamt = src_before % (T::BITS + 1); // carry-flag effectively enlarges operand 1 bit
let res;
if shamt == 0 {
res = dst_before;
dst.write(res.as_()); // dst may be volatile
if src_before == 0 { return } // No flags update
} else {
res = dst_before << shamt
| (flags.cf() as u32) << shamt - 1
| dst_before >> T::BITS + 1 - shamt;
dst.write(res.as_());
}
flags.update(FlagOp::RCL { dst: dst_before as u16,
shamt: shamt,
bits: T::BITS,
old_cf: flags.cf(),
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res as u16,
T::HI_BIT_MASK.into());
}
pub fn rcr<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand + Into<u32>,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>,
u32: AsPrimitive<T>
{
// See comments on rcl().
let dst_before:u32 = dst.read().into();
let src_before = src.read().into(); // may alias dst
let shamt = src_before % (T::BITS + 1); // carry-flag effectively enlarges operand 1 bit
let res;
if shamt == 0 {
res = dst_before;
dst.write(res.as_()); // dst may be volatile
if src_before == 0 { return } // No flags update
} else {
res = dst_before >> shamt
| (flags.cf() as u32) << T::BITS >> shamt
| dst_before << T::BITS + 1 - shamt;
dst.write(res.as_());
}
flags.update(FlagOp::RCR { dst: dst_before as u16,
shamt: shamt,
old_cf: flags.cf(),
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res as u16,
T::HI_BIT_MASK.into());
}
pub fn rol<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>
{
let dst_before = dst.read();
let src_before = src.read(); // may alias dst
if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
let res = dst_before.rotate_left(src_before.as_());
dst.write(res);
flags.update(FlagOp::ROL { dst: dst_before.into(),
shamt: src_before.into(),
rot_mask: T::ROTATE_MASK,
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn ror<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>
{
let dst_before = dst.read();
let src_before = src.read(); // may alias dst
if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
let res = dst_before.rotate_right(src_before.as_());
dst.write(res);
flags.update(FlagOp::ROR { dst: dst_before.into(),
shamt: src_before.into(),
rot_mask: T::ROTATE_MASK,
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf() },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn sar<T, U, LVal, RVal>(flags: &mut Flags, mut dst: LVal, src: RVal)
where T: Operand,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>
{
let dst_before = dst.read();
let src_before = src.read(); // may alias dst
if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
// Before the 286, shifts could be up to 255 bits even though the
// operands themselves could only be 16 bits. The 286 and beyond
// masked the shamt down to 5 bits, but does so regardless of target
// operand size so the result is kinda weird. Since nobody seemed to
// care about this change back in the day, it's tempting for us not
// to care either, but we already need to do work to calculate CF
// (the last bit shifted off), and figuring out what that should be
// with a 5-bit shamt mask doesn't actually sound easier.
let sign_bit = dst_before.hi_bit();
let res = match dst_before.as_signed().checked_shr(src_before.as_()) {
Some(shifted) => T::from_signed(shifted),
None => if sign_bit { T::max_value() } else { T::zero() } // Out-of-Range yields all sign bits
};
dst.write(res);
flags.update(FlagOp::SAR { dst: dst_before.into(), src: src_before.into(), sign_bit },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn sbb<T: Operand>(flags: &mut Flags, mut dst: impl LValue<T>, src: impl RValue<T>)
where bool: Into<T>
{
let dst_before = dst.read();
let src_before = src.read();
let (res_tmp, carry) = dst_before.overflowing_sub(&src_before);
let (res, carry2) = res_tmp.overflowing_sub(&flags.cf().into());
dst.write(res);
flags.update(FlagOp::SUB { dst: dst_before.into(), src: src_before.into(), cf: carry | carry2 },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn scas<T, RVal>(flags: &mut Flags,
bus: &mut Bus,
rep: RepPrefix,
mut cx: Reg,
es: Reg,
mut di: Reg,
needle: RVal)
where RVal: RValue<T>,
T: Operand + RValue<T>,
for<'a> FarPtr<'a>: RValue<T>
{
let needle = needle.read();
string_op!((T, flags, rep, cx, di=di, zf=flags.zf()), {
let elem = FarPtr { bus: bus, segment: es.read(), offset: di.read() };
cmp(flags, elem, needle);
});
}
// See comments on sar()
pub fn shl<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>
{
let dst_before = dst.read();
let src_before = src.read(); // may alias dst
if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
let res = match dst_before.checked_shl(src_before.as_()) {
Some(shifted) => shifted,
None => T::zero()
};
dst.write(res);
flags.update(FlagOp::SHL { dst: dst_before.into(), src: src_before.into() },
res.into(),
T::HI_BIT_MASK.into());
}
// See comments on sar()
pub fn shr<T, U, LVal, RVal>(flags: &mut Flags,
mut dst: LVal,
src: RVal)
where T: Operand,
U: Operand + AsPrimitive<u32>,
LVal: LValue<T>,
RVal: RValue<U>
{
let dst_before = dst.read();
let src_before = src.read(); // may alias dst
if src_before == U::zero() { // 286 and beyond probably masks shamt before this check?
dst.write(dst_before); // dst may be volatile
return; // No flags update
}
let res = match dst_before.checked_shr(src_before.as_()) {
Some(shifted) => shifted,
None => T::zero()
};
dst.write(res);
flags.update(FlagOp::SHR { dst: dst_before.into(), src: src_before.into() },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn stc(flags: &mut Flags) {
flags.update(FlagOp::Eager { cf: true,
pf: flags.pf(),
af: flags.af(),
zf: flags.zf(),
sf: flags.sf(),
of: flags.of() },
0, 0);
}
pub fn std(flags: &mut Flags) {
flags.df = true;
}
pub fn stos<T, RVal>(flags: &Flags,
bus: &mut Bus,
rep: RepPrefix,
mut cx: Reg,
es: Reg,
mut di: Reg,
val: RVal)
where RVal: RValue<T>,
for<'a> FarPtr<'a>: LValue<T>
{
string_op!((T, flags, rep, cx, di=di), {
let mut dst = FarPtr { bus: bus, segment: es.read(), offset: di.read() };
dst.write(val.read());
});
}
pub fn sub<T: Operand>(flags: &mut Flags, mut dst: impl LValue<T>, src: impl RValue<T>) {
let dst_before = dst.read();
let src_before = src.read();
let (res, carry) = dst_before.overflowing_sub(&src_before);
dst.write(res);
flags.update(FlagOp::SUB { dst: dst_before.into(), src: src_before.into(), cf: carry },
res.into(),
T::HI_BIT_MASK.into());
}
pub fn test<T: Operand>(flags: &mut Flags, dst: impl RValue<T>, src: impl RValue<T>) {
let res = dst.read() & src.read();
flags.update(FlagOp::LOGIC, res.into(), T::HI_BIT_MASK.into());
}
pub fn xchg<T, Dst: LValue<T>, Src: LValue<T>>(mut dst: Dst, mut src: Src) {
let tmp = src.read();
src.write(dst.read());
dst.write(tmp);
}
pub fn xor<T: Operand>(flags: &mut Flags, mut dst: impl LValue<T>, src: impl RValue<T>) {
let res = dst.read() ^ src.read();
dst.write(res);
flags.update(FlagOp::LOGIC, res.into(), T::HI_BIT_MASK.into());
}

65
src/emu/pc.rs Normal file
View File

@@ -0,0 +1,65 @@
use std::cell::Cell;
use std::fmt::{Debug, Formatter};
use emu::i8088::i8088;
use emu::util::segoff_to_addr;
const RAM_SIZE: usize = 128 * 1024;
pub struct PC {
pub cpu: i8088,
bus: Bus,
}
pub struct Bus {
pub ram: [u8; RAM_SIZE],
//io:
}
impl Bus {
pub const RAM_LOCATION: usize = 0x0000_0000;
//const EGA_LOCATION: usize = 0x000A_0000;
pub fn read(&self, addr: usize) -> u8 {
self.ram[addr]
}
pub fn write(&mut self, addr: usize, val: u8) {
self.ram[addr] = val;
}
}
impl PC {
pub fn new_with_com_file(comfile: &[u8]) -> PC {
const LOAD_SEGMENT: u16 = 0x0050;
const LOAD_OFFSET: u16 = 0x0100;
const LOAD_ADDRESS: usize = segoff_to_addr(LOAD_SEGMENT, LOAD_OFFSET);
const LOAD_RAM_ADDRESS: usize = LOAD_ADDRESS - Bus::RAM_LOCATION;
assert!(comfile.len() <= 0xFF00, "COM file larger than spec");
debug_assert!(LOAD_RAM_ADDRESS + comfile.len() <= RAM_SIZE,
"No memory for loading COM file");
let mut pc = PC {
cpu: { i8088 { cs: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
ds: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
es: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
ss: Cell::new(LOAD_SEGMENT), // COMs always uses Tiny memory model
sp: Cell::new(0xFFFE),
ip: Cell::new(LOAD_OFFSET),
..i8088::default() } },
bus: Bus { ram: [0; RAM_SIZE] }
};
pc.bus.ram[LOAD_RAM_ADDRESS..LOAD_RAM_ADDRESS + comfile.len()].copy_from_slice(comfile);
pc
}
pub fn run(&mut self) {
self.cpu.run(&mut self.bus);
}
}
impl Debug for PC {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> {
self.cpu.fmt(fmt)
}
}

33
src/emu/util.rs Normal file
View File

@@ -0,0 +1,33 @@
use std::cell::Cell;
use emu::byteorder::{ByteOrder, LittleEndian};
pub const fn segoff_to_addr(segment: u16, offset: u16) -> usize {
let segaddr = (segment as usize) << 4;
segaddr + offset as usize
}
pub fn read_hi(val: &Cell<u16>) -> u8 {
let mut buf = [0; 2];
LittleEndian::write_u16(&mut buf, val.get());
buf[1] as u8
}
pub fn read_lo(val: &Cell<u16>) -> u8 {
let mut buf = [0; 2];
LittleEndian::write_u16(&mut buf, val.get());
buf[0] as u8
}
pub fn write_hi(reg: &Cell<u16>, val: u8) {
let mut buf = [0; 2];
LittleEndian::write_u16(&mut buf, reg.get());
buf[1] = val;
reg.set(LittleEndian::read_u16(&buf))
}
pub fn write_lo(reg: &Cell<u16>, val: u8) {
let mut buf = [0; 2];
LittleEndian::write_u16(&mut buf, reg.get());
buf[0] = val;
reg.set(LittleEndian::read_u16(&buf));
}

View File

@@ -1,10 +1,11 @@
#![feature(conservative_impl_trait)]
#[macro_use] extern crate gfx; #[macro_use] extern crate gfx;
#[macro_use] extern crate log; #[allow(unused_imports)] #[macro_use] extern crate log;
extern crate nalgebra as na;
extern crate piston;
pub mod arena; pub mod arena;
pub mod ega; pub mod ega;
pub mod emu;
pub mod scene; pub mod scene;
pub mod scenes; pub mod scenes;
pub mod tile; pub mod tile;

View File

@@ -1,16 +1,14 @@
use gfx;
use na;
use piston;
use view; use view;
use vr; use vr;
extern crate gfx;
extern crate gfx_device_gl;
extern crate nalgebra as na;
extern crate piston_window;
pub trait Scene<D: gfx::Device, pub trait Scene<D: gfx::Device,
F: gfx::Factory<D::Resources>> { F: gfx::Factory<D::Resources>> {
fn event(&mut self, event: Event); fn event(&mut self, event: Event);
fn update(&mut self, fn update(&mut self,
vr: &mut vr::VR, vr: &mut Option<vr::VR>, // TODO: abstract this out
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>); encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>);
fn render(&self, fn render(&self,
factory: &mut F, factory: &mut F,
@@ -20,9 +18,10 @@ pub trait Scene<D: gfx::Device,
depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>); depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>);
fn origin(&self) -> na::Matrix4<f32>; fn origin(&self) -> na::Matrix4<f32>;
fn mouselook(&self) -> na::Matrix4<f32>;
} }
pub enum Event { pub enum Event {
Vr(vr::Event), Vr(vr::Event),
Piston(piston_window::Input), Piston(piston::input::Input),
} }

View File

@@ -1,18 +1,21 @@
#version 150 #version 150
#define MILLIS_PER_TILE 4000u #define MILLIS_PER_TILE 3000u
in vec2 v_uv; in vec2 v_uv;
flat in uint v_tileidx; flat in uint v_tileidx;
in float v_fade;
out vec4 pixcolor; out vec4 pixcolor;
uniform sampler2DArray t_tiles; uniform sampler2DArray t_tiles;
uniform b_constants { uniform b_constants {
uvec4 anim; uvec4 anim;
float R1; float R1;
float R2; float R2;
float R3;
float haze;
vec4 hazecolor;
}; };
uniform b_locals { uniform b_locals {
mat4 camera;
uint millis; uint millis;
float treadmill_x; float treadmill_x;
float treadmill_y; float treadmill_y;
@@ -24,5 +27,6 @@ void main() {
anim_uv = vec2(v_uv.x, v_uv.y + float(millis % MILLIS_PER_TILE) / MILLIS_PER_TILE); anim_uv = vec2(v_uv.x, v_uv.y + float(millis % MILLIS_PER_TILE) / MILLIS_PER_TILE);
} }
pixcolor = texture(t_tiles, vec3(anim_uv.x, 1.0 - anim_uv.y, v_tileidx)); vec4 texcolor = texture(t_tiles, vec3(anim_uv.x, 1.0 - anim_uv.y, v_tileidx));
pixcolor = mix(texcolor, hazecolor, v_fade);
} }

View File

@@ -8,23 +8,27 @@ in vec2 a_uv;
in uint a_tileidx; in uint a_tileidx;
out vec2 v_uv; out vec2 v_uv;
flat out uint v_tileidx; flat out uint v_tileidx;
out float v_fade;
uniform b_trans { uniform b_trans {
mat4 u_viewmodel;
mat4 u_matrix; mat4 u_matrix;
}; };
uniform b_constants { uniform b_constants {
uvec4 anim; uvec4 anim;
float R1; float R1;
float R2; float R2;
float R3;
float haze;
vec4 hazecolor;
}; };
uniform b_locals { uniform b_locals {
mat4 camera;
uint millis; uint millis;
float treadmill_x; float treadmill_x;
float treadmill_y; float treadmill_y;
}; };
vec3 toroid(vec2 src, float r1, float r2, float r3) { vec3 toroid(vec2 src, float r1, float r2, float r3) {
return vec3(r3 * -1.0 * sin(src.x), // use r3 instead of r2 for "deflated" torus return vec3(r3 * sin(src.x), // use r3 instead of r2 for "deflated" torus
(r1 + r2 * cos(src.x)) * cos(src.y), (r1 + r2 * cos(src.x)) * cos(src.y),
(r1 + r2 * cos(src.x)) * sin(src.y)); (r1 + r2 * cos(src.x)) * sin(src.y));
} }
@@ -35,8 +39,12 @@ void main() {
vec2 thetaphi = vec2(TWO_PI_CIRC * (a_pos.x + treadmill_x), vec2 thetaphi = vec2(TWO_PI_CIRC * (a_pos.x + treadmill_x),
TWO_PI_CIRC * (a_pos.y + treadmill_y)); TWO_PI_CIRC * (a_pos.y + treadmill_y));
float height = R1 * TWO_PI_CIRC; float height = R2 * 4 * TWO_PI_CIRC;
vec3 normal = vec3(toroid(thetaphi, 0, height, height)); vec3 normal = vec3(toroid(thetaphi, 0, height, height)) *
gl_Position = u_matrix * camera * vec3(R2 / R3, 1.0, 1.0);
vec4(toroid(thetaphi, R1, R2, R1) + a_pos.z * normal, 1.0); vec4 model_pos = vec4(toroid(thetaphi, R1, R2, R3) + a_pos.z * normal, 1.0);
gl_Position = u_matrix * model_pos;
vec4 view_pos = u_viewmodel * model_pos;
v_fade = min(1.0, length(view_pos.xyz) / R1 / 2 * haze);
} }

View File

@@ -5,27 +5,26 @@ use vr;
use world as model; use world as model;
use world::HasMap; use world::HasMap;
extern crate gfx; extern crate memmap;
extern crate nalgebra as na;
extern crate num_traits;
extern crate openvr_sys;
extern crate piston_window;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::time::SystemTime; use std::time::SystemTime;
use gfx::tex; use gfx::{self, texture};
use gfx::traits::FactoryExt; use gfx::traits::FactoryExt;
use self::na::ToHomogeneous; use na;
use self::num_traits::identities::One; use piston::input::{Button, ButtonArgs, ButtonState, Input, Key, Motion};
const PI: f32 = ::std::f32::consts::PI;
const TWO_PI_CIRC: f32 = 2.0 * PI / 256.0;
//const R1: f32 = 4096.0;
//const R2: f32 = 1024.0;
//const R1: f32 = 4.0;
//const R2: f32 = 1.0;
const R1: f32 = 256.0; const R1: f32 = 256.0;
const R2: f32 = 64.0; const R2: f32 = 64.0;
const R3: f32 = 128.0;
//const SKY_COLOR: [f32; 4] = [0.15, 0.15, 0.75, 1.0];
const SKY_COLOR: [f32; 4] = [0.005, 0.005, 0.01, 1.0];
gfx_defines! { gfx_defines! {
vertex Vertex { vertex Vertex {
@@ -38,10 +37,12 @@ gfx_defines! {
anim: [u32; 4] = "anim", anim: [u32; 4] = "anim",
r1: f32 = "R1", r1: f32 = "R1",
r2: f32 = "R2", r2: f32 = "R2",
r3: f32 = "R3",
haze: f32 = "haze",
hazecolor: [f32; 4] = "hazecolor",
} }
constant Locals { constant Locals {
camera: [[f32; 4]; 4] = "camera",
millis: u32 = "millis", millis: u32 = "millis",
treadmill_x: f32 = "treadmill_x", treadmill_x: f32 = "treadmill_x",
treadmill_y: f32 = "treadmill_y", treadmill_y: f32 = "treadmill_y",
@@ -76,28 +77,27 @@ fn get_model(world: &model::World) -> (Vec<Vertex>, Vec<u32>) {
10 | 11 | 12 => 1.0, 10 | 11 | 12 => 1.0,
_ => 0.0, _ => 0.0,
}; };
let rf = (((r + 90) % 256) as i16 - 128) as f32; let (rf, cf) = (r as f32, c as f32);
let cf = (((c + 144) % 256) as i16 - 128) as f32;
if alt == 0.0 { if alt == 0.0 {
verticies.extend_from_slice( verticies.extend_from_slice(
&[Vertex { pos: [ cf + 0., -rf - 1., 0. ], uv: [0., 0.], tileidx: tileidx }, &[Vertex { pos: [ cf + 0., rf + 1., 0. ], uv: [0., 0.], tileidx: tileidx },
Vertex { pos: [ cf + 1., -rf - 1., 0. ], uv: [1., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 1., 0. ], uv: [1., 0.], tileidx: tileidx },
Vertex { pos: [ cf + 1., -rf - 0., 0. ], uv: [1., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 0., 0. ], uv: [1., 1.], tileidx: tileidx },
Vertex { pos: [ cf + 0., -rf - 0., 0. ], uv: [0., 1.], tileidx: tileidx },]); Vertex { pos: [ cf + 0., rf + 0., 0. ], uv: [0., 1.], tileidx: tileidx },]);
indicies.extend_from_slice( indicies.extend_from_slice(
&[ v + 0, v + 1, v + 2, &[ v + 0, v + 1, v + 2,
v + 2, v + 3, v + 0 ]); v + 2, v + 3, v + 0 ]);
v += 4; v += 4;
} else { } else {
verticies.extend_from_slice( verticies.extend_from_slice(
&[Vertex { pos: [ cf + 0., -rf - 1., 0. ], uv: [0., 0.], tileidx: tileidx }, &[Vertex { pos: [ cf + 0., rf + 1., 0. ], uv: [0., 0.], tileidx: tileidx },
Vertex { pos: [ cf + 1., -rf - 1., 0. ], uv: [1., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 1., 0. ], uv: [1., 0.], tileidx: tileidx },
Vertex { pos: [ cf + 1., -rf - 0., 0. ], uv: [1., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf + 0., 0. ], uv: [1., 1.], tileidx: tileidx },
Vertex { pos: [ cf + 0., -rf - 0., 0. ], uv: [0., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 0., rf + 0., 0. ], uv: [0., 1.], tileidx: tileidx },
Vertex { pos: [ cf + 0., -rf, 0. ], uv: [0., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 0., rf, 0. ], uv: [0., 0.], tileidx: tileidx },
Vertex { pos: [ cf + 1., -rf, 0. ], uv: [1., 0.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf, 0. ], uv: [1., 0.], tileidx: tileidx },
Vertex { pos: [ cf + 1., -rf, alt ], uv: [1., 1.], tileidx: tileidx }, Vertex { pos: [ cf + 1., rf, alt ], uv: [1., 1.], tileidx: tileidx },
Vertex { pos: [ cf + 0., -rf, alt ], uv: [0., 1.], tileidx: tileidx },]); Vertex { pos: [ cf + 0., rf, alt ], uv: [0., 1.], tileidx: tileidx },]);
indicies.extend_from_slice( indicies.extend_from_slice(
&[ v + 0, v + 1, v + 2, &[ v + 0, v + 1, v + 2,
v + 2, v + 3, v + 0, v + 2, v + 3, v + 0,
@@ -120,7 +120,9 @@ pub struct WorldScene<D: gfx::Device,
F: gfx::Factory<D::Resources>> { F: gfx::Factory<D::Resources>> {
pso: gfx::PipelineState<D::Resources, pipe::Meta>, pso: gfx::PipelineState<D::Resources, pipe::Meta>,
camera: na::Matrix4<f32>, camera: na::Matrix4<f32>,
constants: gfx::handle::Buffer<D::Resources, Constants>, constants: Constants,
constants_buffer: gfx::handle::Buffer<D::Resources, Constants>,
constants_dirty: bool,
locals: gfx::handle::Buffer<D::Resources, Locals>, locals: gfx::handle::Buffer<D::Resources, Locals>,
atlas: gfx::handle::ShaderResourceView<D::Resources, atlas: gfx::handle::ShaderResourceView<D::Resources,
<view::ColorFormat as gfx::format::Formatted>::View>, <view::ColorFormat as gfx::format::Formatted>::View>,
@@ -132,43 +134,61 @@ pub struct WorldScene<D: gfx::Device,
start_time: SystemTime, start_time: SystemTime,
treadmills: (f32, f32), treadmills: (f32, f32),
pads: BTreeMap<u32, (TrackMode, Option<openvr_sys::VRControllerState_t>)>, mouselook: na::Matrix4<f32>,
pads: BTreeMap<u32, (TrackMode, Option<vr::ControllerState>)>,
_worldmap: model::World,
lat: u8,
lng: u8,
} }
impl<D: gfx::Device, F: gfx::Factory<D::Resources>> WorldScene<D, F> { impl<D: gfx::Device, F: gfx::Factory<D::Resources>> WorldScene<D, F> {
pub fn new(device: &mut D, pub fn new(device: &mut D,
factory: &mut F, factory: &mut F,
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>,
aux_command: &mut <D as gfx::Device>::CommandBuffer) -> WorldScene<D, F> { aux_command: &mut <D as gfx::Device>::CommandBuffer) -> WorldScene<D, F> {
let (model, model_idx) = get_model(&get_data_model()); let worldmap = get_data_model();
let (model, model_idx) = get_model(&worldmap);
let (vertex_buffer, slice) = let (vertex_buffer, slice) =
factory.create_vertex_buffer_with_slice(&model, &model_idx[..]); factory.create_vertex_buffer_with_slice(&model, &model_idx[..]);
let constants = factory.create_constant_buffer(1);
encoder.update_constant_buffer(&constants, &Constants { anim: ANIMDATA,
r1: R1,
r2: R2 });
WorldScene { WorldScene {
pso: factory.create_pipeline_simple(VERTEX_SHADER_SRC, pso: factory.create_pipeline_simple(VERTEX_SHADER_SRC,
FRAGMENT_SHADER_SRC, FRAGMENT_SHADER_SRC,
pipe::new()) pipe::new())
.expect("create pipeline"), .expect("create pipeline"),
camera: na::Matrix4::one(), camera: na::Matrix4::identity(),
constants: constants, constants: Constants { anim: ANIMDATA,
r1: R1, r2: R2, r3: R3,
haze: 1.0/2.0f32.sqrt(), hazecolor: SKY_COLOR },
constants_buffer: factory.create_constant_buffer(1),
constants_dirty: true,
locals: factory.create_constant_buffer(1), locals: factory.create_constant_buffer(1),
atlas: tile::get_tiles::<_, _, view::ColorFormat>(device, factory, aux_command), atlas: tile::get_tiles::<_, _, view::ColorFormat>(device, factory, aux_command),
sampler: factory.create_sampler(tex::SamplerInfo::new(tex::FilterMethod::Jrd, sampler: factory.create_sampler(texture::SamplerInfo::new(texture::FilterMethod::Trilinear, //::Jrd
tex::WrapMode::Tile)), texture::WrapMode::Tile)),
f: PhantomData, f: PhantomData,
vbuf: vertex_buffer, vbuf: vertex_buffer,
slice: slice, slice: slice,
start_time: SystemTime::now(), start_time: SystemTime::now(),
treadmills: (0.0, 0.0), treadmills: (0.0, 0.0),
mouselook: na::Matrix4::identity(),
pads: BTreeMap::new(), pads: BTreeMap::new(),
_worldmap: worldmap,
lat: 144,
lng: 90,
} }
} }
fn toroid((x, y): (f32, f32), r1: f32, r2: f32, r3: f32) -> na::Vector3<f32>
{
let x: f32 = TWO_PI_CIRC * x as f32;
let y: f32 = TWO_PI_CIRC * y as f32;
na::Vector3::<f32>::new(r3 * x.sin(), // use r3 instead of r2 for "deflated" torus
(r1 + r2 * x.cos()) * y.cos(),
(r1 + r2 * x.cos()) * y.sin())
}
} }
const ANIMDATA: [u32; 4] = const ANIMDATA: [u32; 4] =
@@ -184,6 +204,7 @@ impl<D: gfx::Device,
use scene::Event::*; use scene::Event::*;
use vr::Event::*; use vr::Event::*;
match event { match event {
// treadmill / camera movement registration
Vr(Touch { dev_idx, .. }) => { Vr(Touch { dev_idx, .. }) => {
self.pads.insert(dev_idx, (TrackMode::Touch, None)); self.pads.insert(dev_idx, (TrackMode::Touch, None));
}, },
@@ -196,12 +217,82 @@ impl<D: gfx::Device,
Vr(Untouch { dev_idx, .. }) => { Vr(Untouch { dev_idx, .. }) => {
self.pads.remove(&dev_idx); self.pads.remove(&dev_idx);
}, },
Piston(Input::Button(ButtonArgs { state: ButtonState::Press,
button: Button::Keyboard(key),
.. })) => {
match key {
// treadmill / camera reset
Key::Backspace => {
self.treadmills = (0.0, 0.0);
},
Key::D0 => {
self.camera = na::Matrix4::identity();
},
// player movement
Key::Up => {
self.lat = self.lat.wrapping_sub(1);
},
Key::Down => {
self.lat = self.lat.wrapping_add(1);
},
Key::Left => {
self.lng = self.lng.wrapping_sub(1);
},
Key::Right => {
self.lng = self.lng.wrapping_add(1);
},
// scale adjustment
Key::Q => {
self.constants = Constants { r1: R1 / 2.0, r2: R2 / 2.0, r3: R3 / 2.0, ..self.constants };
self.constants_dirty = true;
},
Key::D1 => {
self.constants = Constants { r1: R1, r2: R2, r3: R3, ..self.constants };
self.constants_dirty = true;
},
Key::D2 => {
self.constants = Constants { r1: R1 * 2.0, r2: R2 * 2.0, r3: R3 * 2.0, ..self.constants };
self.constants_dirty = true;
},
Key::D3 => {
self.constants = Constants { r1: R1 * 4.0, r2: R2 * 4.0, r3: R3 * 4.0, ..self.constants };
self.constants_dirty = true;
},
Key::D4 => {
self.constants = Constants { r1: R1 * 16.0, r2: R2 * 16.0, r3: R3 * 16.0, ..self.constants };
self.constants_dirty = true;
},
Key::H => {
self.constants = Constants { haze: self.constants.haze * 2.0f32.sqrt().sqrt(), ..self.constants };
println!("haze: {}", self.constants.haze);
self.constants_dirty = true;
},
Key::N => {
self.constants = Constants { haze: self.constants.haze / 2.0f32.sqrt().sqrt(), ..self.constants };
println!("haze: {}", self.constants.haze);
self.constants_dirty = true;
},
_ => ()
}
}
// mouselook
Piston(Input::Move(Motion::MouseCursor(x, y))) => {
self.mouselook = (
na::Rotation3::new(na::Vector3::<f32>::new(y as f32 / 300.0, 0.0, 0.0)) *
na::Rotation3::new(na::Vector3::<f32>::new(0.0, x as f32 / 300.0, 0.0))
).to_homogeneous();
},
_ => () _ => ()
} }
} }
fn update(&mut self, fn update(&mut self,
vr: &mut vr::VR, vr: &mut Option<vr::VR>,
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>) { encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>) {
const NANOS_PER_MILLI: u32 = 1_000_000; const NANOS_PER_MILLI: u32 = 1_000_000;
const MILLIS_PER_SEC: u64 = 1_000; const MILLIS_PER_SEC: u64 = 1_000;
@@ -210,37 +301,37 @@ impl<D: gfx::Device,
for (pad, track) in self.pads.iter_mut() { for (pad, track) in self.pads.iter_mut() {
let mode = track.0; let mode = track.0;
if let Some(state) = vr.get_controller_state(*pad) { if let Some(state) = vr.as_ref().and_then(|vr| vr.get_controller_state(*pad)) {
if let Some(old_state) = track.1 { if let Some(old_state) = track.1 {
match mode { match mode {
TrackMode::Touch => { TrackMode::Touch => {
const THRESHOLD: f32 = 0.005; const THRESHOLD: f32 = 0.005;
const SCALE: f32 = 32.0; const SCALE: f32 = 32.0;
let xdiff = state.rAxis[0].x - old_state.rAxis[0].x; let xdiff = state.axis[0].x - old_state.axis[0].x;
let ydiff = state.rAxis[0].y - old_state.rAxis[0].y; let ydiff = state.axis[0].y - old_state.axis[0].y;
if xdiff.abs() > THRESHOLD { self.treadmills.0 += SCALE * xdiff; } if xdiff.abs() > THRESHOLD { self.treadmills.0 += SCALE * xdiff; }
if ydiff.abs() > THRESHOLD { self.treadmills.1 += SCALE * ydiff; } if ydiff.abs() > THRESHOLD { self.treadmills.1 += SCALE * ydiff; }
}, },
TrackMode::Press => { TrackMode::Press => {
let rot = na::Vector3::new(0.0, 0.0, 0.0); let rot = na::Vector3::new(0.0, 0.0, 0.0);
let speed = R2 * 0.01; let speed = R2 * 0.005;
if state.rAxis[0].x > 0.5 { if state.axis[0].x > 0.5 {
self.camera = na::Similarity3::new(na::Vector3::new(-speed, 0.0, 0.0), self.camera = na::Similarity3::new(na::Vector3::new(-speed, 0.0, 0.0),
rot, 1.0).to_homogeneous() * self.camera; rot, 1.0).to_homogeneous() * self.camera;
} if state.rAxis[0].x < -0.5 { } if state.axis[0].x < -0.5 {
self.camera = na::Similarity3::new(na::Vector3::new( speed, 0.0, 0.0), self.camera = na::Similarity3::new(na::Vector3::new( speed, 0.0, 0.0),
rot, 1.0).to_homogeneous() * self.camera; rot, 1.0).to_homogeneous() * self.camera;
} if state.rAxis[0].y > 0.5 { } if state.axis[0].y > 0.5 {
self.camera = na::Similarity3::new(na::Vector3::new( 0.0, -speed, 0.0), self.camera = na::Similarity3::new(na::Vector3::new( 0.0, -speed, 0.0),
rot, 1.0).to_homogeneous() * self.camera; rot, 1.0).to_homogeneous() * self.camera;
} if state.rAxis[0].y < -0.5 { } if state.axis[0].y < -0.5 {
self.camera = na::Similarity3::new(na::Vector3::new( 0.0, speed, 0.0), self.camera = na::Similarity3::new(na::Vector3::new( 0.0, speed, 0.0),
rot, 1.0).to_homogeneous() * self.camera; rot, 1.0).to_homogeneous() * self.camera;
} }
}, },
} }
if state.unPacketNum == old_state.unPacketNum { if state.packet_num == old_state.packet_num {
continue; continue;
} }
} }
@@ -248,8 +339,12 @@ impl<D: gfx::Device,
} }
} }
encoder.update_constant_buffer(&self.locals, &Locals { camera: *(self.camera).as_ref(), if self.constants_dirty {
millis: millis, self.constants_dirty = false;
encoder.update_constant_buffer(&self.constants_buffer, &self.constants);
}
encoder.update_constant_buffer(&self.locals, &Locals { millis: millis,
treadmill_x: self.treadmills.0, treadmill_x: self.treadmills.0,
treadmill_y: self.treadmills.1 }); treadmill_y: self.treadmills.1 });
} }
@@ -261,10 +356,12 @@ impl<D: gfx::Device,
target: &gfx::handle::RenderTargetView<D::Resources, view::ColorFormat>, target: &gfx::handle::RenderTargetView<D::Resources, view::ColorFormat>,
depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>) { depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>) {
encoder.clear(&target, SKY_COLOR);
encoder.clear_depth(&depth, 1.0);
let pipe = pipe::Data { let pipe = pipe::Data {
vbuf: self.vbuf.clone(), vbuf: self.vbuf.clone(),
trans: trans.clone(), trans: trans.clone(),
constants: self.constants.clone(), constants: self.constants_buffer.clone(),
locals: self.locals.clone(), locals: self.locals.clone(),
atlas: (self.atlas.clone(), self.sampler.clone()), atlas: (self.atlas.clone(), self.sampler.clone()),
pixcolor: target.clone(), pixcolor: target.clone(),
@@ -274,13 +371,22 @@ impl<D: gfx::Device,
} }
fn origin(&self) -> na::Matrix4<f32> { fn origin(&self) -> na::Matrix4<f32> {
na::Similarity3::new(na::Vector3::new(0.0, R1 - R2, 0.0), let (r1, r2, r3) = (self.constants.r1, self.constants.r2, self.constants.r3);
na::Vector3::new(0.0, 0.0, 0.0), 1.0).to_homogeneous() let (y, x) = (self.lat as f32 + 0.5, self.lng as f32 + 0.5); // center of tile
let eye = Self::toroid((x, y), r1, r2, r3);
let looktgt = Self::toroid((x, y - 1.0), r1, r2, r3); // look ahead = north
let normal = Self::toroid((x, y), 0.0, r2, r2).component_mul(&na::Vector3::new(r2 / r3, 1.0, 1.0));
self.camera * na::Isometry3::look_at_rh(&na::Point3::from(eye),
&na::Point3::from(looktgt),
&normal,
).to_homogeneous()
}
fn mouselook(&self) -> na::Matrix4<f32> {
self.mouselook
} }
} }
extern crate memmap;
fn get_data_model() -> model::World { fn get_data_model() -> model::World {
use self::memmap::{Mmap, Protection}; use self::memmap::{Mmap, Protection};
use std::mem::transmute; use std::mem::transmute;

View File

@@ -1,13 +1,11 @@
extern crate gfx;
use ega; use ega;
use ::std; use ::std;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::Path;
use self::gfx::{CommandBuffer, Typed}; use gfx::{self, texture, CommandBuffer};
use self::gfx::tex; use gfx::memory::Typed;
const TILEDIM: u16 = 16; const TILEDIM: u16 = 16;
@@ -32,18 +30,22 @@ pub fn get_tiles<D, F, T>(device: &mut D,
let ega_page = ega::decode(&ega_bytes, ega::Compression::Uncompressed, ega::Tiling::Tiled(TILEDIM)); let ega_page = ega::decode(&ega_bytes, ega::Compression::Uncompressed, ega::Tiling::Tiled(TILEDIM));
let mipmap = ega_page.mipmap(2); let mipmap = ega_page.mipmap(2);
let tex = factory.create_texture_const_u8::<T>(tex::Kind::D2Array(mipmap.dim as u16, let tex = factory.create_texture_immutable_u8::<T>(texture::Kind::D2Array(mipmap.dim as u16,
mipmap.dim as u16, mipmap.dim as u16,
mipmap.len as u16, mipmap.len as u16,
tex::AaMode::Single), texture::AaMode::Single),
&mipmap.slices()) texture::Mipmap::Provided,
.expect("create tile texture"); &mipmap.slices())
.expect("create tile texture");
{ {
let mut manager = gfx::handle::Manager::<D::Resources>::new(); let mut manager = gfx::handle::Manager::<D::Resources>::new();
// XXX: Find out if Textures need to be/can be fenced like Buffers,
// Seems like I should mark tex.1 as being read/written, but it's not a Buffer?
let access = gfx::pso::AccessInfo::new();
let view = manager.ref_srv(tex.1.raw()); let view = manager.ref_srv(tex.1.raw());
command.generate_mipmap(*view); command.generate_mipmap(*view);
device.submit(command); device.submit(command, &access).expect("generate tile mipmaps");
} }
tex.1 tex.1
} }
@@ -64,7 +66,7 @@ impl Tile {
8 => '⨇', // mountain '△' 8 => '⨇', // mountain '△'
9 => '☗', // dungeon 9 => '☗', // dungeon
10 => '⍟', // city 10 => '⍟', // city
11 | 13...15 => '⛫', // castle 11 | 13..=15 => '⛫', // castle
12 => '❖', // village 12 => '❖', // village
22 => '⎔', // tile floor 22 => '⎔', // tile floor
23 => '⟗', // bridge 23 => '⟗', // bridge
@@ -102,7 +104,7 @@ impl Tile {
76 => '⌘', // lava 76 => '⌘', // lava
// 79 => '💥', // attack flash // 79 => '💥', // attack flash
// 88 | 89 => 'ጿ', // beggar // 88 | 89 => 'ጿ', // beggar
96...121 => ::std::char::from_u32((self.val - 31) as u32).unwrap(), 96..=121 => ::std::char::from_u32((self.val - 31) as u32).unwrap(),
122 => '=', // space 122 => '=', // space
123 => '⊐', // right '' 123 => '⊐', // right ''
124 => '⊏', // left '⊨' 124 => '⊏', // left '⊨'

View File

@@ -26,7 +26,7 @@ pub struct Town {
} }
impl HasMap for Town { impl HasMap for Town {
fn map(&self) -> &Map { fn map(&self) -> &dyn Map {
&self.map &self.map
} }
} }

View File

@@ -1,25 +1,24 @@
use vr; use vr::{self, AsMatrix4, VR};
use vr::{AsMatrix4, VR};
extern crate gfx_device_gl; extern crate gfx_device_gl;
extern crate nalgebra as na;
extern crate num_traits;
extern crate piston_window; extern crate piston_window;
extern crate openvr_sys;
use gfx; use gfx;
use gfx::Device; use gfx::Device;
use gfx::traits::FactoryExt; use gfx::traits::FactoryExt;
use self::na::Inverse; use na;
use self::piston_window::{PistonWindow, Window}; use self::piston_window::{PistonWindow, Window};
pub type ColorFormat = gfx::format::Srgba8; pub type ColorFormat = gfx::format::Srgba8;
pub type DepthFormat = gfx::format::DepthStencil; pub type DepthFormat = gfx::format::DepthStencil;
const NEAR: f32 = 0.01; const NEAR: f32 = 0.01;
const FAR: f32 = 1000.0; const FAR: f32 = 3072.0;
gfx_constant_struct! { gfx_constant_struct! {
Trans { Trans {
viewmodel: [[f32; 4]; 4] = "u_viewmodel",
matrix: [[f32; 4]; 4] = "u_matrix", matrix: [[f32; 4]; 4] = "u_matrix",
} }
} }
@@ -29,66 +28,89 @@ pub struct ViewRoot<Dev, T, D>
T: gfx::format::RenderFormat + gfx::format::TextureFormat, T: gfx::format::RenderFormat + gfx::format::TextureFormat,
D: gfx::format::DepthFormat + gfx::format::TextureFormat { D: gfx::format::DepthFormat + gfx::format::TextureFormat {
left: vr::EyeBuffer<T, D>, left: Option<vr::EyeBuffer<T, D>>,
right: vr::EyeBuffer<T, D>, right: Option<vr::EyeBuffer<T, D>>,
trans: gfx::handle::Buffer<Dev::Resources, Trans>, trans: gfx::handle::Buffer<Dev::Resources, Trans>,
} }
impl ViewRoot<gfx_device_gl::Device, ColorFormat, DepthFormat> { impl ViewRoot<gfx_device_gl::Device, ColorFormat, DepthFormat> {
pub fn create_view(window: &mut PistonWindow, vr: &VR) pub fn create_view(window: &mut PistonWindow, vr: &Option<VR>)
-> ViewRoot<gfx_device_gl::Device, ColorFormat, DepthFormat> { -> ViewRoot<gfx_device_gl::Device, ColorFormat, DepthFormat> {
if let &Some(ref vr) = vr {
let render_size = vr.recommended_render_target_size();
let render_size = vr.recommended_render_target_size(); let render_size = (render_size.0 * 100 / 100,
render_size.1 * 100 / 100);
let render_size = vr::Size { width: render_size.width * 220 / 100, let left = vr::create_eyebuffer(&mut window.factory, render_size.0, render_size.1)
height: render_size.height * 220 / 100 }; .expect("create left renderbuffer");
let right = vr::create_eyebuffer(&mut window.factory, render_size.0, render_size.1)
.expect("create right renderbuffer");
let trans = window.factory.create_constant_buffer(1);
let left = vr::create_eyebuffer(&mut window.factory, render_size) window.window.swap_buffers(); // To contain setup calls to Frame 0 in apitrace
.expect("create left renderbuffer");
let right = vr::create_eyebuffer(&mut window.factory, render_size)
.expect("create right renderbuffer");
let trans = window.factory.create_constant_buffer(1);
window.window.swap_buffers(); // To contain setup calls to Frame 0 in apitrace ViewRoot::<gfx_device_gl::Device, ColorFormat, DepthFormat> {
left: Some(left),
ViewRoot::<gfx_device_gl::Device, ColorFormat, DepthFormat> { right: Some(right),
left: left, trans: trans,
right: right, }
trans: trans.clone(), } else {
let trans = window.factory.create_constant_buffer(1);
ViewRoot::<gfx_device_gl::Device, ColorFormat, DepthFormat> {
left: None,
right: None,
trans: trans,
}
} }
} }
pub fn draw(&self, pub fn draw(&self,
window: &mut PistonWindow, window: &mut PistonWindow,
vr: &mut vr::VR, vr: &mut Option<vr::VR>,
scene: &::scene::Scene<gfx_device_gl::Device, gfx_device_gl::Factory>) { scene: &dyn (::scene::Scene<gfx_device_gl::Device, gfx_device_gl::Factory>))
// Get the current sensor state -> Result<(), vr::Error>
let poses = vr.poses(); {
if let &mut Some(ref mut vr) = vr {
// Get the current sensor state
let poses = vr.poses().expect("vr poses");
let mut hmd_mat = poses.poses[0].to_device.as_matrix4(); let mut hmd_mat = poses[0].device_to_absolute_tracking().as_matrix4();
hmd_mat.inverse_mut(); let inv_worked = hmd_mat.try_inverse_mut();
assert!(inv_worked, "hmd matrix invert");
for &(eye, buffers) in [(vr::Eye::Left, &self.left), for &(eye, buffers) in [(vr::Eye::Left, &self.left),
(vr::Eye::Right, &self.right)].into_iter() { (vr::Eye::Right, &self.right)].iter() {
window.encoder.clear(&buffers.target, [0.005, 0.005, 0.01, 1.0]); let target = &buffers.as_ref().expect("vr color buffer").target;
let depth = &buffers.as_ref().expect("vr depth buffer").depth;
window.encoder.clear_depth(&buffers.depth, 1.0); let proj_mat = vr.projection_matrix(eye, NEAR, FAR);
let eye_mat = vr.head_to_eye_transform(eye);
let scene_mat = scene.origin();
let viewmodel_mat = eye_mat * hmd_mat * scene_mat;
let trans = Trans { viewmodel: *viewmodel_mat.as_ref(),
matrix: *(proj_mat * viewmodel_mat).as_ref() };
window.encoder.update_constant_buffer(&self.trans, &trans);
let proj_mat = vr.projection_matrix(eye, NEAR, FAR); scene.render(&mut window.factory,
let eye_mat = vr.head_to_eye_transform(eye); &mut window.encoder,
&self.trans,
&target,
&depth);
}
} else {
// If running without VR, just draw from some default projection near the scene origin
let head_mat = na::Similarity3::new(na::Vector3::new(0.0, -1.5, 0.0),
na::Vector3::new(0.0, 0.0, 0.0),
1.0).to_homogeneous();
let proj_mat = na::geometry::Perspective3::new(1.0, 90.0, NEAR, FAR);
let scene_mat = scene.origin(); let scene_mat = scene.origin();
let trans = Trans { matrix: *(proj_mat * eye_mat * hmd_mat * scene_mat).as_ref() }; let viewmodel_mat = scene.mouselook() * head_mat * scene_mat;
let trans = Trans { viewmodel: *viewmodel_mat.as_ref(),
matrix: *(proj_mat.as_matrix() * viewmodel_mat).as_ref() };
window.encoder.update_constant_buffer(&self.trans, &trans); window.encoder.update_constant_buffer(&self.trans, &trans);
scene.render(&mut window.factory,
&mut window.encoder,
&self.trans,
&buffers.target,
&buffers.depth);
} }
// draw monitor window // draw monitor window
window.encoder.clear(&window.output_color, [0.005, 0.005, 0.01, 1.0]);
window.encoder.clear_depth(&window.output_stencil, 1.0);
scene.render(&mut window.factory, scene.render(&mut window.factory,
&mut window.encoder, &mut window.encoder,
&self.trans, &self.trans,
@@ -96,9 +118,14 @@ impl ViewRoot<gfx_device_gl::Device, ColorFormat, DepthFormat> {
&window.output_stencil); &window.output_stencil);
window.encoder.flush(&mut window.device); window.encoder.flush(&mut window.device);
vr.submit(vr::Eye::Left, &self.left.tex); if let (&mut Some(ref mut vr),
vr.submit(vr::Eye::Right, &self.right.tex); &Some(ref left),
&Some(ref right)) = (vr, &self.left, &self.right) {
vr.submit(vr::Eye::Left, &left.tex)?;
vr.submit(vr::Eye::Right, &right.tex)?;
}
window.window.swap_buffers(); window.window.swap_buffers();
window.device.cleanup(); window.device.cleanup();
Ok(())
} }
} }

175
src/vr.rs
View File

@@ -1,61 +1,84 @@
extern crate gfx;
extern crate gfx_device_gl; extern crate gfx_device_gl;
extern crate nalgebra as na;
extern crate num_traits;
extern crate openvr as vr; extern crate openvr as vr;
extern crate openvr_sys;
pub use self::vr::Eye; use std::fmt::Debug;
pub use self::vr::common::Size;
pub use self::vr::tracking::{TrackedDeviceClass, TrackedDevicePoses}; pub use self::vr::{compositor::texture::{ColorSpace, Handle, Texture},
ControllerState,
Eye,
system::Event as OvrEvent};
use gfx::{self, texture, Factory};
use gfx::memory::Typed;
use na;
use self::gfx::{tex, Factory, Typed};
use self::gfx_device_gl::Resources as GLResources; use self::gfx_device_gl::Resources as GLResources;
use self::na::Inverse;
use self::num_traits::identities::Zero;
use self::num_traits::identities::One;
use self::openvr_sys::{VREvent_Controller_t, VREvent_t};
pub struct VR { pub struct VR {
system: vr::IVRSystem, _context: vr::Context,
compositor: vr::IVRCompositor, system: vr::System,
compositor: vr::Compositor,
origin: vr::TrackingUniverseOrigin,
gfx_handles: gfx::handle::Manager<GLResources>, gfx_handles: gfx::handle::Manager<GLResources>,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
Touch { dev_idx: u32, controller: VREvent_Controller_t }, Touch { dev_idx: u32, button: u32 },
Press { dev_idx: u32, controller: VREvent_Controller_t }, Press { dev_idx: u32, button: u32 },
Unpress { dev_idx: u32, controller: VREvent_Controller_t }, Unpress { dev_idx: u32, button: u32 },
Untouch { dev_idx: u32, controller: VREvent_Controller_t }, Untouch { dev_idx: u32, button: u32 },
Other(VREvent_t), Other { dev_idx: u32, event: OvrEvent },
}
pub use self::vr::button_id;
#[derive(Debug)]
pub enum Error {
Init(Box<dyn Debug>),
Poses(Box<dyn Debug>),
Submit(Box<dyn Debug>)
}
impl From<vr::InitError> for Error {
fn from(e: vr::InitError) -> Self {
Error::Init(Box::new(e))
}
} }
impl VR { impl VR {
pub fn new() -> Result<VR, vr::Error<openvr_sys::EVRInitError>> { pub fn new() -> Result<VR, Error> {
let context = unsafe { vr::init(vr::ApplicationType::Scene)? };
Ok(VR { Ok(VR {
system: try!(vr::init()), system: context.system()?,
compositor: try!(vr::compositor()), compositor: context.compositor()?,
_context: context,
origin: vr::TrackingUniverseOrigin::Standing,
gfx_handles: gfx::handle::Manager::new(), gfx_handles: gfx::handle::Manager::new(),
}) })
} }
pub fn poses(&mut self) -> vr::tracking::TrackedDevicePoses { pub fn poses(&mut self) -> Result<vr::TrackedDevicePoses, Error> {
self.gfx_handles.clear(); self.gfx_handles.clear();
self.compositor.wait_get_poses() self.compositor.wait_get_poses().map(|p| p.render).map_err(|e| Error::Poses(Box::new(e)))
} }
pub fn submit<T>(&mut self, eye: Eye, tex: &gfx::handle::Texture<GLResources, T>) { pub fn submit<T>(&mut self, eye: Eye, tex: &gfx::handle::Texture<GLResources, T>)
-> Result<(), Error> {
let tex_id = match self.gfx_handles.ref_texture(tex.raw()) { let tex_id = match self.gfx_handles.ref_texture(tex.raw()) {
&gfx_device_gl::NewTexture::Surface(id) => id, &gfx_device_gl::NewTexture::Surface(id) => id,
_ => panic!("Not a surface") _ => panic!("Not a surface")
}; };
self.compositor.submit(eye, unsafe {
tex_id as usize, self.compositor.submit(eye,
vr::common::TextureBounds::new((0.0, 1.0), (0.0, 1.0))); &Texture { handle: Handle::OpenGLRenderBuffer(tex_id as usize),
color_space: ColorSpace::Linear },
None,
None).map_err(|e| Error::Submit(Box::new(e)))
}
} }
pub fn recommended_render_target_size(&self) -> Size { pub fn recommended_render_target_size(&self) -> (u32, u32) {
self.system.recommended_render_target_size() self.system.recommended_render_target_size()
} }
@@ -65,74 +88,52 @@ impl VR {
pub fn head_to_eye_transform(self: &Self, eye: Eye) -> na::Matrix4<f32> { pub fn head_to_eye_transform(self: &Self, eye: Eye) -> na::Matrix4<f32> {
let mut mat = self.system.eye_to_head_transform(eye).as_matrix4(); let mut mat = self.system.eye_to_head_transform(eye).as_matrix4();
assert!(mat.inverse_mut(), "inverse eye matrix"); let inv_worked = mat.try_inverse_mut();
assert!(inv_worked, "head_to_eye matrix invert");
mat mat
} }
pub fn poll_next_event(&mut self) -> Option<Event> { pub fn poll_next_event(&mut self) -> Option<Event> {
use self::openvr_sys::EVREventType as EvType; let (event, _) = self.system.poll_next_event_with_pose(self.origin)?;
unsafe {
let system = * { self.system.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
let mut event: openvr_sys::VREvent_t = ::std::mem::zeroed();
if system.PollNextEvent.unwrap()(&mut event, let dev_idx = event.tracked_device_index as u32;
::std::mem::size_of::<openvr_sys::VREvent_t>() as u32 Some(match event.event {
) == 0 { OvrEvent::ButtonTouch(controller) => Event::Touch { dev_idx: dev_idx,
return None; button: controller.button },
} OvrEvent::ButtonPress(controller) => Event::Press { dev_idx: dev_idx,
button: controller.button },
let dev_idx = event.trackedDeviceIndex; OvrEvent::ButtonUnpress(controller) => Event::Unpress { dev_idx: dev_idx,
Some(match ::std::mem::transmute(event.eventType) { button: controller.button },
EvType::EVREventType_VREvent_ButtonTouch => OvrEvent::ButtonUntouch(controller) => Event::Untouch { dev_idx: dev_idx,
Event::Touch { dev_idx: dev_idx as u32, controller: *event.data.controller() }, button: controller.button },
EvType::EVREventType_VREvent_ButtonPress => _ => Event::Other { dev_idx: dev_idx, event: event.event },
Event::Press { dev_idx: dev_idx as u32, controller: *event.data.controller() }, })
EvType::EVREventType_VREvent_ButtonUnpress =>
Event::Unpress { dev_idx: dev_idx as u32, controller: *event.data.controller() },
EvType::EVREventType_VREvent_ButtonUntouch =>
Event::Untouch { dev_idx: dev_idx as u32, controller: *event.data.controller() },
_ => Event::Other(event),
})
}
} }
pub fn get_controller_state(&self, index: u32) -> Option<openvr_sys::VRControllerState_t> { pub fn get_controller_state(&self, index: u32) -> Option<ControllerState> {
unsafe { self.system.controller_state(index)
let system = * { self.system.0 as *const openvr_sys::VR_IVRSystem_FnTable };
let mut state: openvr_sys::VRControllerState_t = ::std::mem::zeroed();
match system.GetControllerState.unwrap()(
index,
&mut state,
) {
0 => None,
_ => Some(state)
}
}
} }
} }
impl Drop for VR { pub trait AsMatrix4<N: na::Real> {
fn drop(&mut self) {
vr::shutdown()
}
}
pub trait AsMatrix4<N> {
fn as_matrix4(self) -> na::Matrix4<N>; fn as_matrix4(self) -> na::Matrix4<N>;
} }
impl<N: Copy + Zero + One> AsMatrix4<N> for [[N; 4]; 3] { impl<N: na::Real> AsMatrix4<N> for [[N; 4]; 3] {
#[inline] #[inline]
fn as_matrix4(self) -> na::Matrix4<N> { fn as_matrix4(self) -> na::Matrix4<N> {
na::Matrix4::new(self[0][0], self[0][1], self[0][2], self[0][3], na::Matrix4::new(self[0][0], self[0][1], self[0][2], self[0][3],
self[1][0], self[1][1], self[1][2], self[1][3], self[1][0], self[1][1], self[1][2], self[1][3],
self[2][0], self[2][1], self[2][2], self[2][3], self[2][0], self[2][1], self[2][2], self[2][3],
N::zero(), N::zero(), N::zero(), N::one()) N::zero(), N::zero(), N::zero(), N::one())
} }
} }
impl<N: Copy> AsMatrix4<N> for [[N; 4]; 4] {
impl<N: na::Real> AsMatrix4<N> for [[N; 4]; 4] {
#[inline] #[inline]
fn as_matrix4(self) -> na::Matrix4<N> { fn as_matrix4(self) -> na::Matrix4<N> {
na::Matrix4::new(self[0][0], self[0][1], self[0][2], self[0][3], na::Matrix4::new(self[0][0], self[0][1], self[0][2], self[0][3],
self[1][0], self[1][1], self[1][2], self[1][3], self[1][0], self[1][1], self[1][2], self[1][3],
self[2][0], self[2][1], self[2][2], self[2][3], self[2][0], self[2][1], self[2][2], self[2][3],
@@ -140,6 +141,7 @@ impl<N: Copy> AsMatrix4<N> for [[N; 4]; 4] {
} }
} }
pub struct EyeBuffer<T, D> pub struct EyeBuffer<T, D>
where T: gfx::format::RenderFormat + gfx::format::TextureFormat, where T: gfx::format::RenderFormat + gfx::format::TextureFormat,
D: gfx::format::DepthFormat + gfx::format::TextureFormat { D: gfx::format::DepthFormat + gfx::format::TextureFormat {
@@ -150,18 +152,21 @@ pub struct EyeBuffer<T, D>
} }
pub fn create_eyebuffer<T, D>(factory: &mut gfx_device_gl::Factory, pub fn create_eyebuffer<T, D>(factory: &mut gfx_device_gl::Factory,
size: Size) width: u32,
height: u32)
-> Result<EyeBuffer<T, D>, gfx::CombinedError> -> Result<EyeBuffer<T, D>, gfx::CombinedError>
where T: gfx::format::RenderFormat + gfx::format::TextureFormat, where T: gfx::format::RenderFormat + gfx::format::TextureFormat,
D: gfx::format::DepthFormat + gfx::format::TextureFormat { D: gfx::format::DepthFormat + gfx::format::TextureFormat {
let tex = try!(factory.create_texture( let tex = factory
tex::Kind::D2(size.width as tex::Size, size.height as tex::Size, tex::AaMode::Single), .create_texture(texture::Kind::D2(width as texture::Size,
1, // levels height as texture::Size,
gfx::RENDER_TARGET, // bind texture::AaMode::Single),
gfx::Usage::GpuOnly, // Usage 1, // levels
Some(<T::Channel as gfx::format::ChannelTyped>::get_channel_type()))); // hint: format::ChannelType? gfx::memory::Bind::RENDER_TARGET, // bind
let tgt = try!(factory.view_texture_as_render_target(&tex, 0, None)); gfx::memory::Usage::Data, // Usage
let depth = try!(factory.create_depth_stencil_view_only(size.width as tex::Size, Some(<T::Channel as gfx::format::ChannelTyped>::get_channel_type()))?; // hint: format::ChannelType?
size.height as tex::Size)); let tgt = factory.view_texture_as_render_target(&tex, 0, None)?;
let depth = factory.create_depth_stencil_view_only(width as texture::Size,
height as texture::Size)?;
Ok(EyeBuffer { tex: tex, target: tgt, depth: depth }) Ok(EyeBuffer { tex: tex, target: tgt, depth: depth })
} }

View File

@@ -1,21 +1,16 @@
extern crate core;
extern crate itertools;
use self::itertools::Itertools;
use tile::Tile; use tile::Tile;
use transpose::TransposableIterator; use transpose::TransposableIterator;
pub type RowIterator<'a> = Box<Iterator<Item = &'a Tile> + 'a>; pub type RowIterator<'a> = Box<dyn Iterator<Item = &'a Tile> + 'a>;
pub type BoxedMapIterator<'a> = Box<Iterator<Item = RowIterator<'a>> + 'a>; pub type BoxedMapIterator<'a> = Box<dyn Iterator<Item = RowIterator<'a>> + 'a>;
pub type ExactBoxedMapIterator<'a> = Box<ExactSizeIterator<Item = RowIterator<'a>> + 'a>; pub type ExactBoxedMapIterator<'a> = Box<dyn ExactSizeIterator<Item = RowIterator<'a>> + 'a>;
pub trait Map { pub trait Map {
fn rows<'a>(&'a self) -> BoxedMapIterator; fn rows<'a>(&'a self) -> BoxedMapIterator;
} }
pub trait HasMap { pub trait HasMap {
fn map(&self) -> &Map; fn map(&self) -> &dyn Map;
} }
const CHUNKDIM: usize = 32; const CHUNKDIM: usize = 32;
@@ -69,7 +64,7 @@ impl Map for World {
} }
impl HasMap for World { impl HasMap for World {
fn map(&self) -> &Map { fn map(&self) -> &dyn Map {
self self
} }
} }