initial commit

This commit is contained in:
Daniel Bulant 2023-01-25 22:56:36 +01:00
commit f84466c03a
9 changed files with 444 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/target
frames
output.ch8
badapple.webm

8
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

11
.idea/chip8badapple.iml Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/misc.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/chip8badapple.iml" filepath="$PROJECT_DIR$/.idea/chip8badapple.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

258
Cargo.lock generated Normal file
View file

@ -0,0 +1,258 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bindgen"
version = "0.59.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chip8badapple"
version = "0.1.0"
dependencies = [
"ffmpeg-next",
]
[[package]]
name = "clang-sys"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "ffmpeg-next"
version = "5.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a80971eee67be0079a1c8890bde68226fe9bd0441740fd6ddd0cee131486b321"
dependencies = [
"bitflags",
"ffmpeg-sys-next",
"libc",
]
[[package]]
name = "ffmpeg-sys-next"
version = "5.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d780b36e092254367e2f1f21191992735c8e23f31a5a5a8678db3a79f775021f"
dependencies = [
"bindgen",
"cc",
"libc",
"num_cpus",
"pkg-config",
"vcpkg",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pkg-config"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "proc-macro2"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "shlex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "chip8badapple"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ffmpeg-next="5.1.1"

134
src/main.rs Normal file
View file

@ -0,0 +1,134 @@
use ffmpeg_next::format::{input, Pixel};
use ffmpeg_next::media::Type;
use ffmpeg_next::software::scaling::{flag::Flags};
use ffmpeg_next::util::frame::video::Video;
use std::env;
use std::fs::File;
use std::io::{Seek, SeekFrom, Write};
const WIDTH: u8 = 64;
const HEIGHT: u8 = 32;
fn main() -> Result<(), ffmpeg_next::Error> {
ffmpeg_next::init().unwrap();
if let Ok(mut ictx) = input(&env::args().nth(1).expect("Cannot open file.")) {
let input = ictx
.streams()
.best(Type::Video)
.ok_or(ffmpeg_next::Error::StreamNotFound)?;
let video_stream_index = input.index();
let mut output_file = File::create("output.ch8").unwrap();
let context_decoder = ffmpeg_next::codec::context::Context::from_parameters(input.parameters())?;
let mut decoder = context_decoder.decoder().video()?;
// clion doesn't like use for this...
let mut scaler = ffmpeg_next::software::scaling::context::Context::get(
// input
decoder.format(),
decoder.width(),
decoder.height(),
// output
Pixel::MonoBlack,
WIDTH as u32,
HEIGHT as u32,
Flags::BILINEAR
)?;
let mut frame_index = 0;
// the below is the main program loop. It's simple, so it's hardcoded.
// V0=x, V1=y, V10=VA=frame count, V11=VB=delay timer
// it uses sprite bank to draw sprites on the screen
// because of frame counting, maximum length is roughly 36.4 minutes (for 30fps)
let base_rom = [
0xA3,0x00, // 0x200 I = 0x300
0x6C,0x01, // 0x202 VC = 1
// main print loop
0x00,0xE0, // 0x204 clear screen,
0x61,0x00, // 0x206 V1 = 0
0x60,0x00, // 0x208 V0 = 0
0xD0,0x1F, // 0x20A draw sprite at V0, V1 with height 15
0x70,0x08, // 0x20C V0 += 8
0x62, 120, // 0x20E V2 = 120
0xF2,0x1E, // 0x210 I += V2
0x30, 32, // 0x212 if V0 == 32 skip
0x12,0x0A, // 0x214 goto 0x20A
0x71, 15, // 0x216 V1 += 15
0x31, 45, // 0x218 if V1 == 45 skip
0x12,0x08, // 0x21A goto 0x208
// wait for next frame. Delay timer ticks every 1/60th of a second, video is 30fps, hence 2 ticks
0x6B,0x02, // 0x21C VB = 2
0xFB,0x15, // 0x21E delay timer = VB
0xFB,0x07, // 0x220 VB = delay timer
0x4B,0x00, // 0x222 if VB != 0 skip
0x12,0x22, // 0x224 goto 0x222
0x12,0x04, // 0x226 goto 0x204
// unreachable, for future use
0x12,0x28, // 0x228 goto 0x228 (self jump, our emulator exits)
0x00,0x00 // crash if we get here
];
output_file.write_all(&base_rom).unwrap();
output_file.seek(std::io::SeekFrom::Start(0x300)).unwrap();
let mut receive_and_process_decoded_frames =
|decoder: &mut ffmpeg_next::decoder::Video| -> Result<(), ffmpeg_next::Error> {
let mut decoded = Video::empty();
while decoder.receive_frame(&mut decoded).is_ok() {
let mut bw_frame = Video::empty();
scaler.run(&decoded, &mut bw_frame)?;
let data = bw_frame.data(0);
// dbg!(data);
let mut sprite_bank: [[u8; 8]; 3] = [
[
0; 8 // there are 8 sprites in 64 pixel wide screen
]; 3 // 3 parts 15 pixels each to fill 32 pixel tall screen
];
for i in 0..8 {
for j in 0..3 {
sprite_bank[j][i] = data[i * 3 + j];
}
}
let mut file = File::create(format!("frames/f{}.pbm", frame_index)).unwrap();
file.write_all(format!("P4\n{} {}\n", bw_frame.width(), bw_frame.height()).as_bytes()).unwrap();
file.write_all(data).unwrap();
for part in 0..3 {
for sprite in 0..8 {
let mut sprite_byte = 0;
for pixel in 0..8 {
if sprite_bank[part][sprite] & 1 << (7 - pixel) != 0 {
sprite_byte |= 1 << (7 - pixel);
}
}
output_file.write_all(&[sprite_byte]).unwrap();
}
}
frame_index += 1;
}
Ok(())
};
for (stream, packet) in ictx.packets() {
if stream.index() == video_stream_index {
decoder.send_packet(&packet)?;
receive_and_process_decoded_frames(&mut decoder)?;
}
}
decoder.send_eof()?;
receive_and_process_decoded_frames(&mut decoder)?;
}
Ok(())
}