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
  • ... 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