73 Commits
zzz ... 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
25 changed files with 2503 additions and 367 deletions

View File

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

View File

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

View File

@@ -4,7 +4,6 @@ use vrtue::vr;
extern crate env_logger;
extern crate gfx;
#[macro_use] extern crate log;
extern crate openvr_sys;
extern crate piston_window;
use self::piston_window::{PistonWindow, Window, WindowSettings};
@@ -22,9 +21,9 @@ pub fn main() {
.build().expect("Building Window");
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");
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");
window.encoder.clear(&left.target, [1.0, 0.0, 0.0, 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.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 {
let _poses = vr.poses();
vr.submit(vr::Eye::Left, &left.tex);
vr.submit(vr::Eye::Right, &right.tex);
vr.submit(vr::Eye::Left, &left.tex).expect("submit left eye");
vr.submit(vr::Eye::Right, &right.tex).expect("submit right eye");
while let Some(ev) = vr.poll_next_event() {
match ev {
vr::Event::Press { dev_idx, controller } => {
println!("Press event on #{}: {:?}", dev_idx, controller);
vr::Event::Press { dev_idx, button } => {
println!("Press event on #{}: {}", dev_idx, button);
},
vr::Event::Unpress { dev_idx, controller } => {
println!("Unpress event on #{}: {:?}", dev_idx, controller);
vr::Event::Unpress { dev_idx, button } => {
println!("Unpress event on #{}: {}", dev_idx, button);
},
vr::Event::Touch { dev_idx, controller } => {
if controller.button == openvr_sys::EVRButtonId_k_EButton_SteamVR_Touchpad as u32 {
vr::Event::Touch { dev_idx, button } => {
if button == vr::button_id::STEAM_VR_TOUCHPAD as u32 {
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 } => {
if controller.button == openvr_sys::EVRButtonId_k_EButton_SteamVR_Touchpad as u32 {
vr::Event::Untouch { dev_idx, button } => {
if button == vr::button_id::STEAM_VR_TOUCHPAD as u32 {
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 => {
@@ -74,7 +73,7 @@ pub fn main() {
for (pad, old) in pads.iter_mut() {
if let Some(state) = vr.get_controller_state(*pad) {
if let Some(old_state) = *old {
if state.unPacketNum == old_state.unPacketNum {
if state.packet_num == old_state.packet_num {
continue;
}
}

View File

@@ -11,7 +11,7 @@ use itertools::Itertools;
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
{
assert_eq!(std::mem::size_of::<M>(), mmap.len());
@@ -40,6 +40,7 @@ fn main() {
print_rows(world.map().rows());
}
#[allow(unstable_name_collisions)]
fn print_rows<'a, T: ?Sized, U: ?Sized>(rows: Box<T>)
where T: Iterator<Item = Box<U>> + '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

@@ -1,6 +1,6 @@
extern crate vrtue;
use vrtue::{context, scenes, view, vr};
use vrtue::engine::{Event, Scene};
use vrtue::{scenes, view, vr};
use vrtue::scene::{Event, Scene};
extern crate env_logger;
extern crate gfx_device_gl;
@@ -8,10 +8,12 @@ extern crate gfx_device_gl;
extern crate piston;
extern crate piston_window;
use self::piston::input::{Button, Input, Key};
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() {
env_logger::init().expect("env logger");
let mut vr = match env::var("NO_VR") {
@@ -20,7 +22,7 @@ pub fn main() {
};
let mut window: PistonWindow =
WindowSettings::new("Hello, Britannia!", [1024; 2])
WindowSettings::new("Hello, Britannia!", WINDOW_SIZE)
.exit_on_esc(true)
.vsync(vr.is_none()) // Let VR throttle framerate, if available
.build().expect("Building Window");
@@ -32,20 +34,17 @@ pub fn main() {
let view = view::ViewRoot::<gfx_device_gl::Device, view::ColorFormat, view::DepthFormat>
::create_view(&mut window, &mut vr);
let mut game = context::VrtueRootContext::new(&mut window.device,
&mut window.factory,
&mut aux_command);
'main:
//while let Some(_) = window.next() {
loop {
scene.update(&mut game, &mut vr, &mut window.encoder);
view.draw(&mut game, &mut window, &mut vr, &scene);
scene.update(&mut vr, &mut window.encoder);
view.draw(&mut window, &mut vr, &scene).expect("main draw");
// handle window events
while let Some(ev) = window.poll_event() {
match ev {
Input::Press(Button::Keyboard(Key::Space)) |
Input::Press(Button::Keyboard(Key::Escape)) => break 'main,
Input::Button(ButtonArgs { button: Button::Keyboard(Key::Space), .. }) |
Input::Button(ButtonArgs { button: Button::Keyboard(Key::Escape), .. }) => break 'main,
_ => debug!("\t{:?}", ev)
}

View File

@@ -1,77 +0,0 @@
use ega;
use engine;
use std::io::Read;
use std::marker::PhantomData;
use std::path::Path;
use gfx::{self, CommandBuffer};
use gfx::memory::Typed;
use gfx::texture;
const TILEDIM: u16 = 16;
pub struct VrtueRootContext<D, F, T>
where D: gfx::Device,
F: gfx::Factory<D::Resources>,
T: gfx::format::TextureFormat {
tiles: gfx::handle::ShaderResourceView<D::Resources, T::View>,
_factory: PhantomData<F>
}
impl<D, F, T> engine::GameContext for VrtueRootContext<D, F, T>
where D: gfx::Device,
F: gfx::Factory<D::Resources>,
T: gfx::format::TextureFormat {}
impl<D, F, T> VrtueRootContext<D, F, T>
where D: gfx::Device,
F: gfx::Factory<D::Resources>,
T: gfx::format::TextureFormat,
T::View: Clone {
pub fn new(device: &mut D,
factory: &mut F,
command: &mut <D as gfx::Device>::CommandBuffer) -> VrtueRootContext<D, F, T> {
VrtueRootContext {
tiles: Self::make_tiles(device, factory, command),
_factory: PhantomData
}
}
pub fn tiles(&self) -> gfx::handle::ShaderResourceView<D::Resources, T::View> {
self.tiles.clone()
}
fn make_tiles(device: &mut D,
factory: &mut F,
command: &mut <D as gfx::Device>::CommandBuffer)
-> gfx::handle::ShaderResourceView<D::Resources, T::View> {
let filename = "data/SHAPES.EGA";
let mut file = ::std::fs::File::open(Path::new(filename))
.expect(&format!("failed opening tiles file: {}", filename));
let mut ega_bytes = Vec::new();
file.read_to_end(&mut ega_bytes).expect("Read tiles file");
let ega_page = ega::decode(&ega_bytes, ega::Compression::Uncompressed, ega::Tiling::Tiled(TILEDIM));
let mipmap = ega_page.mipmap(2);
let tex = factory.create_texture_immutable_u8::<T>(texture::Kind::D2Array(mipmap.dim as u16,
mipmap.dim as u16,
mipmap.len as u16,
texture::AaMode::Single),
&mipmap.slices())
.expect("create tile texture");
{
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());
command.generate_mipmap(*view);
device.submit(command, &access);
}
tex.1
}
}

View File

@@ -124,9 +124,9 @@ fn decode_uncompressed(buf: &[u8]) -> Vec<u8> {
buf.iter()
.flat_map(|tile_byte| {
EGA_PALETTE[(tile_byte >> 4u8 & 0xF) as usize]
.into_iter()
.iter()
.chain(EGA_PALETTE[(tile_byte & 0xF) as usize]
.into_iter())
.iter())
})
.map(|x| *x)
.collect()
@@ -221,11 +221,11 @@ impl U4Lzw {
}
fn hash_primary(root: u8, code: u16) -> usize {
((root as (u16) << 4) ^ code) as 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 base = ((((root as u16) << 1) + code) | 0x800) as u32;
let squared = base * base;
((squared & 0x0003ffc0) >> 6) as usize
}

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,30 +0,0 @@
use gfx;
use na;
use piston;
use view;
use vr;
pub trait GameContext {}
pub trait Scene<G: GameContext,
D: gfx::Device,
F: gfx::Factory<D::Resources>> {
fn event(&mut self, event: Event);
fn update(&mut self,
game: &mut G,
vr: &mut Option<vr::VR>, // TODO: abstract this out
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>);
fn render(&self,
game: &mut G,
trans: &gfx::handle::Buffer<D::Resources, view::Trans>,
target: &gfx::handle::RenderTargetView<D::Resources, view::ColorFormat>,
depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>,
factory: &mut F,
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>);
fn origin(&self) -> na::Matrix4<f32>;
}
pub enum Event {
Vr(vr::Event),
Piston(piston::input::Input),
}

View File

@@ -1,15 +1,12 @@
#![feature(conservative_impl_trait)]
#[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 num_traits;
extern crate piston;
pub mod arena;
pub mod context;
pub mod ega;
pub mod engine;
pub mod emu;
pub mod scene;
pub mod scenes;
pub mod tile;
pub mod town;

View File

@@ -18,6 +18,7 @@ pub trait Scene<D: gfx::Device,
depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>);
fn origin(&self) -> na::Matrix4<f32>;
fn mouselook(&self) -> na::Matrix4<f32>;
}
pub enum Event {

View File

@@ -1,5 +1,5 @@
use context::VrtueRootContext;
use engine;
use scene;
use tile;
use view;
use vr;
use world as model;
@@ -13,9 +13,8 @@ use std::time::SystemTime;
use gfx::{self, texture};
use gfx::traits::FactoryExt;
use na::{self, ToHomogeneous};
use num_traits::identities::One;
use piston::input::{Button, Input, Key};
use na;
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;
@@ -54,7 +53,7 @@ gfx_defines! {
trans: gfx::ConstantBuffer<::view::Trans> = "b_trans",
constants: gfx::ConstantBuffer<Constants> = "b_constants",
locals: gfx::ConstantBuffer<Locals> = "b_locals",
tiles: gfx::TextureSampler<[f32; 4]> = "t_tiles",
atlas: gfx::TextureSampler<[f32; 4]> = "t_tiles",
pixcolor: gfx::RenderTarget<::view::ColorFormat> = "pixcolor",
depth: gfx::DepthTarget<::view::DepthFormat> = gfx::preset::depth::LESS_EQUAL_WRITE,
}
@@ -125,6 +124,8 @@ pub struct WorldScene<D: gfx::Device,
constants_buffer: gfx::handle::Buffer<D::Resources, Constants>,
constants_dirty: bool,
locals: gfx::handle::Buffer<D::Resources, Locals>,
atlas: gfx::handle::ShaderResourceView<D::Resources,
<view::ColorFormat as gfx::format::Formatted>::View>,
sampler: gfx::handle::Sampler<D::Resources>,
f: PhantomData<F>,
@@ -133,18 +134,18 @@ pub struct WorldScene<D: gfx::Device,
start_time: SystemTime,
treadmills: (f32, f32),
pads: BTreeMap<u32, (TrackMode, Option<vr::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> {
pub fn new(_device: &mut D,
impl<D: gfx::Device, F: gfx::Factory<D::Resources>> WorldScene<D, F> {
pub fn new(device: &mut D,
factory: &mut F,
_aux_command: &mut <D as gfx::Device>::CommandBuffer) -> WorldScene<D, F> {
aux_command: &mut <D as gfx::Device>::CommandBuffer) -> WorldScene<D, F> {
let worldmap = get_data_model();
let (model, model_idx) = get_model(&worldmap);
let (vertex_buffer, slice) =
@@ -155,14 +156,15 @@ impl<D: gfx::Device,
FRAGMENT_SHADER_SRC,
pipe::new())
.expect("create pipeline"),
camera: na::Matrix4::one(),
camera: na::Matrix4::identity(),
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),
sampler: factory.create_sampler(texture::SamplerInfo::new(texture::FilterMethod::Jrd,
atlas: tile::get_tiles::<_, _, view::ColorFormat>(device, factory, aux_command),
sampler: factory.create_sampler(texture::SamplerInfo::new(texture::FilterMethod::Trilinear, //::Jrd
texture::WrapMode::Tile)),
f: PhantomData,
@@ -170,6 +172,7 @@ impl<D: gfx::Device,
slice: slice,
start_time: SystemTime::now(),
treadmills: (0.0, 0.0),
mouselook: na::Matrix4::identity(),
pads: BTreeMap::new(),
_worldmap: worldmap,
@@ -195,12 +198,10 @@ const ANIMDATA: [u32; 4] =
0];
impl<D: gfx::Device,
F: gfx::Factory<D::Resources>>
engine::Scene<VrtueRootContext<D, F, view::ColorFormat>, D, F>
for WorldScene<D, F> {
F: gfx::Factory<D::Resources>> scene::Scene<D, F> for WorldScene<D, F> {
fn event(&mut self, event: engine::Event) {
use engine::Event::*;
fn event(&mut self, event: scene::Event) {
use scene::Event::*;
use vr::Event::*;
match event {
// treadmill / camera movement registration
@@ -217,66 +218,80 @@ impl<D: gfx::Device,
self.pads.remove(&dev_idx);
},
// treadmill / camera reset
Piston(Input::Press(Button::Keyboard(Key::Backspace))) => {
self.treadmills = (0.0, 0.0);
},
Piston(Input::Press(Button::Keyboard(Key::D0))) => {
self.camera = na::Matrix4::one();
},
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
Piston(Input::Press(Button::Keyboard(Key::Up))) => {
self.lat = self.lat.wrapping_sub(1);
},
Piston(Input::Press(Button::Keyboard(Key::Down))) => {
self.lat = self.lat.wrapping_add(1);
},
Piston(Input::Press(Button::Keyboard(Key::Left))) => {
self.lng = self.lng.wrapping_sub(1);
},
Piston(Input::Press(Button::Keyboard(Key::Right))) => {
self.lng = self.lng.wrapping_add(1);
},
// 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
Piston(Input::Press(Button::Keyboard(Key::Q))) => {
self.constants = Constants { r1: R1 / 2.0, r2: R2 / 2.0, r3: R3 / 2.0, ..self.constants };
self.constants_dirty = true;
},
Piston(Input::Press(Button::Keyboard(Key::D1))) => {
self.constants = Constants { r1: R1, r2: R2, r3: R3, ..self.constants };
self.constants_dirty = true;
},
Piston(Input::Press(Button::Keyboard(Key::D2))) => {
self.constants = Constants { r1: R1 * 2.0, r2: R2 * 2.0, r3: R3 * 2.0, ..self.constants };
self.constants_dirty = true;
},
Piston(Input::Press(Button::Keyboard(Key::D3))) => {
self.constants = Constants { r1: R1 * 4.0, r2: R2 * 4.0, r3: R3 * 4.0, ..self.constants };
self.constants_dirty = true;
},
Piston(Input::Press(Button::Keyboard(Key::D4))) => {
self.constants = Constants { r1: R1 * 16.0, r2: R2 * 16.0, r3: R3 * 16.0, ..self.constants };
self.constants_dirty = true;
},
// 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;
},
Piston(Input::Press(Button::Keyboard(Key::H))) => {
self.constants = Constants { haze: self.constants.haze * 2.0f32.sqrt().sqrt(), ..self.constants };
println!("haze: {}", self.constants.haze);
self.constants_dirty = true;
},
Piston(Input::Press(Button::Keyboard(Key::N))) => {
self.constants = Constants { haze: self.constants.haze / 2.0f32.sqrt().sqrt(), ..self.constants };
println!("haze: {}", self.constants.haze);
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,
_game: &mut VrtueRootContext<D, F, view::ColorFormat>,
vr: &mut Option<vr::VR>,
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>) {
const NANOS_PER_MILLI: u32 = 1_000_000;
@@ -292,31 +307,31 @@ impl<D: gfx::Device,
TrackMode::Touch => {
const THRESHOLD: f32 = 0.005;
const SCALE: f32 = 32.0;
let xdiff = state.rAxis[0].x - old_state.rAxis[0].x;
let ydiff = state.rAxis[0].y - old_state.rAxis[0].y;
let xdiff = state.axis[0].x - old_state.axis[0].x;
let ydiff = state.axis[0].y - old_state.axis[0].y;
if xdiff.abs() > THRESHOLD { self.treadmills.0 += SCALE * xdiff; }
if ydiff.abs() > THRESHOLD { self.treadmills.1 += SCALE * ydiff; }
},
TrackMode::Press => {
let rot = na::Vector3::new(0.0, 0.0, 0.0);
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),
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),
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),
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),
rot, 1.0).to_homogeneous() * self.camera;
}
},
}
if state.unPacketNum == old_state.unPacketNum {
if state.packet_num == old_state.packet_num {
continue;
}
}
@@ -335,12 +350,11 @@ impl<D: gfx::Device,
}
fn render(&self,
game: &mut VrtueRootContext<D, F, view::ColorFormat>,
_factory: &mut F,
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>,
trans: &gfx::handle::Buffer<D::Resources, view::Trans>,
target: &gfx::handle::RenderTargetView<D::Resources, view::ColorFormat>,
depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>,
_factory: &mut F,
encoder: &mut gfx::Encoder<D::Resources, D::CommandBuffer>) {
depth: &gfx::handle::DepthStencilView<D::Resources, view::DepthFormat>) {
encoder.clear(&target, SKY_COLOR);
encoder.clear_depth(&depth, 1.0);
@@ -349,7 +363,7 @@ impl<D: gfx::Device,
trans: trans.clone(),
constants: self.constants_buffer.clone(),
locals: self.locals.clone(),
tiles: (game.tiles(), self.sampler.clone()),
atlas: (self.atlas.clone(), self.sampler.clone()),
pixcolor: target.clone(),
depth: depth.clone(),
};
@@ -361,12 +375,16 @@ impl<D: gfx::Device,
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) * na::Vector3::new(r2 / r3, 1.0, 1.0);
self.camera * na::Isometry3::look_at_rh(eye.as_point(),
looktgt.as_point(),
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
}
}
fn get_data_model() -> model::World {

View File

@@ -1,9 +1,56 @@
use ega;
use ::std;
use std::io::Read;
use std::path::Path;
use gfx::{self, texture, CommandBuffer};
use gfx::memory::Typed;
const TILEDIM: u16 = 16;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Tile {
pub val: u8,
}
pub fn get_tiles<D, F, T>(device: &mut D,
factory: &mut F,
command: &mut <D as gfx::Device>::CommandBuffer)
-> gfx::handle::ShaderResourceView<D::Resources, T::View>
where D: gfx::Device,
F: gfx::Factory<D::Resources>,
T: gfx::format::TextureFormat {
let filename = "data/SHAPES.EGA";
let mut file = std::fs::File::open(Path::new(filename))
.expect(&format!("failed opening tiles file: {}", filename));
let mut ega_bytes = Vec::new();
file.read_to_end(&mut ega_bytes).expect("Read tiles file");
let ega_page = ega::decode(&ega_bytes, ega::Compression::Uncompressed, ega::Tiling::Tiled(TILEDIM));
let mipmap = ega_page.mipmap(2);
let tex = factory.create_texture_immutable_u8::<T>(texture::Kind::D2Array(mipmap.dim as u16,
mipmap.dim as u16,
mipmap.len as u16,
texture::AaMode::Single),
texture::Mipmap::Provided,
&mipmap.slices())
.expect("create tile texture");
{
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());
command.generate_mipmap(*view);
device.submit(command, &access).expect("generate tile mipmaps");
}
tex.1
}
impl Tile {
pub fn as_char(&self) -> char {
@@ -19,7 +66,7 @@ impl Tile {
8 => '⨇', // mountain '△'
9 => '☗', // dungeon
10 => '⍟', // city
11 | 13...15 => '⛫', // castle
11 | 13..=15 => '⛫', // castle
12 => '❖', // village
22 => '⎔', // tile floor
23 => '⟗', // bridge
@@ -57,7 +104,7 @@ impl Tile {
76 => '⌘', // lava
// 79 => '💥', // attack flash
// 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
123 => '⊐', // right ''
124 => '⊏', // left '⊨'

View File

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

View File

@@ -1,13 +1,13 @@
use engine::{GameContext, Scene};
use vr::{self, AsMatrix4, VR};
extern crate gfx_device_gl;
extern crate piston_window;
extern crate openvr_sys;
use gfx;
use gfx::Device;
use gfx::traits::FactoryExt;
use na::{self, Inverse, ToHomogeneous};
use na;
use self::piston_window::{PistonWindow, Window};
pub type ColorFormat = gfx::format::Srgba8;
@@ -39,12 +39,12 @@ impl 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::Size { width: render_size.width * 100 / 100,
height: render_size.height * 100 / 100 };
let render_size = (render_size.0 * 100 / 100,
render_size.1 * 100 / 100);
let left = vr::create_eyebuffer(&mut window.factory, render_size)
let left = vr::create_eyebuffer(&mut window.factory, render_size.0, render_size.1)
.expect("create left renderbuffer");
let right = vr::create_eyebuffer(&mut window.factory, render_size)
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);
@@ -65,20 +65,22 @@ impl ViewRoot<gfx_device_gl::Device, ColorFormat, DepthFormat> {
}
}
pub fn draw<G: GameContext>(&self,
game: &mut G,
window: &mut PistonWindow,
vr: &mut Option<vr::VR>,
scene: &Scene<G, gfx_device_gl::Device, gfx_device_gl::Factory>) {
pub fn draw(&self,
window: &mut PistonWindow,
vr: &mut Option<vr::VR>,
scene: &dyn (::scene::Scene<gfx_device_gl::Device, gfx_device_gl::Factory>))
-> Result<(), vr::Error>
{
if let &mut Some(ref mut vr) = vr {
// Get the current sensor state
let poses = vr.poses();
let poses = vr.poses().expect("vr poses");
let mut hmd_mat = poses.poses[0].to_device.as_matrix4();
hmd_mat.inverse_mut();
let mut hmd_mat = poses[0].device_to_absolute_tracking().as_matrix4();
let inv_worked = hmd_mat.try_inverse_mut();
assert!(inv_worked, "hmd matrix invert");
for &(eye, buffers) in [(vr::Eye::Left, &self.left),
(vr::Eye::Right, &self.right)].into_iter() {
(vr::Eye::Right, &self.right)].iter() {
let target = &buffers.as_ref().expect("vr color buffer").target;
let depth = &buffers.as_ref().expect("vr depth buffer").depth;
@@ -90,41 +92,40 @@ impl ViewRoot<gfx_device_gl::Device, ColorFormat, DepthFormat> {
matrix: *(proj_mat * viewmodel_mat).as_ref() };
window.encoder.update_constant_buffer(&self.trans, &trans);
scene.render(game,
scene.render(&mut window.factory,
&mut window.encoder,
&self.trans,
&target,
&depth,
&mut window.factory,
&mut window.encoder);
&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::PerspectiveMatrix3::new(1.0, 90.0, NEAR, FAR).to_matrix();
let proj_mat = na::geometry::Perspective3::new(1.0, 90.0, NEAR, FAR);
let scene_mat = scene.origin();
let viewmodel_mat = head_mat * scene_mat;
let viewmodel_mat = scene.mouselook() * head_mat * scene_mat;
let trans = Trans { viewmodel: *viewmodel_mat.as_ref(),
matrix: *(proj_mat * viewmodel_mat).as_ref() };
matrix: *(proj_mat.as_matrix() * viewmodel_mat).as_ref() };
window.encoder.update_constant_buffer(&self.trans, &trans);
}
// draw monitor window
scene.render(game,
scene.render(&mut window.factory,
&mut window.encoder,
&self.trans,
&window.output_color,
&window.output_stencil,
&mut window.factory,
&mut window.encoder);
&window.output_stencil);
window.encoder.flush(&mut window.device);
if let (&mut Some(ref mut vr),
&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);
vr.submit(vr::Eye::Left, &left.tex)?;
vr.submit(vr::Eye::Right, &right.tex)?;
}
window.window.swap_buffers();
window.device.cleanup();
Ok(())
}
}

160
src/vr.rs
View File

@@ -1,60 +1,84 @@
extern crate gfx_device_gl;
extern crate openvr as vr;
extern crate openvr_sys;
pub use self::vr::Eye;
pub use self::vr::common::Size;
pub use self::openvr_sys::VRControllerState_t;
use std::fmt::Debug;
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::{self, Inverse};
use num_traits::identities::Zero;
use num_traits::identities::One;
use na;
use self::gfx_device_gl::Resources as GLResources;
use self::openvr_sys::{VREvent_Controller_t, VREvent_t};
pub struct VR {
system: vr::IVRSystem,
compositor: vr::IVRCompositor,
_context: vr::Context,
system: vr::System,
compositor: vr::Compositor,
origin: vr::TrackingUniverseOrigin,
gfx_handles: gfx::handle::Manager<GLResources>,
}
#[derive(Debug)]
pub enum Event {
Touch { dev_idx: u32, controller: VREvent_Controller_t },
Press { dev_idx: u32, controller: VREvent_Controller_t },
Unpress { dev_idx: u32, controller: VREvent_Controller_t },
Untouch { dev_idx: u32, controller: VREvent_Controller_t },
Other(VREvent_t),
Touch { dev_idx: u32, button: u32 },
Press { dev_idx: u32, button: u32 },
Unpress { dev_idx: u32, button: u32 },
Untouch { dev_idx: u32, button: u32 },
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 {
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 {
system: vr::init()?,
compositor: vr::compositor()?,
system: context.system()?,
compositor: context.compositor()?,
_context: context,
origin: vr::TrackingUniverseOrigin::Standing,
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.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()) {
&gfx_device_gl::NewTexture::Surface(id) => id,
_ => panic!("Not a surface")
};
self.compositor.submit(eye,
tex_id as usize,
vr::common::TextureBounds::new((0.0, 1.0), (0.0, 1.0)));
unsafe {
self.compositor.submit(eye,
&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()
}
@@ -64,74 +88,52 @@ impl VR {
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();
assert!(mat.inverse_mut(), "inverse eye matrix");
let inv_worked = mat.try_inverse_mut();
assert!(inv_worked, "head_to_eye matrix invert");
mat
}
pub fn poll_next_event(&mut self) -> Option<Event> {
use self::openvr_sys::EVREventType as EvType;
unsafe {
let system = * { self.system.0 as *mut openvr_sys::VR_IVRSystem_FnTable };
let mut event: openvr_sys::VREvent_t = ::std::mem::zeroed();
let (event, _) = self.system.poll_next_event_with_pose(self.origin)?;
if system.PollNextEvent.unwrap()(&mut event,
::std::mem::size_of::<openvr_sys::VREvent_t>() as u32
) == 0 {
return None;
}
let dev_idx = event.trackedDeviceIndex;
Some(match ::std::mem::transmute(event.eventType) {
EvType::EVREventType_VREvent_ButtonTouch =>
Event::Touch { dev_idx: dev_idx as u32, controller: *event.data.controller() },
EvType::EVREventType_VREvent_ButtonPress =>
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),
})
}
let dev_idx = event.tracked_device_index as u32;
Some(match event.event {
OvrEvent::ButtonTouch(controller) => Event::Touch { dev_idx: dev_idx,
button: controller.button },
OvrEvent::ButtonPress(controller) => Event::Press { dev_idx: dev_idx,
button: controller.button },
OvrEvent::ButtonUnpress(controller) => Event::Unpress { dev_idx: dev_idx,
button: controller.button },
OvrEvent::ButtonUntouch(controller) => Event::Untouch { dev_idx: dev_idx,
button: controller.button },
_ => Event::Other { dev_idx: dev_idx, event: event.event },
})
}
pub fn get_controller_state(&self, index: u32) -> Option<openvr_sys::VRControllerState_t> {
unsafe {
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)
}
}
pub fn get_controller_state(&self, index: u32) -> Option<ControllerState> {
self.system.controller_state(index)
}
}
impl Drop for VR {
fn drop(&mut self) {
vr::shutdown()
}
}
pub trait AsMatrix4<N> {
pub trait AsMatrix4<N: na::Real> {
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]
fn as_matrix4(self) -> na::Matrix4<N> {
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[2][0], self[2][1], self[2][2], self[2][3],
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]
fn as_matrix4(self) -> na::Matrix4<N> {
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[2][0], self[2][1], self[2][2], self[2][3],
@@ -139,6 +141,7 @@ impl<N: Copy> AsMatrix4<N> for [[N; 4]; 4] {
}
}
pub struct EyeBuffer<T, D>
where T: gfx::format::RenderFormat + gfx::format::TextureFormat,
D: gfx::format::DepthFormat + gfx::format::TextureFormat {
@@ -149,20 +152,21 @@ pub struct EyeBuffer<T, D>
}
pub fn create_eyebuffer<T, D>(factory: &mut gfx_device_gl::Factory,
size: Size)
width: u32,
height: u32)
-> Result<EyeBuffer<T, D>, gfx::CombinedError>
where T: gfx::format::RenderFormat + gfx::format::TextureFormat,
D: gfx::format::DepthFormat + gfx::format::TextureFormat {
let tex = factory
.create_texture(texture::Kind::D2(size.width as texture::Size,
size.height as texture::Size,
.create_texture(texture::Kind::D2(width as texture::Size,
height as texture::Size,
texture::AaMode::Single),
1, // levels
gfx::RENDER_TARGET, // bind
gfx::memory::Usage::GpuOnly, // Usage
gfx::memory::Bind::RENDER_TARGET, // bind
gfx::memory::Usage::Data, // Usage
Some(<T::Channel as gfx::format::ChannelTyped>::get_channel_type()))?; // hint: format::ChannelType?
let tgt = factory.view_texture_as_render_target(&tex, 0, None)?;
let depth = factory.create_depth_stencil_view_only(size.width as texture::Size,
size.height as texture::Size)?;
let depth = factory.create_depth_stencil_view_only(width as texture::Size,
height as texture::Size)?;
Ok(EyeBuffer { tex: tex, target: tgt, depth: depth })
}

View File

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