RsInvaders
(part 1)
Powered by

> whoami michele.damico@gmail.com https://github.com/la10736 https://www.linkedin.com/in/damico/ @PhenoCoder

Disclaimer!
Non sono un Esperto di Emulatori
GRAZIE!
Davide Berra

Da dove Inizio?
- http://emulator101.com/
- Perché space invaders?
- Processore 8080 (z80 semplificato)
- Solo un Chip
- Pochi IO
- Tante Risorse In Rete
- Divertente
- Di cosa è composto un emulatore
- CPU Core
- Clock Manager
- GPU
- IO
- State
- Registri
- 8 Bit
- 16 Bit
- Flags
- Registri
- ... Altro
Memory Bus
IO
CPU
#[derive(Clone)]
pub struct Cpu<M: Mmu, O: OutputBus, I: InputBus, H: CallHook> {
state: State,
mmu: M,
run_state: CpuState,
interrupt_enabled: bool,
output: O,
input: I,
op_code_history: OpCodesHistory,
hook: H
}
#[derive(Default, Clone, Eq, PartialEq, Debug)]
pub struct State {
pub a: RegByte,
pub b: RegByte,
pub c: RegByte,
pub d: RegByte,
pub e: RegByte,
pub h: RegByte,
pub l: RegByte,
pub pc: RegAddress,
pub sp: RegAddress,
pub flags: Flags,
}
#[derive(Default, Clone, PartialEq)]
pub struct Flags(Byte);
#[derive(Copy, Clone)]
pub enum Flag {
Sign,
Zero,
AuxCarry,
Parity,
Carry,
}
- MMU
- IRQ
- Run State
- (Log & Debug)
CPU (Altro)
pub trait Mmu {
fn read_byte(&self, address: Address)
-> Result<Byte>;
fn write_byte(&mut self, address: Address, val: Byte)
-> Result<()>;
...
}
pub trait OutputBus {
fn send(&self, id: Byte, data: Byte);
}
pub trait InputBus {
fn read(&self, id: Byte) -> Byte;
}
Cpu::run()
Cpu::apply(Instruction) : il grande match
pub fn apply(&mut self, instruction: Instruction)
-> Result<Periods> {
match instruction {
Cmc => {
Ok(self.cmc())
}
Stc => {
Ok(self.stc())
}
Inr(r) => {
self.inr(r)
}
Dcr(r) => {
self.dcr(r)
}
Cma => {
self.cma()
}
Daa => {
self.daa()
}
....
}
}pub fn opcode(it: &mut impl
Iterator<Item=Result<Byte, CpuError>>)
-> Result<Instruction, CpuError>
{
Ok(match next_code(it)? {
0x00 => Nop,
0x0f => Rrc,
0x17 => Ral,
0x1a => Ldax(RegPair::DE),
0x1f => Rar,
0x20 => Rim,
0x27 => Daa,
0x22 => Shld(u16_data(it)?),
0x2a => Lhld(u16_data(it)?),
0x2f => Cma,
0x30 => Sim,
0x32 => Sta(u16_data(it)?),
0x37 => Stc,
0x3a => Lda(u16_data(it)?),
0x3c => Inr(Reg::A),
0x3f => Cmc,
0x76 => Hlt,
...
}
}disassembler
Implementazione Gioco
- Interfaccia
- Memory
- GPU
- Interfaccia IO
- Clock sync
Vediamo Sul Codice
Implementazione Gioco
- Front End?
- minifb
- Come e' Mappata la Memoria?
- Dove Vive la vram?
- Buon vecchi puntatori e un po di unsafe
- Attenzione alle move
- Come Implemento Gli IO?
- Single Thread -> RefCell<>
Reverse your mind
Rust Composite Pattern
package com.damico;
public interface Action {
void doAction();
}
package com.damico;
import java.util.ArrayList;
import java.util.List;
public class ActionComposite implements Action {
private final ArrayList<Action> actions;
public ActionComposite(List<Action> actions) {
this.actions = new ArrayList<Action>(actions);
}
@Override
public void doAction() {
for (Action a: actions) {
a.doAction();
}
}
}
package com.damico;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Action action = new ActionComposite(
Arrays.asList(new PrintHello(),
new PrintWorld())
);
action.doAction();
}
public static class PrintHello implements Action {
@Override
public void doAction() {
System.out.print("Hello");
}
}
public static class PrintWorld implements Action {
@Override
public void doAction() {
System.out.println(" World");
}
}
}

Rust Composite Pattern
Trait Object Impl
pub trait Action {
fn do_action(&self);
}
pub struct ActionComposite {
actions: Vec<Box<Action>>
}
impl ActionComposite {
pub fn new(actions: Vec<Box<Action>>) -> Self {
ActionComposite { actions }
}
}
impl Action for ActionComposite {
fn do_action(&self) {
for a in &self.actions {
a.do_action()
}
}
}
struct Hello;
struct World;
impl Action for Hello {
fn do_action(&self) { print!("Hello"); }
}
impl Action for World {
fn do_action(&self) { println!(" World"); }
}
fn main() {
let t_obj = trait_obj::ActionComposite::new(
vec![action(Hello {}), action(World {})]
);
t_obj.do_action();
}
// Utility Function to wrap a concrete impl in a
// dynamic trait object
pub fn action<'a, A: Action + 'a >(a: A) -> Box<Action + 'a> {
Box::new(a)
}

Rust Composite Pattern
Heterogeneous List Impl
pub trait Action {
fn do_action(&self);
}
impl<A: Action, B: Action> Action for (A, B) {
fn do_action(&self) {
self.0.do_action();
self.1.do_action();
}
}
pub trait Compose: Sized {
fn compose<A>(self, other: A) -> (Self, A) {
(self, other)
}
}
impl<A> Compose for A {}
struct Hello;
struct World;
impl Action for Hello {
fn do_action(&self) { print!("Hello"); }
}
impl Action for World {
fn do_action(&self) { println!(" World"); }
}
fn main() {
let tuple = Hello {}.compose(World {});
tuplee.do_action();
println!("... compose by Hello {{}} again:");
tuple.compose(Hello {}).do_action();
}
Rust Composite Pattern
Heterogeneous List Impl... compose! macro
#[derive(Debug)]
struct Hello;
#[derive(Debug)]
struct World;
impl Action for Hello {
fn do_action(&self) { print!("Hello"); }
}
impl Action for World {
fn do_action(&self) { println!(" World"); }
}
macro_rules! compose {
( $first:expr ) => ( ($first) );
( $first:expr, $second:expr, ) => ( ($first, $second) );
( $first:expr, $( $x:expr ),* ) => { { ($first, compose!( $($x),*)) } };
}
fn main() {
println!("{:?}", compose![Hello{}]);
println!("{:?}", compose![Hello{}, World {}]);
println!("{:?}", compose![Hello{}, World {}, Hello {}]);
compose![Hello{}, World {}, Hello {}, World {}].do_action();
}
Hello
(Hello, World)
(Hello, (World, Hello))
Hello World
Hello World
Output
Grazie!

> git clone https://github.com/la10736/rs8080
RsInvaders part 1
By Michele D'Amico
RsInvaders part 1
Scrivere un emulatore di 8080 in Rust e' una occasione per sviluppare tecniche di test, scoprire nuovi pattern e scoprire un po di unsafe.
- 221