From c7dceea3c80cdfae7d749c0c5406f90c513d6d8e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 28 Apr 2024 20:47:30 -0600 Subject: [PATCH 01/89] upload progress. --- .github/workflows/ci-builds.yml | 20 +- .vscode/settings.json | 10 +- CHANGELOG.md | 3 + Cargo.toml | 21 +- README.md | 16 +- {examples => backup/examples}/asm_scratch.rs | 0 {examples => backup/examples}/foo.txt | 0 {examples => backup/examples}/game.rs | 0 .../examples}/game_vblank_draw.rs | 0 {examples => backup/examples}/hello.rs | 0 .../examples}/mode3_pong_example_game.rs | 0 .../examples}/mode3_realtime_example.rs | 0 {examples => backup/examples}/video3_test.rs | 0 {examples => backup/examples}/video4_test.rs | 0 {src => backup/src}/asm_runtime.rs | 0 {src => backup/src}/bios.rs | 0 .../src}/builtin_art/cga_8x8_thick.rs | 0 {src => backup/src}/builtin_art/mod.rs | 0 {src => backup/src}/dma.rs | 0 {src => backup/src}/fixed.rs | 0 {src => backup/src}/gba_cell.rs | 0 {src => backup/src}/interrupts.rs | 0 {src => backup/src}/keys.rs | 0 backup/src/lib.rs | 159 ++++++++++ {src => backup/src}/macros.rs | 0 {src => backup/src}/mem_fns.rs | 0 {src => backup/src}/mgba.rs | 0 backup/src/mmio.rs | 277 ++++++++++++++++ {src => backup/src}/prelude.rs | 0 {src => backup/src}/random.rs | 0 {src => backup/src}/sound.rs | 0 {src => backup/src}/timers.rs | 0 {src => backup/src}/video/mod.rs | 0 {src => backup/src}/video/obj.rs | 0 dump.bat | 3 - dump.sh | 4 - release-roms.bat | 7 - src/critical_section.rs | 24 ++ src/lib.rs | 200 +++--------- src/mmio.rs | 299 ++---------------- 40 files changed, 571 insertions(+), 472 deletions(-) rename {examples => backup/examples}/asm_scratch.rs (100%) rename {examples => backup/examples}/foo.txt (100%) rename {examples => backup/examples}/game.rs (100%) rename {examples => backup/examples}/game_vblank_draw.rs (100%) rename {examples => backup/examples}/hello.rs (100%) rename {examples => backup/examples}/mode3_pong_example_game.rs (100%) rename {examples => backup/examples}/mode3_realtime_example.rs (100%) rename {examples => backup/examples}/video3_test.rs (100%) rename {examples => backup/examples}/video4_test.rs (100%) rename {src => backup/src}/asm_runtime.rs (100%) rename {src => backup/src}/bios.rs (100%) rename {src => backup/src}/builtin_art/cga_8x8_thick.rs (100%) rename {src => backup/src}/builtin_art/mod.rs (100%) rename {src => backup/src}/dma.rs (100%) rename {src => backup/src}/fixed.rs (100%) rename {src => backup/src}/gba_cell.rs (100%) rename {src => backup/src}/interrupts.rs (100%) rename {src => backup/src}/keys.rs (100%) create mode 100644 backup/src/lib.rs rename {src => backup/src}/macros.rs (100%) rename {src => backup/src}/mem_fns.rs (100%) rename {src => backup/src}/mgba.rs (100%) create mode 100644 backup/src/mmio.rs rename {src => backup/src}/prelude.rs (100%) rename {src => backup/src}/random.rs (100%) rename {src => backup/src}/sound.rs (100%) rename {src => backup/src}/timers.rs (100%) rename {src => backup/src}/video/mod.rs (100%) rename {src => backup/src}/video/obj.rs (100%) delete mode 100644 dump.bat delete mode 100755 dump.sh delete mode 100644 release-roms.bat diff --git a/.github/workflows/ci-builds.yml b/.github/workflows/ci-builds.yml index a2cfcb0f..c99bd205 100644 --- a/.github/workflows/ci-builds.yml +++ b/.github/workflows/ci-builds.yml @@ -13,9 +13,6 @@ jobs: - { toolchain: nightly } steps: - uses: actions/checkout@v2 - - - name: Install Apt Dependencies - run: sudo apt-get update && sudo apt-get install binutils-arm-none-eabi - uses: actions-rs/toolchain@v1 with: @@ -25,10 +22,15 @@ jobs: - name: Install Rust Source run: rustup component add rust-src + + - name: Install GNU Binutils + run: sudo apt-get update && sudo apt-get install binutils-arm-none-eabi - - name: Build The Examples - uses: actions-rs/cargo@v1 - with: - toolchain: ${{ matrix.rust.toolchain }} - command: build - args: --examples + - name: Build The Examples (Link With GNU, weak compiler intrinsics) + run: cargo build --examples + + - name: Delete the .cargo/config.toml + run: rm -fr .cargo/config.toml + + - name: Build The Examples (Link With LLD, strong compiler intrinsics) + run: cargo build --examples --target=thumbv4t-none-eabi -Zbuild-std=core -Clink-arg=-Tlinker_scripts/mono_boot.ld diff --git a/.vscode/settings.json b/.vscode/settings.json index b91216ab..b4d09d02 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,7 @@ { - "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.checkOnSave.overrideCommand": [ - "cargo", - "build", - "--quiet", - "--workspace", - "--message-format=json", + "rust-analyzer.cargo.allTargets": false, + "rust-analyzer.check.command": "build", + "rust-analyzer.check.extraArgs": [ "--lib", "--bins", "--examples" diff --git a/CHANGELOG.md b/CHANGELOG.md index 091640f9..9ae0e3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +* **0.12.0:** + * A totally new version! + * **0.11.5:** * Fixed the random number generator `next` method (https://github.com/rust-console/gba/issues/192). * Added optional support for the `critical-section` crate (https://github.com/rust-console/gba/pull/191) diff --git a/Cargo.toml b/Cargo.toml index bff7f570..d0e89852 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,32 +2,39 @@ name = "gba" description = "A crate for 'raw' style GBA development. If you want a 'managed' experience, try the `agb` crate instead." repository = "https://github.com/rust-console/gba" -version = "0.11.5" +version = "0.12.0" edition = "2021" license = "Zlib OR Apache-2.0 OR MIT" [features] -default = ["track_caller"] +default = ["on_gba", "critical-section", "doc_cfg"] +# SEE THE CRATE DOCS FOR SAFETY RELATED INFO REGARDING THIS FEATURE. +on_gba = [] +# utilize `doc_cfg` where appropriate. requires nightly. +doc_cfg = [] track_caller = [] [dependencies] voladdress = "1.3.0" bitfrob = "1" -bracer = "0.1.2" -critical-section = { version = "1.1.2", features = ["restore-state-bool"], optional = true } +critical-section = { version = "1.1.2", features = [ + "restore-state-bool", +], optional = true } [profile.dev] opt-level = 3 incremental = false [profile.dev.package."*"] +# this prevents `core` from having way too many debug assertions debug-assertions = false [profile.release] -codegen-units = 1 +codegen-units = 1 # increases build time, but also can increase inline opportunities incremental = false [package.metadata.docs.rs] -# The crate can only be built for targets that have thumb-interworking support, -# because otherwise the instruction_set attribute can't be used. +# When building the crate docs, we want to see the crate "as if" it was running +# on the GBA. This is the closest target that docs.rs supports by default and +# which *also* supports the `instruction_set` attribute to avoid build errors. targets = ["armv5te-unknown-linux-gnueabi"] diff --git a/README.md b/README.md index e98fe91f..389884cb 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,21 @@ This crate is intended for working with the GBA. -To build for the GBA you'll need to use `build-std` and you'll also need to -activate the `compiler-builtins-weak-intrinsics` feature. +To build for the GBA you'll need to use `build-std` (on Nightly) with the +`thumbv4t-none-eabi` or `armv4t-none-eabi` targets. The two targets are +identical except for which instruction set (thumb or arm) is used by default. It +is suggested that you use the thumb target, unless you know what you're doing and +have a very good reason to use the arm target. -The following should be somewhere in your `.cargo/config.toml`: +Your `.cargo/config.toml` should look something like this: ```toml +[build] +target = "thumbv4t-none-eabi" + [unstable] build-std = ["core"] -build-std-features = ["compiler-builtins-weak-intrinsics"] ``` + +If you don't use this crate on the GBA, you **MUST** disable default features and +then not use the `on_gba` cargo feature. diff --git a/examples/asm_scratch.rs b/backup/examples/asm_scratch.rs similarity index 100% rename from examples/asm_scratch.rs rename to backup/examples/asm_scratch.rs diff --git a/examples/foo.txt b/backup/examples/foo.txt similarity index 100% rename from examples/foo.txt rename to backup/examples/foo.txt diff --git a/examples/game.rs b/backup/examples/game.rs similarity index 100% rename from examples/game.rs rename to backup/examples/game.rs diff --git a/examples/game_vblank_draw.rs b/backup/examples/game_vblank_draw.rs similarity index 100% rename from examples/game_vblank_draw.rs rename to backup/examples/game_vblank_draw.rs diff --git a/examples/hello.rs b/backup/examples/hello.rs similarity index 100% rename from examples/hello.rs rename to backup/examples/hello.rs diff --git a/examples/mode3_pong_example_game.rs b/backup/examples/mode3_pong_example_game.rs similarity index 100% rename from examples/mode3_pong_example_game.rs rename to backup/examples/mode3_pong_example_game.rs diff --git a/examples/mode3_realtime_example.rs b/backup/examples/mode3_realtime_example.rs similarity index 100% rename from examples/mode3_realtime_example.rs rename to backup/examples/mode3_realtime_example.rs diff --git a/examples/video3_test.rs b/backup/examples/video3_test.rs similarity index 100% rename from examples/video3_test.rs rename to backup/examples/video3_test.rs diff --git a/examples/video4_test.rs b/backup/examples/video4_test.rs similarity index 100% rename from examples/video4_test.rs rename to backup/examples/video4_test.rs diff --git a/src/asm_runtime.rs b/backup/src/asm_runtime.rs similarity index 100% rename from src/asm_runtime.rs rename to backup/src/asm_runtime.rs diff --git a/src/bios.rs b/backup/src/bios.rs similarity index 100% rename from src/bios.rs rename to backup/src/bios.rs diff --git a/src/builtin_art/cga_8x8_thick.rs b/backup/src/builtin_art/cga_8x8_thick.rs similarity index 100% rename from src/builtin_art/cga_8x8_thick.rs rename to backup/src/builtin_art/cga_8x8_thick.rs diff --git a/src/builtin_art/mod.rs b/backup/src/builtin_art/mod.rs similarity index 100% rename from src/builtin_art/mod.rs rename to backup/src/builtin_art/mod.rs diff --git a/src/dma.rs b/backup/src/dma.rs similarity index 100% rename from src/dma.rs rename to backup/src/dma.rs diff --git a/src/fixed.rs b/backup/src/fixed.rs similarity index 100% rename from src/fixed.rs rename to backup/src/fixed.rs diff --git a/src/gba_cell.rs b/backup/src/gba_cell.rs similarity index 100% rename from src/gba_cell.rs rename to backup/src/gba_cell.rs diff --git a/src/interrupts.rs b/backup/src/interrupts.rs similarity index 100% rename from src/interrupts.rs rename to backup/src/interrupts.rs diff --git a/src/keys.rs b/backup/src/keys.rs similarity index 100% rename from src/keys.rs rename to backup/src/keys.rs diff --git a/backup/src/lib.rs b/backup/src/lib.rs new file mode 100644 index 00000000..4cd643c8 --- /dev/null +++ b/backup/src/lib.rs @@ -0,0 +1,159 @@ +#![no_std] +#![feature(asm_const)] +#![feature(naked_functions)] +#![warn(clippy::missing_inline_in_public_items)] +#![allow(clippy::let_and_return)] +#![allow(clippy::result_unit_err)] +//#![warn(missing_docs)] + +//! A crate for GBA development. +//! +//! ## How To Make Your Own GBA Project Using This Crate +//! +//! This will require the use of Nightly Rust. Any recent-ish version of Nightly +//! should be fine. +//! +//! [arm-download]: +//! https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain +//! +//! * **Get The ARM Binutils:** You'll need the ARM version of the GNU binutils +//! in your path, specifically the linker (`arm-none-eabi-ld`). Linux folks +//! can use the package manager. Mac and Windows folks can use the [ARM +//! Website][arm-download]. +//! * **Run `rustup component add rust-src`:** This makes rustup keep the +//! standard library source code on hand, which is necessary for `build-std` +//! to work. +//! * **Create A `.cargo/config.toml`:** You'll want to set up a file to provide +//! all the right default settings so that a basic `cargo build` and `cargo +//! run` will "just work". Something like the following is what you probably +//! want. +//! +//! ```toml +//! [build] +//! target = "thumbv4t-none-eabi" +//! +//! [unstable] +//! build-std = ["core"] +//! +//! [target.thumbv4t-none-eabi] +//! runner = "mgba-qt" +//! rustflags = ["-Clink-arg=-Tlinker_scripts/mono_boot.ld"] +//! ``` +//! +//! * **Make Your Executables:** At this point you can make a `bin` or an +//! `example` file. Every executable will need to be `#![no_std]` and +//! `#![no_main]`. They will also need a `#[panic_handler]` defined, as well +//! as a `#[no_mangle] extern "C" fn main() -> ! {}` function, which is what +//! the assembly runtime will call to start your Rust program after it fully +//! initializes the system. The C ABI must be used because Rust's own ABI is +//! not stable. +//! +//! ```rust +//! #![no_std] +//! #![no_main] +//! +//! #[panic_handler] +//! fn panic_handler(_: &core::panic::PanicInfo) -> ! { +//! loop {} +//! } +//! +//! #[no_mangle] +//! extern "C" fn main() -> ! { +//! loop {} +//! } +//! ``` +//! +//! * **Optional: Use `objcopy` and `gbafix`:** The `cargo build` will produce +//! ELF files, which mGBA can run directly. If you want to run your program on +//! real hardware you'll need to first `objcopy` the raw binary out of the ELF +//! into its own file, then Use `gbafix` to give an appropriate header to the +//! file. `objcopy` is part of the ARM binutils you already installed, it +//! should be named `arm-none-eabi-objcopy`. You can get `gbafix` through +//! cargo: `cargo install gbafix`. +//! +//! ## Other GBA-related Crates +//! +//! This crate provides an API to interact with the GBA that is safe, but with +//! minimal restrictions on what components can be changed when. If you'd like +//! an API where the borrow checker provides stronger control over component +//! access then the [agb](https://docs.rs/agb) crate might be what you want. +//! +//! ## Safety +//! +//! All safety considerations for the crate assume that you're building for the +//! `thumbv4t-none-eabi` or `armv4t-none-eabi` targets, using the provided +//! linker script, and then running the code on a GBA. While it's possible to +//! break any of these assumptions, if you do that some or all of the code +//! provided by this crate may become unsound. + +mod macros; + +pub mod asm_runtime; +pub mod bios; +pub mod builtin_art; +#[cfg(feature = "critical-section")] +mod critical_section; +pub mod dma; +pub mod fixed; +pub mod gba_cell; +pub mod interrupts; +pub mod keys; +pub mod mem_fns; +pub mod mgba; +pub mod mmio; +pub mod prelude; +pub mod random; +pub mod sound; +pub mod timers; +pub mod video; + +/// Wraps a value to be aligned to a minimum of 4. +/// +/// If the size of the value held is already a multiple of 4 then this will be +/// the same size as the wrapped value. Otherwise the compiler will add +/// sufficient padding bytes on the end to make the size a multiple of 4. +#[derive(Debug)] +#[repr(C, align(4))] +pub struct Align4(pub T); + +impl Align4<[u8; N]> { + /// Views these bytes as a slice of `u32` + /// ## Panics + /// * If the number of bytes isn't a multiple of 4 + #[inline] + #[must_use] + pub fn as_u32_slice(&self) -> &[u32] { + assert!(self.0.len() % 4 == 0); + // Safety: our struct is aligned to 4, so the pointer will already be + // aligned, we only need to check the length + unsafe { + let data: *const u8 = self.0.as_ptr(); + let len: usize = self.0.len(); + core::slice::from_raw_parts(data.cast::(), len / 4) + } + } + + /// Views these bytes as a slice of `u16` + /// ## Panics + /// * If the number of bytes isn't a multiple of 2 + #[inline] + #[must_use] + pub fn as_u16_slice(&self) -> &[u16] { + assert!(self.0.len() % 2 == 0); + // Safety: our struct is aligned to 4, so the pointer will already be + // aligned, we only need to check the length + unsafe { + let data: *const u8 = self.0.as_ptr(); + let len: usize = self.0.len(); + core::slice::from_raw_parts(data.cast::(), len / 2) + } + } +} + +/// Works like [`include_bytes!`], but the value is wrapped in [`Align4`]. +#[macro_export] +macro_rules! include_aligned_bytes { + ($file:expr $(,)?) => {{ + Align4(*include_bytes!($file)) + }}; +} diff --git a/src/macros.rs b/backup/src/macros.rs similarity index 100% rename from src/macros.rs rename to backup/src/macros.rs diff --git a/src/mem_fns.rs b/backup/src/mem_fns.rs similarity index 100% rename from src/mem_fns.rs rename to backup/src/mem_fns.rs diff --git a/src/mgba.rs b/backup/src/mgba.rs similarity index 100% rename from src/mgba.rs rename to backup/src/mgba.rs diff --git a/backup/src/mmio.rs b/backup/src/mmio.rs new file mode 100644 index 00000000..a392bb27 --- /dev/null +++ b/backup/src/mmio.rs @@ -0,0 +1,277 @@ +#![cfg_attr(rustfmt, rustfmt::skip)] + +//! Contains all the MMIO address definitions for the GBA's components. +//! +//! This module contains *only* the MMIO addresses. The data type definitions +//! for each MMIO control value are stored in the appropriate other modules such +//! as [`video`](crate::video), [`interrupts`](crate::interrupts), etc. +//! +//! In general, the docs for each address are quite short. If you want to +//! understand how a subsystem of the GBA works, you should read the docs for +//! that system's module, and the data type used by the address. +//! +//! The GBATEK names (and thus mGBA names) are used for the MMIO addresses by +//! default. However, in some cases (eg: sound) the GBATEK naming is excessively +//! cryptic, and so new names have been created. Whenever a new name is used, +//! the GBATEK name is still listed as a doc alias for that address. If +//! necessary you can just search the GBATEK name in the rustdoc search bar and +//! the search results will show you the new name. +//! +//! ## Safety +//! +//! The MMIO declarations and wrapper types in this module **must not** be used +//! outside of a GBA. The read and write safety of each address are declared +//! assuming that code is running on a GBA. On any other platform, the +//! declarations are simply incorrect. + +use core::{ffi::c_void, mem::size_of}; +use crate::prelude::*; +pub use bitfrob::u8x2; +pub use voladdress::{Safe, Unsafe, VolAddress, VolBlock, VolSeries, VolGrid2dStrided, VolGrid2d}; + +// Note(Lokathor): This macro lets us stick each address at the start of the +// definition, which lets us easily keep each declaration in address order. +macro_rules! def_mmio { + ($addr:literal = $name:ident : $t:ty $(; $comment:expr )?) => { + // redirect a call **without** an alias list to just pass an empty alias list + def_mmio!($addr = $name/[]: $t $(; $comment)? ); + }; + ($addr:literal = $name:ident / [ $( $alias:literal ),* ]: $t:ty $(; $comment:expr )?) => { + $(#[doc = $comment])? + $(#[doc(alias = $alias)])* + #[allow(missing_docs)] + pub const $name: $t = unsafe { <$t>::new($addr) }; + }; +} + +// Video + +def_mmio!(0x0400_0000 = DISPCNT: VolAddress; "Display Control"); +def_mmio!(0x0400_0004 = DISPSTAT: VolAddress; "Display Status"); +def_mmio!(0x0400_0006 = VCOUNT: VolAddress; "Vertical Counter"); + +def_mmio!(0x0400_0008 = BG0CNT: VolAddress; "Background 0 Control"); +def_mmio!(0x0400_000A = BG1CNT: VolAddress; "Background 1 Control"); +def_mmio!(0x0400_000C = BG2CNT: VolAddress; "Background 2 Control"); +def_mmio!(0x0400_000E = BG3CNT: VolAddress; "Background 3 Control"); + +def_mmio!(0x0400_0010 = BG0HOFS: VolAddress; "Background 0 Horizontal Offset (9-bit, text mode)"); +def_mmio!(0x0400_0012 = BG0VOFS: VolAddress; "Background 0 Vertical Offset (9-bit, text mode)"); + +def_mmio!(0x0400_0014 = BG1HOFS: VolAddress; "Background 1 Horizontal Offset (9-bit, text mode)"); +def_mmio!(0x0400_0016 = BG1VOFS: VolAddress; "Background 1 Vertical Offset (9-bit, text mode)"); + +def_mmio!(0x0400_0018 = BG2HOFS: VolAddress; "Background 2 Horizontal Offset (9-bit, text mode)"); +def_mmio!(0x0400_001A = BG2VOFS: VolAddress; "Background 2 Vertical Offset (9-bit, text mode)"); + +def_mmio!(0x0400_001C = BG3HOFS: VolAddress; "Background 3 Horizontal Offset (9-bit, text mode)"); +def_mmio!(0x0400_001E = BG3VOFS: VolAddress; "Background 3 Vertical Offset (9-bit, text mode)"); + +def_mmio!(0x0400_0020 = BG2PA: VolAddress; "Background 2 Param A (affine mode)"); +def_mmio!(0x0400_0022 = BG2PB: VolAddress; "Background 2 Param B (affine mode)"); +def_mmio!(0x0400_0024 = BG2PC: VolAddress; "Background 2 Param C (affine mode)"); +def_mmio!(0x0400_0026 = BG2PD: VolAddress; "Background 2 Param D (affine mode)"); +def_mmio!(0x0400_0028 = BG2X/["BG2X_L", "BG2X_H"]: VolAddress; "Background 2 X Reference Point (affine/bitmap modes)"); +def_mmio!(0x0400_002C = BG2Y/["BG2Y_L", "BG2Y_H"]: VolAddress; "Background 2 Y Reference Point (affine/bitmap modes)"); + +def_mmio!(0x0400_0030 = BG3PA: VolAddress; "Background 3 Param A (affine mode)"); +def_mmio!(0x0400_0032 = BG3PB: VolAddress; "Background 3 Param B (affine mode)"); +def_mmio!(0x0400_0034 = BG3PC: VolAddress; "Background 3 Param C (affine mode)"); +def_mmio!(0x0400_0036 = BG3PD: VolAddress; "Background 3 Param D (affine mode)"); +def_mmio!(0x0400_0038 = BG3X/["BG3X_L", "BG3X_H"]: VolAddress; "Background 3 X Reference Point (affine/bitmap modes)"); +def_mmio!(0x0400_003C = BG3Y/["BG3Y_L", "BG3Y_H"]: VolAddress; "Background 3 Y Reference Point (affine/bitmap modes)"); + +def_mmio!(0x0400_0040 = WIN0H: VolAddress; "Window 0 Horizontal: high=left, low=(right+1)"); +def_mmio!(0x0400_0042 = WIN1H: VolAddress; "Window 1 Horizontal: high=left, low=(right+1)"); +def_mmio!(0x0400_0044 = WIN0V: VolAddress; "Window 0 Vertical: high=top, low=(bottom+1)"); +def_mmio!(0x0400_0046 = WIN1V: VolAddress; "Window 1 Vertical: high=top, low=(bottom+1)"); +def_mmio!(0x0400_0048 = WININ: VolAddress; "Controls the inside Windows 0 and 1"); +def_mmio!(0x0400_004A = WINOUT: VolAddress; "Controls inside the object window and outside of windows"); + +def_mmio!(0x0400_004C = MOSAIC: VolAddress; "Sets the intensity of all mosaic effects"); +def_mmio!(0x0400_0050 = BLDCNT: VolAddress; "Sets color blend effects"); +def_mmio!(0x0400_0052 = BLDALPHA: VolAddress;"Sets EVA(low) and EVB(high) alpha blend coefficients, allows `0..=16`, in 1/16th units"); +def_mmio!(0x0400_0054 = BLDY: VolAddress;"Sets EVY brightness blend coefficient, allows `0..=16`, in 1/16th units"); + +// Sound + +def_mmio!(0x0400_0060 = TONE1_SWEEP/["SOUND1CNT_L","NR10"]: VolAddress; "Tone 1 Sweep"); +def_mmio!(0x0400_0062 = TONE1_PATTERN/["SOUND1CNT_H","NR11","NR12"]: VolAddress; "Tone 1 Duty/Len/Envelope"); +def_mmio!(0x0400_0064 = TONE1_FREQUENCY/["SOUND1CNT_X","NR13","NR14"]: VolAddress; "Tone 1 Frequency/Control"); + +def_mmio!(0x0400_0068 = TONE2_PATTERN/["SOUND2CNT_L","NR21","NR22"]: VolAddress; "Tone 2 Duty/Len/Envelope"); +def_mmio!(0x0400_006C = TONE2_FREQUENCY/["SOUND2CNT_H","NR23","NR24"]: VolAddress; "Tone 2 Frequency/Control"); + +def_mmio!(0x0400_0070 = WAVE_BANK/["SOUND3CNT_L","NR30"]: VolAddress; "Wave banking controls"); +def_mmio!(0x0400_0072 = WAVE_LEN_VOLUME/["SOUND3CNT_H","NR31","NR32"]: VolAddress; "Wave Length/Volume"); +def_mmio!(0x0400_0074 = WAVE_FREQ/["SOUND3CNT_X","NR33","NR34"]: VolAddress; "Wave Frequency/Control"); + +def_mmio!(0x0400_0078 = NOISE_LEN_ENV/["SOUND4CNT_L","NR41","NR42"]: VolAddress; "Noise Length/Envelope"); +def_mmio!(0x0400_007C = NOISE_FREQ/["SOUND4CNT_H","NR43","NR44"]: VolAddress; "Noise Frequency/Control"); + +def_mmio!(0x0400_0080 = LEFT_RIGHT_VOLUME/["SOUNDCNT_L","NR50","NR51"]: VolAddress;"Left/Right sound control (but GBAs only have one speaker each)."); +def_mmio!(0x0400_0082 = SOUND_MIX/["SOUNDCNT_H"]: VolAddress;"Mixes sound sources out to the left and right"); +def_mmio!(0x0400_0084 = SOUND_ENABLED/["SOUNDCNT_X"]: VolAddress;"Sound active flags (r), as well as the sound primary enable (rw)."); +def_mmio!(0x0400_0088 = SOUNDBIAS: VolAddress;"Provides a bias to set the 'middle point' of sound output."); + +def_mmio!(0x0400_0090 = WAVE_RAM/["WAVE_RAM0_L","WAVE_RAM0_H","WAVE_RAM1_L","WAVE_RAM1_H","WAVE_RAM2_L","WAVE_RAM2_H","WAVE_RAM3_L","WAVE_RAM3_H"]: VolBlock; "Wave memory, `u4`, plays MSB/LSB per byte."); +def_mmio!(0x0400_00A0 = FIFO_A/["FIFO_A_L", "FIFO_A_H"]: VolAddress; "Pushes 4 `i8` samples into the Sound A buffer.\n\nThe buffer is 32 bytes max, playback is LSB first."); +def_mmio!(0x0400_00A4 = FIFO_B/["FIFO_B_L", "FIFO_B_H"]: VolAddress; "Pushes 4 `i8` samples into the Sound B buffer.\n\nThe buffer is 32 bytes max, playback is LSB first."); + +// DMA + +def_mmio!(0x0400_00B0 = DMA0_SRC/["DMA0SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA0 Source Address (internal memory only)"); +def_mmio!(0x0400_00B4 = DMA0_DEST/["DMA0DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA0 Destination Address (internal memory only)"); +def_mmio!(0x0400_00B8 = DMA0_COUNT/["DMA0CNT_L"]: VolAddress; "DMA0 Transfer Count (14-bit, 0=max)"); +def_mmio!(0x0400_00BA = DMA0_CONTROL/["DMA0_CNT_H"]: VolAddress; "DMA0 Control Bits"); + +def_mmio!(0x0400_00BC = DMA1_SRC/["DMA1SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA1 Source Address (non-SRAM memory)"); +def_mmio!(0x0400_00C0 = DMA1_DEST/["DMA1DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA1 Destination Address (internal memory only)"); +def_mmio!(0x0400_00C4 = DMA1_COUNT/["DMA1CNT_L"]: VolAddress; "DMA1 Transfer Count (14-bit, 0=max)"); +def_mmio!(0x0400_00C6 = DMA1_CONTROL/["DMA1_CNT_H"]: VolAddress; "DMA1 Control Bits"); + +def_mmio!(0x0400_00C8 = DMA2_SRC/["DMA2SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA2 Source Address (non-SRAM memory)"); +def_mmio!(0x0400_00CC = DMA2_DEST/["DMA2DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA2 Destination Address (internal memory only)"); +def_mmio!(0x0400_00D0 = DMA2_COUNT/["DMA2CNT_L"]: VolAddress; "DMA2 Transfer Count (14-bit, 0=max)"); +def_mmio!(0x0400_00D2 = DMA2_CONTROL/["DMA2_CNT_H"]: VolAddress; "DMA2 Control Bits"); + +def_mmio!(0x0400_00D4 = DMA3_SRC/["DMA3SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA3 Source Address (non-SRAM memory)"); +def_mmio!(0x0400_00D8 = DMA3_DEST/["DMA3DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA3 Destination Address (non-SRAM memory)"); +def_mmio!(0x0400_00DC = DMA3_COUNT/["DMA3CNT_L"]: VolAddress; "DMA3 Transfer Count (16-bit, 0=max)"); +def_mmio!(0x0400_00DE = DMA3_CONTROL/["DMA3_CNT_H"]: VolAddress; "DMA3 Control Bits"); + +// Timers + +def_mmio!(0x0400_0100 = TIMER0_COUNT/["TM0CNT_L"]: VolAddress; "Timer 0 Count read"); +def_mmio!(0x0400_0100 = TIMER0_RELOAD/["TM0CNT_L"]: VolAddress; "Timer 0 Reload write"); +def_mmio!(0x0400_0102 = TIMER0_CONTROL/["TM0CNT_H"]: VolAddress; "Timer 0 control"); + +def_mmio!(0x0400_0104 = TIMER1_COUNT/["TM1CNT_L"]: VolAddress; "Timer 1 Count read"); +def_mmio!(0x0400_0104 = TIMER1_RELOAD/["TM1CNT_L"]: VolAddress; "Timer 1 Reload write"); +def_mmio!(0x0400_0106 = TIMER1_CONTROL/["TM1CNT_H"]: VolAddress; "Timer 1 control"); + +def_mmio!(0x0400_0108 = TIMER2_COUNT/["TM2CNT_L"]: VolAddress; "Timer 2 Count read"); +def_mmio!(0x0400_0108 = TIMER2_RELOAD/["TM2CNT_L"]: VolAddress; "Timer 2 Reload write"); +def_mmio!(0x0400_010A = TIMER2_CONTROL/["TM2CNT_H"]: VolAddress; "Timer 2 control"); + +def_mmio!(0x0400_010C = TIMER3_COUNT/["TM3CNT_L"]: VolAddress; "Timer 3 Count read"); +def_mmio!(0x0400_010C = TIMER3_RELOAD/["TM3CNT_L"]: VolAddress; "Timer 3 Reload write"); +def_mmio!(0x0400_010E = TIMER3_CONTROL/["TM3CNT_H"]: VolAddress; "Timer 3 control"); + +// Serial (part 1) + +def_mmio!(0x0400_0120 = SIODATA32: VolAddress); +def_mmio!(0x0400_0120 = SIOMULTI0: VolAddress); +def_mmio!(0x0400_0122 = SIOMULTI1: VolAddress); +def_mmio!(0x0400_0124 = SIOMULTI2: VolAddress); +def_mmio!(0x0400_0126 = SIOMULTI3: VolAddress); +def_mmio!(0x0400_0128 = SIOCNT: VolAddress); +def_mmio!(0x0400_012A = SIOMLT_SEND: VolAddress); +def_mmio!(0x0400_012A = SIODATA8: VolAddress); + +// Keys + +def_mmio!(0x0400_0130 = KEYINPUT: VolAddress; "Key state data."); +def_mmio!(0x0400_0132 = KEYCNT: VolAddress; "Key control to configure the key interrupt."); + +// Serial (part 2) + +def_mmio!(0x0400_0134 = RCNT: VolAddress); +def_mmio!(0x0400_0140 = JOYCNT: VolAddress); +def_mmio!(0x0400_0150 = JOY_RECV: VolAddress); +def_mmio!(0x0400_0154 = JOY_TRANS: VolAddress); +def_mmio!(0x0400_0158 = JOYSTAT: VolAddress); + +// Interrupts + +def_mmio!(0x0400_0200 = IE: VolAddress; "Interrupts Enabled: sets which interrupts will be accepted when a subsystem fires an interrupt"); +def_mmio!(0x0400_0202 = IF: VolAddress; "Interrupts Flagged: reads which interrupts are pending, writing bit(s) will clear a pending interrupt."); +def_mmio!(0x0400_0204 = WAITCNT: VolAddress; "Wait state control for interfacing with the ROM.\n\nThis can make reading the ROM give garbage when it's mis-configured!"); +def_mmio!(0x0400_0208 = IME: VolAddress; "Interrupt Master Enable: Allows turning on/off all interrupts with a single access."); + +// mGBA Logging + +def_mmio!(0x04FF_F600 = MGBA_LOG_BUFFER: VolBlock; "The buffer to put logging messages into.\n\nThe first 0 in the buffer is the end of each message."); +def_mmio!(0x04FF_F700 = MGBA_LOG_SEND: VolAddress; "Write to this each time you want to reset a message (it also resets the buffer)."); +def_mmio!(0x04FF_F780 = MGBA_LOG_ENABLE: VolAddress; "Allows you to attempt to activate mGBA logging."); + +// Palette RAM (PALRAM) + +def_mmio!(0x0500_0000 = BACKDROP_COLOR: VolAddress; "Color that's shown when no BG or OBJ draws to a pixel"); +def_mmio!(0x0500_0000 = BG_PALETTE: VolBlock; "Background tile palette entries."); +def_mmio!(0x0500_0200 = OBJ_PALETTE: VolBlock; "Object tile palette entries."); + +#[inline] +#[must_use] +#[cfg_attr(feature="track_caller", track_caller)] +pub const fn bg_palbank(bank: usize) -> VolBlock { + let u = BG_PALETTE.index(bank * 16).as_usize(); + unsafe { VolBlock::new(u) } +} +#[inline] +#[must_use] +#[cfg_attr(feature="track_caller", track_caller)] +pub const fn obj_palbank(bank: usize) -> VolBlock { + let u = OBJ_PALETTE.index(bank * 16).as_usize(); + unsafe { VolBlock::new(u) } +} + +// Video RAM (VRAM) + +/// The VRAM byte offset per screenblock index. +/// +/// This is the same for all background types and sizes. +pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; + +/// The size of the background tile region of VRAM. +/// +/// Background tile index use will work between charblocks, but not past the end +/// of BG tile memory into OBJ tile memory. +pub const BG_TILE_REGION_SIZE: usize = 64 * 1_024; + +def_mmio!(0x0600_0000 = CHARBLOCK0_4BPP: VolBlock; "Charblock 0, 4bpp view (512 tiles)."); +def_mmio!(0x0600_4000 = CHARBLOCK1_4BPP: VolBlock; "Charblock 1, 4bpp view (512 tiles)."); +def_mmio!(0x0600_8000 = CHARBLOCK2_4BPP: VolBlock; "Charblock 2, 4bpp view (512 tiles)."); +def_mmio!(0x0600_C000 = CHARBLOCK3_4BPP: VolBlock; "Charblock 3, 4bpp view (512 tiles)."); + +def_mmio!(0x0600_0000 = CHARBLOCK0_8BPP: VolBlock; "Charblock 0, 8bpp view (256 tiles)."); +def_mmio!(0x0600_4000 = CHARBLOCK1_8BPP: VolBlock; "Charblock 1, 8bpp view (256 tiles)."); +def_mmio!(0x0600_8000 = CHARBLOCK2_8BPP: VolBlock; "Charblock 2, 8bpp view (256 tiles)."); +def_mmio!(0x0600_C000 = CHARBLOCK3_8BPP: VolBlock; "Charblock 3, 8bpp view (256 tiles)."); + +def_mmio!(0x0600_0000 = TEXT_SCREENBLOCKS: VolGrid2dStrided; "Text mode screenblocks."); + +def_mmio!(0x0600_0000 = AFFINE0_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 0)."); + +def_mmio!(0x0600_0000 = AFFINE1_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 1)."); + +def_mmio!(0x0600_0000 = AFFINE2_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 2)."); + +def_mmio!(0x0600_0000 = AFFINE3_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 3)."); + +def_mmio!(0x0600_0000 = VIDEO3_VRAM: VolGrid2d; "Video mode 3 bitmap"); + +def_mmio!(0x0600_0000 = VIDEO4_VRAM: VolGrid2dStrided; "Video mode 4 palette maps (frames 0 and 1). Each entry is two palette indexes."); + +def_mmio!(0x0600_0000 = VIDEO5_VRAM: VolGrid2dStrided; "Video mode 5 bitmaps (frames 0 and 1)."); + +def_mmio!(0x0601_0000 = OBJ_TILES: VolBlock; "Object tiles. In video modes 3, 4, and 5 only indices 512..=1023 are available."); + +// Object Attribute Memory (OAM) + +def_mmio!(0x0700_0000 = OBJ_ATTR0: VolSeries()}>; "Object attributes 0."); +def_mmio!(0x0700_0002 = OBJ_ATTR1: VolSeries()}>; "Object attributes 1."); +def_mmio!(0x0700_0004 = OBJ_ATTR2: VolSeries()}>; "Object attributes 2."); + +def_mmio!(0x0700_0000 = OBJ_ATTR_ALL: VolSeries()}>; "Object attributes (all in one)."); + +def_mmio!(0x0700_0006 = AFFINE_PARAM_A: VolSeries()}>; "Affine parameters A."); +def_mmio!(0x0700_000E = AFFINE_PARAM_B: VolSeries()}>; "Affine parameters B."); +def_mmio!(0x0700_0016 = AFFINE_PARAM_C: VolSeries()}>; "Affine parameters C."); +def_mmio!(0x0700_001E = AFFINE_PARAM_D: VolSeries()}>; "Affine parameters D."); + +// Cartridge IO port +// https://problemkaputt.de/gbatek.htm#gbacartioportgpio +def_mmio!(0x0800_00C4 = IO_PORT_DATA: VolAddress; "I/O port data"); +def_mmio!(0x0800_00C6 = IO_PORT_DIRECTION: VolAddress; "I/O port direction"); +def_mmio!(0x0800_00C8 = IO_PORT_CONTROL: VolAddress; "I/O port control"); diff --git a/src/prelude.rs b/backup/src/prelude.rs similarity index 100% rename from src/prelude.rs rename to backup/src/prelude.rs diff --git a/src/random.rs b/backup/src/random.rs similarity index 100% rename from src/random.rs rename to backup/src/random.rs diff --git a/src/sound.rs b/backup/src/sound.rs similarity index 100% rename from src/sound.rs rename to backup/src/sound.rs diff --git a/src/timers.rs b/backup/src/timers.rs similarity index 100% rename from src/timers.rs rename to backup/src/timers.rs diff --git a/src/video/mod.rs b/backup/src/video/mod.rs similarity index 100% rename from src/video/mod.rs rename to backup/src/video/mod.rs diff --git a/src/video/obj.rs b/backup/src/video/obj.rs similarity index 100% rename from src/video/obj.rs rename to backup/src/video/obj.rs diff --git a/dump.bat b/dump.bat deleted file mode 100644 index 642ee3bb..00000000 --- a/dump.bat +++ /dev/null @@ -1,3 +0,0 @@ -cargo build --examples --verbose -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/hello >target/dump-hello.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/asm_scratch >target/dump-asm_scratch.txt diff --git a/dump.sh b/dump.sh deleted file mode 100755 index f3ce3525..00000000 --- a/dump.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -cargo build --examples --verbose -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/hello >target/dump-hello.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/asm_scratch >target/dump-asm_scratch.txt diff --git a/release-roms.bat b/release-roms.bat deleted file mode 100644 index c43aeddf..00000000 --- a/release-roms.bat +++ /dev/null @@ -1,7 +0,0 @@ -cargo build --examples --release - -arm-none-eabi-objcopy -O binary target/thumbv4t-none-eabi/release/examples/hello target/hello.gba -gbafix -p -thello -cHELO -mRS target/hello.gba - -arm-none-eabi-objcopy -O binary target/thumbv4t-none-eabi/release/examples/game target/game.gba -gbafix -p -tgame -cGAME -mRS target/game.gba diff --git a/src/critical_section.rs b/src/critical_section.rs index 9bcb7b1a..abd33e6b 100644 --- a/src/critical_section.rs +++ b/src/critical_section.rs @@ -1,3 +1,5 @@ +//! Support for the [critical-section](https://docs.rs/critical-section) crate. + use critical_section::{set_impl, Impl, RawRestoreState}; use crate::mmio::IME; @@ -5,14 +7,36 @@ use crate::mmio::IME; struct GbaCriticalSection; set_impl!(GbaCriticalSection); +#[cfg(feature = "on_gba")] unsafe impl Impl for GbaCriticalSection { + /// # Safety + /// This function has no pre-conditions. unsafe fn acquire() -> RawRestoreState { let restore = IME.read(); IME.write(false); restore } + /// # Safety + /// This function has no pre-conditions. unsafe fn release(restore: RawRestoreState) { IME.write(restore); } } + +#[cfg(not(feature = "on_gba"))] +unsafe impl Impl for GbaCriticalSection { + /// # Safety + /// This function will always panic, so I guess you could say it's always + /// safe. + unsafe fn acquire() -> RawRestoreState { + panic!() + } + + /// # Safety + /// This function will always panic, so I guess you could say it's always + /// safe. + unsafe fn release(restore: RawRestoreState) { + panic!() + } +} diff --git a/src/lib.rs b/src/lib.rs index 4cd643c8..66072722 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,159 +1,51 @@ #![no_std] -#![feature(asm_const)] -#![feature(naked_functions)] -#![warn(clippy::missing_inline_in_public_items)] -#![allow(clippy::let_and_return)] -#![allow(clippy::result_unit_err)] -//#![warn(missing_docs)] +#![warn(missing_docs)] +#![warn(unsafe_op_in_unsafe_fn)] +#![cfg_attr(feature = "doc_cfg", feature(doc_cfg))] -//! A crate for GBA development. -//! -//! ## How To Make Your Own GBA Project Using This Crate -//! -//! This will require the use of Nightly Rust. Any recent-ish version of Nightly -//! should be fine. -//! -//! [arm-download]: -//! https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain -//! -//! * **Get The ARM Binutils:** You'll need the ARM version of the GNU binutils -//! in your path, specifically the linker (`arm-none-eabi-ld`). Linux folks -//! can use the package manager. Mac and Windows folks can use the [ARM -//! Website][arm-download]. -//! * **Run `rustup component add rust-src`:** This makes rustup keep the -//! standard library source code on hand, which is necessary for `build-std` -//! to work. -//! * **Create A `.cargo/config.toml`:** You'll want to set up a file to provide -//! all the right default settings so that a basic `cargo build` and `cargo -//! run` will "just work". Something like the following is what you probably -//! want. -//! -//! ```toml -//! [build] -//! target = "thumbv4t-none-eabi" -//! -//! [unstable] -//! build-std = ["core"] -//! -//! [target.thumbv4t-none-eabi] -//! runner = "mgba-qt" -//! rustflags = ["-Clink-arg=-Tlinker_scripts/mono_boot.ld"] -//! ``` -//! -//! * **Make Your Executables:** At this point you can make a `bin` or an -//! `example` file. Every executable will need to be `#![no_std]` and -//! `#![no_main]`. They will also need a `#[panic_handler]` defined, as well -//! as a `#[no_mangle] extern "C" fn main() -> ! {}` function, which is what -//! the assembly runtime will call to start your Rust program after it fully -//! initializes the system. The C ABI must be used because Rust's own ABI is -//! not stable. -//! -//! ```rust -//! #![no_std] -//! #![no_main] -//! -//! #[panic_handler] -//! fn panic_handler(_: &core::panic::PanicInfo) -> ! { -//! loop {} -//! } -//! -//! #[no_mangle] -//! extern "C" fn main() -> ! { -//! loop {} -//! } -//! ``` -//! -//! * **Optional: Use `objcopy` and `gbafix`:** The `cargo build` will produce -//! ELF files, which mGBA can run directly. If you want to run your program on -//! real hardware you'll need to first `objcopy` the raw binary out of the ELF -//! into its own file, then Use `gbafix` to give an appropriate header to the -//! file. `objcopy` is part of the ARM binutils you already installed, it -//! should be named `arm-none-eabi-objcopy`. You can get `gbafix` through -//! cargo: `cargo install gbafix`. -//! -//! ## Other GBA-related Crates -//! -//! This crate provides an API to interact with the GBA that is safe, but with -//! minimal restrictions on what components can be changed when. If you'd like -//! an API where the borrow checker provides stronger control over component -//! access then the [agb](https://docs.rs/agb) crate might be what you want. -//! -//! ## Safety -//! -//! All safety considerations for the crate assume that you're building for the -//! `thumbv4t-none-eabi` or `armv4t-none-eabi` targets, using the provided -//! linker script, and then running the code on a GBA. While it's possible to -//! break any of these assumptions, if you do that some or all of the code -//! provided by this crate may become unsound. - -mod macros; +//! A crate for 'raw' style Game Boy Advance (GBA) development, where any code +//! can access any hardware component at any time, with no special ceremony. +//! +//! * **Note:** If you want a 'managed' hardware style, more like many other +//! "embedded-wg" experiences, where hardware access is declared though the +//! type system by passing around zero-sized token types, try the +//! [agb](https://docs.rs/agb) crate instead. +//! +//! # Crate Features +//! +//! * `on_gba` (**Default:** enabled): When this feature is used, the crate +//! assumes that you're building the crate for, and running the code on, the +//! Game Boy Advance. The build target is expected to be `thumbv4t-none-eabi` +//! or `armv4t-none-eabi`, any other targets may have a build error. Further, +//! the specific device is assumed to be the GBA, which is used to determine +//! the safety of all direct hardware access using MMIO. This feature is on by +//! default because the primary purpose of this crate is to assist in the +//! building of GBA games, but you *can* disable the feature and build the +//! crate anyway, such as if you want to use any of the crate's data type +//! definitions within a build script on your host machine. When this feature +//! is disabled, GBA specific internals of functions *may* be replaced with +//! runtime panics when necessary. How much of this crate actually works on +//! non-GBA platforms is **not** covered by our SemVer! +//! * `critical-section` (**Default:** enabled): activates an implementation to +//! support for the [critical-section](https://docs.rs/critical-section) +//! crate. +//! * `track_caller` (**Default:** disabled): Causes some functions that can +//! panic to add the [track_caller][ref-track-caller] attribute. The attribute +//! adds a "secret" function argument to pass the `Location` of the call, so +//! it can reduce performance when a function is not inlined (more data has to +//! be pushed onto the stack per function call). Suggested for debugging only. +//! +//! [ref-track-caller]: +//! https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute +//! +//! # Additional Information +//! +//! * Development Environment Setup +//! * Project Setup +//! * Learning GBA Programming -pub mod asm_runtime; -pub mod bios; -pub mod builtin_art; -#[cfg(feature = "critical-section")] -mod critical_section; -pub mod dma; -pub mod fixed; -pub mod gba_cell; -pub mod interrupts; -pub mod keys; -pub mod mem_fns; -pub mod mgba; pub mod mmio; -pub mod prelude; -pub mod random; -pub mod sound; -pub mod timers; -pub mod video; - -/// Wraps a value to be aligned to a minimum of 4. -/// -/// If the size of the value held is already a multiple of 4 then this will be -/// the same size as the wrapped value. Otherwise the compiler will add -/// sufficient padding bytes on the end to make the size a multiple of 4. -#[derive(Debug)] -#[repr(C, align(4))] -pub struct Align4(pub T); - -impl Align4<[u8; N]> { - /// Views these bytes as a slice of `u32` - /// ## Panics - /// * If the number of bytes isn't a multiple of 4 - #[inline] - #[must_use] - pub fn as_u32_slice(&self) -> &[u32] { - assert!(self.0.len() % 4 == 0); - // Safety: our struct is aligned to 4, so the pointer will already be - // aligned, we only need to check the length - unsafe { - let data: *const u8 = self.0.as_ptr(); - let len: usize = self.0.len(); - core::slice::from_raw_parts(data.cast::(), len / 4) - } - } - /// Views these bytes as a slice of `u16` - /// ## Panics - /// * If the number of bytes isn't a multiple of 2 - #[inline] - #[must_use] - pub fn as_u16_slice(&self) -> &[u16] { - assert!(self.0.len() % 2 == 0); - // Safety: our struct is aligned to 4, so the pointer will already be - // aligned, we only need to check the length - unsafe { - let data: *const u8 = self.0.as_ptr(); - let len: usize = self.0.len(); - core::slice::from_raw_parts(data.cast::(), len / 2) - } - } -} - -/// Works like [`include_bytes!`], but the value is wrapped in [`Align4`]. -#[macro_export] -macro_rules! include_aligned_bytes { - ($file:expr $(,)?) => {{ - Align4(*include_bytes!($file)) - }}; -} +#[cfg(feature = "critical-section")] +#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "critical-section")))] +pub mod critical_section; diff --git a/src/mmio.rs b/src/mmio.rs index a392bb27..d1455378 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -1,277 +1,22 @@ -#![cfg_attr(rustfmt, rustfmt::skip)] - -//! Contains all the MMIO address definitions for the GBA's components. -//! -//! This module contains *only* the MMIO addresses. The data type definitions -//! for each MMIO control value are stored in the appropriate other modules such -//! as [`video`](crate::video), [`interrupts`](crate::interrupts), etc. -//! -//! In general, the docs for each address are quite short. If you want to -//! understand how a subsystem of the GBA works, you should read the docs for -//! that system's module, and the data type used by the address. -//! -//! The GBATEK names (and thus mGBA names) are used for the MMIO addresses by -//! default. However, in some cases (eg: sound) the GBATEK naming is excessively -//! cryptic, and so new names have been created. Whenever a new name is used, -//! the GBATEK name is still listed as a doc alias for that address. If -//! necessary you can just search the GBATEK name in the rustdoc search bar and -//! the search results will show you the new name. -//! -//! ## Safety -//! -//! The MMIO declarations and wrapper types in this module **must not** be used -//! outside of a GBA. The read and write safety of each address are declared -//! assuming that code is running on a GBA. On any other platform, the -//! declarations are simply incorrect. - -use core::{ffi::c_void, mem::size_of}; -use crate::prelude::*; -pub use bitfrob::u8x2; -pub use voladdress::{Safe, Unsafe, VolAddress, VolBlock, VolSeries, VolGrid2dStrided, VolGrid2d}; - -// Note(Lokathor): This macro lets us stick each address at the start of the -// definition, which lets us easily keep each declaration in address order. -macro_rules! def_mmio { - ($addr:literal = $name:ident : $t:ty $(; $comment:expr )?) => { - // redirect a call **without** an alias list to just pass an empty alias list - def_mmio!($addr = $name/[]: $t $(; $comment)? ); - }; - ($addr:literal = $name:ident / [ $( $alias:literal ),* ]: $t:ty $(; $comment:expr )?) => { - $(#[doc = $comment])? - $(#[doc(alias = $alias)])* - #[allow(missing_docs)] - pub const $name: $t = unsafe { <$t>::new($addr) }; - }; -} - -// Video - -def_mmio!(0x0400_0000 = DISPCNT: VolAddress; "Display Control"); -def_mmio!(0x0400_0004 = DISPSTAT: VolAddress; "Display Status"); -def_mmio!(0x0400_0006 = VCOUNT: VolAddress; "Vertical Counter"); - -def_mmio!(0x0400_0008 = BG0CNT: VolAddress; "Background 0 Control"); -def_mmio!(0x0400_000A = BG1CNT: VolAddress; "Background 1 Control"); -def_mmio!(0x0400_000C = BG2CNT: VolAddress; "Background 2 Control"); -def_mmio!(0x0400_000E = BG3CNT: VolAddress; "Background 3 Control"); - -def_mmio!(0x0400_0010 = BG0HOFS: VolAddress; "Background 0 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_0012 = BG0VOFS: VolAddress; "Background 0 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_0014 = BG1HOFS: VolAddress; "Background 1 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_0016 = BG1VOFS: VolAddress; "Background 1 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_0018 = BG2HOFS: VolAddress; "Background 2 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_001A = BG2VOFS: VolAddress; "Background 2 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_001C = BG3HOFS: VolAddress; "Background 3 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_001E = BG3VOFS: VolAddress; "Background 3 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_0020 = BG2PA: VolAddress; "Background 2 Param A (affine mode)"); -def_mmio!(0x0400_0022 = BG2PB: VolAddress; "Background 2 Param B (affine mode)"); -def_mmio!(0x0400_0024 = BG2PC: VolAddress; "Background 2 Param C (affine mode)"); -def_mmio!(0x0400_0026 = BG2PD: VolAddress; "Background 2 Param D (affine mode)"); -def_mmio!(0x0400_0028 = BG2X/["BG2X_L", "BG2X_H"]: VolAddress; "Background 2 X Reference Point (affine/bitmap modes)"); -def_mmio!(0x0400_002C = BG2Y/["BG2Y_L", "BG2Y_H"]: VolAddress; "Background 2 Y Reference Point (affine/bitmap modes)"); - -def_mmio!(0x0400_0030 = BG3PA: VolAddress; "Background 3 Param A (affine mode)"); -def_mmio!(0x0400_0032 = BG3PB: VolAddress; "Background 3 Param B (affine mode)"); -def_mmio!(0x0400_0034 = BG3PC: VolAddress; "Background 3 Param C (affine mode)"); -def_mmio!(0x0400_0036 = BG3PD: VolAddress; "Background 3 Param D (affine mode)"); -def_mmio!(0x0400_0038 = BG3X/["BG3X_L", "BG3X_H"]: VolAddress; "Background 3 X Reference Point (affine/bitmap modes)"); -def_mmio!(0x0400_003C = BG3Y/["BG3Y_L", "BG3Y_H"]: VolAddress; "Background 3 Y Reference Point (affine/bitmap modes)"); - -def_mmio!(0x0400_0040 = WIN0H: VolAddress; "Window 0 Horizontal: high=left, low=(right+1)"); -def_mmio!(0x0400_0042 = WIN1H: VolAddress; "Window 1 Horizontal: high=left, low=(right+1)"); -def_mmio!(0x0400_0044 = WIN0V: VolAddress; "Window 0 Vertical: high=top, low=(bottom+1)"); -def_mmio!(0x0400_0046 = WIN1V: VolAddress; "Window 1 Vertical: high=top, low=(bottom+1)"); -def_mmio!(0x0400_0048 = WININ: VolAddress; "Controls the inside Windows 0 and 1"); -def_mmio!(0x0400_004A = WINOUT: VolAddress; "Controls inside the object window and outside of windows"); - -def_mmio!(0x0400_004C = MOSAIC: VolAddress; "Sets the intensity of all mosaic effects"); -def_mmio!(0x0400_0050 = BLDCNT: VolAddress; "Sets color blend effects"); -def_mmio!(0x0400_0052 = BLDALPHA: VolAddress;"Sets EVA(low) and EVB(high) alpha blend coefficients, allows `0..=16`, in 1/16th units"); -def_mmio!(0x0400_0054 = BLDY: VolAddress;"Sets EVY brightness blend coefficient, allows `0..=16`, in 1/16th units"); - -// Sound - -def_mmio!(0x0400_0060 = TONE1_SWEEP/["SOUND1CNT_L","NR10"]: VolAddress; "Tone 1 Sweep"); -def_mmio!(0x0400_0062 = TONE1_PATTERN/["SOUND1CNT_H","NR11","NR12"]: VolAddress; "Tone 1 Duty/Len/Envelope"); -def_mmio!(0x0400_0064 = TONE1_FREQUENCY/["SOUND1CNT_X","NR13","NR14"]: VolAddress; "Tone 1 Frequency/Control"); - -def_mmio!(0x0400_0068 = TONE2_PATTERN/["SOUND2CNT_L","NR21","NR22"]: VolAddress; "Tone 2 Duty/Len/Envelope"); -def_mmio!(0x0400_006C = TONE2_FREQUENCY/["SOUND2CNT_H","NR23","NR24"]: VolAddress; "Tone 2 Frequency/Control"); - -def_mmio!(0x0400_0070 = WAVE_BANK/["SOUND3CNT_L","NR30"]: VolAddress; "Wave banking controls"); -def_mmio!(0x0400_0072 = WAVE_LEN_VOLUME/["SOUND3CNT_H","NR31","NR32"]: VolAddress; "Wave Length/Volume"); -def_mmio!(0x0400_0074 = WAVE_FREQ/["SOUND3CNT_X","NR33","NR34"]: VolAddress; "Wave Frequency/Control"); - -def_mmio!(0x0400_0078 = NOISE_LEN_ENV/["SOUND4CNT_L","NR41","NR42"]: VolAddress; "Noise Length/Envelope"); -def_mmio!(0x0400_007C = NOISE_FREQ/["SOUND4CNT_H","NR43","NR44"]: VolAddress; "Noise Frequency/Control"); - -def_mmio!(0x0400_0080 = LEFT_RIGHT_VOLUME/["SOUNDCNT_L","NR50","NR51"]: VolAddress;"Left/Right sound control (but GBAs only have one speaker each)."); -def_mmio!(0x0400_0082 = SOUND_MIX/["SOUNDCNT_H"]: VolAddress;"Mixes sound sources out to the left and right"); -def_mmio!(0x0400_0084 = SOUND_ENABLED/["SOUNDCNT_X"]: VolAddress;"Sound active flags (r), as well as the sound primary enable (rw)."); -def_mmio!(0x0400_0088 = SOUNDBIAS: VolAddress;"Provides a bias to set the 'middle point' of sound output."); - -def_mmio!(0x0400_0090 = WAVE_RAM/["WAVE_RAM0_L","WAVE_RAM0_H","WAVE_RAM1_L","WAVE_RAM1_H","WAVE_RAM2_L","WAVE_RAM2_H","WAVE_RAM3_L","WAVE_RAM3_H"]: VolBlock; "Wave memory, `u4`, plays MSB/LSB per byte."); -def_mmio!(0x0400_00A0 = FIFO_A/["FIFO_A_L", "FIFO_A_H"]: VolAddress; "Pushes 4 `i8` samples into the Sound A buffer.\n\nThe buffer is 32 bytes max, playback is LSB first."); -def_mmio!(0x0400_00A4 = FIFO_B/["FIFO_B_L", "FIFO_B_H"]: VolAddress; "Pushes 4 `i8` samples into the Sound B buffer.\n\nThe buffer is 32 bytes max, playback is LSB first."); - -// DMA - -def_mmio!(0x0400_00B0 = DMA0_SRC/["DMA0SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA0 Source Address (internal memory only)"); -def_mmio!(0x0400_00B4 = DMA0_DEST/["DMA0DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA0 Destination Address (internal memory only)"); -def_mmio!(0x0400_00B8 = DMA0_COUNT/["DMA0CNT_L"]: VolAddress; "DMA0 Transfer Count (14-bit, 0=max)"); -def_mmio!(0x0400_00BA = DMA0_CONTROL/["DMA0_CNT_H"]: VolAddress; "DMA0 Control Bits"); - -def_mmio!(0x0400_00BC = DMA1_SRC/["DMA1SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA1 Source Address (non-SRAM memory)"); -def_mmio!(0x0400_00C0 = DMA1_DEST/["DMA1DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA1 Destination Address (internal memory only)"); -def_mmio!(0x0400_00C4 = DMA1_COUNT/["DMA1CNT_L"]: VolAddress; "DMA1 Transfer Count (14-bit, 0=max)"); -def_mmio!(0x0400_00C6 = DMA1_CONTROL/["DMA1_CNT_H"]: VolAddress; "DMA1 Control Bits"); - -def_mmio!(0x0400_00C8 = DMA2_SRC/["DMA2SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA2 Source Address (non-SRAM memory)"); -def_mmio!(0x0400_00CC = DMA2_DEST/["DMA2DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA2 Destination Address (internal memory only)"); -def_mmio!(0x0400_00D0 = DMA2_COUNT/["DMA2CNT_L"]: VolAddress; "DMA2 Transfer Count (14-bit, 0=max)"); -def_mmio!(0x0400_00D2 = DMA2_CONTROL/["DMA2_CNT_H"]: VolAddress; "DMA2 Control Bits"); - -def_mmio!(0x0400_00D4 = DMA3_SRC/["DMA3SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA3 Source Address (non-SRAM memory)"); -def_mmio!(0x0400_00D8 = DMA3_DEST/["DMA3DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA3 Destination Address (non-SRAM memory)"); -def_mmio!(0x0400_00DC = DMA3_COUNT/["DMA3CNT_L"]: VolAddress; "DMA3 Transfer Count (16-bit, 0=max)"); -def_mmio!(0x0400_00DE = DMA3_CONTROL/["DMA3_CNT_H"]: VolAddress; "DMA3 Control Bits"); - -// Timers - -def_mmio!(0x0400_0100 = TIMER0_COUNT/["TM0CNT_L"]: VolAddress; "Timer 0 Count read"); -def_mmio!(0x0400_0100 = TIMER0_RELOAD/["TM0CNT_L"]: VolAddress; "Timer 0 Reload write"); -def_mmio!(0x0400_0102 = TIMER0_CONTROL/["TM0CNT_H"]: VolAddress; "Timer 0 control"); - -def_mmio!(0x0400_0104 = TIMER1_COUNT/["TM1CNT_L"]: VolAddress; "Timer 1 Count read"); -def_mmio!(0x0400_0104 = TIMER1_RELOAD/["TM1CNT_L"]: VolAddress; "Timer 1 Reload write"); -def_mmio!(0x0400_0106 = TIMER1_CONTROL/["TM1CNT_H"]: VolAddress; "Timer 1 control"); - -def_mmio!(0x0400_0108 = TIMER2_COUNT/["TM2CNT_L"]: VolAddress; "Timer 2 Count read"); -def_mmio!(0x0400_0108 = TIMER2_RELOAD/["TM2CNT_L"]: VolAddress; "Timer 2 Reload write"); -def_mmio!(0x0400_010A = TIMER2_CONTROL/["TM2CNT_H"]: VolAddress; "Timer 2 control"); - -def_mmio!(0x0400_010C = TIMER3_COUNT/["TM3CNT_L"]: VolAddress; "Timer 3 Count read"); -def_mmio!(0x0400_010C = TIMER3_RELOAD/["TM3CNT_L"]: VolAddress; "Timer 3 Reload write"); -def_mmio!(0x0400_010E = TIMER3_CONTROL/["TM3CNT_H"]: VolAddress; "Timer 3 control"); - -// Serial (part 1) - -def_mmio!(0x0400_0120 = SIODATA32: VolAddress); -def_mmio!(0x0400_0120 = SIOMULTI0: VolAddress); -def_mmio!(0x0400_0122 = SIOMULTI1: VolAddress); -def_mmio!(0x0400_0124 = SIOMULTI2: VolAddress); -def_mmio!(0x0400_0126 = SIOMULTI3: VolAddress); -def_mmio!(0x0400_0128 = SIOCNT: VolAddress); -def_mmio!(0x0400_012A = SIOMLT_SEND: VolAddress); -def_mmio!(0x0400_012A = SIODATA8: VolAddress); - -// Keys - -def_mmio!(0x0400_0130 = KEYINPUT: VolAddress; "Key state data."); -def_mmio!(0x0400_0132 = KEYCNT: VolAddress; "Key control to configure the key interrupt."); - -// Serial (part 2) - -def_mmio!(0x0400_0134 = RCNT: VolAddress); -def_mmio!(0x0400_0140 = JOYCNT: VolAddress); -def_mmio!(0x0400_0150 = JOY_RECV: VolAddress); -def_mmio!(0x0400_0154 = JOY_TRANS: VolAddress); -def_mmio!(0x0400_0158 = JOYSTAT: VolAddress); - -// Interrupts - -def_mmio!(0x0400_0200 = IE: VolAddress; "Interrupts Enabled: sets which interrupts will be accepted when a subsystem fires an interrupt"); -def_mmio!(0x0400_0202 = IF: VolAddress; "Interrupts Flagged: reads which interrupts are pending, writing bit(s) will clear a pending interrupt."); -def_mmio!(0x0400_0204 = WAITCNT: VolAddress; "Wait state control for interfacing with the ROM.\n\nThis can make reading the ROM give garbage when it's mis-configured!"); -def_mmio!(0x0400_0208 = IME: VolAddress; "Interrupt Master Enable: Allows turning on/off all interrupts with a single access."); - -// mGBA Logging - -def_mmio!(0x04FF_F600 = MGBA_LOG_BUFFER: VolBlock; "The buffer to put logging messages into.\n\nThe first 0 in the buffer is the end of each message."); -def_mmio!(0x04FF_F700 = MGBA_LOG_SEND: VolAddress; "Write to this each time you want to reset a message (it also resets the buffer)."); -def_mmio!(0x04FF_F780 = MGBA_LOG_ENABLE: VolAddress; "Allows you to attempt to activate mGBA logging."); - -// Palette RAM (PALRAM) - -def_mmio!(0x0500_0000 = BACKDROP_COLOR: VolAddress; "Color that's shown when no BG or OBJ draws to a pixel"); -def_mmio!(0x0500_0000 = BG_PALETTE: VolBlock; "Background tile palette entries."); -def_mmio!(0x0500_0200 = OBJ_PALETTE: VolBlock; "Object tile palette entries."); - -#[inline] -#[must_use] -#[cfg_attr(feature="track_caller", track_caller)] -pub const fn bg_palbank(bank: usize) -> VolBlock { - let u = BG_PALETTE.index(bank * 16).as_usize(); - unsafe { VolBlock::new(u) } -} -#[inline] -#[must_use] -#[cfg_attr(feature="track_caller", track_caller)] -pub const fn obj_palbank(bank: usize) -> VolBlock { - let u = OBJ_PALETTE.index(bank * 16).as_usize(); - unsafe { VolBlock::new(u) } -} - -// Video RAM (VRAM) - -/// The VRAM byte offset per screenblock index. -/// -/// This is the same for all background types and sizes. -pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; - -/// The size of the background tile region of VRAM. -/// -/// Background tile index use will work between charblocks, but not past the end -/// of BG tile memory into OBJ tile memory. -pub const BG_TILE_REGION_SIZE: usize = 64 * 1_024; - -def_mmio!(0x0600_0000 = CHARBLOCK0_4BPP: VolBlock; "Charblock 0, 4bpp view (512 tiles)."); -def_mmio!(0x0600_4000 = CHARBLOCK1_4BPP: VolBlock; "Charblock 1, 4bpp view (512 tiles)."); -def_mmio!(0x0600_8000 = CHARBLOCK2_4BPP: VolBlock; "Charblock 2, 4bpp view (512 tiles)."); -def_mmio!(0x0600_C000 = CHARBLOCK3_4BPP: VolBlock; "Charblock 3, 4bpp view (512 tiles)."); - -def_mmio!(0x0600_0000 = CHARBLOCK0_8BPP: VolBlock; "Charblock 0, 8bpp view (256 tiles)."); -def_mmio!(0x0600_4000 = CHARBLOCK1_8BPP: VolBlock; "Charblock 1, 8bpp view (256 tiles)."); -def_mmio!(0x0600_8000 = CHARBLOCK2_8BPP: VolBlock; "Charblock 2, 8bpp view (256 tiles)."); -def_mmio!(0x0600_C000 = CHARBLOCK3_8BPP: VolBlock; "Charblock 3, 8bpp view (256 tiles)."); - -def_mmio!(0x0600_0000 = TEXT_SCREENBLOCKS: VolGrid2dStrided; "Text mode screenblocks."); - -def_mmio!(0x0600_0000 = AFFINE0_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 0)."); - -def_mmio!(0x0600_0000 = AFFINE1_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 1)."); - -def_mmio!(0x0600_0000 = AFFINE2_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 2)."); - -def_mmio!(0x0600_0000 = AFFINE3_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 3)."); - -def_mmio!(0x0600_0000 = VIDEO3_VRAM: VolGrid2d; "Video mode 3 bitmap"); - -def_mmio!(0x0600_0000 = VIDEO4_VRAM: VolGrid2dStrided; "Video mode 4 palette maps (frames 0 and 1). Each entry is two palette indexes."); - -def_mmio!(0x0600_0000 = VIDEO5_VRAM: VolGrid2dStrided; "Video mode 5 bitmaps (frames 0 and 1)."); - -def_mmio!(0x0601_0000 = OBJ_TILES: VolBlock; "Object tiles. In video modes 3, 4, and 5 only indices 512..=1023 are available."); - -// Object Attribute Memory (OAM) - -def_mmio!(0x0700_0000 = OBJ_ATTR0: VolSeries()}>; "Object attributes 0."); -def_mmio!(0x0700_0002 = OBJ_ATTR1: VolSeries()}>; "Object attributes 1."); -def_mmio!(0x0700_0004 = OBJ_ATTR2: VolSeries()}>; "Object attributes 2."); - -def_mmio!(0x0700_0000 = OBJ_ATTR_ALL: VolSeries()}>; "Object attributes (all in one)."); - -def_mmio!(0x0700_0006 = AFFINE_PARAM_A: VolSeries()}>; "Affine parameters A."); -def_mmio!(0x0700_000E = AFFINE_PARAM_B: VolSeries()}>; "Affine parameters B."); -def_mmio!(0x0700_0016 = AFFINE_PARAM_C: VolSeries()}>; "Affine parameters C."); -def_mmio!(0x0700_001E = AFFINE_PARAM_D: VolSeries()}>; "Affine parameters D."); - -// Cartridge IO port -// https://problemkaputt.de/gbatek.htm#gbacartioportgpio -def_mmio!(0x0800_00C4 = IO_PORT_DATA: VolAddress; "I/O port data"); -def_mmio!(0x0800_00C6 = IO_PORT_DIRECTION: VolAddress; "I/O port direction"); -def_mmio!(0x0800_00C8 = IO_PORT_CONTROL: VolAddress; "I/O port control"); +//! Definitions for Memory-mapped IO (hardware control). + +use voladdress::VolAddress; + +/// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` +/// cargo feature. +#[cfg(feature = "on_gba")] +type SOGBA = voladdress::Safe; +#[cfg(not(feature = "on_gba"))] +type SOGBA = voladdress::Unsafe; + +type PlainAddr = VolAddress; + +/// Interrupt Master Enable +/// +/// * When this is set to `true`, hardware interrupts that are flagged will +/// immediately run the interrupt handler. +/// * When this is `false`, any interrupt events that are flagged will be left +/// pending until this is again set to `true`. +/// +/// This defaults to `false`. +pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; From 502f681c09074bbab421cad25436d53c4fe14c75 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 17:52:10 -0600 Subject: [PATCH 02/89] asm startup incomplete, but can boot a tiny example. --- .cargo/config.toml | 2 - .github/workflows/ci-builds.yml | 3 + Cargo.toml | 21 +- dump.bat | 2 + examples/do_nothing.rs | 9 + linker_scripts/mono_boot.ld | 28 +-- src/asm_runtime.rs | 360 ++++++++++++++++++++++++++++++++ src/critical_section.rs | 37 ++-- src/gba_cell.rs | 16 ++ src/lib.rs | 91 +++++--- src/mmio.rs | 10 +- src/panic_handlers.rs | 7 + src/per_project_setup.rs | 110 ++++++++++ src/per_system_setup.rs | 39 ++++ 14 files changed, 673 insertions(+), 62 deletions(-) create mode 100644 dump.bat create mode 100644 examples/do_nothing.rs create mode 100644 src/asm_runtime.rs create mode 100644 src/gba_cell.rs create mode 100644 src/panic_handlers.rs create mode 100644 src/per_project_setup.rs create mode 100644 src/per_system_setup.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 9b9b1ce2..99678165 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,12 +3,10 @@ target = "thumbv4t-none-eabi" [unstable] build-std = ["core"] -build-std-features = ["compiler-builtins-weak-intrinsics"] [target.thumbv4t-none-eabi] runner = "mgba-qt" rustflags = [ "-Clinker=arm-none-eabi-ld", "-Clink-arg=-Tlinker_scripts/mono_boot.ld", - "--emit=mir", ] diff --git a/.github/workflows/ci-builds.yml b/.github/workflows/ci-builds.yml index c99bd205..d2791fcc 100644 --- a/.github/workflows/ci-builds.yml +++ b/.github/workflows/ci-builds.yml @@ -34,3 +34,6 @@ jobs: - name: Build The Examples (Link With LLD, strong compiler intrinsics) run: cargo build --examples --target=thumbv4t-none-eabi -Zbuild-std=core -Clink-arg=-Tlinker_scripts/mono_boot.ld + + - name: Build The Crate With No Default Features (build script usage simulation) + run: cargo build --no-default-features diff --git a/Cargo.toml b/Cargo.toml index d0e89852..b31b6d32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,12 +7,27 @@ edition = "2021" license = "Zlib OR Apache-2.0 OR MIT" [features] -default = ["on_gba", "critical-section", "doc_cfg"] +default = ["on_gba"] # SEE THE CRATE DOCS FOR SAFETY RELATED INFO REGARDING THIS FEATURE. on_gba = [] -# utilize `doc_cfg` where appropriate. requires nightly. +# utilize `doc_cfg` where appropriate. requires nightly. intended mostly for use +# during docs.rs documentation generation. doc_cfg = [] +# Activates the `track_caller` attribute on various functions. Use of the +# `track_caller` attribute on a function adds a "secret" extra argument for the +# `Location` of the caller, which can reduce performance if the function is not +# inlined (meaning `Location` is passed via the stack). This is only needed for +# debugging, and so it's off by default. track_caller = [] +# The assembly runtime's irq handler will take extra steps to allow nested +# interrupts during calls to the Rust interrupt handler, and also the Rust +# interrupt handler will be called with the CPU in System mode, allowing for +# full stack usage. If you do not use this feature then the Rust IRQ handler +# will be called with the CPU in IRQ mode with nested interrupts disabled at the +# CPU level. IRQ mode still has enough stack space to do any *reasonable* IRQ +# handling, so you do NOT normally need to use this unless you need nested +# interrupt support or extremely large stack usage during the handler. +robust_irq_handler = [] [dependencies] voladdress = "1.3.0" @@ -20,6 +35,7 @@ bitfrob = "1" critical-section = { version = "1.1.2", features = [ "restore-state-bool", ], optional = true } +bytemuck = { version = "1.16.0", optional = true } [profile.dev] opt-level = 3 @@ -38,3 +54,4 @@ incremental = false # on the GBA. This is the closest target that docs.rs supports by default and # which *also* supports the `instruction_set` attribute to avoid build errors. targets = ["armv5te-unknown-linux-gnueabi"] +features = ["doc_cfg"] diff --git a/dump.bat b/dump.bat new file mode 100644 index 00000000..eb915322 --- /dev/null +++ b/dump.bat @@ -0,0 +1,2 @@ +cargo build --examples +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/do_nothing >target/ex-do_nothing.txt diff --git a/examples/do_nothing.rs b/examples/do_nothing.rs new file mode 100644 index 00000000..b20e9a88 --- /dev/null +++ b/examples/do_nothing.rs @@ -0,0 +1,9 @@ +#![no_std] +#![no_main] + +gba::panic_handler!(empty_loop); + +#[no_mangle] +pub extern "C" fn main() -> ! { + loop {} +} diff --git a/linker_scripts/mono_boot.ld b/linker_scripts/mono_boot.ld index ff0233b8..045323a1 100644 --- a/linker_scripts/mono_boot.ld +++ b/linker_scripts/mono_boot.ld @@ -1,6 +1,6 @@ /* THIS LINKER SCRIPT FILE IS RELEASED TO THE PUBLIC DOMAIN (SPDX: CC0-1.0) */ -ENTRY(__start) +ENTRY(_start) MEMORY { ewram (w!x) : ORIGIN = 0x2000000, LENGTH = 256K @@ -11,7 +11,7 @@ MEMORY { SECTIONS { .text : { /* be sure that the ROM header is the very first */ - *(.text.gba_rom_header); + *(.text._start); *(.text .text.*); . = ALIGN(4); } >rom = 0x00 @@ -22,42 +22,42 @@ SECTIONS { } >rom = 0x00 . = ALIGN(4); - __iwram_position_in_rom = .; + _iwram_position_in_rom = .; .data : { - __iwram_start = ABSOLUTE(.); + _iwram_start = ABSOLUTE(.); *(.data .data.*); *(.iwram .iwram.*); . = ALIGN(4); - __iwram_end = ABSOLUTE(.); + _iwram_end = ABSOLUTE(.); } >iwram AT>rom = 0x00 . = ALIGN(4); - __ewram_position_in_rom = __iwram_position_in_rom + (__iwram_end - __iwram_start); + _ewram_position_in_rom = _iwram_position_in_rom + (_iwram_end - _iwram_start); .ewram : { - __ewram_start = ABSOLUTE(.); + _ewram_start = ABSOLUTE(.); *(.ewram .ewram.*); . = ALIGN(4); - __ewram_end = ABSOLUTE(.); + _ewram_end = ABSOLUTE(.); } >ewram AT>rom = 0x00 . = ALIGN(4); - __bss_position_in_rom = __ewram_position_in_rom + (__ewram_end - __ewram_start); + _bss_position_in_rom = _ewram_position_in_rom + (_ewram_end - _ewram_start); .bss : { - __bss_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); *(.bss .bss.*); . = ALIGN(4); - __bss_end = ABSOLUTE(.); + _bss_end = ABSOLUTE(.); } >iwram - __iwram_word_copy_count = (__iwram_end - __iwram_start) / 4; - __ewram_word_copy_count = (__ewram_end - __ewram_start) / 4; - __bss_word_clear_count = (__bss_end - __bss_start) / 4; + _iwram_word_copy_count = (_iwram_end - _iwram_start) / 4; + _ewram_word_copy_count = (_ewram_end - _ewram_start) / 4; + _bss_word_clear_count = (_bss_end - _bss_start) / 4; /* rust-lld demands we keep the `section header string table` */ .shstrtab 0 : { *(.shstrtab) } diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs new file mode 100644 index 00000000..c3ed7445 --- /dev/null +++ b/src/asm_runtime.rs @@ -0,0 +1,360 @@ +#![allow(unused_macros)] + +//! Assembly runtime and support functions for the GBA. + +// Note(Lokathor): Functions here will *definitely* panic without the `on_gba` +// cargo feature enabled, and so they should all have the `track_caller` +// attribute set whenever the `on_gba` feature is *disabled* + +use crate::gba_cell::GbaCell; + +macro_rules! on_gba_or_unimplemented { + ($($token_tree:tt)*) => { + #[cfg(feature="on_gba")] + { + $($token_tree)* + } + #[cfg(not(feature="on_gba"))] + unimplemented!() + } +} + +/// Inserts a `nop` instruction. +#[inline(always)] +#[cfg_attr(not(feature = "on_gba"), track_caller)] +pub fn nop() { + on_gba_or_unimplemented! { + unsafe { + core::arch::asm! { + "nop", + } + } + } +} + +/// Atomically swap `x` and the 32-bit value stored at `ptr`. +/// +/// ## Safety +/// This both reads and writes `ptr`, so all the usual rules of that apply. +#[inline] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +#[cfg_attr(not(feature = "on_gba"), track_caller)] +pub unsafe fn swp(mut ptr: *mut u32, x: u32) -> u32 { + on_gba_or_unimplemented! { + let output: u32; + // Note(Lokathor): This won't actually alter the pointer register, but we + // *tell* LLVM that it will because the pointer register can't be used as + // the output of the swapping operation. + #[allow(unused_assignments)] + unsafe { + core::arch::asm! { + "swp {output}, {input}, [{addr}]", + output = lateout(reg) output, + input = in(reg) x, + addr = inlateout(reg) ptr, + } + } + output + } +} + +/// Atomically swap `x` and the 8-bit value stored at `ptr`. +/// +/// ## Safety +/// This both reads and writes `ptr`, so all the usual rules of that apply. +#[inline] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +#[cfg_attr(not(feature = "on_gba"), track_caller)] +pub unsafe fn swpb(mut ptr: *mut u8, x: u8) -> u8 { + on_gba_or_unimplemented! { + let output: u8; + // Note(Lokathor): This won't actually alter the pointer register, but we + // *tell* LLVM that it will because the pointer register can't be used as + // the output of the swapping operation. + #[allow(unused_assignments)] + unsafe { + core::arch::asm! { + "swpb {output}, {input}, [{addr}]", + output = lateout(reg) output, + input = in(reg) x, + addr = inlateout(reg) ptr, + } + } + output + } +} + +#[cfg(target_feature = "thumb-mode")] +macro_rules! a32_code { + ($($asm_line:expr),+ $(,)?) => { + concat!( + ".code 32\n", + + $( concat!($asm_line, "\n") ),+ , + + ".code 16\n", + ) + } +} +#[cfg(not(target_feature = "thumb-mode"))] +macro_rules! a32_code { + ($($asm_line:expr),+ $(,)?) => { + concat!( + $( concat!($asm_line, "\n") ),+ , + ) + } +} + +/// If `on_gba` is enabled, makes a `global_asm` for the function given. +/// +/// If `on_gba` is disabled, this does nothing. +macro_rules! global_a32_fn { + ( + $name:ident [iwram=true] { + $($asm_line:expr),+ $(,)? + } + ) => { + #[cfg(feature = "on_gba")] + core::arch::global_asm!{ + a32_code! { + concat!(".section .iwram.text.", stringify!($name), ", \"x\" "), + concat!(".global ",stringify!($name)), + concat!(stringify!($name),":"), + $( concat!($asm_line, "\n") ),+ , + ".pool", + } + } + }; + ( + $name:ident [] { + $($asm_line:expr),+ $(,)? + } + ) => { + #[cfg(feature = "on_gba")] + core::arch::global_asm!{ + a32_code! { + concat!(".section .text.", stringify!($name), ", \"x\" "), + concat!(".global ",stringify!($name)), + concat!(stringify!($name),":"), + $( concat!($asm_line, "\n") ),+ , + ".pool", + } + } + }; +} + +macro_rules! while_swapped { + ( + ptr=$ptr:literal, val=$val:literal { + $($asm_line:expr),+ $(,)? + } + ) => { + concat!( + concat!("swp ",$val,", ",$val,", [",$ptr,"]\n"), + + $( concat!($asm_line, "\n") ),+ , + + concat!("swp ",$val,", ",$val,", [",$ptr,"]\n"), + ) + } +} + +macro_rules! with_spsr_held_in { + ($reg:literal, { + $($asm_line:expr),* $(,)? + }) => { + concat!( + concat!("mrs ", $reg, ", SPSR\n"), + $( concat!($asm_line, "\n") ),* , + concat!("msr SPSR, ", $reg, "\n"), + ) + } +} + +macro_rules! set_cpu_control { + // CPSR control bits are: `I F T MMMMM`, and T must always be left as 0. + // * 0b10011: Supervisor (SVC) + // * 0b11111: System (SYS) + (System, irq_masked: false, fiq_masked: false) => { + "msr CPSR_c, #0b00011111\n" + }; + (Supervisor, irq_masked: true, fiq_masked: false) => { + "msr CPSR_c, #0b10010010\n" + }; +} + +macro_rules! when { + ($reg:literal == $op2:literal [label_id=$label:literal] { + $($asm_line:expr),* $(,)? + }) => { + concat!( + concat!("cmp ", $reg, ", ", $op2, "\n"), + concat!("bne ", $label, "f\n"), + $( concat!($asm_line, "\n") ),* , + concat!($label, ":\n"), + ) + }; + ($reg:literal != $op2:literal [label_id=$label:literal] { + $($asm_line:expr),* $(,)? + }) => { + concat!( + concat!("cmp ", $reg, ", ", $op2, "\n"), + concat!("beq ", $label, "f\n"), + $( concat!($asm_line, "\n") ),* , + concat!($label, ":\n"), + ) + }; + ($reg:literal >=u $op2:literal [label_id=$label:literal] { + $($asm_line:expr),* $(,)? + }) => { + concat!( + concat!("cmp ", $reg, ", ", $op2, "\n"), + concat!("bcc ", $label, "f\n"), // cc: Unsigned LT + $( concat!($asm_line, "\n") ),* , + concat!($label, ":\n"), + ) + }; + ($reg:literal <=u $op2:literal [label_id=$label:literal] { + $($asm_line:expr),* $(,)? + }) => { + concat!( + concat!("cmp ", $reg, ", ", $op2, "\n"), + concat!("bhi ", $label, "f\n"), // hi: Unsigned GT + $( concat!($asm_line, "\n") ),* , + concat!($label, ":\n"), + ) + }; +} + +/// Sets `lr` properly and then uses `bx` on the register given. +macro_rules! a32_fake_blx { + (reg=$reg_name:expr, label_id=$label:expr) => { + concat!( + concat!("adr lr, ", $label, "f\n"), + concat!("bx ", $reg_name, "\n"), + concat!($label, ":\n"), + ) + }; +} + +macro_rules! with_pushed_registers { + ($reglist:expr, { + $($asm_line:expr),* $(,)? + }) => { + concat!( + concat!("push ", $reglist, "\n"), + $( concat!($asm_line, "\n") ),* , + concat!("pop ", $reglist, "\n"), + ) + } +} + +global_a32_fn! {_start [] { + "b 1f", + ".space 0xE0", + "1:", + + "mov r12, #0x04000000", + + // Configure WAITCNT to the GBATEK suggested default + "add r0, r12, #0x204", + "ldr r1, =0x4317", + "strh r1, [r0]", + + // TODO: iwram copying + + // TODO: ewram copying + + // TODO: bss zeroing + + // Tell the BIOS about our irq handler + "ldr r0, =_asm_runtime_irq_handler", + "str r0, [r12, #-4]", + + // Note(Lokathor): we do a `bx` instead of a `b` because it saves 4 *entire* + // bytes (!), since `main` will usually be a t32 function and thus usually + // requires a linker shim to call. + "ldr r0, =main", + "bx r0", + + // TODO: should we soft reset or something if `main` returns? +}} + +#[cfg(not(feature = "robust_irq_handler"))] +global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { + // handle MMIO interrupt system + "ldr r0, [r12, #-8] /* read IE_IF */", + "and r0, r0, r0, LSR #16 /* r0 = IE & IF */", + "strh r0, [r12, #-6] /* write IF */", + + // Now the interrupt bits are in r0 as a `u16` + + // handle BIOS interrupt system + "sub r2, r12, #(0x208+8) /* BIOS_IF address */", + "ldrh r1, [r2] /* read the `has_occurred` flags */", + "orr r1, r1, r0 /* activate the new bits, if any */", + "strh r1, [r2] /* update the value */", + + // Get the user handler fn pointer, call it if non-null. + "ldr r1, =USER_IRQ_HANDLER", + "ldr r1, [r1]", + when!("r1" != "#0" [label_id=9] { + with_pushed_registers!("{{r0, lr}}", { + a32_fake_blx!(reg="r1", label_id=1), + }), + }), + + // return to the BIOS + "bx lr", +}} + +#[cfg(feature = "robust_irq_handler")] +global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { + // Suppress IME while this is running. If the user wants to allow for + // interrupts *during* other interrupts they can enable IME in their handler. + "add r12, r0, #0x208", + "mov r3, #0", + while_swapped! { ptr="r12", val="r3" { + // handle MMIO interrupt system + "ldr r0, [r12, #-8] /* read IE_IF */", + "and r0, r0, r0, LSR #16 /* r0 = IE & IF */", + "strh r0, [r12, #-6] /* write IF */", + + // Now the interrupt bits are in r0 as a `u16` + + // handle BIOS interrupt system + "sub r2, r12, #(0x208+8) /* BIOS_IF address */", + "ldrh r1, [r2] /* read the `has_occurred` flags */", + "orr r1, r1, r0 /* activate the new bits, if any */", + "strh r1, [r2] /* update the value */", + + // Get the user handler fn pointer, call it if non-null. + "ldr r1, =USER_IRQ_HANDLER", + "ldr r1, [r1]", + when!("r1" != "#0" [label_id=9] { + with_spsr_held_in!("r2", { + // We have to preserve: + // * r2: spsr + // * r3: old IME value + // * r12: IME address + // * lr: our handler return address + with_pushed_registers!("{{r2, r3, r12, lr}}", { + // Note(Lokathor): LLVM won't ever leave the stack alignment as less + // than 8 so we skip trying to align it to 8 by hand. + set_cpu_control!(System, irq_masked: false, fiq_masked: false), + a32_fake_blx!(reg="r1", label_id=1), + set_cpu_control!(Supervisor, irq_masked: true, fiq_masked: false), + }), + }) + }), + }}, + + // return to the BIOS + "bx lr", +}} + +/// The user-provided interrupt request handler function. +#[no_mangle] +#[cfg(feature = "on_gba")] +pub static USER_IRQ_HANDLER: GbaCell> = + GbaCell::new(None); diff --git a/src/critical_section.rs b/src/critical_section.rs index abd33e6b..4976e2fc 100644 --- a/src/critical_section.rs +++ b/src/critical_section.rs @@ -5,20 +5,33 @@ use critical_section::{set_impl, Impl, RawRestoreState}; use crate::mmio::IME; struct GbaCriticalSection; +#[cfg(feature = "on_gba")] set_impl!(GbaCriticalSection); #[cfg(feature = "on_gba")] unsafe impl Impl for GbaCriticalSection { - /// # Safety - /// This function has no pre-conditions. + /// ## Safety + /// * This function has no pre-conditions. + /// * This uses `IME` to disable interrupts. Technically there's a 2 CPU cycle + /// delay between `IME` being disabled and interrupts actually being unable + /// to run. This function is marked `inline(never)`, so just the time it + /// takes the CPU to return from the function should be enough to prevent + /// any problems. Technically that's "only a hint" though. Even then, any + /// code running in ROM will generally be slow enough for is to not matter + /// just because of ROM access speeds. If your code is running in IWRAM and + /// you wanted to be absolutely paranoid you could insert two calls to + /// [`nop`][crate::asm_runtime::nop] after calling this function. + /// Personally, even then I wouldn't bother. + #[inline(never)] unsafe fn acquire() -> RawRestoreState { let restore = IME.read(); IME.write(false); restore } - /// # Safety - /// This function has no pre-conditions. + /// ## Safety + /// * This function has no pre-conditions. + #[inline] unsafe fn release(restore: RawRestoreState) { IME.write(restore); } @@ -26,17 +39,17 @@ unsafe impl Impl for GbaCriticalSection { #[cfg(not(feature = "on_gba"))] unsafe impl Impl for GbaCriticalSection { - /// # Safety - /// This function will always panic, so I guess you could say it's always - /// safe. + /// ## Safety + /// * This function will always panic. + #[track_caller] unsafe fn acquire() -> RawRestoreState { - panic!() + unimplemented!() } - /// # Safety - /// This function will always panic, so I guess you could say it's always - /// safe. + /// ## Safety + /// * This function will always panic. + #[track_caller] unsafe fn release(restore: RawRestoreState) { - panic!() + unimplemented!() } } diff --git a/src/gba_cell.rs b/src/gba_cell.rs new file mode 100644 index 00000000..102bc634 --- /dev/null +++ b/src/gba_cell.rs @@ -0,0 +1,16 @@ +//! Provides the [`GbaCell`] type. + +/// A "cell" type suitable to hold a global on the GBA. +#[repr(transparent)] +pub struct GbaCell(core::cell::UnsafeCell); + +#[cfg(feature = "on_gba")] +#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "on_gba")))] +unsafe impl Sync for GbaCell {} + +impl GbaCell { + /// Constructs a new cell with the value given + pub const fn new(t: T) -> Self { + Self(core::cell::UnsafeCell::new(t)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 66072722..271dbe66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(not(feature = "on_gba"), allow(unused))] #![warn(missing_docs)] #![warn(unsafe_op_in_unsafe_fn)] #![cfg_attr(feature = "doc_cfg", feature(doc_cfg))] @@ -11,41 +12,73 @@ //! type system by passing around zero-sized token types, try the //! [agb](https://docs.rs/agb) crate instead. //! -//! # Crate Features +//! # This Is Intended For The Game Boy Advance //! -//! * `on_gba` (**Default:** enabled): When this feature is used, the crate -//! assumes that you're building the crate for, and running the code on, the -//! Game Boy Advance. The build target is expected to be `thumbv4t-none-eabi` -//! or `armv4t-none-eabi`, any other targets may have a build error. Further, -//! the specific device is assumed to be the GBA, which is used to determine -//! the safety of all direct hardware access using MMIO. This feature is on by -//! default because the primary purpose of this crate is to assist in the -//! building of GBA games, but you *can* disable the feature and build the -//! crate anyway, such as if you want to use any of the crate's data type -//! definitions within a build script on your host machine. When this feature -//! is disabled, GBA specific internals of functions *may* be replaced with -//! runtime panics when necessary. How much of this crate actually works on -//! non-GBA platforms is **not** covered by our SemVer! -//! * `critical-section` (**Default:** enabled): activates an implementation to -//! support for the [critical-section](https://docs.rs/critical-section) -//! crate. -//! * `track_caller` (**Default:** disabled): Causes some functions that can -//! panic to add the [track_caller][ref-track-caller] attribute. The attribute -//! adds a "secret" function argument to pass the `Location` of the call, so -//! it can reduce performance when a function is not inlined (more data has to -//! be pushed onto the stack per function call). Suggested for debugging only. +//! When the `on_gba` crate feature is used, the crate assumes that you're +//! building the crate for, and also running the code on, the Game Boy Advance. +//! The build target is expected to be `thumbv4t-none-eabi` or +//! `armv4t-none-eabi`, and any other targets might have a build error. Further, +//! the specific device you run the code on is assumed to be the GBA (or a GBA +//! emulator). These facts are used by the `unsafe` code in this crate. //! -//! [ref-track-caller]: -//! https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute +//! This crate feature is **on by default** because the primary purpose of this +//! crate is to assist in the building of GBA games, but you *can* disable the +//! feature and build the crate anyway. How much of this crate actually works on +//! non-GBA platforms is **not** covered by our SemVer! Building and using the +//! crate without the `on_gba` feature is intended for non-GBA code that wants +//! the data type definitions the crate provides, such as a build script running +//! on your development machine. Without the `on_gba` feature enabled, any GBA +//! specific functions that "don't make sense" outside of a GBA context (such as +//! functions using inline assembly) will just be `unimplemented!()`, and +//! calling them will trigger a panic. //! -//! # Additional Information -//! -//! * Development Environment Setup -//! * Project Setup -//! * Learning GBA Programming +//! If you're not familiar with GBA programming some explanations are provided +//! on separate pages: +//! * [Per System Setup][`per_system_setup`] +//! * [Per Project Setup][`per_project_setup`] +pub mod asm_runtime; +pub mod gba_cell; pub mod mmio; +pub mod panic_handlers; +pub mod per_project_setup; +pub mod per_system_setup; #[cfg(feature = "critical-section")] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "critical-section")))] pub mod critical_section; + +/// Declares one of the functions in the [`panic_handlers`] module to be the +/// handler for your program. +/// +/// Valid inputs are the name of any of the functions in that module: +/// * [`empty_loop`][crate::panic_handlers::empty_loop] +/// +/// There's no special magic here, it just saves you on typing it all out +/// yourself. +#[macro_export] +macro_rules! panic_handler { + ($i:ident) => { + #[panic_handler] + fn panic_handler(info: &core::panic::PanicInfo) -> ! { + gba::panic_handlers::$i(info) + } + }; +} + +/// A color value. +/// +/// This is a bit-packed linear RGB color value with 5 bits per channel: +/// ```text +/// 0bX_BBBBB_GGGGG_RRRRR +/// ``` +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct Color(pub u16); + +#[cfg(feature = "bytemuck")] +#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] +unsafe impl bytemuck::Zeroable for Color {} +#[cfg(feature = "bytemuck")] +#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] +unsafe impl bytemuck::Pod for Color {} diff --git a/src/mmio.rs b/src/mmio.rs index d1455378..d151e3fc 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -1,13 +1,14 @@ //! Definitions for Memory-mapped IO (hardware control). -use voladdress::VolAddress; +#[allow(unused_imports)] +use voladdress::{Safe, Unsafe, VolAddress}; /// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` /// cargo feature. #[cfg(feature = "on_gba")] -type SOGBA = voladdress::Safe; +type SOGBA = Safe; #[cfg(not(feature = "on_gba"))] -type SOGBA = voladdress::Unsafe; +type SOGBA = Unsafe; type PlainAddr = VolAddress; @@ -19,4 +20,7 @@ type PlainAddr = VolAddress; /// pending until this is again set to `true`. /// /// This defaults to `false`. +/// +/// Technically there's a two CPU cycle delay between this being written and +/// interrupts actually being enabled/disabled. In practice, it doesn't matter. pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; diff --git a/src/panic_handlers.rs b/src/panic_handlers.rs new file mode 100644 index 00000000..d9f37da1 --- /dev/null +++ b/src/panic_handlers.rs @@ -0,0 +1,7 @@ +//! Various panic handler functions that you might find useful. + +/// Just performs an empty `loop` +#[inline] +pub fn empty_loop(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/per_project_setup.rs b/src/per_project_setup.rs new file mode 100644 index 00000000..101f3780 --- /dev/null +++ b/src/per_project_setup.rs @@ -0,0 +1,110 @@ +//! Explanation of suggested setup per GBA project. +//! +//! ## Cargo Configuration +//! +//! We need to use cargo's unstable `build-std` ability to build the +//! `thumbv4t-none-eabi` target, and we'll want to have that be the default +//! target for basically everything we do. +//! +//! You'll want a GBA emulator for testing your GBA rom. I use +//! [mGBA](https://mgba.io/), but others also have success with +//! [No$GBA](https://www.nogba.com/). Whatever you pick, you should set it as +//! the `runner` under `[target.thumbv4t-none-eabi]` +//! +//! As discussed in the [Per System Setup][crate::per_system_setup], only the +//! GNU `objdump` will be able to correctly dump a GBA program. It's possible to +//! use the LLVM linker and only GNU for `objdump`, but the GNU `objdump` won't +//! be able to show all info if you mix utilities like this, because (i guess?) +//! the GNU `objdump` doesn't understand LLVM linker info format. Using the GNU +//! linker will make sure you can see all the debug info. I've never noticed a +//! linking speed difference between LLVM and GNU on the GBA, programs just +//! don't get that big. Put `-Clinker=arm-none-eabi-ld` into the `rustflags` for +//! our target and we'll get the GNU linker. +//! +//! Regardless of if you're using the LLVM or GNU linker, you'll need a linker +//! script. The [Github Repo][github_script] for this project has a usable +//! linker script. You should save this file into your own project, such as in a +//! folder called `linker_scripts/` under the name `mono_boot.ld`. Then we add a +//! `rustflags` argument with `-Clink-arg=-T`, using the script's path +//! relative to our project root. +//! +//! [github_script]: +//! https://github.com/rust-console/gba/blob/main/linker_scripts/mono_boot.ld +//! +//! Your `.cargo/config.toml` should probably look like the following: +//! ```toml +//! [build] +//! target = "thumbv4t-none-eabi" +//! +//! [unstable] +//! build-std = ["core"] +//! +//! [target.thumbv4t-none-eabi] +//! runner = "mgba-qt" +//! rustflags = [ +//! "-Clinker=arm-none-eabi-ld", +//! "-Clink-arg=-Tlinker_scripts/mono_boot.ld", +//! ] +//! ``` +//! +//! * If you want the `compiler_builtins` crate to provide weak intrinsics so +//! that you can override them yourself, you can set `build-std-features = +//! ["compiler-builtins-weak-intrinsics"]` in the `[unstable]` section. This +//! is not normally needed, but if you some day try to write your own +//! intrinsics impls you'll want to know it's available. +//! +//! ## Rust Analyzer +//! +//! The `test` crate won't be available when using `build-std` like this. To +//! make Rust Analyzer work without constantly complaining about a missing +//! `test` crate, add this to your `.vscode/settings.json` (or whatever similar +//! file in your editor of choice) +//! +//! ```json +//! { +//! "rust-analyzer.cargo.allTargets": false, +//! "rust-analyzer.check.command": "build", +//! "rust-analyzer.check.extraArgs": [ +//! "--lib", +//! "--bins", +//! "--examples" +//! ] +//! } +//! ``` +//! +//! ## `no_std` and `no_main` +//! +//! The full standard library isn't available on the GBA, it's a [bare metal] +//! environment, so we'll only have access to the [`core`] crate. +//! +//! * You'll need to use the [`#![no_std]`][no_std] and [`#![no_main]`][no_main] +//! attributes on your binary. +//! * You'll also need to declare a [panic handler][panic_handler]. You can do +//! this yourself or you can use the [`panic_handler!`][crate::panic_handler] +//! macro from this crate to type a little less. +//! * You need to provide a correct `main` function for the assembly runtime to +//! call once it has made the GBA ready for your Rust program. It needs to be +//! an `extern "C" fn` named `main` (using [`#![no_mangle]`][no_mangle]), and +//! which takes no arguments and never returns (`-> !`). +//! +//! Here's a minimal example: +//! ```rust +//! #![no_std] +//! #![no_main] +//! +//! gba::panic_handler!(empty_loop); +//! +//! #[no_mangle] +//! pub fn main() -> ! { +//! loop {} +//! } +//! ``` +//! +//! [bare metal]: +//! https://docs.rust-embedded.org/book/intro/no-std.html#bare-metal-environments +//! [no_std]: +//! https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute +//! [no_main]: +//! https://doc.rust-lang.org/reference/crates-and-source-files.html#the-no_main-attribute +//! [no_mangle]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute +//! [panic_handler]: https://doc.rust-lang.org/nomicon/panic-handler.html diff --git a/src/per_system_setup.rs b/src/per_system_setup.rs new file mode 100644 index 00000000..d8d86601 --- /dev/null +++ b/src/per_system_setup.rs @@ -0,0 +1,39 @@ +//! Explanation of required setup per development machine. +//! +//! ## GNU Binutils +//! +//! The linker that comes with Rust (part of the LLVM utils) is capable of +//! linking a GBA rom with no special steps. However, the *other* LLVM binutils +//! do not all understand the "interworking" code that exists on the GBA and +//! similar old ARM targets. +//! +//! If you want to be able to dump the assembly of a compiled file and have it +//! all be readable, you will need to get the GNU objdump that's available as +//! part of the GNU binutils. If you just use the LLVM objdump then any +//! functions using `a32` code will get disassembled as complete nonsense. +//! +//! The GNU binutils are configured for each target family separately, and in +//! this case we want the binutils for `arm-none-eabi`. To get an appropriate +//! set of utilities you can go to the [ARM GNU Toolchain] website (Windows, +//! Mac, and Linux downloads), or if you're on Linux you can probably find it in +//! your package manager under a name like `binutils-arm-none-eabi` or something +//! like that. +//! +//! [ARM GNU Toolchain]: +//! https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain +//! +//! ## Nightly Rust Toolchain +//! +//! You'll need a [Nightly channel][channels] of Rust available, because we'll +//! need to use the `build-std` nightly ability of `cargo`. +//! +//! [channels]: https://rust-lang.github.io/rustup/concepts/channels.html +//! +//! ## Rust Source +//! +//! You'll need to have the `rust-src` component from `rustup` installed. This +//! is also used by `build-std`. +//! +//! ```sh +//! rustup component add rust-src +//! ``` From 316792692d19dbe36290290f9fe17b7e4cb42add Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 17:57:52 -0600 Subject: [PATCH 03/89] CI fix --- .github/workflows/ci-builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-builds.yml b/.github/workflows/ci-builds.yml index d2791fcc..844b59d0 100644 --- a/.github/workflows/ci-builds.yml +++ b/.github/workflows/ci-builds.yml @@ -33,7 +33,7 @@ jobs: run: rm -fr .cargo/config.toml - name: Build The Examples (Link With LLD, strong compiler intrinsics) - run: cargo build --examples --target=thumbv4t-none-eabi -Zbuild-std=core -Clink-arg=-Tlinker_scripts/mono_boot.ld + run: RUSTFLAGS="-Clink-arg=-Tlinker_scripts/mono_boot.ld" cargo build --examples --target=thumbv4t-none-eabi -Zbuild-std=core - name: Build The Crate With No Default Features (build script usage simulation) run: cargo build --no-default-features From a7970748ab2c4b1064ae7ca47bb844133c64fba4 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 18:00:36 -0600 Subject: [PATCH 04/89] fix up the changelog again. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd5a05f..f2a278d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.12 +* A totally new version! + +## 0.11 and older + * **0.11.6:** * `on_gba` feature (default: enabled) that signals if the crate is running on a GBA. Limited portions of the crate *can* be used even when not on the GBA (such as in a build script). From d1269d1f9fd47e9b091acd81fe3169b6d86792fd Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 18:28:05 -0600 Subject: [PATCH 05/89] optionally use the `fixed` crate. --- .github/workflows/ci-builds.yml | 3 + Cargo.toml | 1 + src/gba_fixed.rs | 342 ++++++++++++++++++++++++++++++++ src/lib.rs | 57 ++++++ 4 files changed, 403 insertions(+) create mode 100644 src/gba_fixed.rs diff --git a/.github/workflows/ci-builds.yml b/.github/workflows/ci-builds.yml index 844b59d0..226da783 100644 --- a/.github/workflows/ci-builds.yml +++ b/.github/workflows/ci-builds.yml @@ -28,6 +28,9 @@ jobs: - name: Build The Examples (Link With GNU, weak compiler intrinsics) run: cargo build --examples + + - name: Check that the `fixed` crate support is fine + run: cargo build --examples --features=fixed - name: Delete the .cargo/config.toml run: rm -fr .cargo/config.toml diff --git a/Cargo.toml b/Cargo.toml index b31b6d32..bf142103 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ critical-section = { version = "1.1.2", features = [ "restore-state-bool", ], optional = true } bytemuck = { version = "1.16.0", optional = true } +fixed = { version = "1.27.0", default-features = false, optional = true } [profile.dev] opt-level = 3 diff --git a/src/gba_fixed.rs b/src/gba_fixed.rs new file mode 100644 index 00000000..b61cfbcb --- /dev/null +++ b/src/gba_fixed.rs @@ -0,0 +1,342 @@ +//! Basic fixed point math module used as a fallback if the `fixed` crate isn't +//! included. + +use core::ops::*; + +/// A [fixed-point][wp-fp] number. This transparently wraps an integer with a +/// const generic for how many bits are fractional. +/// +/// [wp-fp]: https://en.wikipedia.org/wiki/Fixed-point_arithmetic +/// +/// * This type is generic, but the `I` type is intended to be a signed or +/// unsigned integer of a fixed bit size: `i8`, `i16`, `i32`, `u8`, `u16`, or +/// `u32`. This type is *not* semver supported to work with any other `I` +/// type. If it does work for other types of `I`, that's on accident. +/// * The `B` value is the number of bits that form the fractional part. It +/// should be *less than* the number of bits in the integer's type. Multiply +/// and divide ops need to shift the value by `B`, and so if `B` is greater +/// than or equal to the integer's size the op will panic. +#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct Fixed(I); + +macro_rules! impl_trait_op_unit { + ($t:ty, $trait:ident, $op:ident) => { + impl $trait for Fixed<$t, B> { + type Output = Self; + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + fn $op(self) -> Self::Output { + Self::$op(self) + } + } + }; +} +macro_rules! impl_trait_op_self_rhs { + ($t:ty, $trait:ident, $op:ident) => { + impl $trait for Fixed<$t, B> { + type Output = Self; + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + fn $op(self, rhs: Self) -> Self::Output { + Self::$op(self, rhs) + } + } + }; +} +macro_rules! impl_trait_op_assign_self_rhs { + ($t:ty, $trait:ident, $op:ident, $op_assign:ident) => { + impl $trait for Fixed<$t, B> { + #[inline] + #[cfg_attr(feature = "track_caller", track_caller)] + fn $op_assign(&mut self, rhs: Self) { + *self = self.$op(rhs); + } + } + }; +} +macro_rules! impl_shift_self_u32 { + ($t:ty, $trait:ident, $op:ident) => { + impl $trait for Fixed<$t, B> { + type Output = Self; + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + fn $op(self, rhs: u32) -> Self::Output { + Self::$op(self, rhs) + } + } + }; +} +macro_rules! impl_shift_assign_self_u32 { + ($t:ty, $trait:ident, $op:ident, $op_assign:ident) => { + impl $trait for Fixed<$t, B> { + #[inline] + #[cfg_attr(feature = "track_caller", track_caller)] + fn $op_assign(&mut self, rhs: u32) { + *self = self.$op(rhs); + } + } + }; +} + +macro_rules! impl_common_fixed_ops { + ($t:ty) => { + impl Fixed<$t, B> { + /// Shifts the value left by `B`, wrapping it into the range of this Fixed + /// type. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn wrapping_from(i: $t) -> Self { + Self(i << B) + } + + /// Makes a `Fixed` directly from a raw inner value (no shift). + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn from_raw(i: $t) -> Self { + Self(i) + } + + /// Unwraps the inner value directly into the base type (no shift). + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn into_raw(self) -> $t { + self.0 + } + + /// Bitwise Not. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn not(self) -> Self { + Self(!self.0) + } + + /// Addition. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } + + /// Subtraction. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn sub(self, rhs: Self) -> Self { + Self(self.0 - rhs.0) + } + + /// Remainder. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn rem(self, rhs: Self) -> Self { + Self(self.0 % rhs.0) + } + + /// Bitwise AND. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn bitand(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } + + /// Bitwise OR. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn bitor(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } + + /// Bitwise XOR. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn bitxor(self, rhs: Self) -> Self { + Self(self.0 ^ rhs.0) + } + + /// Bit-shift Left. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn shl(self, rhs: u32) -> Self { + Self(self.0 << rhs) + } + + /// Bit-shift Right. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn shr(self, rhs: u32) -> Self { + Self(self.0 >> rhs) + } + } + impl_trait_op_unit!($t, Not, not); + impl_trait_op_self_rhs!($t, Add, add); + impl_trait_op_self_rhs!($t, Sub, sub); + impl_trait_op_self_rhs!($t, Mul, mul); + impl_trait_op_self_rhs!($t, Div, div); + impl_trait_op_self_rhs!($t, Rem, rem); + impl_trait_op_self_rhs!($t, BitAnd, bitand); + impl_trait_op_self_rhs!($t, BitOr, bitor); + impl_trait_op_self_rhs!($t, BitXor, bitxor); + impl_shift_self_u32!($t, Shl, shl); + impl_shift_self_u32!($t, Shr, shr); + impl_trait_op_assign_self_rhs!($t, AddAssign, add, add_assign); + impl_trait_op_assign_self_rhs!($t, SubAssign, sub, sub_assign); + impl_trait_op_assign_self_rhs!($t, MulAssign, mul, mul_assign); + impl_trait_op_assign_self_rhs!($t, DivAssign, div, div_assign); + impl_trait_op_assign_self_rhs!($t, RemAssign, rem, rem_assign); + impl_trait_op_assign_self_rhs!($t, BitAndAssign, bitand, bitand_assign); + impl_trait_op_assign_self_rhs!($t, BitOrAssign, bitor, bitor_assign); + impl_trait_op_assign_self_rhs!($t, BitXorAssign, bitxor, bitxor_assign); + impl_shift_assign_self_u32!($t, ShlAssign, shl, shl_assign); + impl_shift_assign_self_u32!($t, ShrAssign, shr, shr_assign); + }; +} +impl_common_fixed_ops!(i8); +impl_common_fixed_ops!(i16); +impl_common_fixed_ops!(i32); +impl_common_fixed_ops!(u8); +impl_common_fixed_ops!(u16); +impl_common_fixed_ops!(u32); + +macro_rules! impl_signed_fixed_ops { + ($t:ty, $unsigned:ty) => { + impl Fixed<$t, B> { + /// Negate. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn neg(self) -> Self { + Self(-self.0) + } + + /// If the number is negative or not. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn is_negative(self) -> bool { + self.0 < 0 + } + + /// Multiply. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn mul(self, rhs: Self) -> Self { + let raw = (self.0 as i32) * (rhs.0 as i32); + Self((raw >> B) as $t) + } + + /// Divide. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn div(self, rhs: Self) -> Self { + let m = (self.0 as i32) * (1 << B); + let d = m / (rhs.0 as i32); + Self(d as $t) + } + + /// Fractional part of the value. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn fract(self) -> Self { + let frac_mask = (<$unsigned>::MAX >> (<$t>::BITS - B)); + Self((self.0.unsigned_abs() & frac_mask) as $t) + } + + /// Whole part of the value. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn trunc(self) -> Self { + Self(((self.0.unsigned_abs() >> B) << B) as $t) + } + } + impl_trait_op_unit!($t, Neg, neg); + impl core::fmt::Debug for Fixed<$t, B> { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let whole: $t = self.trunc().into_raw() >> B; + let fract: $t = self.fract().into_raw(); + let divisor: $t = 1 << B; + if self.is_negative() { + let whole = whole.unsigned_abs(); + write!(f, "-({whole}+{fract}/{divisor})") + } else { + write!(f, "{whole}+{fract}/{divisor}") + } + } + } + }; +} +impl_signed_fixed_ops!(i8, u8); +impl_signed_fixed_ops!(i16, u16); +impl_signed_fixed_ops!(i32, u32); + +macro_rules! impl_unsigned_fixed_ops { + ($t:ty) => { + impl Fixed<$t, B> { + /// Multiply. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn mul(self, rhs: Self) -> Self { + let raw = (self.0 as u32) * (rhs.0 as u32); + Self((raw >> B) as $t) + } + + /// Divide. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn div(self, rhs: Self) -> Self { + let m = (self.0 as u32) * (1 << B); + let d = m / (rhs.0 as u32); + Self(d as $t) + } + + /// Fractional part of the value. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn fract(self) -> Self { + Self(self.0 & (<$t>::MAX >> (<$t>::BITS - B))) + } + + /// Whole part of the value. + #[inline] + #[must_use] + #[cfg_attr(feature = "track_caller", track_caller)] + pub const fn trunc(self) -> Self { + Self(self.0 & (<$t>::MAX << B)) + } + } + impl core::fmt::Debug for Fixed<$t, B> { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let whole: $t = self.trunc().into_raw() >> B; + let fract: $t = self.fract().into_raw(); + let divisor: $t = 1 << B; + write!(f, "{whole}+{fract}/{divisor}") + } + } + }; +} +impl_unsigned_fixed_ops!(u8); +impl_unsigned_fixed_ops!(u16); +impl_unsigned_fixed_ops!(u32); diff --git a/src/lib.rs b/src/lib.rs index 271dbe66..8b60fbb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,11 +39,68 @@ pub mod asm_runtime; pub mod gba_cell; +pub mod gba_fixed; pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; pub mod per_system_setup; +/// `i16` with 8 bits of fixed-point fraction. +/// +/// This is used by the affine matrix entries. +/// +/// * This build of the crate uses the [`fixed`] crate +#[cfg(feature = "fixed")] +#[allow(non_camel_case_types)] +pub type i16fx8 = fixed::FixedI16; + +/// `i16` with 14 bits of fixed-point fraction. +/// +/// This is used by the [`ArcTan`](crate::bios::ArcTan) and +/// [`ArcTan2`](crate::bios::ArcTan2) BIOS functions. +/// +/// * This build of the crate uses the [`fixed`] crate +#[cfg(feature = "fixed")] +#[allow(non_camel_case_types)] +pub type i16fx14 = fixed::FixedI16; + +/// `i32` with 8 bits of fixed-point fraction. +/// +/// This is used by the background reference point entries. +/// +/// * This build of the crate uses the [`fixed`] crate +#[cfg(feature = "fixed")] +#[allow(non_camel_case_types)] +pub type i32fx8 = fixed::FixedI32; + +/// `i16` with 8 bits of fixed-point fraction. +/// +/// This is used by the affine matrix entries. +/// +/// * This build of the crate uses the [`gba_fixed`] module +#[cfg(not(feature = "fixed"))] +#[allow(non_camel_case_types)] +pub type i16fx8 = crate::gba_fixed::Fixed; + +/// `i16` with 14 bits of fixed-point fraction. +/// +/// This is used by the [`ArcTan`](crate::bios::ArcTan) and +/// [`ArcTan2`](crate::bios::ArcTan2) BIOS functions. +/// +/// * This build of the crate uses the [`gba_fixed`] module +#[cfg(not(feature = "fixed"))] +#[allow(non_camel_case_types)] +pub type i16fx14 = crate::gba_fixed::Fixed; + +/// `i32` with 8 bits of fixed-point fraction. +/// +/// This is used by the background reference point entries. +/// +/// * This build of the crate uses the [`gba_fixed`] module +#[cfg(not(feature = "fixed"))] +#[allow(non_camel_case_types)] +pub type i32fx8 = crate::gba_fixed::Fixed; + #[cfg(feature = "critical-section")] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "critical-section")))] pub mod critical_section; From f513b58e1037c0405358cae4766454379739cbb0 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 21:05:22 -0600 Subject: [PATCH 06/89] move the macro to the crate root, we'll use it elsewhere surely --- src/asm_runtime.rs | 11 ----------- src/lib.rs | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index c3ed7445..2e0dbd61 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -8,17 +8,6 @@ use crate::gba_cell::GbaCell; -macro_rules! on_gba_or_unimplemented { - ($($token_tree:tt)*) => { - #[cfg(feature="on_gba")] - { - $($token_tree)* - } - #[cfg(not(feature="on_gba"))] - unimplemented!() - } -} - /// Inserts a `nop` instruction. #[inline(always)] #[cfg_attr(not(feature = "on_gba"), track_caller)] diff --git a/src/lib.rs b/src/lib.rs index 8b60fbb4..31f58d4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,17 @@ //! * [Per System Setup][`per_system_setup`] //! * [Per Project Setup][`per_project_setup`] +macro_rules! on_gba_or_unimplemented { + ($($token_tree:tt)*) => { + #[cfg(feature="on_gba")] + { + $($token_tree)* + } + #[cfg(not(feature="on_gba"))] + unimplemented!() + } +} + pub mod asm_runtime; pub mod gba_cell; pub mod gba_fixed; From 2a535b3017bbba740ef501aa6717671bc71c6ceb Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 21:05:42 -0600 Subject: [PATCH 07/89] cover all features in CI, not just `fixed` --- .github/workflows/ci-builds.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-builds.yml b/.github/workflows/ci-builds.yml index 226da783..4de1481a 100644 --- a/.github/workflows/ci-builds.yml +++ b/.github/workflows/ci-builds.yml @@ -29,8 +29,8 @@ jobs: - name: Build The Examples (Link With GNU, weak compiler intrinsics) run: cargo build --examples - - name: Check that the `fixed` crate support is fine - run: cargo build --examples --features=fixed + - name: Check that all our features work + run: cargo build --examples --all-features - name: Delete the .cargo/config.toml run: rm -fr .cargo/config.toml @@ -38,5 +38,5 @@ jobs: - name: Build The Examples (Link With LLD, strong compiler intrinsics) run: RUSTFLAGS="-Clink-arg=-Tlinker_scripts/mono_boot.ld" cargo build --examples --target=thumbv4t-none-eabi -Zbuild-std=core - - name: Build The Crate With No Default Features (build script usage simulation) + - name: Build The Crate For Host With No Default Features (build script usage simulation) run: cargo build --no-default-features From e43928fc6182e34701243805940867996301cd90 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 21:05:54 -0600 Subject: [PATCH 08/89] expand GbaCell to be useful. --- src/gba_cell.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 2 deletions(-) diff --git a/src/gba_cell.rs b/src/gba_cell.rs index 102bc634..75cf1b77 100644 --- a/src/gba_cell.rs +++ b/src/gba_cell.rs @@ -1,16 +1,148 @@ //! Provides the [`GbaCell`] type. +use core::{ + fmt::Debug, + num::{NonZeroI16, NonZeroI32, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU8}, + ptr::NonNull, +}; + +use crate::Color; + +/// Marker trait bound for the methods of [`GbaCell`]. +/// +/// When a type implements this trait it indicates that the type can be +/// atomically loaded/stored using a single volatile access. +/// +/// ## Safety +/// The type must fit in a single register and have an alignment equal to its +/// size. Generally that means it should be one of: +/// +/// * an 8, 16, or 32 bit integer +/// * a function pointer +/// * a data pointer to a sized type +/// * an optional non-null pointer (to function or sized data) +/// * a `repr(transparent)` newtype over one of the above +pub unsafe trait GbaCellSafe: Copy {} +// Note(Lokathor): The list here is not exhaustive, it's just all the stuff I +// thought of at the time. Add more as necessary. +unsafe impl GbaCellSafe for u8 {} +unsafe impl GbaCellSafe for u16 {} +unsafe impl GbaCellSafe for u32 {} +unsafe impl GbaCellSafe for i8 {} +unsafe impl GbaCellSafe for i16 {} +unsafe impl GbaCellSafe for i32 {} +unsafe impl GbaCellSafe for NonZeroI16 {} +unsafe impl GbaCellSafe for NonZeroI32 {} +unsafe impl GbaCellSafe for NonZeroI8 {} +unsafe impl GbaCellSafe for NonZeroU16 {} +unsafe impl GbaCellSafe for NonZeroU32 {} +unsafe impl GbaCellSafe for NonZeroU8 {} +unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe for char {} +unsafe impl GbaCellSafe for bool {} +unsafe impl GbaCellSafe for Color {} +unsafe impl GbaCellSafe for *const T {} +unsafe impl GbaCellSafe for *mut T {} +unsafe impl GbaCellSafe for NonNull {} +unsafe impl GbaCellSafe for Option> {} +unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe + for crate::gba_fixed::Fixed +{ +} +#[cfg(feature = "fixed")] +unsafe impl GbaCellSafe for fixed::FixedI32 {} +#[cfg(feature = "fixed")] +unsafe impl GbaCellSafe for fixed::FixedI16 {} +#[cfg(feature = "fixed")] +unsafe impl GbaCellSafe for fixed::FixedI8 {} +#[cfg(feature = "fixed")] +unsafe impl GbaCellSafe for fixed::FixedU32 {} +#[cfg(feature = "fixed")] +unsafe impl GbaCellSafe for fixed::FixedU16 {} +#[cfg(feature = "fixed")] +unsafe impl GbaCellSafe for fixed::FixedU8 {} + /// A "cell" type suitable to hold a global on the GBA. #[repr(transparent)] pub struct GbaCell(core::cell::UnsafeCell); +impl Debug for GbaCell +where + T: GbaCellSafe + Debug, +{ + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + ::fmt(&self.read(), f) + } +} +impl Default for GbaCell +where + T: GbaCellSafe + Default, +{ + #[inline] + #[must_use] + fn default() -> Self { + Self::new(T::default()) + } +} +impl Clone for GbaCell +where + T: GbaCellSafe + Default, +{ + #[inline] + #[must_use] + fn clone(&self) -> Self { + Self::new(self.read()) + } +} #[cfg(feature = "on_gba")] -#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "on_gba")))] unsafe impl Sync for GbaCell {} -impl GbaCell { +impl GbaCell +where + T: GbaCellSafe, +{ /// Constructs a new cell with the value given + #[inline] + #[must_use] pub const fn new(t: T) -> Self { Self(core::cell::UnsafeCell::new(t)) } + + /// Read the value in the cell. + /// + /// ## Panics + /// The size and alignment of the type must be equal, and they must be 1, 2, + /// or 4. Anything else will panic. + #[inline] + #[must_use] + #[cfg(feature = "on_gba")] + #[cfg_attr(feature = "track_caller", track_caller)] + pub fn read(&self) -> T { + match (core::mem::size_of::(), core::mem::align_of::()) { + (4, 4) | (2, 2) | (1, 1) => unsafe { self.0.get().read_volatile() }, + _ => unimplemented!(), + } + } + + /// Writes a new value to the cell. + /// + /// ## Panics + /// The size and alignment of the type must be equal, and they must be 1, 2, + /// or 4. Anything else will panic. + #[inline] + #[cfg(feature = "on_gba")] + #[cfg_attr(feature = "track_caller", track_caller)] + pub fn write(&self, t: T) { + match (core::mem::size_of::(), core::mem::align_of::()) { + (4, 4) | (2, 2) | (1, 1) => unsafe { self.0.get().write_volatile(t) }, + _ => unimplemented!(), + } + } } From c9114dea514c9358c3a266c10cd6c89dc8ab5865 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 13 May 2024 21:08:01 -0600 Subject: [PATCH 09/89] correct the cfg problems. --- src/gba_cell.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gba_cell.rs b/src/gba_cell.rs index 75cf1b77..fa8acfa4 100644 --- a/src/gba_cell.rs +++ b/src/gba_cell.rs @@ -71,6 +71,7 @@ unsafe impl GbaCellSafe for fixed::FixedU8 {} /// A "cell" type suitable to hold a global on the GBA. #[repr(transparent)] pub struct GbaCell(core::cell::UnsafeCell); +#[cfg(feature = "on_gba")] impl Debug for GbaCell where T: GbaCellSafe + Debug, @@ -90,6 +91,7 @@ where Self::new(T::default()) } } +#[cfg(feature = "on_gba")] impl Clone for GbaCell where T: GbaCellSafe + Default, From d855484c32af634b538ed7afaaedf16e784700ae Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 14 May 2024 11:58:09 -0600 Subject: [PATCH 10/89] small rearrangement --- src/gba_cell.rs | 4 ++-- src/lib.rs | 44 +++++-------------------------------------- src/panic_handlers.rs | 19 +++++++++++++++++++ src/video.rs | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 41 deletions(-) create mode 100644 src/video.rs diff --git a/src/gba_cell.rs b/src/gba_cell.rs index fa8acfa4..d6330cd4 100644 --- a/src/gba_cell.rs +++ b/src/gba_cell.rs @@ -6,7 +6,7 @@ use core::{ ptr::NonNull, }; -use crate::Color; +use crate::video::Color; /// Marker trait bound for the methods of [`GbaCell`]. /// @@ -14,7 +14,7 @@ use crate::Color; /// atomically loaded/stored using a single volatile access. /// /// ## Safety -/// The type must fit in a single register and have an alignment equal to its +/// The type must fit in a single register, and have an alignment equal to its /// size. Generally that means it should be one of: /// /// * an 8, 16, or 32 bit integer diff --git a/src/lib.rs b/src/lib.rs index 31f58d4c..d975f240 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,11 @@ pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; pub mod per_system_setup; +pub mod video; + +#[cfg(feature = "critical-section")] +#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "critical-section")))] +pub mod critical_section; /// `i16` with 8 bits of fixed-point fraction. /// @@ -111,42 +116,3 @@ pub type i16fx14 = crate::gba_fixed::Fixed; #[cfg(not(feature = "fixed"))] #[allow(non_camel_case_types)] pub type i32fx8 = crate::gba_fixed::Fixed; - -#[cfg(feature = "critical-section")] -#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "critical-section")))] -pub mod critical_section; - -/// Declares one of the functions in the [`panic_handlers`] module to be the -/// handler for your program. -/// -/// Valid inputs are the name of any of the functions in that module: -/// * [`empty_loop`][crate::panic_handlers::empty_loop] -/// -/// There's no special magic here, it just saves you on typing it all out -/// yourself. -#[macro_export] -macro_rules! panic_handler { - ($i:ident) => { - #[panic_handler] - fn panic_handler(info: &core::panic::PanicInfo) -> ! { - gba::panic_handlers::$i(info) - } - }; -} - -/// A color value. -/// -/// This is a bit-packed linear RGB color value with 5 bits per channel: -/// ```text -/// 0bX_BBBBB_GGGGG_RRRRR -/// ``` -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct Color(pub u16); - -#[cfg(feature = "bytemuck")] -#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] -unsafe impl bytemuck::Zeroable for Color {} -#[cfg(feature = "bytemuck")] -#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] -unsafe impl bytemuck::Pod for Color {} diff --git a/src/panic_handlers.rs b/src/panic_handlers.rs index d9f37da1..e32a3ce0 100644 --- a/src/panic_handlers.rs +++ b/src/panic_handlers.rs @@ -1,5 +1,24 @@ //! Various panic handler functions that you might find useful. +/// Declares one of the functions in the +/// [`panic_handlers`](crate::panic_handlers) module to be the handler for your +/// program. +/// +/// Valid inputs are the name of any of the functions in that module: +/// * [`empty_loop`][crate::panic_handlers::empty_loop] +/// +/// There's no special magic here, it just saves you on typing it all out +/// yourself. +#[macro_export] +macro_rules! panic_handler { + ($i:ident) => { + #[panic_handler] + fn panic_handler(info: &core::panic::PanicInfo) -> ! { + $crate::panic_handlers::$i(info) + } + }; +} + /// Just performs an empty `loop` #[inline] pub fn empty_loop(_: &core::panic::PanicInfo) -> ! { diff --git a/src/video.rs b/src/video.rs new file mode 100644 index 00000000..b102a739 --- /dev/null +++ b/src/video.rs @@ -0,0 +1,18 @@ +//! + +/// A color value. +/// +/// This is a bit-packed linear RGB color value with 5 bits per channel: +/// ```text +/// 0bX_BBBBB_GGGGG_RRRRR +/// ``` +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct Color(pub u16); + +#[cfg(feature = "bytemuck")] +#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] +unsafe impl bytemuck::Zeroable for Color {} +#[cfg(feature = "bytemuck")] +#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] +unsafe impl bytemuck::Pod for Color {} From ca6d56843d98810ff9d262918f5cafae693ec6e5 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 14 May 2024 12:48:24 -0600 Subject: [PATCH 11/89] keyinput example works. --- .github/workflows/ci-builds.yml | 2 +- dump.bat | 3 ++ examples/basic_keyinput.rs | 18 ++++++++ src/gba_cell.rs | 3 +- src/gba_fixed.rs | 21 +++++++++ src/lib.rs | 13 ++++++ src/mmio.rs | 28 ++++++++++++ src/video.rs | 77 +++++++++++++++++++++++++++++++++ 8 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 examples/basic_keyinput.rs diff --git a/.github/workflows/ci-builds.yml b/.github/workflows/ci-builds.yml index 4de1481a..ba9d4b7e 100644 --- a/.github/workflows/ci-builds.yml +++ b/.github/workflows/ci-builds.yml @@ -39,4 +39,4 @@ jobs: run: RUSTFLAGS="-Clink-arg=-Tlinker_scripts/mono_boot.ld" cargo build --examples --target=thumbv4t-none-eabi -Zbuild-std=core - name: Build The Crate For Host With No Default Features (build script usage simulation) - run: cargo build --no-default-features + run: cargo build --no-default-features --lib diff --git a/dump.bat b/dump.bat index eb915322..dd0d8bd3 100644 --- a/dump.bat +++ b/dump.bat @@ -1,2 +1,5 @@ cargo build --examples + arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/do_nothing >target/ex-do_nothing.txt + +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/basic_keyinput >target/ex-basic_keyinput.txt diff --git a/examples/basic_keyinput.rs b/examples/basic_keyinput.rs new file mode 100644 index 00000000..ae1979cf --- /dev/null +++ b/examples/basic_keyinput.rs @@ -0,0 +1,18 @@ +#![no_std] +#![no_main] + +use gba::{ + mmio::{BACKDROP_COLOR, DISPCNT, KEYINPUT}, + video::{Color, DisplayControl}, +}; + +gba::panic_handler!(empty_loop); + +#[no_mangle] +pub extern "C" fn main() -> ! { + DISPCNT.write(DisplayControl::new()); + loop { + let keys = KEYINPUT.read(); + BACKDROP_COLOR.write(Color(keys.0)); + } +} diff --git a/src/gba_cell.rs b/src/gba_cell.rs index d6330cd4..4c4ef7e5 100644 --- a/src/gba_cell.rs +++ b/src/gba_cell.rs @@ -6,7 +6,7 @@ use core::{ ptr::NonNull, }; -use crate::video::Color; +use crate::{video::Color, KeyInput}; /// Marker trait bound for the methods of [`GbaCell`]. /// @@ -46,6 +46,7 @@ unsafe impl GbaCellSafe for Option {} unsafe impl GbaCellSafe for char {} unsafe impl GbaCellSafe for bool {} unsafe impl GbaCellSafe for Color {} +unsafe impl GbaCellSafe for KeyInput {} unsafe impl GbaCellSafe for *const T {} unsafe impl GbaCellSafe for *mut T {} unsafe impl GbaCellSafe for NonNull {} diff --git a/src/gba_fixed.rs b/src/gba_fixed.rs index b61cfbcb..b118469c 100644 --- a/src/gba_fixed.rs +++ b/src/gba_fixed.rs @@ -1,5 +1,8 @@ //! Basic fixed point math module used as a fallback if the `fixed` crate isn't //! included. +//! +//! Most notably, the fixed point type here uses const generics instead of type +//! level numbers. use core::ops::*; @@ -20,6 +23,24 @@ use core::ops::*; #[repr(transparent)] pub struct Fixed(I); +// TODO: surely there's gotta be a better way to swap between these two fixed +// point forms? + +#[cfg(feature = "fixed")] +impl From> for Fixed { + #[inline] + fn from(value: fixed::FixedI16) -> Self { + Self(value.to_bits()) + } +} +#[cfg(feature = "fixed")] +impl From> for fixed::FixedI16 { + #[inline] + fn from(value: Fixed) -> Self { + fixed::FixedI16::from_bits(value.0) + } +} + macro_rules! impl_trait_op_unit { ($t:ty, $trait:ident, $op:ident) => { impl $trait for Fixed<$t, B> { diff --git a/src/lib.rs b/src/lib.rs index d975f240..d0798912 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,3 +116,16 @@ pub type i16fx14 = crate::gba_fixed::Fixed; #[cfg(not(feature = "fixed"))] #[allow(non_camel_case_types)] pub type i32fx8 = crate::gba_fixed::Fixed; + +/// Keypad input state. +#[derive(Clone, Copy, Default)] +#[repr(transparent)] +pub struct KeyInput(pub u16); +impl KeyInput { + /// If `a` is pressed (left primary button) + #[inline] + #[must_use] + pub const fn a(self) -> bool { + !bitfrob::u16_get_bit(0, self.0) + } +} diff --git a/src/mmio.rs b/src/mmio.rs index d151e3fc..651fc7d1 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -3,6 +3,11 @@ #[allow(unused_imports)] use voladdress::{Safe, Unsafe, VolAddress}; +use crate::{ + video::{Color, DisplayControl}, + KeyInput, +}; + /// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` /// cargo feature. #[cfg(feature = "on_gba")] @@ -10,7 +15,25 @@ type SOGBA = Safe; #[cfg(not(feature = "on_gba"))] type SOGBA = Unsafe; +/// Responds "normally" to read/write, just holds a setting type PlainAddr = VolAddress; +/// Read-only addr +type RoAddr = VolAddress; + +/// Display Control setting. +/// +/// This sets what background mode is active, as well as various related +/// details. +/// +/// Unlike most MMIO, this doesn't have an "all 0" state at boot. The +/// `forced_blank` bit it left set by the BIOS's startup routine. +pub const DISPCNT: PlainAddr = + unsafe { VolAddress::new(0x0400_0000) }; + +/// Key Input (read-only). +/// +/// Gives the low-active button state of all system buttons. +pub const KEYINPUT: RoAddr = unsafe { VolAddress::new(0x0400_0130) }; /// Interrupt Master Enable /// @@ -24,3 +47,8 @@ type PlainAddr = VolAddress; /// Technically there's a two CPU cycle delay between this being written and /// interrupts actually being enabled/disabled. In practice, it doesn't matter. pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; + +/// The backdrop color is the color shown when no *other* element is displayed +/// in a given pixel. +pub const BACKDROP_COLOR: PlainAddr = + unsafe { VolAddress::new(0x0500_0000) }; diff --git a/src/video.rs b/src/video.rs index b102a739..add4fba5 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,5 +1,7 @@ //! +use bitfrob::{u16_with_bit, u16_with_value}; + /// A color value. /// /// This is a bit-packed linear RGB color value with 5 bits per channel: @@ -16,3 +18,78 @@ unsafe impl bytemuck::Zeroable for Color {} #[cfg(feature = "bytemuck")] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] unsafe impl bytemuck::Pod for Color {} + +/// Controls the overall background settings. +/// +/// The video mode is the most important property here. It controls how most +/// other display-related things will act. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[repr(transparent)] +pub struct DisplayControl(u16); +impl DisplayControl { + /// Makes a new, empty value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Assigns the background mode. + /// + /// ## Panics + /// There is a debug assert that the `mode` is less than 6. + #[inline] + pub const fn with_bg_mode(self, mode: u8) -> Self { + debug_assert!(mode < 6); + Self(u16_with_value(0, 2, self.0, mode as u16)) + } + /// Sets if Frame 1 should be used or not. + /// + /// Only has an effect in background modes 4 and 5. + #[inline] + pub const fn with_frame1_active(self, frame1: bool) -> Self { + Self(u16_with_bit(4, self.0, frame1)) + } + #[inline] + pub const fn with_hblank_oam_free(self, frame1: bool) -> Self { + Self(u16_with_bit(5, self.0, frame1)) + } + #[inline] + pub const fn with_obj_vram_1d(self, frame1: bool) -> Self { + Self(u16_with_bit(6, self.0, frame1)) + } + #[inline] + pub const fn with_forced_blank(self, frame1: bool) -> Self { + Self(u16_with_bit(7, self.0, frame1)) + } + #[inline] + pub const fn with_bg0(self, frame1: bool) -> Self { + Self(u16_with_bit(8, self.0, frame1)) + } + #[inline] + pub const fn with_bg1(self, frame1: bool) -> Self { + Self(u16_with_bit(9, self.0, frame1)) + } + #[inline] + pub const fn with_bg2(self, frame1: bool) -> Self { + Self(u16_with_bit(10, self.0, frame1)) + } + #[inline] + pub const fn with_bg3(self, frame1: bool) -> Self { + Self(u16_with_bit(11, self.0, frame1)) + } + #[inline] + pub const fn with_objects(self, frame1: bool) -> Self { + Self(u16_with_bit(12, self.0, frame1)) + } + #[inline] + pub const fn with_win0(self, frame1: bool) -> Self { + Self(u16_with_bit(13, self.0, frame1)) + } + #[inline] + pub const fn with_win1(self, frame1: bool) -> Self { + Self(u16_with_bit(14, self.0, frame1)) + } + #[inline] + pub const fn with_object_window(self, frame1: bool) -> Self { + Self(u16_with_bit(15, self.0, frame1)) + } +} From 865cb95d787dd311bf5d1b3f34605175bf5b7168 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 14 May 2024 14:59:31 -0600 Subject: [PATCH 12/89] the linker script isn't linking right --- examples/basic_keyinput.rs | 19 +++++-- src/asm_runtime.rs | 67 +++++++++++++++++++---- src/bios.rs | 56 +++++++++++++++++++ src/gba_cell.rs | 3 +- src/lib.rs | 107 +++++++++++++++++++++++++++++++++++++ src/mmio.rs | 29 +++++++++- src/video.rs | 94 +++++++++++++++++++++++++++++++- 7 files changed, 357 insertions(+), 18 deletions(-) create mode 100644 src/bios.rs diff --git a/examples/basic_keyinput.rs b/examples/basic_keyinput.rs index ae1979cf..670e5df8 100644 --- a/examples/basic_keyinput.rs +++ b/examples/basic_keyinput.rs @@ -2,17 +2,28 @@ #![no_main] use gba::{ - mmio::{BACKDROP_COLOR, DISPCNT, KEYINPUT}, - video::{Color, DisplayControl}, + asm_runtime::USER_IRQ_HANDLER, + bios::VBlankIntrWait, + mmio::{BACKDROP_COLOR, DISPCNT, DISPSTAT, IE, IME, KEYINPUT}, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, }; gba::panic_handler!(empty_loop); #[no_mangle] pub extern "C" fn main() -> ! { + USER_IRQ_HANDLER.write(Some(handler)); + IE.write(IrqBits::new().with_vblank(true)); + IME.write(true); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); DISPCNT.write(DisplayControl::new()); loop { - let keys = KEYINPUT.read(); - BACKDROP_COLOR.write(Color(keys.0)); + VBlankIntrWait(); } } + +extern "C" fn handler(_: IrqBits) { + let keys = KEYINPUT.read(); + BACKDROP_COLOR.write(Color(keys.0)); +} diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 2e0dbd61..f100365d 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -6,7 +6,7 @@ // cargo feature enabled, and so they should all have the `track_caller` // attribute set whenever the `on_gba` feature is *disabled* -use crate::gba_cell::GbaCell; +use crate::{gba_cell::GbaCell, IrqBits}; /// Inserts a `nop` instruction. #[inline(always)] @@ -250,11 +250,46 @@ global_a32_fn! {_start [] { "ldr r1, =0x4317", "strh r1, [r0]", - // TODO: iwram copying - - // TODO: ewram copying + /* iwram copy */ + "_iwram_copy:", + "ldr r4, =_iwram_word_copy_count", + when!("r4" != "#0" [label_id=1] { + "add r3, r12, #0xB0", + "mov r5, #(1<<10|1<<15)", + "ldr r0, =_iwram_start", + "ldr r2, =_iwram_position_in_rom", + "str r2, [r3]", /* source */ + "str r0, [r3, #4]", /* destination */ + "strh r4, [r3, #8]", /* word count */ + "strh r5, [r3, #10]", /* set control bits */ + }), + "_iwram_copy_done:", + + /* ewram copy */ + "ldr r4, =_ewram_word_copy_count", + when!("r4" != "#0" [label_id=1] { + "add r3, r12, #0xB0", + "mov r5, #(1<<10|1<<15)", + "ldr r0, =_ewram_start", + "ldr r2, =_ewram_position_in_rom", + "str r2, [r3]", /* source */ + "str r0, [r3, #4]", /* destination */ + "strh r4, [r3, #8]", /* word count */ + "strh r5, [r3, #10]", /* set control bits */ + }), - // TODO: bss zeroing + /* bss zero */ + "_begin_zeroing:", + "ldr r4, =_bss_word_clear_count", + when!("r4" != "#0" [label_id=1] { + "ldr r0, =_bss_start", + "mov r2, #0", + "2:", + "str r2, [r0], #4", + "subs r4, r4, #1", + "bne 2b", + }), + "_end_zeroing:", // Tell the BIOS about our irq handler "ldr r0, =_asm_runtime_irq_handler", @@ -265,23 +300,31 @@ global_a32_fn! {_start [] { // requires a linker shim to call. "ldr r0, =main", "bx r0", + "_literals:", // TODO: should we soft reset or something if `main` returns? }} #[cfg(not(feature = "robust_irq_handler"))] global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { + /* At function entry: + * r0: holds 0x0400_0000 + */ + + // Put IME into r12 as a base pointer. + "add r12, r0, #0x208", + // handle MMIO interrupt system - "ldr r0, [r12, #-8] /* read IE_IF */", - "and r0, r0, r0, LSR #16 /* r0 = IE & IF */", + "ldr r0, [r12, #-8] /* r0 = IE_IF.read() */", + "and r0, r0, r0, LSR #16 /* r0 = IE & IF */", "strh r0, [r12, #-6] /* write IF */", // Now the interrupt bits are in r0 as a `u16` // handle BIOS interrupt system - "sub r2, r12, #(0x208+8) /* BIOS_IF address */", + "sub r2, r12, #(0x208+8) /* r0 = BIOS_IF address */", "ldrh r1, [r2] /* read the `has_occurred` flags */", - "orr r1, r1, r0 /* activate the new bits, if any */", + "orr r1, r1, r0 /* activate the new bits, if any */", "strh r1, [r2] /* update the value */", // Get the user handler fn pointer, call it if non-null. @@ -299,6 +342,10 @@ global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { #[cfg(feature = "robust_irq_handler")] global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { + /* At function entry: + * r0: holds 0x0400_0000 + */ + // Suppress IME while this is running. If the user wants to allow for // interrupts *during* other interrupts they can enable IME in their handler. "add r12, r0, #0x208", @@ -345,5 +392,5 @@ global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { /// The user-provided interrupt request handler function. #[no_mangle] #[cfg(feature = "on_gba")] -pub static USER_IRQ_HANDLER: GbaCell> = +pub static USER_IRQ_HANDLER: GbaCell> = GbaCell::new(None); diff --git a/src/bios.rs b/src/bios.rs new file mode 100644 index 00000000..41326e55 --- /dev/null +++ b/src/bios.rs @@ -0,0 +1,56 @@ +#![allow(non_snake_case)] + +//! Module for calls to BIOS functions. + +use crate::IrqBits; + +/// `0x04`: Waits for a specific interrupt type(s) to happen. +/// +/// Pauses the CPU until any of the interrupt types set in `target_irqs` to +/// occur. This can create a significant savings of the battery while you're +/// waiting, so use this function when possible. +/// +/// **Important:** This function forces [`IME`](crate::mmio::IME) on. +/// +/// Your interrupt handler (if any) will be run before this function returns. +/// +/// If none of the interrupts specified in `target_irqs` are properly configured +/// to fire then this function will loop forever without returning. +/// +/// This function uses a special BIOS variable to track what interrupts have +/// occured recently. +/// * If `ignore_existing` is set, then any previous interrupts (since +/// `IntrWait` was last called) that match `target_irqs` are *ignored* and +/// this function will wait for a new target interrupt to occur. +/// * Otherwise, any previous interrupts that match `target_irqs` will cause the +/// function to return immediately without waiting for a new interrupt. +#[inline] +#[instruction_set(arm::t32)] +pub fn IntrWait(ignore_existing: bool, target_irqs: IrqBits) { + unsafe { + core::arch::asm! { + "swi #0x04", + inout("r0") ignore_existing as u32 => _, + inout("r1") target_irqs.0 => _, + out("r3") _, + options(preserves_flags), + } + }; +} + +/// `0x05`: Builtin shorthand for [`IntrWait(true, IrqBits::VBLANK)`](IntrWait) +#[inline] +#[instruction_set(arm::t32)] +pub fn VBlankIntrWait() { + on_gba_or_unimplemented!( + unsafe { + core::arch::asm! { + "swi #0x05", + out("r0") _, + out("r1") _, + out("r3") _, + options(preserves_flags), + } + }; + ); +} diff --git a/src/gba_cell.rs b/src/gba_cell.rs index 4c4ef7e5..ba8f9b44 100644 --- a/src/gba_cell.rs +++ b/src/gba_cell.rs @@ -6,7 +6,7 @@ use core::{ ptr::NonNull, }; -use crate::{video::Color, KeyInput}; +use crate::{video::Color, IrqBits, KeyInput}; /// Marker trait bound for the methods of [`GbaCell`]. /// @@ -52,6 +52,7 @@ unsafe impl GbaCellSafe for *mut T {} unsafe impl GbaCellSafe for NonNull {} unsafe impl GbaCellSafe for Option> {} unsafe impl GbaCellSafe for Option {} +unsafe impl GbaCellSafe for Option {} unsafe impl GbaCellSafe for crate::gba_fixed::Fixed { diff --git a/src/lib.rs b/src/lib.rs index d0798912..2594023c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ //! * [Per System Setup][`per_system_setup`] //! * [Per Project Setup][`per_project_setup`] +use bitfrob::{u16_get_bit, u16_with_bit}; + macro_rules! on_gba_or_unimplemented { ($($token_tree:tt)*) => { #[cfg(feature="on_gba")] @@ -49,6 +51,7 @@ macro_rules! on_gba_or_unimplemented { } pub mod asm_runtime; +pub mod bios; pub mod gba_cell; pub mod gba_fixed; pub mod mmio; @@ -129,3 +132,107 @@ impl KeyInput { !bitfrob::u16_get_bit(0, self.0) } } + +/// Interrupt bit flags. +#[derive(Clone, Copy, Default)] +#[repr(transparent)] +pub struct IrqBits(u16); +impl IrqBits { + /// Makes a new, empty value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Vertical-blank + #[inline] + #[must_use] + pub const fn vblank(self) -> bool { + u16_get_bit(0, self.0) + } + /// Horizontal-blank + #[inline] + #[must_use] + pub const fn hblank(self) -> bool { + u16_get_bit(1, self.0) + } + /// Vertical-counter match + #[inline] + #[must_use] + pub const fn vcount(self) -> bool { + u16_get_bit(2, self.0) + } + /// Timer 0 overflow + #[inline] + #[must_use] + pub const fn timer0(self) -> bool { + u16_get_bit(3, self.0) + } + /// Timer 1 overflow + #[inline] + #[must_use] + pub const fn timer1(self) -> bool { + u16_get_bit(4, self.0) + } + /// Timer 2 overflow + #[inline] + #[must_use] + pub const fn timer2(self) -> bool { + u16_get_bit(5, self.0) + } + /// Timer 3 overflow + #[inline] + #[must_use] + pub const fn timer3(self) -> bool { + u16_get_bit(6, self.0) + } + /// Serial port communication + #[inline] + #[must_use] + pub const fn serial(self) -> bool { + u16_get_bit(7, self.0) + } + /// DMA 0 complete + #[inline] + #[must_use] + pub const fn dma0(self) -> bool { + u16_get_bit(8, self.0) + } + /// DMA 1 complete + #[inline] + #[must_use] + pub const fn dma1(self) -> bool { + u16_get_bit(9, self.0) + } + /// DMA 2 complete + #[inline] + #[must_use] + pub const fn dma2(self) -> bool { + u16_get_bit(10, self.0) + } + /// DMA 3 complete + #[inline] + #[must_use] + pub const fn dma3(self) -> bool { + u16_get_bit(11, self.0) + } + /// Keypad match + #[inline] + #[must_use] + pub const fn keypad(self) -> bool { + u16_get_bit(12, self.0) + } + /// Game pak + #[inline] + #[must_use] + pub const fn gamepak(self) -> bool { + u16_get_bit(13, self.0) + } + /* * * * */ + + /// Set if vblank triggers an interrupt. + #[inline] + #[must_use] + pub const fn with_vblank(self, vblank: bool) -> Self { + Self(u16_with_bit(0, self.0, vblank)) + } +} diff --git a/src/mmio.rs b/src/mmio.rs index 651fc7d1..63f695d8 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -4,8 +4,8 @@ use voladdress::{Safe, Unsafe, VolAddress}; use crate::{ - video::{Color, DisplayControl}, - KeyInput, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, KeyInput, }; /// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` @@ -30,11 +30,36 @@ type RoAddr = VolAddress; pub const DISPCNT: PlainAddr = unsafe { VolAddress::new(0x0400_0000) }; +/// Display Status setting. +/// +/// Gives info on the display state, and controls display-based interrupts. +pub const DISPSTAT: PlainAddr = + unsafe { VolAddress::new(0x0400_0004) }; + +/// The current scanline that the display is working on. +/// +/// Values of 160 to 227 indicate that a vertical blank line is happening. +pub const VCOUNT: RoAddr = unsafe { VolAddress::new(0x0400_0006) }; + /// Key Input (read-only). /// /// Gives the low-active button state of all system buttons. pub const KEYINPUT: RoAddr = unsafe { VolAddress::new(0x0400_0130) }; +/// Interrupts Enabled. +/// +/// When any sub-system is set to "send" interrupts, that interrupt type must +/// *also* be configured here or it won't actually be "received" by the CPU. +pub const IE: PlainAddr = unsafe { VolAddress::new(0x0400_0200) }; + +/// Interrupts Flagged. +/// +/// These are the interrupts that are pending, and haven't been handled. Clear a +/// pending interrupt by writing an [`IrqBits`] value with that bit enabled. The +/// assembly runtime handles this automatically, so you don't normally need to +/// interact with `IF` at all. +pub const IF: PlainAddr = unsafe { VolAddress::new(0x0400_0202) }; + /// Interrupt Master Enable /// /// * When this is set to `true`, hardware interrupts that are flagged will diff --git a/src/video.rs b/src/video.rs index add4fba5..a01a0123 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,6 +1,6 @@ //! -use bitfrob::{u16_with_bit, u16_with_value}; +use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; /// A color value. /// @@ -48,48 +48,140 @@ impl DisplayControl { pub const fn with_frame1_active(self, frame1: bool) -> Self { Self(u16_with_bit(4, self.0, frame1)) } + /// During Horizontal-blank, the OAM memory won't be accessed by the display. + /// + /// Preventing the display from using OAM during h-blank lets you access the + /// memory at full speed, and should be done if you want to use DMA with OAM. + /// However, it gives the display less time for OAM processing, so fewer + /// objects can be drawn per scanline. #[inline] pub const fn with_hblank_oam_free(self, frame1: bool) -> Self { Self(u16_with_bit(5, self.0, frame1)) } + /// Causes the object vram to act as a strictly linear region of tiles. + /// + /// If this is *off* (the default), object vram acts like a 2d region that's + /// 32 tiles wide. + /// + /// ```text + /// +----+----+----+----+... + /// | 0 | 1 | 2 | 3 | + /// +----+----+----+----+... + /// | 32 | 33 | 34 | 35 | + /// +----+----+----+----+... + /// | 64 | 65 | 66 | 67 | + /// +----+----+----+----+... + /// . . . . . + /// . . . . . + /// ``` + /// + /// When an object is more than one tile in size, the object's tiles are + /// filled from left to right, top row to bottom row. This setting affects the + /// filling process. + /// + /// * In 2d mode, moving "down" a row in the object's tiles also moves down a + /// row in the VRAM selection (+32 tile indexes). If an object is 2x2 tiles, + /// starting at index 0, then it would use tiles 0, 1, 32, and 33. + /// * In 1d mode, moving "down" a row in the object's tiles has no special + /// effect, and just keeps consuming tiles linearly. If an object is 2x2 + /// tiles, starting at index 0, then it would use tiles 0, 1, 2, and 3. #[inline] pub const fn with_obj_vram_1d(self, frame1: bool) -> Self { Self(u16_with_bit(6, self.0, frame1)) } + /// When this is set, the display is forced to be blank (white pixels) + /// + /// The display won't access any video memory, allowing you to set things up + /// without any stalls, and the player won't see any intermediate results. #[inline] pub const fn with_forced_blank(self, frame1: bool) -> Self { Self(u16_with_bit(7, self.0, frame1)) } + /// Display background layer 0. #[inline] pub const fn with_bg0(self, frame1: bool) -> Self { Self(u16_with_bit(8, self.0, frame1)) } + /// Display background layer 1. #[inline] pub const fn with_bg1(self, frame1: bool) -> Self { Self(u16_with_bit(9, self.0, frame1)) } + /// Display background layer 2. #[inline] pub const fn with_bg2(self, frame1: bool) -> Self { Self(u16_with_bit(10, self.0, frame1)) } + /// Display background layer 3. #[inline] pub const fn with_bg3(self, frame1: bool) -> Self { Self(u16_with_bit(11, self.0, frame1)) } + /// Display the objects. #[inline] pub const fn with_objects(self, frame1: bool) -> Self { Self(u16_with_bit(12, self.0, frame1)) } + /// Use window 0 as part of the window effect. #[inline] pub const fn with_win0(self, frame1: bool) -> Self { Self(u16_with_bit(13, self.0, frame1)) } + /// Use window 1 as part of the window effect. #[inline] pub const fn with_win1(self, frame1: bool) -> Self { Self(u16_with_bit(14, self.0, frame1)) } + /// Use the object window as part of the window effect. #[inline] pub const fn with_object_window(self, frame1: bool) -> Self { Self(u16_with_bit(15, self.0, frame1)) } } + +/// Gives info about the display state and sets display interrupts. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[repr(transparent)] +pub struct DisplayStatus(u16); +impl DisplayStatus { + /// Makes a new, empty value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// If the display is currently in vertical blank. + #[inline] + pub const fn is_currently_vblank(self) -> bool { + u16_get_bit(0, self.0) + } + /// If the display is currently in horizontal blank. + #[inline] + pub const fn is_currently_hblank(self) -> bool { + u16_get_bit(1, self.0) + } + /// If the display's vertical count matches the vertical count irq setting. + #[inline] + pub const fn is_currently_vcounter(self) -> bool { + u16_get_bit(2, self.0) + } + /// Enables sending vertical blank interrupts. + #[inline] + pub const fn with_vblank_irq(self, frame1: bool) -> Self { + Self(u16_with_bit(3, self.0, frame1)) + } + /// Enables sending horizontal blank interrupts. + #[inline] + pub const fn with_hblank_irq(self, frame1: bool) -> Self { + Self(u16_with_bit(4, self.0, frame1)) + } + /// Enables sending vertical count match interrupts. + #[inline] + pub const fn with_vcounter_irq(self, frame1: bool) -> Self { + Self(u16_with_bit(5, self.0, frame1)) + } + /// The vertical count line to match on. + #[inline] + pub const fn with_vcount_irq_line(self, line: u8) -> Self { + Self(u16_with_value(8, 15, self.0, line as u16)) + } +} From c0487832e667a4766e11354117db11ee9eae49d2 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 14 May 2024 15:41:50 -0600 Subject: [PATCH 13/89] the whole example works now (the DMA offsets were miscalculated) --- examples/basic_keyinput.rs | 16 ++++++++++++- src/asm_runtime.rs | 48 +++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/examples/basic_keyinput.rs b/examples/basic_keyinput.rs index 670e5df8..a38c44de 100644 --- a/examples/basic_keyinput.rs +++ b/examples/basic_keyinput.rs @@ -13,17 +13,31 @@ gba::panic_handler!(empty_loop); #[no_mangle] pub extern "C" fn main() -> ! { + // Set a handler, and then configure interrupts on (IME), vblank interrupts + // enabled for receiving (IE), and vblank interrupts being sent (DISPSTAT). + // All these steps can be done in any order. USER_IRQ_HANDLER.write(Some(handler)); - IE.write(IrqBits::new().with_vblank(true)); IME.write(true); + IE.write(IrqBits::new().with_vblank(true)); DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); + + // Once the program is ready, we turn off the forced blank bit in the display + // to begin showing things, and it will trigger a vblank interrupt after each + // draw cycle. DISPCNT.write(DisplayControl::new()); + + // The body of the game is to just sleep until each vblank (this saves a lot + // of battery power), then immediately upon waking we just go back to sleep. + // The handler is effectively run "during" this wait call. loop { VBlankIntrWait(); } } extern "C" fn handler(_: IrqBits) { + // As an example of what we can do with the per-frame key data, we use it to + // set a color to the backdrop. When keys are pressed/released, the color of + // the backdrop will change. let keys = KEYINPUT.read(); BACKDROP_COLOR.write(Color(keys.0)); } diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index f100365d..faa7c28d 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -106,7 +106,7 @@ macro_rules! global_a32_fn { #[cfg(feature = "on_gba")] core::arch::global_asm!{ a32_code! { - concat!(".section .iwram.text.", stringify!($name), ", \"x\" "), + concat!(".section .iwram.text.", stringify!($name), ", \"ax\",%progbits "), concat!(".global ",stringify!($name)), concat!(stringify!($name),":"), $( concat!($asm_line, "\n") ),+ , @@ -122,7 +122,7 @@ macro_rules! global_a32_fn { #[cfg(feature = "on_gba")] core::arch::global_asm!{ a32_code! { - concat!(".section .text.", stringify!($name), ", \"x\" "), + concat!(".section .text.", stringify!($name), ", \"ax\",%progbits "), concat!(".global ",stringify!($name)), concat!(stringify!($name),":"), $( concat!($asm_line, "\n") ),+ , @@ -244,6 +244,7 @@ global_a32_fn! {_start [] { "1:", "mov r12, #0x04000000", + "add r3, r12, #0xD4", // DMA3 base address // Configure WAITCNT to the GBATEK suggested default "add r0, r12, #0x204", @@ -251,35 +252,30 @@ global_a32_fn! {_start [] { "strh r1, [r0]", /* iwram copy */ - "_iwram_copy:", - "ldr r4, =_iwram_word_copy_count", - when!("r4" != "#0" [label_id=1] { - "add r3, r12, #0xB0", - "mov r5, #(1<<10|1<<15)", - "ldr r0, =_iwram_start", - "ldr r2, =_iwram_position_in_rom", - "str r2, [r3]", /* source */ - "str r0, [r3, #4]", /* destination */ - "strh r4, [r3, #8]", /* word count */ - "strh r5, [r3, #10]", /* set control bits */ + "ldr r0, =_iwram_word_copy_count", + when!("r0" != "#0" [label_id=1] { + "ldr r1, =_iwram_position_in_rom", + "str r1, [r3]", + "ldr r1, =_iwram_start", + "str r1, [r3, #4]", + "strh r0, [r3, #8]", + "mov r1, #(1<<10|1<<15)", + "strh r1, [r3, #10]", }), - "_iwram_copy_done:", /* ewram copy */ "ldr r4, =_ewram_word_copy_count", when!("r4" != "#0" [label_id=1] { - "add r3, r12, #0xB0", - "mov r5, #(1<<10|1<<15)", - "ldr r0, =_ewram_start", - "ldr r2, =_ewram_position_in_rom", - "str r2, [r3]", /* source */ - "str r0, [r3, #4]", /* destination */ - "strh r4, [r3, #8]", /* word count */ - "strh r5, [r3, #10]", /* set control bits */ + "ldr r1, =_ewram_position_in_rom", + "str r1, [r3]", + "ldr r1, =_ewram_start", + "str r1, [r3, #4]", + "strh r0, [r3, #8]", + "mov r1, #(1<<10|1<<15)", + "strh r1, [r3, #10]", }), /* bss zero */ - "_begin_zeroing:", "ldr r4, =_bss_word_clear_count", when!("r4" != "#0" [label_id=1] { "ldr r0, =_bss_start", @@ -289,7 +285,6 @@ global_a32_fn! {_start [] { "subs r4, r4, #1", "bne 2b", }), - "_end_zeroing:", // Tell the BIOS about our irq handler "ldr r0, =_asm_runtime_irq_handler", @@ -300,7 +295,6 @@ global_a32_fn! {_start [] { // requires a linker shim to call. "ldr r0, =main", "bx r0", - "_literals:", // TODO: should we soft reset or something if `main` returns? }} @@ -346,9 +340,11 @@ global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { * r0: holds 0x0400_0000 */ + // Put IME into r12 as a base pointer. + "add r12, r0, #0x208", + // Suppress IME while this is running. If the user wants to allow for // interrupts *during* other interrupts they can enable IME in their handler. - "add r12, r0, #0x208", "mov r3, #0", while_swapped! { ptr="r12", val="r3" { // handle MMIO interrupt system From 5168cd2f6a963eb249bfed484b5551d1b7bc854b Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 14 May 2024 15:53:58 -0600 Subject: [PATCH 14/89] docs --- examples/basic_keyinput.rs | 3 ++- src/bios.rs | 31 ++++++++++++++++++++----------- src/lib.rs | 4 +++- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/examples/basic_keyinput.rs b/examples/basic_keyinput.rs index a38c44de..73f8c21a 100644 --- a/examples/basic_keyinput.rs +++ b/examples/basic_keyinput.rs @@ -28,7 +28,8 @@ pub extern "C" fn main() -> ! { // The body of the game is to just sleep until each vblank (this saves a lot // of battery power), then immediately upon waking we just go back to sleep. - // The handler is effectively run "during" this wait call. + // The handler is effectively run "during" this wait call (after the GBA wakes + // up, but before the BIOS function call returns). loop { VBlankIntrWait(); } diff --git a/src/bios.rs b/src/bios.rs index 41326e55..e514f74e 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -1,6 +1,13 @@ #![allow(non_snake_case)] //! Module for calls to BIOS functions. +//! +//! The BIOS functions aren't called using a normal foreign function call (eg: +//! using the `extern "C"` ABI). Instead, a special instruction `swi` +//! (software-interrupt) is executed, and an immediate data byte in the +//! instruction tells the BIOS what function to execute. Because of this, the +//! BIOS functions have a rather high calls overhead compared to a normal +//! foreign function. use crate::IrqBits; @@ -25,22 +32,24 @@ use crate::IrqBits; /// * Otherwise, any previous interrupts that match `target_irqs` will cause the /// function to return immediately without waiting for a new interrupt. #[inline] -#[instruction_set(arm::t32)] +#[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] pub fn IntrWait(ignore_existing: bool, target_irqs: IrqBits) { - unsafe { - core::arch::asm! { - "swi #0x04", - inout("r0") ignore_existing as u32 => _, - inout("r1") target_irqs.0 => _, - out("r3") _, - options(preserves_flags), - } - }; + on_gba_or_unimplemented!( + unsafe { + core::arch::asm! { + "swi #0x04", + inout("r0") ignore_existing as u32 => _, + inout("r1") target_irqs.0 => _, + out("r3") _, + options(preserves_flags), + } + }; + ); } /// `0x05`: Builtin shorthand for [`IntrWait(true, IrqBits::VBLANK)`](IntrWait) #[inline] -#[instruction_set(arm::t32)] +#[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] pub fn VBlankIntrWait() { on_gba_or_unimplemented!( unsafe { diff --git a/src/lib.rs b/src/lib.rs index 2594023c..fd2a2fee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,6 +138,9 @@ impl KeyInput { #[repr(transparent)] pub struct IrqBits(u16); impl IrqBits { + /// The vblank bit. + pub const VBLANK: Self = Self::new().with_vblank(true); + /// Makes a new, empty value. #[inline] pub const fn new() -> Self { @@ -227,7 +230,6 @@ impl IrqBits { pub const fn gamepak(self) -> bool { u16_get_bit(13, self.0) } - /* * * * */ /// Set if vblank triggers an interrupt. #[inline] From 305f01ea574d460389db6b5dde8ec1f9d0310017 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 14 May 2024 23:47:33 -0600 Subject: [PATCH 15/89] examples of all three bitmap modes. --- Cargo.toml | 2 +- examples/mode3.rs | 38 +++++++++++++++++++++++ examples/mode4.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++ examples/mode5.rs | 57 ++++++++++++++++++++++++++++++++++ src/lib.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++ src/mmio.rs | 51 +++++++++++++++++++++++++++++-- src/video.rs | 69 ++++++++++++++++++++++++++++------------- 7 files changed, 347 insertions(+), 26 deletions(-) create mode 100644 examples/mode3.rs create mode 100644 examples/mode4.rs create mode 100644 examples/mode5.rs diff --git a/Cargo.toml b/Cargo.toml index bf142103..bbbc0810 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ track_caller = [] robust_irq_handler = [] [dependencies] -voladdress = "1.3.0" +voladdress = "1.4.0" bitfrob = "1" critical-section = { version = "1.1.2", features = [ "restore-state-bool", diff --git a/examples/mode3.rs b/examples/mode3.rs new file mode 100644 index 00000000..e5dd132f --- /dev/null +++ b/examples/mode3.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +use gba::{ + bios::VBlankIntrWait, + mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, +}; + +gba::panic_handler!(empty_loop); + +#[no_mangle] +pub extern "C" fn main() -> ! { + IME.write(true); + IE.write(IrqBits::new().with_vblank(true)); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); + + DISPCNT.write(DisplayControl::new().with_bg_mode(3).with_bg2(true)); + + let max_x = MODE3_VRAM.width() as i16 - 1; + let max_y = MODE3_VRAM.height() as i16 - 1; + let mut x: i16 = 5; + let mut y: i16 = 15; + let mut color = Color(0b0_11100_11110_11111); + + loop { + VBlankIntrWait(); + color.0 = color.0.rotate_left(1); + + let keys = KEYINPUT.read(); + x = (x + i16::from(keys.dx())).clamp(0, max_x); + y = (y + i16::from(keys.dy())).clamp(0, max_y); + + let addr = MODE3_VRAM.index(x as usize, (max_y - y) as usize); + addr.write(color); + } +} diff --git a/examples/mode4.rs b/examples/mode4.rs new file mode 100644 index 00000000..8766d56f --- /dev/null +++ b/examples/mode4.rs @@ -0,0 +1,78 @@ +#![no_std] +#![no_main] + +use gba::{ + bios::VBlankIntrWait, + mmio::{BG_PALRAM, DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE4_VRAM}, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, +}; + +gba::panic_handler!(empty_loop); + +#[no_mangle] +pub extern "C" fn main() -> ! { + IME.write(true); + IE.write(IrqBits::new().with_vblank(true)); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); + + DISPCNT.write(DisplayControl::new().with_bg_mode(4).with_bg2(true)); + + let max_x = 2 * MODE4_VRAM.get_frame(0).unwrap().width() as i16 - 1; + let max_y = MODE4_VRAM.get_frame(0).unwrap().height() as i16 - 1; + let mut x: i16 = 5; + let mut y: i16 = 15; + let mut frame: usize = 0; + let mut pal_index: u8 = 0; + + { + let mut red = 1; + let mut green = 3; + let mut blue = 5; + BG_PALRAM.iter().for_each(|addr| { + red = (red + 3) % 256; + green = (green + 5) % 256; + blue = (blue + 7) % 256; + let color = Color::from_rgb(red, green, blue); + addr.write(color) + }); + } + + loop { + VBlankIntrWait(); + pal_index = pal_index.wrapping_add(1); + + // keypad moves the update point + let keys = KEYINPUT.read(); + x = (x + i16::from(keys.dx())).clamp(0, max_x); + y = (y + i16::from(keys.dy())).clamp(0, max_y); + + // L and R pick the frame + if keys.r() && frame == 0 { + frame = 1; + } + if keys.l() && frame == 1 { + frame = 0; + } + DISPCNT.write( + DisplayControl::new() + .with_bg_mode(4) + .with_bg2(true) + .with_frame1_active(frame > 0), + ); + + // we have to do some silly dancing here because we can't write just a `u8` + // in VRAM. We have to read a `u8x2` combo, update part of it, then write it + // back to VRAM. + let addr = MODE4_VRAM + .get_frame(frame) + .unwrap() + .index((x / 2) as usize, (max_y - y) as usize); + let old = addr.read(); + addr.write(if (x & 1) != 0 { + old.with_high(pal_index) + } else { + old.with_low(pal_index) + }); + } +} diff --git a/examples/mode5.rs b/examples/mode5.rs new file mode 100644 index 00000000..89a5f7a5 --- /dev/null +++ b/examples/mode5.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] + +use gba::{ + bios::VBlankIntrWait, + mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE5_VRAM}, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, +}; + +gba::panic_handler!(empty_loop); + +#[no_mangle] +pub extern "C" fn main() -> ! { + IME.write(true); + IE.write(IrqBits::new().with_vblank(true)); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); + + DISPCNT.write(DisplayControl::new().with_bg_mode(5).with_bg2(true)); + + let max_x = MODE5_VRAM.get_frame(0).unwrap().width() as i16 - 1; + let max_y = MODE5_VRAM.get_frame(0).unwrap().height() as i16 - 1; + let mut x: i16 = 5; + let mut y: i16 = 15; + let mut frame: usize = 0; + let mut color = Color(0b0_11100_11110_11111); + + loop { + VBlankIntrWait(); + color.0 = color.0.rotate_left(1); + + // keypad moves the update point + let keys = KEYINPUT.read(); + x = (x + i16::from(keys.dx())).clamp(0, max_x); + y = (y + i16::from(keys.dy())).clamp(0, max_y); + + // L and R pick the frame + if keys.r() && frame == 0 { + frame = 1; + } + if keys.l() && frame == 1 { + frame = 0; + } + DISPCNT.write( + DisplayControl::new() + .with_bg_mode(5) + .with_bg2(true) + .with_frame1_active(frame > 0), + ); + + let addr = MODE5_VRAM + .get_frame(frame) + .unwrap() + .index(x as usize, (max_y - y) as usize); + addr.write(color); + } +} diff --git a/src/lib.rs b/src/lib.rs index fd2a2fee..ff05c794 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,6 +131,84 @@ impl KeyInput { pub const fn a(self) -> bool { !bitfrob::u16_get_bit(0, self.0) } + /// If `b` is pressed (right primary button) + #[inline] + #[must_use] + pub const fn b(self) -> bool { + !bitfrob::u16_get_bit(1, self.0) + } + /// If `select` is pressed (lower/left secondary button) + #[inline] + #[must_use] + pub const fn select(self) -> bool { + !bitfrob::u16_get_bit(2, self.0) + } + /// If `start` is pressed (upper/right secondary button) + #[inline] + #[must_use] + pub const fn start(self) -> bool { + !bitfrob::u16_get_bit(3, self.0) + } + /// If `right` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn right(self) -> bool { + !bitfrob::u16_get_bit(4, self.0) + } + /// If `left` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn left(self) -> bool { + !bitfrob::u16_get_bit(5, self.0) + } + /// If `up` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn up(self) -> bool { + !bitfrob::u16_get_bit(6, self.0) + } + /// If `down` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn down(self) -> bool { + !bitfrob::u16_get_bit(7, self.0) + } + /// If `r` is pressed (right shoulder button) + #[inline] + #[must_use] + pub const fn r(self) -> bool { + !bitfrob::u16_get_bit(8, self.0) + } + /// If `l` is pressed (left shoulder button) + #[inline] + #[must_use] + pub const fn l(self) -> bool { + !bitfrob::u16_get_bit(9, self.0) + } + /// Delta X of the d-pad. right +1, left -1. + #[inline] + #[must_use] + pub const fn dx(self) -> i8 { + if self.right() { + 1 + } else if self.left() { + -1 + } else { + 0 + } + } + /// Delta Y of the d-pad. up +1, down -1. + #[inline] + #[must_use] + pub const fn dy(self) -> i8 { + if self.up() { + 1 + } else if self.down() { + -1 + } else { + 0 + } + } } /// Interrupt bit flags. diff --git a/src/mmio.rs b/src/mmio.rs index 63f695d8..ee19e473 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -1,7 +1,9 @@ //! Definitions for Memory-mapped IO (hardware control). +use bitfrob::u8x2; #[allow(unused_imports)] -use voladdress::{Safe, Unsafe, VolAddress}; +use voladdress::VolAddress; +use voladdress::{VolBlock, VolGrid2d, VolGrid2dStrided}; use crate::{ video::{Color, DisplayControl, DisplayStatus}, @@ -11,9 +13,9 @@ use crate::{ /// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` /// cargo feature. #[cfg(feature = "on_gba")] -type SOGBA = Safe; +type SOGBA = voladdress::Safe; #[cfg(not(feature = "on_gba"))] -type SOGBA = Unsafe; +type SOGBA = voladdress::Unsafe; /// Responds "normally" to read/write, just holds a setting type PlainAddr = VolAddress; @@ -77,3 +79,46 @@ pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; /// in a given pixel. pub const BACKDROP_COLOR: PlainAddr = unsafe { VolAddress::new(0x0500_0000) }; + +/// Palette data for the backgrounds +pub const BG_PALRAM: VolBlock = + unsafe { VolBlock::new(0x0500_0000) }; + +/// Palette data for the objects. +pub const OBJ_PALRAM: VolBlock = + unsafe { VolBlock::new(0x0500_0000) }; + +/// The VRAM's view in Video Mode 3. +/// +/// Each location is a direct color value. +pub const MODE3_VRAM: VolGrid2d = + unsafe { VolGrid2d::new(0x0600_0000) }; + +/// The VRAM's view in Video Mode 4. +/// +/// Each location is a pair of palette indexes into the background palette. +/// Because the VRAM can't be written with a single byte, we have to work with +/// this in units of [`u8x2`]. It's annoying, I know. +pub const MODE4_VRAM: VolGrid2dStrided< + u8x2, + SOGBA, + SOGBA, + { 240 / 2 }, + 160, + 2, + 0xA000, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The VRAM's view in Video Mode 5. +/// +/// Each location is a direct color value, but there's a lower image size to +/// allow for two frames. +pub const MODE5_VRAM: VolGrid2dStrided< + Color, + SOGBA, + SOGBA, + 160, + 128, + 2, + 0xA000, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; diff --git a/src/video.rs b/src/video.rs index a01a0123..4a72ed39 100644 --- a/src/video.rs +++ b/src/video.rs @@ -11,6 +11,31 @@ use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct Color(pub u16); +impl Color { + /// Total black + pub const BLACK: Color = Color(0b0_00000_00000_00000); + /// Full red + pub const RED: Color = Color(0b0_00000_00000_11111); + /// Full green (lime green) + pub const GREEN: Color = Color(0b0_00000_11111_00000); + /// Full yellow + pub const YELLOW: Color = Color(0b0_00000_11111_11111); + /// Full blue (dark blue) + pub const BLUE: Color = Color(0b0_11111_00000_00000); + /// Full magenta (pinkish purple) + pub const MAGENTA: Color = Color(0b0_11111_00000_11111); + /// Full cyan (bright light blue) + pub const CYAN: Color = Color(0b0_11111_11111_00000); + /// Full white + pub const WHITE: Color = Color(0b0_11111_11111_11111); + + /// Constructs a new color value from the given channel values. + #[inline] + #[must_use] + pub const fn from_rgb(r: u16, g: u16, b: u16) -> Self { + Self(r & 0b11111 | (g & 0b11111) << 5 | (b & 0b11111) << 10) + } +} #[cfg(feature = "bytemuck")] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "bytemuck")))] @@ -55,8 +80,8 @@ impl DisplayControl { /// However, it gives the display less time for OAM processing, so fewer /// objects can be drawn per scanline. #[inline] - pub const fn with_hblank_oam_free(self, frame1: bool) -> Self { - Self(u16_with_bit(5, self.0, frame1)) + pub const fn with_hblank_oam_free(self, free: bool) -> Self { + Self(u16_with_bit(5, self.0, free)) } /// Causes the object vram to act as a strictly linear region of tiles. /// @@ -86,56 +111,56 @@ impl DisplayControl { /// effect, and just keeps consuming tiles linearly. If an object is 2x2 /// tiles, starting at index 0, then it would use tiles 0, 1, 2, and 3. #[inline] - pub const fn with_obj_vram_1d(self, frame1: bool) -> Self { - Self(u16_with_bit(6, self.0, frame1)) + pub const fn with_obj_vram_1d(self, vram_1d: bool) -> Self { + Self(u16_with_bit(6, self.0, vram_1d)) } /// When this is set, the display is forced to be blank (white pixels) /// /// The display won't access any video memory, allowing you to set things up /// without any stalls, and the player won't see any intermediate results. #[inline] - pub const fn with_forced_blank(self, frame1: bool) -> Self { - Self(u16_with_bit(7, self.0, frame1)) + pub const fn with_forced_blank(self, forced: bool) -> Self { + Self(u16_with_bit(7, self.0, forced)) } /// Display background layer 0. #[inline] - pub const fn with_bg0(self, frame1: bool) -> Self { - Self(u16_with_bit(8, self.0, frame1)) + pub const fn with_bg0(self, bg0: bool) -> Self { + Self(u16_with_bit(8, self.0, bg0)) } /// Display background layer 1. #[inline] - pub const fn with_bg1(self, frame1: bool) -> Self { - Self(u16_with_bit(9, self.0, frame1)) + pub const fn with_bg1(self, bg1: bool) -> Self { + Self(u16_with_bit(9, self.0, bg1)) } /// Display background layer 2. #[inline] - pub const fn with_bg2(self, frame1: bool) -> Self { - Self(u16_with_bit(10, self.0, frame1)) + pub const fn with_bg2(self, bg2: bool) -> Self { + Self(u16_with_bit(10, self.0, bg2)) } /// Display background layer 3. #[inline] - pub const fn with_bg3(self, frame1: bool) -> Self { - Self(u16_with_bit(11, self.0, frame1)) + pub const fn with_bg3(self, bg3: bool) -> Self { + Self(u16_with_bit(11, self.0, bg3)) } /// Display the objects. #[inline] - pub const fn with_objects(self, frame1: bool) -> Self { - Self(u16_with_bit(12, self.0, frame1)) + pub const fn with_objects(self, objects: bool) -> Self { + Self(u16_with_bit(12, self.0, objects)) } /// Use window 0 as part of the window effect. #[inline] - pub const fn with_win0(self, frame1: bool) -> Self { - Self(u16_with_bit(13, self.0, frame1)) + pub const fn with_win0(self, win0: bool) -> Self { + Self(u16_with_bit(13, self.0, win0)) } /// Use window 1 as part of the window effect. #[inline] - pub const fn with_win1(self, frame1: bool) -> Self { - Self(u16_with_bit(14, self.0, frame1)) + pub const fn with_win1(self, win1: bool) -> Self { + Self(u16_with_bit(14, self.0, win1)) } /// Use the object window as part of the window effect. #[inline] - pub const fn with_object_window(self, frame1: bool) -> Self { - Self(u16_with_bit(15, self.0, frame1)) + pub const fn with_object_window(self, object_window: bool) -> Self { + Self(u16_with_bit(15, self.0, object_window)) } } From 2405506e642aa023a1c16eb7aeed8b9ca1315b1d Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 14 May 2024 23:54:52 -0600 Subject: [PATCH 16/89] update the asm dumper --- dump.bat | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dump.bat b/dump.bat index dd0d8bd3..fb5ec474 100644 --- a/dump.bat +++ b/dump.bat @@ -3,3 +3,9 @@ cargo build --examples arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/do_nothing >target/ex-do_nothing.txt arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/basic_keyinput >target/ex-basic_keyinput.txt + +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode3 >target/ex-mode3.txt + +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode4 >target/ex-mode4.txt + +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode5 >target/ex-mode5.txt From 5016b577b40d52755946a0db4d62e3782b9853e7 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 21 May 2024 23:14:48 -0600 Subject: [PATCH 17/89] Transition to using the new `bracer` API --- Cargo.toml | 10 +- src/asm_runtime.rs | 385 ++++++++++++--------------------------------- 2 files changed, 104 insertions(+), 291 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bbbc0810..1bd52c80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,15 +19,6 @@ doc_cfg = [] # inlined (meaning `Location` is passed via the stack). This is only needed for # debugging, and so it's off by default. track_caller = [] -# The assembly runtime's irq handler will take extra steps to allow nested -# interrupts during calls to the Rust interrupt handler, and also the Rust -# interrupt handler will be called with the CPU in System mode, allowing for -# full stack usage. If you do not use this feature then the Rust IRQ handler -# will be called with the CPU in IRQ mode with nested interrupts disabled at the -# CPU level. IRQ mode still has enough stack space to do any *reasonable* IRQ -# handling, so you do NOT normally need to use this unless you need nested -# interrupt support or extremely large stack usage during the handler. -robust_irq_handler = [] [dependencies] voladdress = "1.4.0" @@ -37,6 +28,7 @@ critical-section = { version = "1.1.2", features = [ ], optional = true } bytemuck = { version = "1.16.0", optional = true } fixed = { version = "1.27.0", default-features = false, optional = true } +bracer = "0.2.1" [profile.dev] opt-level = 3 diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index faa7c28d..cbe4e26f 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -7,6 +7,7 @@ // attribute set whenever the `on_gba` feature is *disabled* use crate::{gba_cell::GbaCell, IrqBits}; +use bracer::*; /// Inserts a `nop` instruction. #[inline(always)] @@ -73,20 +74,19 @@ pub unsafe fn swpb(mut ptr: *mut u8, x: u8) -> u8 { } } +// Proc-macros can't see the target being built for, so we use this declarative +// macro to determine if we're on a thumb target (and need to force our asm into +// a32 mode) or if we're not on thumb (and our asm can pass through untouched). #[cfg(target_feature = "thumb-mode")] -macro_rules! a32_code { +macro_rules! force_a32 { ($($asm_line:expr),+ $(,)?) => { - concat!( - ".code 32\n", - + t32_with_a32_scope! { $( concat!($asm_line, "\n") ),+ , - - ".code 16\n", - ) + } } } #[cfg(not(target_feature = "thumb-mode"))] -macro_rules! a32_code { +macro_rules! force_a32 { ($($asm_line:expr),+ $(,)?) => { concat!( $( concat!($asm_line, "\n") ),+ , @@ -94,299 +94,120 @@ macro_rules! a32_code { } } -/// If `on_gba` is enabled, makes a `global_asm` for the function given. -/// -/// If `on_gba` is disabled, this does nothing. -macro_rules! global_a32_fn { - ( - $name:ident [iwram=true] { - $($asm_line:expr),+ $(,)? - } - ) => { - #[cfg(feature = "on_gba")] - core::arch::global_asm!{ - a32_code! { - concat!(".section .iwram.text.", stringify!($name), ", \"ax\",%progbits "), - concat!(".global ",stringify!($name)), - concat!(stringify!($name),":"), - $( concat!($asm_line, "\n") ),+ , - ".pool", - } - } - }; - ( - $name:ident [] { - $($asm_line:expr),+ $(,)? - } - ) => { - #[cfg(feature = "on_gba")] - core::arch::global_asm!{ - a32_code! { - concat!(".section .text.", stringify!($name), ", \"ax\",%progbits "), - concat!(".global ",stringify!($name)), - concat!(stringify!($name),":"), - $( concat!($asm_line, "\n") ),+ , - ".pool", - } - } - }; -} - -macro_rules! while_swapped { - ( - ptr=$ptr:literal, val=$val:literal { - $($asm_line:expr),+ $(,)? - } - ) => { - concat!( - concat!("swp ",$val,", ",$val,", [",$ptr,"]\n"), - - $( concat!($asm_line, "\n") ),+ , - - concat!("swp ",$val,", ",$val,", [",$ptr,"]\n"), - ) - } -} +#[cfg(feature = "on_gba")] +core::arch::global_asm! { + put_fn_in_section!(".text._start"), + ".global _start", + force_a32!{ + "_start:", + "b 1f", + ".space 0xE0", + "1:", + + "mov r12, #0x04000000", + "add r3, r12, #0xD4", // DMA3 base address + + // Configure WAITCNT to the GBATEK suggested default + "add r0, r12, #0x204", + "ldr r1, =0x4317", + "strh r1, [r0]", + + /* iwram copy */ + "ldr r0, =_iwram_word_copy_count", + when!(("r0" != "#0") { + "ldr r1, =_iwram_position_in_rom", + "str r1, [r3]", + "ldr r1, =_iwram_start", + "str r1, [r3, #4]", + "strh r0, [r3, #8]", + "mov r1, #(1<<10|1<<15)", + "strh r1, [r3, #10]", + }), -macro_rules! with_spsr_held_in { - ($reg:literal, { - $($asm_line:expr),* $(,)? - }) => { - concat!( - concat!("mrs ", $reg, ", SPSR\n"), - $( concat!($asm_line, "\n") ),* , - concat!("msr SPSR, ", $reg, "\n"), - ) - } -} + /* ewram copy */ + "ldr r4, =_ewram_word_copy_count", + when!(("r4" != "#0") { + "ldr r1, =_ewram_position_in_rom", + "str r1, [r3]", + "ldr r1, =_ewram_start", + "str r1, [r3, #4]", + "strh r0, [r3, #8]", + "mov r1, #(1<<10|1<<15)", + "strh r1, [r3, #10]", + }), -macro_rules! set_cpu_control { - // CPSR control bits are: `I F T MMMMM`, and T must always be left as 0. - // * 0b10011: Supervisor (SVC) - // * 0b11111: System (SYS) - (System, irq_masked: false, fiq_masked: false) => { - "msr CPSR_c, #0b00011111\n" - }; - (Supervisor, irq_masked: true, fiq_masked: false) => { - "msr CPSR_c, #0b10010010\n" - }; -} + /* bss zero */ + "ldr r4, =_bss_word_clear_count", + when!(("r4" != "#0") { + "ldr r0, =_bss_start", + "mov r2, #0", + "2:", + "str r2, [r0], #4", + "subs r4, r4, #1", + "bne 2b", + }), -macro_rules! when { - ($reg:literal == $op2:literal [label_id=$label:literal] { - $($asm_line:expr),* $(,)? - }) => { - concat!( - concat!("cmp ", $reg, ", ", $op2, "\n"), - concat!("bne ", $label, "f\n"), - $( concat!($asm_line, "\n") ),* , - concat!($label, ":\n"), - ) - }; - ($reg:literal != $op2:literal [label_id=$label:literal] { - $($asm_line:expr),* $(,)? - }) => { - concat!( - concat!("cmp ", $reg, ", ", $op2, "\n"), - concat!("beq ", $label, "f\n"), - $( concat!($asm_line, "\n") ),* , - concat!($label, ":\n"), - ) - }; - ($reg:literal >=u $op2:literal [label_id=$label:literal] { - $($asm_line:expr),* $(,)? - }) => { - concat!( - concat!("cmp ", $reg, ", ", $op2, "\n"), - concat!("bcc ", $label, "f\n"), // cc: Unsigned LT - $( concat!($asm_line, "\n") ),* , - concat!($label, ":\n"), - ) - }; - ($reg:literal <=u $op2:literal [label_id=$label:literal] { - $($asm_line:expr),* $(,)? - }) => { - concat!( - concat!("cmp ", $reg, ", ", $op2, "\n"), - concat!("bhi ", $label, "f\n"), // hi: Unsigned GT - $( concat!($asm_line, "\n") ),* , - concat!($label, ":\n"), - ) - }; -} + // Tell the BIOS about our irq handler + "ldr r0, =_asm_runtime_irq_handler", + "str r0, [r12, #-4]", -/// Sets `lr` properly and then uses `bx` on the register given. -macro_rules! a32_fake_blx { - (reg=$reg_name:expr, label_id=$label:expr) => { - concat!( - concat!("adr lr, ", $label, "f\n"), - concat!("bx ", $reg_name, "\n"), - concat!($label, ":\n"), - ) - }; -} + // Note(Lokathor): we do a `bx` instead of a `b` because it saves 4 *entire* + // bytes (!), since `main` will usually be a t32 function and thus usually + // requires a linker shim to call. + "ldr r0, =main", + "bx r0", -macro_rules! with_pushed_registers { - ($reglist:expr, { - $($asm_line:expr),* $(,)? - }) => { - concat!( - concat!("push ", $reglist, "\n"), - $( concat!($asm_line, "\n") ),* , - concat!("pop ", $reglist, "\n"), - ) + // TODO: should we soft reset or something if `main` returns? } } -global_a32_fn! {_start [] { - "b 1f", - ".space 0xE0", - "1:", - - "mov r12, #0x04000000", - "add r3, r12, #0xD4", // DMA3 base address - - // Configure WAITCNT to the GBATEK suggested default - "add r0, r12, #0x204", - "ldr r1, =0x4317", - "strh r1, [r0]", - - /* iwram copy */ - "ldr r0, =_iwram_word_copy_count", - when!("r0" != "#0" [label_id=1] { - "ldr r1, =_iwram_position_in_rom", - "str r1, [r3]", - "ldr r1, =_iwram_start", - "str r1, [r3, #4]", - "strh r0, [r3, #8]", - "mov r1, #(1<<10|1<<15)", - "strh r1, [r3, #10]", - }), - - /* ewram copy */ - "ldr r4, =_ewram_word_copy_count", - when!("r4" != "#0" [label_id=1] { - "ldr r1, =_ewram_position_in_rom", - "str r1, [r3]", - "ldr r1, =_ewram_start", - "str r1, [r3, #4]", - "strh r0, [r3, #8]", - "mov r1, #(1<<10|1<<15)", - "strh r1, [r3, #10]", - }), - - /* bss zero */ - "ldr r4, =_bss_word_clear_count", - when!("r4" != "#0" [label_id=1] { - "ldr r0, =_bss_start", - "mov r2, #0", - "2:", - "str r2, [r0], #4", - "subs r4, r4, #1", - "bne 2b", - }), - - // Tell the BIOS about our irq handler - "ldr r0, =_asm_runtime_irq_handler", - "str r0, [r12, #-4]", - - // Note(Lokathor): we do a `bx` instead of a `b` because it saves 4 *entire* - // bytes (!), since `main` will usually be a t32 function and thus usually - // requires a linker shim to call. - "ldr r0, =main", - "bx r0", - - // TODO: should we soft reset or something if `main` returns? -}} - -#[cfg(not(feature = "robust_irq_handler"))] -global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { - /* At function entry: - * r0: holds 0x0400_0000 - */ - - // Put IME into r12 as a base pointer. - "add r12, r0, #0x208", - - // handle MMIO interrupt system - "ldr r0, [r12, #-8] /* r0 = IE_IF.read() */", - "and r0, r0, r0, LSR #16 /* r0 = IE & IF */", - "strh r0, [r12, #-6] /* write IF */", - - // Now the interrupt bits are in r0 as a `u16` - - // handle BIOS interrupt system - "sub r2, r12, #(0x208+8) /* r0 = BIOS_IF address */", - "ldrh r1, [r2] /* read the `has_occurred` flags */", - "orr r1, r1, r0 /* activate the new bits, if any */", - "strh r1, [r2] /* update the value */", - - // Get the user handler fn pointer, call it if non-null. - "ldr r1, =USER_IRQ_HANDLER", - "ldr r1, [r1]", - when!("r1" != "#0" [label_id=9] { - with_pushed_registers!("{{r0, lr}}", { - a32_fake_blx!(reg="r1", label_id=1), - }), - }), - - // return to the BIOS - "bx lr", -}} - -#[cfg(feature = "robust_irq_handler")] -global_a32_fn! {_asm_runtime_irq_handler [iwram=true] { - /* At function entry: - * r0: holds 0x0400_0000 - */ +#[cfg(feature = "on_gba")] +core::arch::global_asm! { + put_fn_in_section!(".iwram.text._asm_runtime_irq_handler"), + ".global _asm_runtime_irq_handler", + force_a32!{ + "_asm_runtime_irq_handler:", - // Put IME into r12 as a base pointer. - "add r12, r0, #0x208", + // At function entry: + // * r0: holds 0x0400_0000 + // + // We're allowed to use the usual C ABI registers. - // Suppress IME while this is running. If the user wants to allow for - // interrupts *during* other interrupts they can enable IME in their handler. - "mov r3, #0", - while_swapped! { ptr="r12", val="r3" { // handle MMIO interrupt system - "ldr r0, [r12, #-8] /* read IE_IF */", - "and r0, r0, r0, LSR #16 /* r0 = IE & IF */", - "strh r0, [r12, #-6] /* write IF */", + "add r12, r0, #0x200", // 16-bit access offsets can't be too big + "ldr r1, [r12]", // IE_IF.read32() + "and r1, r1, r1, LSR #16", // IE & IF + "strh r1, [r12, #2]", // write IF - // Now the interrupt bits are in r0 as a `u16` + // Now: + // * r0: holds 0x0400_0000 + // * r1: irq bits // handle BIOS interrupt system - "sub r2, r12, #(0x208+8) /* BIOS_IF address */", - "ldrh r1, [r2] /* read the `has_occurred` flags */", - "orr r1, r1, r0 /* activate the new bits, if any */", - "strh r1, [r2] /* update the value */", + "ldrh r2, [r0, #-8]", // read the `has_occurred` flags + "orr r2, r2, r1", // activate the new bits, if any + "strh r2, [r0, #-8]", // update the value + + // Now: + // * r0: holds 0x0400_0000 + // * r1: irq bits // Get the user handler fn pointer, call it if non-null. - "ldr r1, =USER_IRQ_HANDLER", - "ldr r1, [r1]", - when!("r1" != "#0" [label_id=9] { - with_spsr_held_in!("r2", { - // We have to preserve: - // * r2: spsr - // * r3: old IME value - // * r12: IME address - // * lr: our handler return address - with_pushed_registers!("{{r2, r3, r12, lr}}", { - // Note(Lokathor): LLVM won't ever leave the stack alignment as less - // than 8 so we skip trying to align it to 8 by hand. - set_cpu_control!(System, irq_masked: false, fiq_masked: false), - a32_fake_blx!(reg="r1", label_id=1), - set_cpu_control!(Supervisor, irq_masked: true, fiq_masked: false), - }), - }) + "ldr r12, ={USER_IRQ_HANDLER}", + "ldr r12, [r12]", + when!(("r12" != "#0") { + "mov r0, r1", + "push {{r0, lr}}", + a32_fake_blx!("r12"), + "pop {{r0, lr}}", }), - }}, - // return to the BIOS - "bx lr", -}} + // return to the BIOS + "bx lr", + }, + USER_IRQ_HANDLER = sym USER_IRQ_HANDLER, +} /// The user-provided interrupt request handler function. -#[no_mangle] -#[cfg(feature = "on_gba")] pub static USER_IRQ_HANDLER: GbaCell> = GbaCell::new(None); From 6ee3fcb22d82fe7aef6d91e916618495638b292f Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 21 May 2024 23:15:31 -0600 Subject: [PATCH 18/89] comments --- src/asm_runtime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index cbe4e26f..91082b5a 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -105,7 +105,7 @@ core::arch::global_asm! { "1:", "mov r12, #0x04000000", - "add r3, r12, #0xD4", // DMA3 base address + "add r3, r12, #0xD4", // Compute the DMA3 base address // Configure WAITCNT to the GBATEK suggested default "add r0, r12, #0x204", From 2ab6e461ee63bf22bad2123fbedc956112b376af Mon Sep 17 00:00:00 2001 From: Lokathor Date: Tue, 21 May 2024 23:58:56 -0600 Subject: [PATCH 19/89] more comments --- src/asm_runtime.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 91082b5a..2e533998 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -100,12 +100,17 @@ core::arch::global_asm! { ".global _start", force_a32!{ "_start:", + // space for the rom header to be placed after compilation. "b 1f", ".space 0xE0", "1:", "mov r12, #0x04000000", - "add r3, r12, #0xD4", // Compute the DMA3 base address + "add r3, r12, #0xD4", + + // Now + // * r12: mmio base + // * r3: dma3 base // Configure WAITCNT to the GBATEK suggested default "add r0, r12, #0x204", @@ -116,11 +121,11 @@ core::arch::global_asm! { "ldr r0, =_iwram_word_copy_count", when!(("r0" != "#0") { "ldr r1, =_iwram_position_in_rom", - "str r1, [r3]", + "str r1, [r3]", // src "ldr r1, =_iwram_start", - "str r1, [r3, #4]", - "strh r0, [r3, #8]", - "mov r1, #(1<<10|1<<15)", + "str r1, [r3, #4]", // dest + "strh r0, [r3, #8]", // transfers + "mov r1, #(1<<10|1<<15)", // 32-bit transfers, enable "strh r1, [r3, #10]", }), @@ -197,6 +202,9 @@ core::arch::global_asm! { "ldr r12, [r12]", when!(("r12" != "#0") { "mov r0, r1", + // we need to save `lr`, and we need to save an even number of registers + // to keep the stack aligned to 8 for the C ABI, so we'll also save `r0`, + // though it's not actually of use. "push {{r0, lr}}", a32_fake_blx!("r12"), "pop {{r0, lr}}", From 8fa334ed0bf115aae3ab8da73c30b6cc7e6fef4c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Wed, 22 May 2024 00:02:07 -0600 Subject: [PATCH 20/89] that was necessary for the static after all. --- src/asm_runtime.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 2e533998..5cc28103 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -217,5 +217,6 @@ core::arch::global_asm! { } /// The user-provided interrupt request handler function. +#[cfg(feature = "on_gba")] pub static USER_IRQ_HANDLER: GbaCell> = GbaCell::new(None); From 0d9d9d9b7fad41095620818023c295c657c4907a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 23 May 2024 08:34:39 -0600 Subject: [PATCH 21/89] demo font art --- images/CGA8x8thick.png | Bin 0 -> 1270 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/CGA8x8thick.png diff --git a/images/CGA8x8thick.png b/images/CGA8x8thick.png new file mode 100644 index 0000000000000000000000000000000000000000..26a97fd2f5a7bc58c9af20241f6f43a2a1dfd298 GIT binary patch literal 1270 zcmV-$L^jOo(9A&oA$MUL*#|-9 z)Dfi9>)dk42gDzcOU}6(1OuHMiuG+-m|>aI?8Pi|S~o$cQ>7X2+BgBL)#~ooPp?X) zk_6DU{?Hu40K-8wJ41kPS?=9$?2!p z0IwHKld}L+72wsiF&B+=A)G%iT@Ja3D7D)Si?%I*F=960IwHJ$fbMJxi0nZr>xY$F zK8316)bJPw=%=k)8t#b7Mpq4xGrAwWbjdjmEZ9%c9eF0S!Wb&nXyM4T6;`P8<3X%c z?ugoZD**f!*p0iQz1LtY+VVBxzuq^Uw^}>RfD#}_GSOnCcl|Z^fHhbg5+wfOnw4I` zo>7#1?+8RggN~((?rIP#47`L7OW`*@_keS*n|qM zv@xW53=Kd8g>b8_%8E285#=y$G?DNLStGG;O-kWW2s+HQW-Iy=H`tF<^*px}9&NT= zXP^tt`6o{tY31zA#={l4=w7^t40xMwI|iEQLWoJLVDl9NsW8sjuzzxK-a#bMu&lu^ zUFRW?SlLb~giRJJpOh5%BI-KIA%;T$M~?n1KxZwQ0$3hj=jm6g(X;E+Xa+7!K#SP% zh*lmM$VPu{m1KYo;zGFb+S!o++wmrZq`NNU@F0BZ#lW+*hA|jeYqg%2eBnST@Di?F z{4m(S1F!vDIZZvQDE5T5RC9oICSuQAq-4o)fOY_ng`#bk@AZWM^(xe1t2Ti)p`$xb zdP&y1a=3epab53ocmF`WI)qidYMK(D!~nbOsE!g3Pru0+yhJRNcyIKh8# zH_vu}RofnG?K_p&3S`(my7#1%0OOX{8n~fm-DzO&7vc#wEMPRJVyFTaZsumr^TH`S zQc6W0Gh69`%>J}#n%E}sG!r1nPYO0hVI)v(n6+)Is-n};`zYKe${41h1ZP2LsDCI+ zj@YL)At1jUK{}%ywW9V}V3-#N%ds6tahv|BAe_;zFkp4ZjQw}RT{h_rvc@@gVMj0^ zr^zLJFfj=FuQ`mxrBv~_VX@flGUIU%-b*+-JYm-*ZAf+*iIsDpbfEt2)Yz1+mPjYO z=GbQ9MI6=1DKdiuShWpB<-Qk=&ylU^7%g@L2VX!M@Z_qUlDw>kIydyNU#N!j}LR_=kl#y!A0tu$g(uKxv& zC#SP_!|CvDxMOI&&*P!_mEA(;={5nz(L8# z#7Dvr>Y5W(`I1rz?GeJN0J_>S5EB?=z*ajA4h|n<_|}JNH!!^9laCi4uQSm|iGi}} g?oZtLe#LV67vM41oMvGK5dZ)H07*qoM6N<$f)|8l{{R30 literal 0 HcmV?d00001 From 6e009b73d2ef9e020937ed95dd80e37a15ac41cc Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 23 May 2024 09:04:52 -0600 Subject: [PATCH 22/89] start of a mode0 example: we can decompress the sample tiles. --- examples/mode0.rs | 28 ++++++++++++++ images/CGA8x8thick.s | 81 +++++++++++++++++++++++++++++++++++++++++ src/bios.rs | 17 +++++++++ src/lib.rs | 1 + src/sample_art.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 examples/mode0.rs create mode 100644 images/CGA8x8thick.s create mode 100644 src/sample_art.rs diff --git a/examples/mode0.rs b/examples/mode0.rs new file mode 100644 index 00000000..5d5dc76a --- /dev/null +++ b/examples/mode0.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +use gba::{ + bios::VBlankIntrWait, + mmio::{BG_PALRAM, DISPCNT, DISPSTAT, IE, IME}, + sample_art::decompress_sample_font_to_vram_4bpp, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, +}; + +gba::panic_handler!(empty_loop); + +#[no_mangle] +pub extern "C" fn main() -> ! { + decompress_sample_font_to_vram_4bpp(); + BG_PALRAM.index(1).write(Color::WHITE); + // TODO: set up the tilemap to look like something interesting + + IME.write(true); + IE.write(IrqBits::new().with_vblank(true)); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); + + DISPCNT.write(DisplayControl::new().with_bg_mode(0).with_bg0(true)); + loop { + VBlankIntrWait(); + } +} diff --git a/images/CGA8x8thick.s b/images/CGA8x8thick.s new file mode 100644 index 00000000..55256479 --- /dev/null +++ b/images/CGA8x8thick.s @@ -0,0 +1,81 @@ + +@{{BLOCK(CGA8x8thick) + +@======================================================================= +@ +@ CGA8x8thick, 128x128@4, +@ + 256 tiles lz77 compressed +@ Total size: 1684 = 1684 +@ +@ Time-stamp: 2024-05-23, 08:42:27 +@ Exported by Cearn's GBA Image Transmogrifier, v0.8.6 +@ ( http://www.coranac.com/projects/#grit ) +@ +@======================================================================= + + .section .rodata + .align 2 + .global CGA8x8thickTiles @ 1684 bytes +CGA8x8thickTiles: + .word 0x00200010,0xF0000030,0x10059001,0x42011111,0x01060001,0x20101001,0x111E1107,0x01100110 + .word 0x1B101A00,0x73111F10,0x10010011,0x00081019,0x10153000,0x1F109F12,0x10001101,0x03202320 + .word 0x21004F00,0x505110FF,0x00730060,0xF017100F,0x101B701F,0x4760FF33,0x37408A10,0x43F04720 + .word 0xC8701360,0x30FFC500,0x500B309D,0x60B5305E,0x30BF9079,0xCF2420CE,0xF6000001,0x03300000 + .word 0x1A60FE10,0x21FFF330,0x21053113,0x4107211F,0x1145110E,0xDF4B0116,0x01701700,0x50071110 + .word 0x5003405B,0xFE77119F,0x3240B340,0x5D010720,0x46414D50,0xFF007A21,0x07306B11,0x79510320 + .word 0x9B11F730,0xAD31FB10,0x018210FF,0x01981193,0x302111D1,0x015F5186,0xF001FF67,0x38113981 + .word 0xC3217C11,0x58312820,0x41FF5C31,0x50AF6057,0x80C750BB,0x21E360DB,0xFF07408E,0x59023B52 + + .word 0x68110F01,0xA0310370,0x23226D21,0x121650FF,0xF23F510B,0x41E540AF,0x909FC0FC,0xAE22FF9B + .word 0x0370BFF0,0x17803752,0x5BA2DB90,0x12FFA231,0x221BB064,0x207E52C3,0xD0C321CB,0xEFDA4101 + .word 0xBB50E231,0x22108161,0x1166337F,0xFF3C53B4,0x8B63DBB2,0xE3C2B752,0x01F0BBF0,0x031498F0 + .word 0x309F13FF,0x40BB60DA,0xE1AED1C3,0x30D2418F,0x0770FFFB,0x1D232F60,0xCF31DA32,0x6B402F14 + .word 0x24FD3E61,0x32C34345,0x43B8313B,0x101342F7,0x43FF4432,0x6067525B,0x6401F036,0x20F6C117 + .word 0xFF3E728C,0x4C800F60,0x0F34FEB1,0x2B44FE24,0xCA33DCF1,0xF043A1FF,0xE20CA201,0xF0D6B146 + .word 0xD04CD201,0x4271FFE0,0x7B31DF63,0x8033A525,0xC024C815,0xD2FF9F91,0x148B70A0,0x549864C3 + .word 0xF0AE44B2,0xFF27431F,0xCC44BB93,0x4393E131,0xF664BC35,0x3FE01B42,0x447360FF,0x555FC0C3 + + .word 0x82FCA03E,0x55BF50C7,0x3FD0FF8F,0x98711FB0,0x6FF11321,0x0FF00FF0,0xC1FFDFB1,0xF17FD297 + .word 0xB207B2FB,0xE1E7B1AF,0xFF08937F,0xFF41FF60,0x03507C15,0xFF738B63,0x8E56B766,0x35C442FF + .word 0x86C14642,0x478264CB,0xB7DF943B,0xD755FF3F,0x03440597,0xF1240B45,0x87248508,0x06FF0710 + .word 0xF02355B6,0x6736571F,0x377FC0E0,0xFF6050AA,0xDB908F97,0xF7A4DF50,0xBF82A485,0x7FE254A2 + .word 0x30D380FF,0x470F41BF,0x868E5807,0x403F517A,0x8F13FF9F,0xFB183F79,0x1F702457,0x44774097 + .word 0x97FF1F95,0xF1435758,0x910E37BF,0x18FBA13F,0xFFD908BB,0x0043B728,0xDFB03FF0,0x57951F54 + .word 0xD0099FD3,0x535FF1FF,0xA401F0BF,0x5919F05F,0x483B9657,0x3B11FF54,0x5111AF1A,0x13913F91 + .word 0x1FD22F81,0x84FF0397,0x1848295F,0xA19F46A2,0xE8B332BF,0xFFDF8046,0xA04AC3F3,0x3CF03F61 + + .word 0x3F9B8F47,0x01F0B380,0x5A01F0FF,0xF09483FB,0x3807AC20,0x77AB37E3,0x9EA0FF5F,0x20A5FF93 + .word 0x1F366795,0xD84AFFA5,0x97FF5B50,0xA83FE0BF,0x5BFF772B,0x7CBFD2DE,0xFF6166C7,0xEB5AC765 + .word 0x3F28D84C,0xC357BF64,0x9FF20763,0xF408A9FF,0x6A1B3703,0x7303B452,0xB05BF41F,0x425AFF5F + .word 0x0344478D,0xA7C3E3B9,0xC7D364C8,0x13FF5FE0,0x9A07C4D7,0xB03FF145,0x7B1734A4,0xFF7FF1E1 + .word 0xFF7DE3F9,0x047B1F30,0x42E91C0E,0xFFC0E3D3,0xCBA7F3FF,0x7DFF8382,0x6A63AA1D,0xC04BF347 + .word 0x3F72FF5F,0xF8309397,0xAB79DB2C,0xFEA8092E,0x6BFF5F4A,0x806FFC40,0x9933E00F,0xC397514A + .word 0xFFBCF36B,0x9F9407F6,0xCF4C97E3,0x03F18395,0x9FF32069,0x2C3F88FF,0x4E1C3D93,0x2F7F678C + .word 0xA467A75B,0xFF49FF5F,0x1FF06697,0x1FF02FC3,0x7A51FFB2,0x2CFF844D,0x489F7033,0x70A34DC3 + + .word 0x86DFF0F3,0xFF67C99F,0x47681FE0,0x7F99DFF3,0xC07848B5,0x1FD726F5,0x673B30FF,0x7D8F46E3 + .word 0x7BEBFA1F,0x5F877213,0xAF56FF0A,0xA71F6BDC,0x313CFF45,0xD74D8067,0x73FF9F54,0xFBFFA787 + .word 0xF03B834B,0xD11FB01F,0xFF9FFB63,0x438F9FF2,0x1FF0CA7A,0x1FC6DFF2,0x1F626241,0x66BB5AFF + .word 0xBC7BF01E,0x3BAF57DF,0x88E73E57,0x137AFE14,0xCF39BF66,0x324393D0,0xF7A547CE,0x7F017F11 + .word 0xF7D1466D,0x173F3820,0x837B26BE,0x3FF3FFBF,0x7FF28073,0xBFF1009F,0x7FF11FF0,0xF6FFBB96 + .word 0x6B5F7AC3,0x5FD940A7,0x3ADF7BE3,0xFF345CDD,0x736BFBA3,0xDB6EF37A,0x1E7EBEC7,0x8BAE1FF7 + .word 0x8F61F5FF,0xAE21B09F,0x4C2C4FDD,0x80D15FE1,0xBA11FF1F,0xE510D125,0x1BF61449,0xC44C13A0 + .word 0x35FF5350,0x90A2B05B,0x407B7017,0x0D4B0DC0,0x9107F04E,0x10100F40,0x01013407,0xFF07F001 + + .word 0x763C0F30,0x5A17D102,0x0FC0A91C,0x03F0BBFA,0x2FF396FF,0x6017F0E7,0x1A1FE017,0x1D038087 + .word 0x13A0FF07,0x1FF0A3F1,0x5FF08352,0x57205F60,0xC0FF3727,0xF003F05F,0xF077505F,0x87B7B03F + .word 0xFF988997,0xB8C927C0,0x99F21FF1,0x5FF127CA,0x5FF0C4CB,0xD99F81FF,0xF053FCD9,0xF224EC53 + .word 0xF0939CC3,0x7FB0FF7F,0x578057F0,0x6BF11FC0,0x03F08123,0xF6FF5424,0xF0FCE3F8,0x3877423F + .word 0xA03FF0CF,0xFF2720D7,0x7F803FF0,0x3FE01FC0,0x7FE098C7,0xB7C15FC0,0xE23FF0FF,0xD0BFF05F + .word 0xCEBFF17F,0x70DFF053,0xDFF1FF67,0x7FF0BFE1,0x7FF0DFF1,0x77F0A485,0xF1FFDFA0,0xF2D7601F + .word 0xF087F45F,0x93A7F09C,0xFF01F02F,0xBEF00770,0x03F021F0,0x11F00770,0x4DF003B0,0x9C01F0FF + .word 0x6DC50C47,0x907BFFCB,0x6FF98E07,0xC6BEFFB7,0xA3577395,0x3B900790,0x8347724C,0xCEFF473B + + .word 0x716B163F,0x6F9B9EB6,0xC0BFCEB5,0xFF3FF7DB,0xBBE84B79,0x5BFB6769,0x57DD4B98,0xFB268B58 + .word 0x3AFF9DFF,0xB01FF903,0xBC4727DF,0xD0543EBB,0xDF60FF1F,0x3F69AF5B,0x1FBBBE5D,0xA38801F0 + .word 0xB9FFCA6B,0x9A5F9D73,0x51979973,0x5F3C59B7,0xFFFF7E35,0x9C601780,0xA0896695,0x03F06FF3 + .word 0x93F003D2,0x9F80E2FF,0xFA2FEF23,0xD601F01F,0xB701F08B,0x11C5FF8F,0x9D5D1593,0x6F3F433C + .word 0x3ACB17F3,0xFAF84381,0xA4669097,0xF001F018,0x18180001 + .size CGA8x8thickTiles, .-CGA8x8thickTiles + +@}}BLOCK(CGA8x8thick) diff --git a/src/bios.rs b/src/bios.rs index e514f74e..d7352317 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -63,3 +63,20 @@ pub fn VBlankIntrWait() { }; ); } + +/// `0x12` TODO: document this more +#[inline] +#[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] +pub unsafe fn LZ77UnCompReadNormalWrite16bit(src: *const u32, dst: *mut u16) { + on_gba_or_unimplemented!( + unsafe { + core::arch::asm! { + "swi #0x12", + inout("r0") src => _, + inout("r1") dst => _, + out("r3") _, + options(preserves_flags), + } + }; + ); +} diff --git a/src/lib.rs b/src/lib.rs index ff05c794..8b077f2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,7 @@ pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; pub mod per_system_setup; +pub mod sample_art; pub mod video; #[cfg(feature = "critical-section")] diff --git a/src/sample_art.rs b/src/sample_art.rs new file mode 100644 index 00000000..aaaf8a21 --- /dev/null +++ b/src/sample_art.rs @@ -0,0 +1,87 @@ +use crate::{bios::LZ77UnCompReadNormalWrite16bit, mmio::MODE3_VRAM}; + +/// Decompresses the sample font (256 tiles) into VRAM. +pub fn decompress_sample_font_to_vram_4bpp() { + // TODO: we should allow inputting *where* in VRAM the tiles go, instead of + // always using the VRAM base address. + unsafe { + LZ77UnCompReadNormalWrite16bit( + CGA_8X8_THICK_LZ77.as_ptr(), + MODE3_VRAM.as_usize() as *mut u16, + ) + } +} + +static CGA_8X8_THICK_LZ77: [u32; 421] = [ + 0x00200010, 0xF0000030, 0x10059001, 0x42011111, 0x01060001, 0x20101001, + 0x111E1107, 0x01100110, 0x1B101A00, 0x73111F10, 0x10010011, 0x00081019, + 0x10153000, 0x1F109F12, 0x10001101, 0x03202320, 0x21004F00, 0x505110FF, + 0x00730060, 0xF017100F, 0x101B701F, 0x4760FF33, 0x37408A10, 0x43F04720, + 0xC8701360, 0x30FFC500, 0x500B309D, 0x60B5305E, 0x30BF9079, 0xCF2420CE, + 0xF6000001, 0x03300000, 0x1A60FE10, 0x21FFF330, 0x21053113, 0x4107211F, + 0x1145110E, 0xDF4B0116, 0x01701700, 0x50071110, 0x5003405B, 0xFE77119F, + 0x3240B340, 0x5D010720, 0x46414D50, 0xFF007A21, 0x07306B11, 0x79510320, + 0x9B11F730, 0xAD31FB10, 0x018210FF, 0x01981193, 0x302111D1, 0x015F5186, + 0xF001FF67, 0x38113981, 0xC3217C11, 0x58312820, 0x41FF5C31, 0x50AF6057, + 0x80C750BB, 0x21E360DB, 0xFF07408E, 0x59023B52, 0x68110F01, 0xA0310370, + 0x23226D21, 0x121650FF, 0xF23F510B, 0x41E540AF, 0x909FC0FC, 0xAE22FF9B, + 0x0370BFF0, 0x17803752, 0x5BA2DB90, 0x12FFA231, 0x221BB064, 0x207E52C3, + 0xD0C321CB, 0xEFDA4101, 0xBB50E231, 0x22108161, 0x1166337F, 0xFF3C53B4, + 0x8B63DBB2, 0xE3C2B752, 0x01F0BBF0, 0x031498F0, 0x309F13FF, 0x40BB60DA, + 0xE1AED1C3, 0x30D2418F, 0x0770FFFB, 0x1D232F60, 0xCF31DA32, 0x6B402F14, + 0x24FD3E61, 0x32C34345, 0x43B8313B, 0x101342F7, 0x43FF4432, 0x6067525B, + 0x6401F036, 0x20F6C117, 0xFF3E728C, 0x4C800F60, 0x0F34FEB1, 0x2B44FE24, + 0xCA33DCF1, 0xF043A1FF, 0xE20CA201, 0xF0D6B146, 0xD04CD201, 0x4271FFE0, + 0x7B31DF63, 0x8033A525, 0xC024C815, 0xD2FF9F91, 0x148B70A0, 0x549864C3, + 0xF0AE44B2, 0xFF27431F, 0xCC44BB93, 0x4393E131, 0xF664BC35, 0x3FE01B42, + 0x447360FF, 0x555FC0C3, 0x82FCA03E, 0x55BF50C7, 0x3FD0FF8F, 0x98711FB0, + 0x6FF11321, 0x0FF00FF0, 0xC1FFDFB1, 0xF17FD297, 0xB207B2FB, 0xE1E7B1AF, + 0xFF08937F, 0xFF41FF60, 0x03507C15, 0xFF738B63, 0x8E56B766, 0x35C442FF, + 0x86C14642, 0x478264CB, 0xB7DF943B, 0xD755FF3F, 0x03440597, 0xF1240B45, + 0x87248508, 0x06FF0710, 0xF02355B6, 0x6736571F, 0x377FC0E0, 0xFF6050AA, + 0xDB908F97, 0xF7A4DF50, 0xBF82A485, 0x7FE254A2, 0x30D380FF, 0x470F41BF, + 0x868E5807, 0x403F517A, 0x8F13FF9F, 0xFB183F79, 0x1F702457, 0x44774097, + 0x97FF1F95, 0xF1435758, 0x910E37BF, 0x18FBA13F, 0xFFD908BB, 0x0043B728, + 0xDFB03FF0, 0x57951F54, 0xD0099FD3, 0x535FF1FF, 0xA401F0BF, 0x5919F05F, + 0x483B9657, 0x3B11FF54, 0x5111AF1A, 0x13913F91, 0x1FD22F81, 0x84FF0397, + 0x1848295F, 0xA19F46A2, 0xE8B332BF, 0xFFDF8046, 0xA04AC3F3, 0x3CF03F61, + 0x3F9B8F47, 0x01F0B380, 0x5A01F0FF, 0xF09483FB, 0x3807AC20, 0x77AB37E3, + 0x9EA0FF5F, 0x20A5FF93, 0x1F366795, 0xD84AFFA5, 0x97FF5B50, 0xA83FE0BF, + 0x5BFF772B, 0x7CBFD2DE, 0xFF6166C7, 0xEB5AC765, 0x3F28D84C, 0xC357BF64, + 0x9FF20763, 0xF408A9FF, 0x6A1B3703, 0x7303B452, 0xB05BF41F, 0x425AFF5F, + 0x0344478D, 0xA7C3E3B9, 0xC7D364C8, 0x13FF5FE0, 0x9A07C4D7, 0xB03FF145, + 0x7B1734A4, 0xFF7FF1E1, 0xFF7DE3F9, 0x047B1F30, 0x42E91C0E, 0xFFC0E3D3, + 0xCBA7F3FF, 0x7DFF8382, 0x6A63AA1D, 0xC04BF347, 0x3F72FF5F, 0xF8309397, + 0xAB79DB2C, 0xFEA8092E, 0x6BFF5F4A, 0x806FFC40, 0x9933E00F, 0xC397514A, + 0xFFBCF36B, 0x9F9407F6, 0xCF4C97E3, 0x03F18395, 0x9FF32069, 0x2C3F88FF, + 0x4E1C3D93, 0x2F7F678C, 0xA467A75B, 0xFF49FF5F, 0x1FF06697, 0x1FF02FC3, + 0x7A51FFB2, 0x2CFF844D, 0x489F7033, 0x70A34DC3, 0x86DFF0F3, 0xFF67C99F, + 0x47681FE0, 0x7F99DFF3, 0xC07848B5, 0x1FD726F5, 0x673B30FF, 0x7D8F46E3, + 0x7BEBFA1F, 0x5F877213, 0xAF56FF0A, 0xA71F6BDC, 0x313CFF45, 0xD74D8067, + 0x73FF9F54, 0xFBFFA787, 0xF03B834B, 0xD11FB01F, 0xFF9FFB63, 0x438F9FF2, + 0x1FF0CA7A, 0x1FC6DFF2, 0x1F626241, 0x66BB5AFF, 0xBC7BF01E, 0x3BAF57DF, + 0x88E73E57, 0x137AFE14, 0xCF39BF66, 0x324393D0, 0xF7A547CE, 0x7F017F11, + 0xF7D1466D, 0x173F3820, 0x837B26BE, 0x3FF3FFBF, 0x7FF28073, 0xBFF1009F, + 0x7FF11FF0, 0xF6FFBB96, 0x6B5F7AC3, 0x5FD940A7, 0x3ADF7BE3, 0xFF345CDD, + 0x736BFBA3, 0xDB6EF37A, 0x1E7EBEC7, 0x8BAE1FF7, 0x8F61F5FF, 0xAE21B09F, + 0x4C2C4FDD, 0x80D15FE1, 0xBA11FF1F, 0xE510D125, 0x1BF61449, 0xC44C13A0, + 0x35FF5350, 0x90A2B05B, 0x407B7017, 0x0D4B0DC0, 0x9107F04E, 0x10100F40, + 0x01013407, 0xFF07F001, 0x763C0F30, 0x5A17D102, 0x0FC0A91C, 0x03F0BBFA, + 0x2FF396FF, 0x6017F0E7, 0x1A1FE017, 0x1D038087, 0x13A0FF07, 0x1FF0A3F1, + 0x5FF08352, 0x57205F60, 0xC0FF3727, 0xF003F05F, 0xF077505F, 0x87B7B03F, + 0xFF988997, 0xB8C927C0, 0x99F21FF1, 0x5FF127CA, 0x5FF0C4CB, 0xD99F81FF, + 0xF053FCD9, 0xF224EC53, 0xF0939CC3, 0x7FB0FF7F, 0x578057F0, 0x6BF11FC0, + 0x03F08123, 0xF6FF5424, 0xF0FCE3F8, 0x3877423F, 0xA03FF0CF, 0xFF2720D7, + 0x7F803FF0, 0x3FE01FC0, 0x7FE098C7, 0xB7C15FC0, 0xE23FF0FF, 0xD0BFF05F, + 0xCEBFF17F, 0x70DFF053, 0xDFF1FF67, 0x7FF0BFE1, 0x7FF0DFF1, 0x77F0A485, + 0xF1FFDFA0, 0xF2D7601F, 0xF087F45F, 0x93A7F09C, 0xFF01F02F, 0xBEF00770, + 0x03F021F0, 0x11F00770, 0x4DF003B0, 0x9C01F0FF, 0x6DC50C47, 0x907BFFCB, + 0x6FF98E07, 0xC6BEFFB7, 0xA3577395, 0x3B900790, 0x8347724C, 0xCEFF473B, + 0x716B163F, 0x6F9B9EB6, 0xC0BFCEB5, 0xFF3FF7DB, 0xBBE84B79, 0x5BFB6769, + 0x57DD4B98, 0xFB268B58, 0x3AFF9DFF, 0xB01FF903, 0xBC4727DF, 0xD0543EBB, + 0xDF60FF1F, 0x3F69AF5B, 0x1FBBBE5D, 0xA38801F0, 0xB9FFCA6B, 0x9A5F9D73, + 0x51979973, 0x5F3C59B7, 0xFFFF7E35, 0x9C601780, 0xA0896695, 0x03F06FF3, + 0x93F003D2, 0x9F80E2FF, 0xFA2FEF23, 0xD601F01F, 0xB701F08B, 0x11C5FF8F, + 0x9D5D1593, 0x6F3F433C, 0x3ACB17F3, 0xFAF84381, 0xA4669097, 0xF001F018, + 0x18180001, +]; From 506e2f0e7fc48fbbe2cac8663c9a0c34e5e4880c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 23 May 2024 09:06:42 -0600 Subject: [PATCH 23/89] we don't need that assembly file. --- images/CGA8x8thick.s | 81 -------------------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 images/CGA8x8thick.s diff --git a/images/CGA8x8thick.s b/images/CGA8x8thick.s deleted file mode 100644 index 55256479..00000000 --- a/images/CGA8x8thick.s +++ /dev/null @@ -1,81 +0,0 @@ - -@{{BLOCK(CGA8x8thick) - -@======================================================================= -@ -@ CGA8x8thick, 128x128@4, -@ + 256 tiles lz77 compressed -@ Total size: 1684 = 1684 -@ -@ Time-stamp: 2024-05-23, 08:42:27 -@ Exported by Cearn's GBA Image Transmogrifier, v0.8.6 -@ ( http://www.coranac.com/projects/#grit ) -@ -@======================================================================= - - .section .rodata - .align 2 - .global CGA8x8thickTiles @ 1684 bytes -CGA8x8thickTiles: - .word 0x00200010,0xF0000030,0x10059001,0x42011111,0x01060001,0x20101001,0x111E1107,0x01100110 - .word 0x1B101A00,0x73111F10,0x10010011,0x00081019,0x10153000,0x1F109F12,0x10001101,0x03202320 - .word 0x21004F00,0x505110FF,0x00730060,0xF017100F,0x101B701F,0x4760FF33,0x37408A10,0x43F04720 - .word 0xC8701360,0x30FFC500,0x500B309D,0x60B5305E,0x30BF9079,0xCF2420CE,0xF6000001,0x03300000 - .word 0x1A60FE10,0x21FFF330,0x21053113,0x4107211F,0x1145110E,0xDF4B0116,0x01701700,0x50071110 - .word 0x5003405B,0xFE77119F,0x3240B340,0x5D010720,0x46414D50,0xFF007A21,0x07306B11,0x79510320 - .word 0x9B11F730,0xAD31FB10,0x018210FF,0x01981193,0x302111D1,0x015F5186,0xF001FF67,0x38113981 - .word 0xC3217C11,0x58312820,0x41FF5C31,0x50AF6057,0x80C750BB,0x21E360DB,0xFF07408E,0x59023B52 - - .word 0x68110F01,0xA0310370,0x23226D21,0x121650FF,0xF23F510B,0x41E540AF,0x909FC0FC,0xAE22FF9B - .word 0x0370BFF0,0x17803752,0x5BA2DB90,0x12FFA231,0x221BB064,0x207E52C3,0xD0C321CB,0xEFDA4101 - .word 0xBB50E231,0x22108161,0x1166337F,0xFF3C53B4,0x8B63DBB2,0xE3C2B752,0x01F0BBF0,0x031498F0 - .word 0x309F13FF,0x40BB60DA,0xE1AED1C3,0x30D2418F,0x0770FFFB,0x1D232F60,0xCF31DA32,0x6B402F14 - .word 0x24FD3E61,0x32C34345,0x43B8313B,0x101342F7,0x43FF4432,0x6067525B,0x6401F036,0x20F6C117 - .word 0xFF3E728C,0x4C800F60,0x0F34FEB1,0x2B44FE24,0xCA33DCF1,0xF043A1FF,0xE20CA201,0xF0D6B146 - .word 0xD04CD201,0x4271FFE0,0x7B31DF63,0x8033A525,0xC024C815,0xD2FF9F91,0x148B70A0,0x549864C3 - .word 0xF0AE44B2,0xFF27431F,0xCC44BB93,0x4393E131,0xF664BC35,0x3FE01B42,0x447360FF,0x555FC0C3 - - .word 0x82FCA03E,0x55BF50C7,0x3FD0FF8F,0x98711FB0,0x6FF11321,0x0FF00FF0,0xC1FFDFB1,0xF17FD297 - .word 0xB207B2FB,0xE1E7B1AF,0xFF08937F,0xFF41FF60,0x03507C15,0xFF738B63,0x8E56B766,0x35C442FF - .word 0x86C14642,0x478264CB,0xB7DF943B,0xD755FF3F,0x03440597,0xF1240B45,0x87248508,0x06FF0710 - .word 0xF02355B6,0x6736571F,0x377FC0E0,0xFF6050AA,0xDB908F97,0xF7A4DF50,0xBF82A485,0x7FE254A2 - .word 0x30D380FF,0x470F41BF,0x868E5807,0x403F517A,0x8F13FF9F,0xFB183F79,0x1F702457,0x44774097 - .word 0x97FF1F95,0xF1435758,0x910E37BF,0x18FBA13F,0xFFD908BB,0x0043B728,0xDFB03FF0,0x57951F54 - .word 0xD0099FD3,0x535FF1FF,0xA401F0BF,0x5919F05F,0x483B9657,0x3B11FF54,0x5111AF1A,0x13913F91 - .word 0x1FD22F81,0x84FF0397,0x1848295F,0xA19F46A2,0xE8B332BF,0xFFDF8046,0xA04AC3F3,0x3CF03F61 - - .word 0x3F9B8F47,0x01F0B380,0x5A01F0FF,0xF09483FB,0x3807AC20,0x77AB37E3,0x9EA0FF5F,0x20A5FF93 - .word 0x1F366795,0xD84AFFA5,0x97FF5B50,0xA83FE0BF,0x5BFF772B,0x7CBFD2DE,0xFF6166C7,0xEB5AC765 - .word 0x3F28D84C,0xC357BF64,0x9FF20763,0xF408A9FF,0x6A1B3703,0x7303B452,0xB05BF41F,0x425AFF5F - .word 0x0344478D,0xA7C3E3B9,0xC7D364C8,0x13FF5FE0,0x9A07C4D7,0xB03FF145,0x7B1734A4,0xFF7FF1E1 - .word 0xFF7DE3F9,0x047B1F30,0x42E91C0E,0xFFC0E3D3,0xCBA7F3FF,0x7DFF8382,0x6A63AA1D,0xC04BF347 - .word 0x3F72FF5F,0xF8309397,0xAB79DB2C,0xFEA8092E,0x6BFF5F4A,0x806FFC40,0x9933E00F,0xC397514A - .word 0xFFBCF36B,0x9F9407F6,0xCF4C97E3,0x03F18395,0x9FF32069,0x2C3F88FF,0x4E1C3D93,0x2F7F678C - .word 0xA467A75B,0xFF49FF5F,0x1FF06697,0x1FF02FC3,0x7A51FFB2,0x2CFF844D,0x489F7033,0x70A34DC3 - - .word 0x86DFF0F3,0xFF67C99F,0x47681FE0,0x7F99DFF3,0xC07848B5,0x1FD726F5,0x673B30FF,0x7D8F46E3 - .word 0x7BEBFA1F,0x5F877213,0xAF56FF0A,0xA71F6BDC,0x313CFF45,0xD74D8067,0x73FF9F54,0xFBFFA787 - .word 0xF03B834B,0xD11FB01F,0xFF9FFB63,0x438F9FF2,0x1FF0CA7A,0x1FC6DFF2,0x1F626241,0x66BB5AFF - .word 0xBC7BF01E,0x3BAF57DF,0x88E73E57,0x137AFE14,0xCF39BF66,0x324393D0,0xF7A547CE,0x7F017F11 - .word 0xF7D1466D,0x173F3820,0x837B26BE,0x3FF3FFBF,0x7FF28073,0xBFF1009F,0x7FF11FF0,0xF6FFBB96 - .word 0x6B5F7AC3,0x5FD940A7,0x3ADF7BE3,0xFF345CDD,0x736BFBA3,0xDB6EF37A,0x1E7EBEC7,0x8BAE1FF7 - .word 0x8F61F5FF,0xAE21B09F,0x4C2C4FDD,0x80D15FE1,0xBA11FF1F,0xE510D125,0x1BF61449,0xC44C13A0 - .word 0x35FF5350,0x90A2B05B,0x407B7017,0x0D4B0DC0,0x9107F04E,0x10100F40,0x01013407,0xFF07F001 - - .word 0x763C0F30,0x5A17D102,0x0FC0A91C,0x03F0BBFA,0x2FF396FF,0x6017F0E7,0x1A1FE017,0x1D038087 - .word 0x13A0FF07,0x1FF0A3F1,0x5FF08352,0x57205F60,0xC0FF3727,0xF003F05F,0xF077505F,0x87B7B03F - .word 0xFF988997,0xB8C927C0,0x99F21FF1,0x5FF127CA,0x5FF0C4CB,0xD99F81FF,0xF053FCD9,0xF224EC53 - .word 0xF0939CC3,0x7FB0FF7F,0x578057F0,0x6BF11FC0,0x03F08123,0xF6FF5424,0xF0FCE3F8,0x3877423F - .word 0xA03FF0CF,0xFF2720D7,0x7F803FF0,0x3FE01FC0,0x7FE098C7,0xB7C15FC0,0xE23FF0FF,0xD0BFF05F - .word 0xCEBFF17F,0x70DFF053,0xDFF1FF67,0x7FF0BFE1,0x7FF0DFF1,0x77F0A485,0xF1FFDFA0,0xF2D7601F - .word 0xF087F45F,0x93A7F09C,0xFF01F02F,0xBEF00770,0x03F021F0,0x11F00770,0x4DF003B0,0x9C01F0FF - .word 0x6DC50C47,0x907BFFCB,0x6FF98E07,0xC6BEFFB7,0xA3577395,0x3B900790,0x8347724C,0xCEFF473B - - .word 0x716B163F,0x6F9B9EB6,0xC0BFCEB5,0xFF3FF7DB,0xBBE84B79,0x5BFB6769,0x57DD4B98,0xFB268B58 - .word 0x3AFF9DFF,0xB01FF903,0xBC4727DF,0xD0543EBB,0xDF60FF1F,0x3F69AF5B,0x1FBBBE5D,0xA38801F0 - .word 0xB9FFCA6B,0x9A5F9D73,0x51979973,0x5F3C59B7,0xFFFF7E35,0x9C601780,0xA0896695,0x03F06FF3 - .word 0x93F003D2,0x9F80E2FF,0xFA2FEF23,0xD601F01F,0xB701F08B,0x11C5FF8F,0x9D5D1593,0x6F3F433C - .word 0x3ACB17F3,0xFAF84381,0xA4669097,0xF001F018,0x18180001 - .size CGA8x8thickTiles, .-CGA8x8thickTiles - -@}}BLOCK(CGA8x8thick) From 09fc4bbebded4afd2ca536191a66001be8cf000c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 23 May 2024 23:44:45 -0600 Subject: [PATCH 24/89] Add types for tile data. --- examples/mode0.rs | 7 ++++--- src/mmio.rs | 10 +++++++++- src/sample_art.rs | 20 ++++++++++++++++---- src/video.rs | 30 ++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/examples/mode0.rs b/examples/mode0.rs index 5d5dc76a..ec1c91d6 100644 --- a/examples/mode0.rs +++ b/examples/mode0.rs @@ -3,8 +3,8 @@ use gba::{ bios::VBlankIntrWait, - mmio::{BG_PALRAM, DISPCNT, DISPSTAT, IE, IME}, - sample_art::decompress_sample_font_to_vram_4bpp, + mmio::{BG_PALRAM, DISPCNT, DISPSTAT, IE, IME, VRAM_BG_TILE4}, + sample_art::decompress_cga_face_to_vram_4bpp, video::{Color, DisplayControl, DisplayStatus}, IrqBits, }; @@ -13,7 +13,8 @@ gba::panic_handler!(empty_loop); #[no_mangle] pub extern "C" fn main() -> ! { - decompress_sample_font_to_vram_4bpp(); + decompress_cga_face_to_vram_4bpp(VRAM_BG_TILE4.as_region()); + BG_PALRAM.index(1).write(Color::WHITE); // TODO: set up the tilemap to look like something interesting diff --git a/src/mmio.rs b/src/mmio.rs index ee19e473..f8eb08fe 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -6,7 +6,7 @@ use voladdress::VolAddress; use voladdress::{VolBlock, VolGrid2d, VolGrid2dStrided}; use crate::{ - video::{Color, DisplayControl, DisplayStatus}, + video::{Color, DisplayControl, DisplayStatus, Tile4bpp}, IrqBits, KeyInput, }; @@ -88,6 +88,14 @@ pub const BG_PALRAM: VolBlock = pub const OBJ_PALRAM: VolBlock = unsafe { VolBlock::new(0x0500_0000) }; +/// The VRAM's background tile view, using 4bpp tiles. +pub const VRAM_BG_TILE4: VolBlock = + unsafe { VolBlock::new(0x0600_0000) }; + +/// The VRAM's background tile view, using 8bpp tiles. +pub const VRAM_BG_TILE8: VolBlock = + unsafe { VolBlock::new(0x0600_0000) }; + /// The VRAM's view in Video Mode 3. /// /// Each location is a direct color value. diff --git a/src/sample_art.rs b/src/sample_art.rs index aaaf8a21..e56fcb9c 100644 --- a/src/sample_art.rs +++ b/src/sample_art.rs @@ -1,13 +1,25 @@ -use crate::{bios::LZ77UnCompReadNormalWrite16bit, mmio::MODE3_VRAM}; +//! Sample art data that the crate uses in examples. +//! +//! You can also use this if you think it looks cool. -/// Decompresses the sample font (256 tiles) into VRAM. -pub fn decompress_sample_font_to_vram_4bpp() { +use voladdress::{Safe, VolRegion}; + +use crate::{bios::LZ77UnCompReadNormalWrite16bit, video::Tile4bpp}; + +/// Decompresses the sample CGA font face into VRAM. +/// +/// The decompressed data will be 4bpp. +/// +/// ## Panics +/// The provided destination needs to be at least 256 tiles, or this will panic. +pub fn decompress_cga_face_to_vram_4bpp(dest: VolRegion) { + assert!(dest.len() >= 256); // TODO: we should allow inputting *where* in VRAM the tiles go, instead of // always using the VRAM base address. unsafe { LZ77UnCompReadNormalWrite16bit( CGA_8X8_THICK_LZ77.as_ptr(), - MODE3_VRAM.as_usize() as *mut u16, + dest.as_mut_ptr().cast(), ) } } diff --git a/src/video.rs b/src/video.rs index 4a72ed39..e1b44d4c 100644 --- a/src/video.rs +++ b/src/video.rs @@ -210,3 +210,33 @@ impl DisplayStatus { Self(u16_with_value(8, 15, self.0, line as u16)) } } + +/// Data for a 4-bit-per-pixel tile. +/// +/// The tile is 8 pixels wide and 8 pixels tall. Each pixel is 4 bits, giving an +/// index within the palbank for this tile's visual element. An index of 0 is a +/// "transparent" pixel. For alignment purposes, all the data is bit packed as +/// `u32` values. +/// +/// Generally, you are expected to make tile art on your development machine in +/// some way, and then pack it into your ROM as a static value. The data is then +/// copied from ROM into the correct VRAM location at runtime. You are not +/// expected to manipulate particular pixels within a tile at runtime. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct Tile4bpp(pub [u32; 8]); + +/// Data for a 8-bit-per-pixel tile. +/// +/// The tile is 8 pixels wide and 8 pixels tall. Each pixel is 8 bits, giving an +/// index within the full palette for this tile's visual element. An index of 0 +/// is a "transparent" pixel. For alignment purposes, all the data is bit packed +/// as `u32` values. +/// +/// Generally, you are expected to make tile art on your development machine in +/// some way, and then pack it into your ROM as a static value. The data is then +/// copied from ROM into the correct VRAM location at runtime. You are not +/// expected to manipulate particular pixels within a tile at runtime. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct Tile8bpp(pub [u32; 16]); From 82922c59a921eb4a44cb5c2f6976b7e857efc100 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sat, 25 May 2024 18:09:36 -0600 Subject: [PATCH 25/89] delete junk files --- backup/examples/asm_scratch.rs | 17 ----------------- backup/examples/foo.txt | 1 - 2 files changed, 18 deletions(-) delete mode 100644 backup/examples/asm_scratch.rs delete mode 100644 backup/examples/foo.txt diff --git a/backup/examples/asm_scratch.rs b/backup/examples/asm_scratch.rs deleted file mode 100644 index dfa1c773..00000000 --- a/backup/examples/asm_scratch.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![no_std] -#![no_main] -#![allow(unused_imports)] - -//! Scratch space for checking the asm output of stuff. - -use gba::prelude::*; - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[no_mangle] -extern "C" fn main() -> ! { - loop {} -} diff --git a/backup/examples/foo.txt b/backup/examples/foo.txt deleted file mode 100644 index c3050487..00000000 --- a/backup/examples/foo.txt +++ /dev/null @@ -1 +0,0 @@ -usedinhello.rs \ No newline at end of file From 0e3493f78157c8e840e5115da5e730100c3ea4b5 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sat, 25 May 2024 18:09:47 -0600 Subject: [PATCH 26/89] restore this example into the source tree --- backup/examples/mode3_pong_example_game.rs | 159 ------------------- examples/mode3_pong_example_game.rs | 175 +++++++++++++++++++++ 2 files changed, 175 insertions(+), 159 deletions(-) delete mode 100644 backup/examples/mode3_pong_example_game.rs create mode 100644 examples/mode3_pong_example_game.rs diff --git a/backup/examples/mode3_pong_example_game.rs b/backup/examples/mode3_pong_example_game.rs deleted file mode 100644 index 67ec9cb2..00000000 --- a/backup/examples/mode3_pong_example_game.rs +++ /dev/null @@ -1,159 +0,0 @@ -/* -* Made by Evan Goemer -* Discord: @evangoemer -*/ - -#![no_std] -#![no_main] - -use gba::{prelude::*, mem_fns::__aeabi_memset}; - -const SCREEN_WIDTH: u16 = 240; -const SCREEN_HEIGHT: u16 = 160; - -const PADDLE_WIDTH: u16 = 4; -const PADDLE_HEIGHT: u16 = 20; -const BALL_SIZE: u16 = 2; - -struct Paddle { - x: u16, - y: u16, -} - -struct Ball { - x: u16, - y: u16, - dx: i16, - dy: i16, -} - -impl Paddle { - fn new(x: u16, y: u16) -> Self { - Self { - x, - y, - } - } - - fn update(&mut self) { - let keys = KEYINPUT.read(); - if keys.up() && self.y > 1 { - self.y -= 1; - } - - if keys.down() && self.y + PADDLE_HEIGHT + 1 < SCREEN_HEIGHT { - self.y += 1; - } - } -} - -impl Ball { - fn new(x: u16, y: u16) -> Self { - Self { x, y, dx: 1, dy: 1 } - } - - fn update(&mut self, paddle1: &Paddle, paddle2: &Paddle) { - if self.y <= 0 || self.y + BALL_SIZE >= SCREEN_HEIGHT { - self.dy = -self.dy; - } - - if self.x + BALL_SIZE >= paddle1.x - && self.x <= paddle1.x + PADDLE_WIDTH - && self.y + BALL_SIZE >= paddle1.y - && self.y <= paddle1.y + PADDLE_HEIGHT - { - self.dx = -self.dx; - self.dy = -self.dy; - } - - if self.x + BALL_SIZE >= paddle2.x - && self.x <= paddle2.x + PADDLE_WIDTH - && self.y + BALL_SIZE >= paddle2.y - && self.y <= paddle2.y + PADDLE_HEIGHT - { - self.dx = -self.dx; - self.dy = -self.dy; - } - - - if self.x + BALL_SIZE <= 1 + BALL_SIZE { - self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2; - self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2; - self.dx = 1; - self.dy = 1; - } - - if self.x >= SCREEN_WIDTH - BALL_SIZE - 1 { - self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2; - self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2; - self.dx = -1; - self.dy = 1; - } - self.x = (self.x as i16 + self.dx) as u16; - self.y = (self.y as i16 + self.dy) as u16; - } -} - -static SPRITE_POSITIONS: [GbaCell; 6] = [ - GbaCell::new(0), - GbaCell::new(0), - GbaCell::new(0), - GbaCell::new(0), - GbaCell::new(0), - GbaCell::new(0), -]; - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[no_mangle] -fn main() -> ! { - DISPCNT.write( - DisplayControl::new().with_video_mode(VideoMode::_3).with_show_bg2(true), - ); - - RUST_IRQ_HANDLER.write(Some(draw_sprites)); - DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true)); - IE.write(IrqBits::VBLANK); - IME.write(true); - - let mut left_paddle = Paddle::new(10, SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2); - let mut right_paddle = Paddle::new(SCREEN_WIDTH as u16 - 10 - PADDLE_WIDTH, SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2); - let mut ball = Ball::new(SCREEN_WIDTH as u16 / 2, SCREEN_HEIGHT as u16 / 2); - - loop { - left_paddle.update(); - right_paddle.update(); - ball.update(&left_paddle, &right_paddle); - - SPRITE_POSITIONS[0].write(left_paddle.x); - SPRITE_POSITIONS[1].write(left_paddle.y); - SPRITE_POSITIONS[2].write(right_paddle.x); - SPRITE_POSITIONS[3].write(right_paddle.y); - SPRITE_POSITIONS[4].write(ball.x); - SPRITE_POSITIONS[5].write(ball.y); - - VBlankIntrWait(); - } -} - -extern "C" fn draw_sprites(_bits: IrqBits) { - unsafe { - let p = VIDEO3_VRAM.as_usize() as *mut u8; - __aeabi_memset(p, 240*160*2, 0) - } - - draw_rect(SPRITE_POSITIONS[0].read(), SPRITE_POSITIONS[1].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::WHITE); - draw_rect(SPRITE_POSITIONS[2].read(), SPRITE_POSITIONS[3].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::WHITE); - draw_rect(SPRITE_POSITIONS[4].read(), SPRITE_POSITIONS[5].read(), BALL_SIZE, BALL_SIZE, Color::WHITE); -} - -fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { - for i in 0..width { - for j in 0..height { - VIDEO3_VRAM.index((x + i) as usize, (y + j) as usize).write(color); - } - } -} diff --git a/examples/mode3_pong_example_game.rs b/examples/mode3_pong_example_game.rs new file mode 100644 index 00000000..29096f25 --- /dev/null +++ b/examples/mode3_pong_example_game.rs @@ -0,0 +1,175 @@ +/* + * Made by Evan Goemer + * Discord: @evangoemer + */ + +#![no_std] +#![no_main] + +const SCREEN_WIDTH: u16 = 240; +const SCREEN_HEIGHT: u16 = 160; + +const PADDLE_WIDTH: u16 = 4; +const PADDLE_HEIGHT: u16 = 20; +const BALL_SIZE: u16 = 2; + +struct Paddle { + x: u16, + y: u16, +} + +struct Ball { + x: u16, + y: u16, + dx: i16, + dy: i16, +} + +impl Paddle { + fn new(x: u16, y: u16) -> Self { + Self { x, y } + } + + fn update(&mut self) { + let keys = KEYINPUT.read(); + if keys.up() && self.y > 1 { + self.y -= 1; + } + + if keys.down() && self.y + PADDLE_HEIGHT + 1 < SCREEN_HEIGHT { + self.y += 1; + } + } +} + +impl Ball { + fn new(x: u16, y: u16) -> Self { + Self { x, y, dx: 1, dy: 1 } + } + + fn update(&mut self, paddle1: &Paddle, paddle2: &Paddle) { + if self.y <= 0 || self.y + BALL_SIZE >= SCREEN_HEIGHT { + self.dy = -self.dy; + } + + if self.x + BALL_SIZE >= paddle1.x + && self.x <= paddle1.x + PADDLE_WIDTH + && self.y + BALL_SIZE >= paddle1.y + && self.y <= paddle1.y + PADDLE_HEIGHT + { + self.dx = -self.dx; + self.dy = -self.dy; + } + + if self.x + BALL_SIZE >= paddle2.x + && self.x <= paddle2.x + PADDLE_WIDTH + && self.y + BALL_SIZE >= paddle2.y + && self.y <= paddle2.y + PADDLE_HEIGHT + { + self.dx = -self.dx; + self.dy = -self.dy; + } + + if self.x + BALL_SIZE <= 1 + BALL_SIZE { + self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2; + self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2; + self.dx = 1; + self.dy = 1; + } + + if self.x >= SCREEN_WIDTH - BALL_SIZE - 1 { + self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2; + self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2; + self.dx = -1; + self.dy = 1; + } + self.x = (self.x as i16 + self.dx) as u16; + self.y = (self.y as i16 + self.dy) as u16; + } +} + +static SPRITE_POSITIONS: [GbaCell; 6] = [ + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), +]; + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +fn main() -> ! { + DISPCNT.write( + DisplayControl::new().with_video_mode(VideoMode::_3).with_show_bg2(true), + ); + + RUST_IRQ_HANDLER.write(Some(draw_sprites)); + DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true)); + IE.write(IrqBits::VBLANK); + IME.write(true); + + let mut left_paddle = + Paddle::new(10, SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2); + let mut right_paddle = Paddle::new( + SCREEN_WIDTH as u16 - 10 - PADDLE_WIDTH, + SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2, + ); + let mut ball = Ball::new(SCREEN_WIDTH as u16 / 2, SCREEN_HEIGHT as u16 / 2); + + loop { + left_paddle.update(); + right_paddle.update(); + ball.update(&left_paddle, &right_paddle); + + SPRITE_POSITIONS[0].write(left_paddle.x); + SPRITE_POSITIONS[1].write(left_paddle.y); + SPRITE_POSITIONS[2].write(right_paddle.x); + SPRITE_POSITIONS[3].write(right_paddle.y); + SPRITE_POSITIONS[4].write(ball.x); + SPRITE_POSITIONS[5].write(ball.y); + + VBlankIntrWait(); + } +} + +extern "C" fn draw_sprites(_bits: IrqBits) { + unsafe { + let p = VIDEO3_VRAM.as_usize() as *mut u8; + __aeabi_memset(p, 240 * 160 * 2, 0) + } + + draw_rect( + SPRITE_POSITIONS[0].read(), + SPRITE_POSITIONS[1].read(), + PADDLE_WIDTH, + PADDLE_HEIGHT, + Color::WHITE, + ); + draw_rect( + SPRITE_POSITIONS[2].read(), + SPRITE_POSITIONS[3].read(), + PADDLE_WIDTH, + PADDLE_HEIGHT, + Color::WHITE, + ); + draw_rect( + SPRITE_POSITIONS[4].read(), + SPRITE_POSITIONS[5].read(), + BALL_SIZE, + BALL_SIZE, + Color::WHITE, + ); +} + +fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { + for i in 0..width { + for j in 0..height { + VIDEO3_VRAM.index((x + i) as usize, (y + j) as usize).write(color); + } + } +} From 9b32f67f79e2180ffc02fbe3e114854f2f422de0 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sat, 25 May 2024 18:10:20 -0600 Subject: [PATCH 27/89] pong is a trademark so we must use another name --- examples/{mode3_pong_example_game.rs => paddle_ball.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{mode3_pong_example_game.rs => paddle_ball.rs} (100%) diff --git a/examples/mode3_pong_example_game.rs b/examples/paddle_ball.rs similarity index 100% rename from examples/mode3_pong_example_game.rs rename to examples/paddle_ball.rs From a65840048e956c7b233afd866e8959d41dc8de78 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sat, 25 May 2024 18:17:34 -0600 Subject: [PATCH 28/89] it compiles, but doesn't run right. probably we need to make the screen clear faster. --- examples/paddle_ball.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 29096f25..8a7b4620 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -6,6 +6,15 @@ #![no_std] #![no_main] +use gba::{ + asm_runtime::USER_IRQ_HANDLER, + bios::VBlankIntrWait, + gba_cell::GbaCell, + mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, +}; + const SCREEN_WIDTH: u16 = 240; const SCREEN_HEIGHT: u16 = 160; @@ -104,12 +113,10 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! { #[no_mangle] fn main() -> ! { - DISPCNT.write( - DisplayControl::new().with_video_mode(VideoMode::_3).with_show_bg2(true), - ); + DISPCNT.write(DisplayControl::new().with_bg_mode(3).with_bg2(true)); - RUST_IRQ_HANDLER.write(Some(draw_sprites)); - DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true)); + USER_IRQ_HANDLER.write(Some(draw_sprites)); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); IE.write(IrqBits::VBLANK); IME.write(true); @@ -138,10 +145,10 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - unsafe { - let p = VIDEO3_VRAM.as_usize() as *mut u8; - __aeabi_memset(p, 240 * 160 * 2, 0) - } + MODE3_VRAM + .into_block::<{ 240 * 160 }>() + .iter() + .for_each(|a| a.write(Color::BLACK)); draw_rect( SPRITE_POSITIONS[0].read(), @@ -169,7 +176,7 @@ extern "C" fn draw_sprites(_bits: IrqBits) { fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { for i in 0..width { for j in 0..height { - VIDEO3_VRAM.index((x + i) as usize, (y + j) as usize).write(color); + MODE3_VRAM.index((x + i) as usize, (y + j) as usize).write(color); } } } From 2c04eecd4ea4c820b9c061e2562a923e31b6342f Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 00:05:31 -0600 Subject: [PATCH 29/89] part of the panic message in the ROM is getting copied to vram --- examples/paddle_ball.rs | 38 +++++++++++++++++++++----------------- src/mmio.rs | 15 ++++++++++++++- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 8a7b4620..9a7f558c 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -10,9 +10,12 @@ use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, + mmio::{ + DISPCNT, DISPSTAT, DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, + DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, + }, video::{Color, DisplayControl, DisplayStatus}, - IrqBits, + IrqBits, KeyInput, }; const SCREEN_WIDTH: u16 = 240; @@ -39,8 +42,7 @@ impl Paddle { Self { x, y } } - fn update(&mut self) { - let keys = KEYINPUT.read(); + fn update(&mut self, keys: KeyInput) { if keys.up() && self.y > 1 { self.y -= 1; } @@ -106,10 +108,7 @@ static SPRITE_POSITIONS: [GbaCell; 6] = [ GbaCell::new(0), ]; -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo) -> ! { - loop {} -} +gba::panic_handler!(empty_loop); #[no_mangle] fn main() -> ! { @@ -129,8 +128,9 @@ fn main() -> ! { let mut ball = Ball::new(SCREEN_WIDTH as u16 / 2, SCREEN_HEIGHT as u16 / 2); loop { - left_paddle.update(); - right_paddle.update(); + let keys = KEYINPUT.read(); + left_paddle.update(keys); + right_paddle.update(keys); ball.update(&left_paddle, &right_paddle); SPRITE_POSITIONS[0].write(left_paddle.x); @@ -145,31 +145,35 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - MODE3_VRAM - .into_block::<{ 240 * 160 }>() - .iter() - .for_each(|a| a.write(Color::BLACK)); + unsafe { + // Clear VRAM using DMA3 + let x = &0_u32; + DMA3_SOURCE.write((x as *const u32).cast()); + DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as *mut _); + DMA3_TRANSFER_COUNT.write(240 * 160 / 2); + DMA3_CONTROL.write(1 << 15 | 1 << 10 | 2 << 7); + } draw_rect( SPRITE_POSITIONS[0].read(), SPRITE_POSITIONS[1].read(), PADDLE_WIDTH, PADDLE_HEIGHT, - Color::WHITE, + Color::RED, ); draw_rect( SPRITE_POSITIONS[2].read(), SPRITE_POSITIONS[3].read(), PADDLE_WIDTH, PADDLE_HEIGHT, - Color::WHITE, + Color::GREEN, ); draw_rect( SPRITE_POSITIONS[4].read(), SPRITE_POSITIONS[5].read(), BALL_SIZE, BALL_SIZE, - Color::WHITE, + Color::CYAN, ); } diff --git a/src/mmio.rs b/src/mmio.rs index f8eb08fe..c0963b92 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -1,9 +1,11 @@ //! Definitions for Memory-mapped IO (hardware control). +use core::ffi::c_void; + use bitfrob::u8x2; #[allow(unused_imports)] use voladdress::VolAddress; -use voladdress::{VolBlock, VolGrid2d, VolGrid2dStrided}; +use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided}; use crate::{ video::{Color, DisplayControl, DisplayStatus, Tile4bpp}, @@ -21,6 +23,8 @@ type SOGBA = voladdress::Unsafe; type PlainAddr = VolAddress; /// Read-only addr type RoAddr = VolAddress; +/// Write-only addr +type WoAddr = VolAddress; /// Display Control setting. /// @@ -43,6 +47,15 @@ pub const DISPSTAT: PlainAddr = /// Values of 160 to 227 indicate that a vertical blank line is happening. pub const VCOUNT: RoAddr = unsafe { VolAddress::new(0x0400_0006) }; +pub const DMA3_SOURCE: WoAddr<*const c_void> = + unsafe { VolAddress::new(0x0400_00D4) }; +pub const DMA3_DESTINATION: WoAddr<*mut c_void> = + unsafe { VolAddress::new(0x0400_00D8) }; +pub const DMA3_TRANSFER_COUNT: WoAddr = + unsafe { VolAddress::new(0x0400_00DC) }; +pub const DMA3_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00DE) }; + /// Key Input (read-only). /// /// Gives the low-active button state of all system buttons. From 44e38ee814ba1aba6bf4211fd06767307ce664cb Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 00:27:25 -0600 Subject: [PATCH 30/89] DMA source mode *must* be inc if the source addr is in ROM. --- dump.bat | 6 +++++- examples/paddle_ball.rs | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dump.bat b/dump.bat index fb5ec474..1996735e 100644 --- a/dump.bat +++ b/dump.bat @@ -1,11 +1,15 @@ cargo build --examples +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/basic_keyinput >target/ex-basic_keyinput.txt + arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/do_nothing >target/ex-do_nothing.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/basic_keyinput >target/ex-basic_keyinput.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode0 >target/ex-mode0.txt arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode3 >target/ex-mode3.txt arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode4 >target/ex-mode4.txt arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode5 >target/ex-mode5.txt + +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/paddle_ball >target/ex-paddle_ball.txt diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 9a7f558c..0187f452 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -6,13 +6,15 @@ #![no_std] #![no_main] +use core::ptr::addr_of; + use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, mmio::{ DISPCNT, DISPSTAT, DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, - DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, + DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, OBJ_PALRAM, }, video::{Color, DisplayControl, DisplayStatus}, IrqBits, KeyInput, @@ -147,8 +149,8 @@ fn main() -> ! { extern "C" fn draw_sprites(_bits: IrqBits) { unsafe { // Clear VRAM using DMA3 - let x = &0_u32; - DMA3_SOURCE.write((x as *const u32).cast()); + let x: u32 = 0 * OBJ_PALRAM.index(0).read().0 as u32; + DMA3_SOURCE.write(addr_of!(x).cast()); DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as *mut _); DMA3_TRANSFER_COUNT.write(240 * 160 / 2); DMA3_CONTROL.write(1 << 15 | 1 << 10 | 2 << 7); From 3188fb40b8444c8a1470287a842b391fde14ed7c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 00:29:35 -0600 Subject: [PATCH 31/89] delete old examples that we have replacements for. --- backup/examples/hello.rs | 101 - backup/examples/mode3_realtime_example.rs | 37 - backup/examples/video3_test.rs | 4300 --------------------- backup/examples/video4_test.rs | 1515 -------- 4 files changed, 5953 deletions(-) delete mode 100644 backup/examples/hello.rs delete mode 100644 backup/examples/mode3_realtime_example.rs delete mode 100644 backup/examples/video3_test.rs delete mode 100644 backup/examples/video4_test.rs diff --git a/backup/examples/hello.rs b/backup/examples/hello.rs deleted file mode 100644 index 2c98e6df..00000000 --- a/backup/examples/hello.rs +++ /dev/null @@ -1,101 +0,0 @@ -#![no_std] -#![no_main] - -use core::fmt::Write; -use gba::prelude::*; - -#[panic_handler] -fn panic_handler(info: &core::panic::PanicInfo) -> ! { - #[cfg(debug_assertions)] - if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) { - writeln!(logger, "{info}").ok(); - } - loop {} -} - -#[allow(dead_code)] -const FOO: Align4<[u8; 14]> = include_aligned_bytes!("foo.txt"); - -#[link_section = ".ewram"] -static FRAME_KEYS: GbaCell = GbaCell::new(KeyInput::new()); - -#[link_section = ".iwram"] -extern "C" fn irq_handler(_: IrqBits) { - // We'll read the keys during vblank and store it for later. - FRAME_KEYS.write(KEYINPUT.read()); -} - -#[no_mangle] -extern "C" fn main() -> ! { - RUST_IRQ_HANDLER.write(Some(irq_handler)); - DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true)); - IE.write(IrqBits::VBLANK); - IME.write(true); - - if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Debug) { - writeln!(logger, "hello!").ok(); - - let fx_u: Fixed = - Fixed::::wrapping_from(7) + Fixed::::from_raw(12); - writeln!(logger, "fixed unsigned: {fx_u:?}").ok(); - - let fx_i1: Fixed = - Fixed::::wrapping_from(8) + Fixed::::from_raw(15); - writeln!(logger, "fixed signed positive: {fx_i1:?}").ok(); - - let fx_i2: Fixed = Fixed::::wrapping_from(0) - - Fixed::::wrapping_from(3) - - Fixed::::from_raw(17); - writeln!(logger, "fixed signed negative: {fx_i2:?}").ok(); - } - - { - // get our tile data into memory. - Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP.as_region(), 0); - } - - { - // set up the tilemap - let tsb = TEXT_SCREENBLOCKS.get_frame(31).unwrap(); - for y in 0..16 { - let row = tsb.get_row(y).unwrap(); - for (x, addr) in row.iter().enumerate().take(16) { - let te = TextEntry::new().with_tile((y * 16 + x) as u16); - addr.write(te); - } - } - } - - { - // Set BG0 to use the tilemap we just made, and set it to be shown. - BG0CNT.write(BackgroundControl::new().with_screenblock(31)); - DISPCNT.write(DisplayControl::new().with_show_bg0(true)); - } - - let mut x_off = 0_u32; - let mut y_off = 0_u32; - let mut backdrop_color = Color(0); - loop { - VBlankIntrWait(); - // show current frame - BACKDROP_COLOR.write(backdrop_color); - BG0HOFS.write(x_off as u16); - BG0VOFS.write(y_off as u16); - - // prep next frame - let k = FRAME_KEYS.read(); - backdrop_color = Color(k.to_u16()); - if k.up() { - y_off = y_off.wrapping_add(1); - } - if k.down() { - y_off = y_off.wrapping_sub(1); - } - if k.left() { - x_off = x_off.wrapping_add(1); - } - if k.right() { - x_off = x_off.wrapping_sub(1); - } - } -} diff --git a/backup/examples/mode3_realtime_example.rs b/backup/examples/mode3_realtime_example.rs deleted file mode 100644 index c7633997..00000000 --- a/backup/examples/mode3_realtime_example.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Made by Evan Goemer - * Discord: @evangoemer - */ - -#![no_std] -#![no_main] - -use gba::prelude::*; - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[no_mangle] -fn main() -> ! { - DISPCNT.write( - DisplayControl::new().with_video_mode(VideoMode::_3).with_show_bg2(true), - ); - - let mut red = 0; - let mut green = 255; - let mut blue = 0; - - for y in 0..160 { - for x in 0..240 { - let color = Color::from_rgb(red, green, blue); - VIDEO3_VRAM.index(x, y).write(color); - - red = (red + 1) % 256; - green = (green + 3) % 256; - blue = (blue + 5) % 256; - } - } - loop {} -} diff --git a/backup/examples/video3_test.rs b/backup/examples/video3_test.rs deleted file mode 100644 index 4aa664db..00000000 --- a/backup/examples/video3_test.rs +++ /dev/null @@ -1,4300 +0,0 @@ -#![no_std] -#![no_main] - -use gba::{mem_fns::__aeabi_memcpy, prelude::*}; - -#[panic_handler] -fn panic_handler(info: &core::panic::PanicInfo) -> ! { - #[cfg(debug_assertions)] - if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) { - use core::fmt::Write; - writeln!(logger, "{info}").ok(); - } - loop {} -} - -#[no_mangle] -fn main() -> ! { - let a = TEXT_SCREENBLOCKS.get_frame(0).unwrap().as_usize(); - unsafe { - __aeabi_memcpy( - a as _, - PIXELS.as_ptr().cast(), - core::mem::size_of_val(PIXELS) as _, - ) - }; - DISPCNT.write( - DisplayControl::new().with_video_mode(VideoMode::_3).with_show_bg2(true), - ); - loop {} -} - -pub const PIXELS: &[u16] = &[ - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, - 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2E06, 0x2E06, 0x2E06, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2E06, 0x2E06, 0x2E06, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, 0x27BE, - 0x27BE, 0x27BE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x61C5, 0x61C5, 0x61C5, 0x61C5, - 0x61C5, 0x61C5, 0x61C5, 0x61C5, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, - 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, - 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, - 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, 0x2518, 0x2518, - 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x2518, 0x2518, 0x2518, - 0x2518, 0x2518, 0x2518, 0x2518, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, - 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, 0x77DE, -]; diff --git a/backup/examples/video4_test.rs b/backup/examples/video4_test.rs deleted file mode 100644 index 56cbcc41..00000000 --- a/backup/examples/video4_test.rs +++ /dev/null @@ -1,1515 +0,0 @@ -#![no_std] -#![no_main] - -use gba::{mem_fns::__aeabi_memcpy, prelude::*}; - -#[panic_handler] -fn panic_handler(info: &core::panic::PanicInfo) -> ! { - #[cfg(debug_assertions)] - if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) { - use core::fmt::Write; - writeln!(logger, "{info}").ok(); - } - loop {} -} - -#[no_mangle] -fn main() -> ! { - let a = TEXT_SCREENBLOCKS.get_frame(0).unwrap().as_usize(); - unsafe { - __aeabi_memcpy( - a as _, - INDEXES.as_ptr().cast(), - core::mem::size_of_val(INDEXES) as _, - ) - }; - BG_PALETTE.iter().zip(PALETTE.iter()).for_each(|(va, i)| { - va.write(Color(*i)); - }); - DISPCNT.write( - DisplayControl::new().with_video_mode(VideoMode::_4).with_show_bg2(true), - ); - loop {} -} - -pub const INDEXES: &[u8] = &[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, - 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 2, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, - 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, - 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, - 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, 2, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 0, - 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, - 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, - 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, - 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, - 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, - 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, - 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, - 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, - 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 3, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, - 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 0, - 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, - 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, - 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, - 3, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, - 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 0, 0, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, - 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, - 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, - 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, - 4, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 4, - 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, - 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, - 4, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, - 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, - 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, - 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0, - 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, - 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 4, 4, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, - 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -]; - -pub const PALETTE: &[u16] = &[0x77DE, 0x2E06, 0x27BE, 0x61C5, 0x2518]; From d7b002b6489dbb9f8f400f7212ba35f3b260680a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 14:59:19 -0600 Subject: [PATCH 32/89] spelling --- src/bios.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bios.rs b/src/bios.rs index d7352317..19215072 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -25,7 +25,7 @@ use crate::IrqBits; /// to fire then this function will loop forever without returning. /// /// This function uses a special BIOS variable to track what interrupts have -/// occured recently. +/// occurred recently. /// * If `ignore_existing` is set, then any previous interrupts (since /// `IntrWait` was last called) that match `target_irqs` are *ignored* and /// this function will wait for a new target interrupt to occur. From c4748bf3712f4eb9db0ff2e1db9b4ad6a1f1062f Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 14:59:44 -0600 Subject: [PATCH 33/89] update impls --- src/video.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video.rs b/src/video.rs index e1b44d4c..ff11b9b3 100644 --- a/src/video.rs +++ b/src/video.rs @@ -48,7 +48,7 @@ unsafe impl bytemuck::Pod for Color {} /// /// The video mode is the most important property here. It controls how most /// other display-related things will act. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Default)] #[repr(transparent)] pub struct DisplayControl(u16); impl DisplayControl { @@ -165,7 +165,7 @@ impl DisplayControl { } /// Gives info about the display state and sets display interrupts. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Default)] #[repr(transparent)] pub struct DisplayStatus(u16); impl DisplayStatus { @@ -222,7 +222,7 @@ impl DisplayStatus { /// some way, and then pack it into your ROM as a static value. The data is then /// copied from ROM into the correct VRAM location at runtime. You are not /// expected to manipulate particular pixels within a tile at runtime. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] #[repr(transparent)] pub struct Tile4bpp(pub [u32; 8]); @@ -237,6 +237,6 @@ pub struct Tile4bpp(pub [u32; 8]); /// some way, and then pack it into your ROM as a static value. The data is then /// copied from ROM into the correct VRAM location at runtime. You are not /// expected to manipulate particular pixels within a tile at runtime. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] #[repr(transparent)] pub struct Tile8bpp(pub [u32; 16]); From 1bb4e7b95e4c5a4edb1bc1b3a1e6ca08a7395b2e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 14:59:56 -0600 Subject: [PATCH 34/89] dma mmio --- src/mmio.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/src/mmio.rs b/src/mmio.rs index c0963b92..24e23175 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -8,6 +8,7 @@ use voladdress::VolAddress; use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided}; use crate::{ + dma::DmaControl, video::{Color, DisplayControl, DisplayStatus, Tile4bpp}, IrqBits, KeyInput, }; @@ -23,8 +24,6 @@ type SOGBA = voladdress::Unsafe; type PlainAddr = VolAddress; /// Read-only addr type RoAddr = VolAddress; -/// Write-only addr -type WoAddr = VolAddress; /// Display Control setting. /// @@ -47,13 +46,92 @@ pub const DISPSTAT: PlainAddr = /// Values of 160 to 227 indicate that a vertical blank line is happening. pub const VCOUNT: RoAddr = unsafe { VolAddress::new(0x0400_0006) }; -pub const DMA3_SOURCE: WoAddr<*const c_void> = +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA0_SOURCE: VolAddress<*const c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00B0) }; + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA0_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00B4) }; + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x4000` +pub const DMA0_TRANSFER_COUNT: VolAddress = + unsafe { VolAddress::new(0x0400_00B8) }; + +/// DMA3 Control Bits. +pub const DMA0_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00BA) }; + +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA1_SOURCE: VolAddress<*const c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00BC) }; + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA1_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00C0) }; + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x4000` +pub const DMA1_TRANSFER_COUNT: VolAddress = + unsafe { VolAddress::new(0x0400_00C4) }; + +/// DMA3 Control Bits. +pub const DMA1_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00C6) }; + +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA2_SOURCE: VolAddress<*const c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00C8) }; + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA2_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00CC) }; + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x4000` +pub const DMA2_TRANSFER_COUNT: VolAddress = + unsafe { VolAddress::new(0x0400_00D0) }; + +/// DMA3 Control Bits. +pub const DMA2_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00D2) }; + +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA3_SOURCE: VolAddress<*const c_void, (), Unsafe> = unsafe { VolAddress::new(0x0400_00D4) }; -pub const DMA3_DESTINATION: WoAddr<*mut c_void> = + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA3_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = unsafe { VolAddress::new(0x0400_00D8) }; -pub const DMA3_TRANSFER_COUNT: WoAddr = + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x1_0000` +pub const DMA3_TRANSFER_COUNT: VolAddress = unsafe { VolAddress::new(0x0400_00DC) }; -pub const DMA3_CONTROL: VolAddress = + +/// DMA3 Control Bits. +pub const DMA3_CONTROL: VolAddress = unsafe { VolAddress::new(0x0400_00DE) }; /// Key Input (read-only). From 4c1822493405beef60cf8173a1d97004a90c2b7a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 16:58:15 -0600 Subject: [PATCH 35/89] something about memset is messed up right now so the example doesn't run. --- Cargo.toml | 7 + backup/src/asm_runtime.rs | 161 ------------------- backup/src/mem_fns.rs | 322 ++------------------------------------ examples/paddle_ball.rs | 19 +-- src/dma.rs | 218 ++++++++++++++++++++++++++ src/lib.rs | 6 +- src/mem/clear.rs | 22 +++ src/mem/copy.rs | 34 ++++ src/mem/mod.rs | 9 ++ src/mem/set.rs | 161 +++++++++++++++++++ src/video.rs | 13 ++ 11 files changed, 486 insertions(+), 486 deletions(-) create mode 100644 src/dma.rs create mode 100644 src/mem/clear.rs create mode 100644 src/mem/copy.rs create mode 100644 src/mem/mod.rs create mode 100644 src/mem/set.rs diff --git a/Cargo.toml b/Cargo.toml index 1bd52c80..d3a59803 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,13 @@ doc_cfg = [] # inlined (meaning `Location` is passed via the stack). This is only needed for # debugging, and so it's off by default. track_caller = [] +# Applies the `no_mangle` attribute to the `memcpy` family of functions, causing +# them to override the same intrinsics from the `compiler_builtins` crate. When +# using this you will need to set cargo's `build-std-features` list to include +# "compiler-builtins-weak-intrinsics" +no_mangle_memcpy = [] +# As above, but for `memset` and `memclr` fns. +no_mangle_memset = [] [dependencies] voladdress = "1.4.0" diff --git a/backup/src/asm_runtime.rs b/backup/src/asm_runtime.rs index 63f2749b..0de3dcf0 100644 --- a/backup/src/asm_runtime.rs +++ b/backup/src/asm_runtime.rs @@ -1,164 +1,3 @@ -//! This module holds the assembly runtime that supports your Rust program. -//! -//! Most importantly, you can set the [`RUST_IRQ_HANDLER`] variable to assign -//! which function should be run during a hardware interrupt. -//! * When a hardware interrupt occurs, control first goes to the BIOS, which -//! will then call the assembly runtime's handler. -//! * The assembly runtime handler will properly acknowledge the interrupt -//! within the system on its own without you having to do anything. -//! * If a function is set in the `RUST_IRQ_HANDLER` variable then that function -//! will be called and passed the bits for which interrupt(s) occurred. - -use crate::{ - dma::DmaControl, - gba_cell::GbaCell, - interrupts::IrqFn, - mgba::MGBA_LOGGING_ENABLE_REQUEST, - mmio::{DMA3_SRC, IME, MGBA_LOG_ENABLE}, -}; - -/// The function pointer that the assembly runtime calls when an interrupt -/// occurs. -pub static RUST_IRQ_HANDLER: GbaCell> = GbaCell::new(None); - -const DMA_32_BIT_MEMCPY: DmaControl = - DmaControl::new().with_transfer_32bit(true).with_enabled(true); - -const DMA3_OFFSET: usize = DMA3_SRC.as_usize() - 0x0400_0000; -const IME_OFFSET: usize = IME.as_usize() - 0x0400_0000; - -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".text.gba_rom_header"] -unsafe extern "C" fn __start() -> ! { - core::arch::asm!( - "b 1f", - ".space 0xE0", - "1:", /* post header */ - "mov r12, #{mmio_base}", - "add r0, r12, #{waitcnt_offset}", - "ldr r1, ={waitcnt_setting}", - "strh r1, [r0]", - - /* iwram copy */ - "ldr r4, =__iwram_word_copy_count", - bracer::when!("r4" != "#0" [label_id=1] { - "add r3, r12, #{dma3_offset}", - "mov r5, #{dma3_setting}", - "ldr r0, =__iwram_start", - "ldr r2, =__iwram_position_in_rom", - "str r2, [r3]", /* source */ - "str r0, [r3, #4]", /* destination */ - "strh r4, [r3, #8]", /* word count */ - "strh r5, [r3, #10]", /* set control bits */ - }), - - /* ewram copy */ - "ldr r4, =__ewram_word_copy_count", - bracer::when!("r4" != "#0" [label_id=1] { - "add r3, r12, #{dma3_offset}", - "mov r5, #{dma3_setting}", - "ldr r0, =__ewram_start", - "ldr r2, =__ewram_position_in_rom", - "str r2, [r3]", /* source */ - "str r0, [r3, #4]", /* destination */ - "strh r4, [r3, #8]", /* word count */ - "strh r5, [r3, #10]", /* set control bits */ - }), - - /* bss zero */ - "ldr r4, =__bss_word_clear_count", - bracer::when!("r4" != "#0" [label_id=1] { - "ldr r0, =__bss_start", - "mov r2, #0", - "2:", - "str r2, [r0], #4", - "subs r4, r4, #1", - "bne 2b", - }), - - /* assign the runtime irq handler */ - "ldr r1, ={runtime_irq_handler}", - "str r1, [r12, #-4]", - - /* ask for mGBA logging to be enabled. This should be harmless if we're not using mgba. */ - "ldr r0, ={mgba_log_enable}", - "ldr r1, ={mgba_logging_enable_request}", - "str r1, [r0]", - - /* call to rust main */ - "ldr r0, =main", - "bx r0", - // main shouldn't return, but if it does just SoftReset - "swi #0", - mmio_base = const 0x0400_0000, - waitcnt_offset = const 0x204, - waitcnt_setting = const 0x4317 /*sram8,r0:3.1,r1:4.2,r2:8.2,no_phi,prefetch*/, - dma3_offset = const DMA3_OFFSET, - dma3_setting = const DMA_32_BIT_MEMCPY.to_u16(), - runtime_irq_handler = sym runtime_irq_handler, - mgba_log_enable = const MGBA_LOG_ENABLE.as_usize(), - mgba_logging_enable_request = const MGBA_LOGGING_ENABLE_REQUEST, - options(noreturn) - ) -} - -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.runtime.irq.handler"] -unsafe extern "C" fn runtime_irq_handler() { - // On Entry: r0 = 0x0400_0000 (mmio_base) - core::arch::asm!( - /* swap IME off, user can turn it back on if they want */ - "add r12, r0, #{ime_offset}", - "mov r3, #0", - "swp r3, r3, [r12]", - - /* Read/Update IE and IF */ - "ldr r0, [r12, #-8]", - "and r0, r0, r0, LSR #16", - "strh r0, [r12, #-6]", - - /* Read/Update BIOS_IF */ - "sub r2, r12, #(0x208+8)", - "ldrh r1, [r2]", - "orr r1, r1, r0", - "strh r1, [r2]", - - /* Call the Rust fn pointer (if set), using System mode */ - "ldr r1, ={RUST_IRQ_HANDLER}", - "ldr r1, [r1]", - bracer::when!("r1" != "#0" [label_id=9] { - bracer::with_spsr_held_in!("r2", { - bracer::set_cpu_control!(System, irq_masked: false, fiq_masked: false), - - // Note(Lokathor): We are *SKIPPING* the part where we ensure that the - // System stack pointer is aligned to 8 during the call to the rust - // function. This is *technically* against the AAPCS ABI, but the GBA's - // ARMv4T CPU does not even support any instructions that require an - // alignment of 8. By not bothering to align the stack, we save about 5 - // cycles total. Which is neat, but if this were on the DS (which has an - // ARMv5TE CPU) you'd want to ensure the aligned stack. - - bracer::with_pushed_registers!("{{r2, r3, r12, lr}}", { - bracer::adr_lr_then_bx_to!(reg="r1", label_id=1) - }), - - bracer::set_cpu_control!(Supervisor, irq_masked: true, fiq_masked: false), - }), - }), - - /* Restore initial IME setting and return */ - "swp r3, r3, [r12]", - "bx lr", - ime_offset = const IME_OFFSET, - RUST_IRQ_HANDLER = sym RUST_IRQ_HANDLER, - options(noreturn) - ) -} - // For now, the division fns can just keep living here. /// Returns 0 in `r0`, while placing the `numerator` into `r1`. diff --git a/backup/src/mem_fns.rs b/backup/src/mem_fns.rs index bd925ad1..5fc94b2f 100644 --- a/backup/src/mem_fns.rs +++ b/backup/src/mem_fns.rs @@ -5,48 +5,17 @@ use core::ffi::c_void; -/// Byte copy between exclusive regions. -/// -/// * This will *always* copy one byte at a time, making it suitable for use -/// with SRAM memory. -/// -/// ## Safety -/// * If `byte_count` is zero then the pointers are not used and they can be any value. -/// * If `byte_count` is non-zero then: -/// * Both pointers must be valid for the number of bytes given. -/// * The two regions must either be *entirely* disjoint or *entirely* overlapping. -/// Partial overlap is not allowed. -#[inline] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memcpy1"] -pub unsafe extern "C" fn __aeabi_memcpy1( - dest: *mut u8, src: *const u8, byte_count: usize, -) { - core::arch::asm! { - "1:", - "subs {count}, {count}, #1", - "ldrbge {temp}, [{src}], #1", - "strbge {temp}, [{dest}], #1", - "bgt 1b", - temp = out(reg) _, - count = inout(reg) byte_count => _, - src = inout(reg) src => _, - dest = inout(reg) dest => _, - options(nostack) - } -} - -/// Halfword copy between exclusive regions. +/// `u16` copy between exclusive regions. /// /// * If the `byte_count` is odd then a single byte copy will happen at the end. /// /// ## Safety -/// * If `byte_count` is zero then the pointers are not used and they can be any value. +/// * If `byte_count` is zero then the pointers are not used and they can be any +/// value. /// * If `byte_count` is non-zero then: /// * Both pointers must be valid for the span used and aligned to 2. -/// * The two regions must either be *entirely* disjoint or *entirely* overlapping. -/// Partial overlap is not allowed. +/// * The two regions must either be *entirely* disjoint or *entirely* +/// overlapping. Partial overlap is not allowed. #[inline] #[no_mangle] #[instruction_set(arm::a32)] @@ -79,11 +48,12 @@ pub unsafe extern "C" fn __aeabi_memcpy2( /// will happen at the end. /// /// ## Safety -/// * If `byte_count` is zero then the pointers are not used and they can be any value. +/// * If `byte_count` is zero then the pointers are not used and they can be any +/// value. /// * If `byte_count` is non-zero then: /// * Both pointers must be valid for the span used and aligned to 4. -/// * The two regions must either be *entirely* disjoint or *entirely* overlapping. -/// Partial overlap is not allowed. +/// * The two regions must either be *entirely* disjoint or *entirely* +/// overlapping. Partial overlap is not allowed. #[naked] #[no_mangle] #[instruction_set(arm::a32)] @@ -149,11 +119,12 @@ pub unsafe extern "C" fn __aeabi_memcpy8( /// Arbitrary-width copy between exclusive regions. /// /// ## Safety -/// * If `byte_count` is zero then the pointers are not used and they can be any value. +/// * If `byte_count` is zero then the pointers are not used and they can be any +/// value. /// * If `byte_count` is non-zero then: /// * Both pointers must be valid for the span used (no required alignment). -/// * The two regions must either be *entirely* disjoint or *entirely* overlapping. -/// Partial overlap is not allowed. +/// * The two regions must either be *entirely* disjoint or *entirely* +/// overlapping. Partial overlap is not allowed. #[naked] #[no_mangle] #[instruction_set(arm::a32)] @@ -419,270 +390,3 @@ pub unsafe extern "C" fn memmove( options(noreturn) } } - -// SET - -/// Copy between non-exclusive regions, prefer [`__aeabi_memset`] if possible. -/// -/// This function is provided only for API completeness, because in some cases -/// the compiler might automatically generate a call to this function. -#[inline] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memset4"] -pub unsafe extern "C" fn __aeabi_memset4( - dest: *mut u32, byte_count: usize, byte: i32, -) { - __aeabi_memset(dest.cast(), byte_count, byte) -} - -/// Copy between non-exclusive regions, prefer [`__aeabi_memset`] if possible. -/// -/// This function is provided only for API completeness, because in some cases -/// the compiler might automatically generate a call to this function. -#[inline] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memset8"] -pub unsafe extern "C" fn __aeabi_memset8( - dest: *mut u32, byte_count: usize, byte: i32, -) { - __aeabi_memset(dest.cast(), byte_count, byte) -} - -/// Sets all bytes in the region to the `byte` given. -/// -/// Because of historical reasons, the byte is passed in as an `i32`, but only -/// the lowest 8 bits are used. -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memset"] -pub unsafe extern "C" fn __aeabi_memset( - dest: *mut u8, byte_count: usize, byte: i32, -) { - core::arch::asm! { - bracer::when!("r1" >=u "#8" [label_id=7] { - // duplicate the byte across all of r2 and r3 - "and r2, r2, #0xFF", - "orr r2, r2, r2, lsl #8", - "orr r2, r2, r2, lsl #16", - "mov r3, r2", - - // align the pointer for word ops - "tst r0, #0b1", - "subne r1, r1, #1", - "strbne r2, [r0], #1", - "tst r0, #0b10", - "subne r1, r1, #2", - "strhne r2, [r0], #2", - - bracer::when!("r1" >=u "#32" [label_id=8] { - bracer::with_pushed_registers!("{{r4-r9}}", { - "mov r4, r2", - "mov r5, r2", - "mov r6, r2", - "mov r7, r2", - "mov r8, r2", - "mov r9, r2", - "1:", - "subs r1, r1, #32", - "stmge r0!, {{r2-r9}}", - "bgt 1b", - }), - "bxeq lr", - }), - - // set 4 words - "tst r1, #0b10000", - "stmne r0!, {{r2, r3}}", - "stmne r0!, {{r2, r3}}", - - // set 2 and/or 1 words - "lsls r12, r1, #29", - "stmcs r0!, {{r2, r3}}", - "strmi r2, [r0], #4", - - // set halfword and/or byte - "lsls r12, r1, #31", - "strhcs r2, [r0], #2", - "strbmi r2, [r0], #1", - "bx lr", - }), - // byte loop - "9:", - "subs r1, r1, #1", - "strbcs r2, [r0], #1", - "bgt 9b", - "bx lr", - options(noreturn) - } -} - -/// Write a value to all bytes in the region, prefer [`__aeabi_memset`] if -/// possible. -/// -/// This is the libc version of a memory set. It's required to return the `dest` -/// pointer at the end of the call, which makes it need an extra push/pop -/// compared to a direct call to `__aeabi_memset`. Also, the argument ordering -/// is swapped, so shuffling registers costs a few cycles. -/// -/// * **Returns:** The `dest` pointer. -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.memset"] -pub unsafe extern "C" fn memset( - dest: *mut u8, byte: i32, byte_count: usize, -) -> *mut u8 { - core::arch::asm! { - bracer::with_pushed_registers!("{{r0, lr}}", { - "mov r3, r2", - "mov r2, r1", - "mov r1, r3", - "bl {__aeabi_memset}", - }), - "bx lr", - __aeabi_memset = sym __aeabi_memset, - options(noreturn) - } -} - -// CLEAR - -/// Just call [`__aeabi_memset`] with 0 as the `byte` instead. -/// -/// This function is provided only for API completeness, because in some cases -/// the compiler might automatically generate a call to this function. -#[inline] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memclr4"] -pub unsafe extern "C" fn __aeabi_memclr4(dest: *mut u32, byte_count: usize) { - __aeabi_memset(dest.cast(), byte_count, 0) -} - -/// Just call [`__aeabi_memset`] with 0 as the `byte` instead. -/// -/// This function is provided only for API completeness, because in some cases -/// the compiler might automatically generate a call to this function. -#[inline] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memclr8"] -pub unsafe extern "C" fn __aeabi_memclr8(dest: *mut u32, byte_count: usize) { - __aeabi_memset(dest.cast(), byte_count, 0) -} - -/// Just call [`__aeabi_memset`] with 0 as the `byte` instead. -/// -/// This function is provided only for API completeness, because in some cases -/// the compiler might automatically generate a call to this function. -#[inline] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memclr"] -pub unsafe extern "C" fn __aeabi_memclr(dest: *mut u8, byte_count: usize) { - __aeabi_memset(dest, byte_count, 0) -} - -/// Reads 4 bytes, starting at the address given. -/// -/// See [__aeabi_uread4] -/// -/// [__aeabi_uread4]: https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#unaligned-memory-access -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.uread4"] -unsafe extern "C" fn __aeabi_uread4(address: *const c_void) -> u32 { - core::arch::asm!( - "ldrb r2, [r0]", - "ldrb r3, [r0, #1]", - "orr r2, r2, r3, lsl #8", - "ldrb r3, [r0, #2]", - "orr r2, r2, r3, lsl #16", - "ldrb r3, [r0, #3]", - "orr r2, r2, r3, lsl #24", - "mov r0, r2", - "bx lr", - options(noreturn), - ) -} - -/// Writes 4 bytes, starting at the address given. -/// -/// See [__aeabi_uwrite4] -/// -/// [__aeabi_uwrite4]: https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#unaligned-memory-access -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.uwrite4"] -unsafe extern "C" fn __aeabi_uwrite4(value: u32, address: *mut c_void) { - core::arch::asm!( - "strb r0, [r1]", - "lsr r2, r0, #8", - "strb r2, [r1, #1]", - "lsr r2, r2, #8", - "strb r2, [r1, #2]", - "lsr r2, r2, #8", - "strb r2, [r1, #3]", - "bx lr", - options(noreturn), - ) -} - -/// Reads 8 bytes, starting at the address given. -/// -/// See [__aeabi_uread8] -/// -/// [__aeabi_uread8]: https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#unaligned-memory-access -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.uread8"] -unsafe extern "C" fn __aeabi_uread8(address: *const c_void) -> u64 { - core::arch::asm!( - "ldrb r1, [r0, #4]", - "ldrb r2, [r0, #5]", - "orr r1, r1, r2, lsl #8", - "ldrb r2, [r0, #6]", - "orr r1, r1, r2, lsl #16", - "ldrb r2, [r0, #7]", - "orr r1, r1, r2, lsl #24", - "b {__aeabi_uread4}", - __aeabi_uread4 = sym __aeabi_uread4, - options(noreturn), - ) -} - -/// Writes 8 bytes, starting at the address given. -/// -/// See [__aeabi_uwrite8] -/// -/// [__aeabi_uwrite8]: https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#unaligned-memory-access -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.uwrite8"] -unsafe extern "C" fn __aeabi_uwrite8(value: u64, address: *mut c_void) { - core::arch::asm!( - "strb r0, [r2]", - "lsr r3, r0, #8", - "strb r3, [r2, #1]", - "lsr r3, r3, #8", - "strb r3, [r2, #2]", - "lsr r3, r3, #8", - "strb r3, [r2, #3]", - "strb r1, [r2, #4]", - "lsr r3, r1, #8", - "strb r3, [r2, #5]", - "lsr r3, r3, #8", - "strb r3, [r2, #6]", - "lsr r3, r3, #8", - "strb r3, [r2, #7]", - "bx lr", - options(noreturn), - ) -} diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 0187f452..16336376 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -6,17 +6,13 @@ #![no_std] #![no_main] -use core::ptr::addr_of; - use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - mmio::{ - DISPCNT, DISPSTAT, DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, - DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, OBJ_PALRAM, - }, - video::{Color, DisplayControl, DisplayStatus}, + mem::__aeabi_memclr, + mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, + video::{mode3_clear_to, Color, DisplayControl, DisplayStatus}, IrqBits, KeyInput, }; @@ -147,14 +143,7 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - unsafe { - // Clear VRAM using DMA3 - let x: u32 = 0 * OBJ_PALRAM.index(0).read().0 as u32; - DMA3_SOURCE.write(addr_of!(x).cast()); - DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as *mut _); - DMA3_TRANSFER_COUNT.write(240 * 160 / 2); - DMA3_CONTROL.write(1 << 15 | 1 << 10 | 2 << 7); - } + mode3_clear_to(Color::BLACK); draw_rect( SPRITE_POSITIONS[0].read(), diff --git a/src/dma.rs b/src/dma.rs new file mode 100644 index 00000000..a7b7dd0d --- /dev/null +++ b/src/dma.rs @@ -0,0 +1,218 @@ +//! Module for the GBA's Direct Memory Access (DMA) units. +//! +//! ## Basics +//! +//! The GBA has 4 DMA units, numbered 0 through 3. They all work in a similar +//! way, but they each have slightly different limitations and intended use. +//! +//! When any DMA is active, the CPU itself is paused. It won't execute code, and +//! it won't even handle interrupts. Once the DMA is completed normal CPU +//! operations will continue, and only then will any pending interrupts be +//! handled. If you need all interrupts to be handled as quickly as possible +//! (such as serial port interrupts when it's active) then you should not use +//! DMA during that time. +//! +//! Similarly, if more than one DMA is set to be active at the same time, the +//! lower numbered DMA unit "wins" and will perform its operation first, then +//! the higher number DMA will run. +//! +//! The DMA units can transfer data using various configurations. The most +//! common uses of DMA are: +//! +//! 1) Copying large quantities of data, either from ROM into RAM or between two +//! different regions of RAM. The DMA units are faster at copying data than +//! even the most efficient CPU-based copy loops. +//! 2) Copying data at special moments. The DMA units can be set to run at +//! either horizontal or vertical blank time, or when the sound FIFO buffer +//! runs out. +//! +//! ## Unit Differences +//! +//! * DMA 0 can only transfer between memory on the GBA itself, it cannot access +//! ROM or SRAM. It's usually used for smaller, very high priority transfers. +//! * DMA 1 and 2 can transfer from ROM memory. These units are intended to be +//! used with the FIFO sound buffers, so that playback of a sound can happen +//! smoothly regardless of the current position of the CPU within the program. +//! * DMA 3 can transfer from ROM, and also into ROM if the game cart supports +//! that. This DMA unit is the one usually used for loading graphical data +//! from ROM into VRAM. +//! +//! ## Safety +//! +//! Using the DMA units is equivalent to playing around with raw pointers. It +//! must be handled very carefully, or memory corruption can occur. + +use bitfrob::{u16_with_bit, u16_with_region}; +use voladdress::{Safe, VolRegion}; + +use crate::{ + mmio::{DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, DMA3_TRANSFER_COUNT}, + video::Tile4bpp, +}; + +/// Controls the activity of a DMA unit. +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct DmaControl(u16); +impl DmaControl { + /// Makes a new, empty value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Unwrap the raw bits. + #[inline] + pub const fn into_u16(self) -> u16 { + self.0 + } + /// Sets the DMA destination address control, see [`DmaDestAddr`] + #[inline] + pub const fn with_dest_addr(self, dest: DmaDestAddr) -> Self { + Self(u16_with_region(5, 6, self.0, dest as u16)) + } + /// Sets the DMA source address control, see [`DmaSrcAddr`]. + /// + /// When transferring from ROM, this setting is **ignored** and the DMA unit + /// will just act as if `Increment` is set. + #[inline] + pub const fn with_src_addr(self, src: DmaSrcAddr) -> Self { + Self(u16_with_region(7, 8, self.0, src as u16)) + } + /// If the DMA unit should repeat again at the next start timing. + #[inline] + pub const fn with_repeat(self, repeat: bool) -> Self { + Self(u16_with_bit(9, self.0, repeat)) + } + /// If the DMA unit should transfer `u32` data (otherwise it's `u16) + #[inline] + pub const fn with_u32_transfer(self, u32: bool) -> Self { + Self(u16_with_bit(10, self.0, u32)) + } + /// Sets the start timing of the DMA activity, see [`DmaStart`] + #[inline] + pub const fn with_start_time(self, start: DmaStart) -> Self { + Self(u16_with_region(12, 13, self.0, start as u16)) + } + /// If this DMA unit should send an IRQ after it completes the transfer. + #[inline] + pub const fn with_irq(self, irq: bool) -> Self { + Self(u16_with_bit(14, self.0, irq)) + } + /// If the DMA unit is enabled. + #[inline] + pub const fn with_enabled(self, enabled: bool) -> Self { + Self(u16_with_bit(15, self.0, enabled)) + } +} + +/// DMA Destination address settings. +#[derive(Debug, Clone, Copy, Default)] +#[repr(u16)] +pub enum DmaDestAddr { + /// After each transfer, the destination moves 1 element forward. + #[default] + Increment = 0 << 5, + /// After each transfer, the destination moves 1 element backward. + Decrement = 1 << 5, + /// After each transfer, the destination does not change. + Fixed = 2 << 5, + /// After each transfer, the destination moves 1 element forward. + /// + /// **Also**, when beginning a repeated DMA cycle, the destination address + /// reloads to the initial value that was set by the user. + IncReload = 3 << 5, +} +/// DMA Source address settings. +/// +/// When transferring from ROM, this setting is **ignored** and the DMA unit +/// will just act as if `Increment` is set. +#[derive(Debug, Clone, Copy, Default)] +#[repr(u16)] +pub enum DmaSrcAddr { + /// After each transfer, the source address moves 1 element forward. + #[default] + Increment = 0 << 7, + /// After each transfer, the source address moves 1 element backward. + Decrement = 1 << 7, + /// After each transfer, the destination does not change. + Fixed = 2 << 7, +} +/// When the DMA unit should begin transferring data. +#[derive(Debug, Clone, Copy, Default)] +#[repr(u16)] +pub enum DmaStart { + /// Makes the DMA unit run "right away". + /// + /// There's actually a 2 CPU cycle lag between enabling a DMA with + /// `Immediate` timing and it actually activating. + #[default] + Immediate = 0 << 12, + /// The DMA unit will start at Vertical Blank. + /// + /// The DMA will end up running *before* the IRQ handler. + VBlank = 1 << 12, + /// The DMA unit will start at Horizontal Blank. + /// + /// The DMA will end up running *before* the IRQ handler. + HBlank = 2 << 12, + /// The DMA will run with a special timing depending on what DMA unit it is. + /// + /// * This cannot be used with DMA 0. + /// * For DMA 1 and 2 it runs when the FIFO buffer runs out. + /// * For DMA 3 this is how you do Video Capture (which isn't currently + /// supported by this crate). + Special = 3 << 12, +} + +/// Copies `u32` data using DMA 3. +/// +/// Works like the +/// [`copy_nonoverlapping`][core::intrinsics::copy_nonoverlapping] function, but +/// it's performed using DMA 3. +/// +/// ## Safety +/// * The number of `u32` values to copy must be `<= 0x1_0000` (65,536). This is +/// trivially true under all normal conditions, since this is the size of +/// EWRAM, which is the largest non-ROM memory region. +/// * `src` must be aligned and readable for `count` elements. +/// * `dest` must be aligned and writable for `count` elements. +/// * The two regions must not overlap. +/// * `count` must not be 0. +#[inline(never)] +pub unsafe fn dma3_u32_copy(src: *const u32, dest: *mut u32, count: usize) { + on_gba_or_unimplemented!( + const CONTROL: DmaControl = + DmaControl::new().with_u32_transfer(true).with_enabled(true); + debug_assert!(count <= u16::MAX as usize); + unsafe { + DMA3_SOURCE.write(src.cast()); + DMA3_DESTINATION.write(dest.cast()); + DMA3_TRANSFER_COUNT.write(count as u16); + DMA3_CONTROL.write(CONTROL); + // Assumption: because this function is `inline(never)`, the time to + // return to the caller is enough to ensure that the DMA controls aren't + // used before the DMA's 2 cycle "immediate activation" delay is over. + } + ); +} + +/// Copies [`Tile4`] data using DMA3. +/// +/// ## Panics +/// * The `src` and `dest` must have the same length. +#[inline] +pub fn dma3_copy_tile4( + src: &[Tile4bpp], dest: VolRegion, +) { + assert_eq!(src.len(), dest.len()); + if src.len() == 0 { + return; + } + // Safety: There's no writable region of memory that has more tiles that the + // maximum transfer size of DMA3, so the length will never exceed our limit. + // The requirements for the source to be readable and the destination to be + // writable are satisfied by the data types of the input values. + unsafe { + dma3_u32_copy(src.as_ptr().cast(), dest.as_mut_ptr().cast(), src.len() * 8) + }; +} diff --git a/src/lib.rs b/src/lib.rs index 8b077f2c..cbc43ac8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,14 +46,18 @@ macro_rules! on_gba_or_unimplemented { $($token_tree)* } #[cfg(not(feature="on_gba"))] - unimplemented!() + { + unimplemented!() + } } } pub mod asm_runtime; pub mod bios; +pub mod dma; pub mod gba_cell; pub mod gba_fixed; +pub mod mem; pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; diff --git a/src/mem/clear.rs b/src/mem/clear.rs new file mode 100644 index 00000000..dc8f689f --- /dev/null +++ b/src/mem/clear.rs @@ -0,0 +1,22 @@ +use super::{__aeabi_memset, _bulk_set_util}; + +#[inline] +#[instruction_set(arm::a32)] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memclr8(dest: *mut u32, byte_count: usize) { + unsafe { _bulk_set_util(dest, byte_count, 0, 0) } +} + +#[inline] +#[instruction_set(arm::a32)] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memclr4(dest: *mut u32, byte_count: usize) { + unsafe { _bulk_set_util(dest, byte_count, 0, 0) } +} + +#[inline] +#[instruction_set(arm::a32)] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memclr(dest: *mut u8, byte_count: usize) { + unsafe { __aeabi_memset(dest.cast(), byte_count, 0) } +} diff --git a/src/mem/copy.rs b/src/mem/copy.rs new file mode 100644 index 00000000..5c05dfe0 --- /dev/null +++ b/src/mem/copy.rs @@ -0,0 +1,34 @@ +/// `u8` copy between exclusive regions. +/// +/// * This will *always* copy one byte at a time, and the code is always stored +/// in IWRAM, making it suitable for use with SRAM memory. +/// +/// ## Safety +/// * If `byte_count` is zero then the pointers are not used at all, and they +/// can be any value. +/// * If `byte_count` is non-zero then: +/// * Both pointers must be valid for the number of bytes given. +/// * The two regions must either be *entirely* disjoint or *entirely* +/// overlapping. Partial overlap is not allowed. +#[inline] +#[instruction_set(arm::a32)] +#[link_section = ".iwram.__aeabi_memcpy1"] +#[cfg_attr(feature = "no_mangle_memcpy", no_mangle)] +pub unsafe extern "C" fn __aeabi_memcpy1( + dest: *mut u8, src: *const u8, byte_count: usize, +) { + on_gba_or_unimplemented!(unsafe { + core::arch::asm! { + "1:", + "subs {count}, {count}, #1", + "ldrbge {temp}, [{src}], #1", + "strbge {temp}, [{dest}], #1", + "bgt 1b", + temp = out(reg) _, + count = inout(reg) byte_count => _, + src = inout(reg) src => _, + dest = inout(reg) dest => _, + options(nostack) + } + }); +} diff --git a/src/mem/mod.rs b/src/mem/mod.rs new file mode 100644 index 00000000..727674c0 --- /dev/null +++ b/src/mem/mod.rs @@ -0,0 +1,9 @@ +//! Low-level memory manipulation functions. + +mod clear; +mod copy; +mod set; + +pub use clear::*; +pub use copy::*; +pub use set::*; diff --git a/src/mem/set.rs b/src/mem/set.rs new file mode 100644 index 00000000..acf4e417 --- /dev/null +++ b/src/mem/set.rs @@ -0,0 +1,161 @@ +/// Sets all bytes in the region to the `byte` given. +/// +/// Because of historical reasons, the byte is passed in as an `i32`, but only +/// the lowest 8 bits are used. +/// +/// ## Safety +/// * `dest` must be valid to write to for `byte_count` bytes. +#[instruction_set(arm::a32)] +#[link_section = ".iwram.__aeabi_memset"] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memset( + mut dest: *mut u8, mut byte_count: usize, byte: i32, +) { + if byte_count >= 8 { + let byte: u8 = byte as u8; + let byte: u16 = (byte as u16) << 8 | (byte as u16); + let byte: u32 = (byte as u32) << 16 | (byte as u32); + + if (dest as usize & 1) != 0 { + unsafe { dest.write_volatile(byte as u8) }; + dest = unsafe { dest.add(1) }; + byte_count -= 1; + } + + let mut dest = dest.cast::(); + debug_assert!(dest.is_aligned()); + + if (dest as usize & 0b10) != 0 { + unsafe { dest.write_volatile(byte as u16) }; + dest = unsafe { dest.add(1) }; + byte_count -= 2; + } + + let dest = dest.cast::(); + debug_assert!(dest.is_aligned()); + + let byte_r2 = byte; + let byte_r3 = byte; + unsafe { _bulk_set_util(dest, byte_count, byte_r2, byte_r3) }; + } else { + let byte = byte as u8; + for _ in 0..byte_count { + unsafe { dest.write_volatile(byte) }; + dest = unsafe { dest.add(1) }; + } + } +} + +/// Like [`__aeabi_memset`], but for a known-aligned pointer. +/// +/// ## Safety +/// * `dest` must be valid to write to for `byte_count` bytes. +/// * The `dest` must be aligned to 4. +#[inline] +#[instruction_set(arm::a32)] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memset4( + dest: *mut u32, byte_count: usize, byte: i32, +) { + debug_assert!(dest.is_aligned()); + if byte_count >= 8 { + let byte: u8 = byte as u8; + let byte: u16 = (byte as u16) << 8 | (byte as u16); + let byte: u32 = (byte as u32) << 16 | (byte as u32); + unsafe { _bulk_set_util(dest, byte_count, byte, byte) }; + } else { + let mut dest = dest.cast::(); + let byte = byte as u8; + for _ in 0..byte_count { + unsafe { dest.write_volatile(byte) }; + dest = unsafe { dest.add(1) }; + } + } +} + +/// Sets `reg2` and `reg3` all across the destination. +/// ## Safety +/// * `dest` must be aligned and valid to write for `byte_count` bytes. +#[instruction_set(arm::a32)] +#[link_section = ".iwram._bulk_set_util"] +pub(crate) unsafe extern "C" fn _bulk_set_util( + mut dest: *mut u32, mut byte_count: usize, reg2: u32, reg3: u32, +) { + if byte_count >= 32 { + unsafe { + core::arch::asm!( + "push {{r4-r9}}", + "mov r4, r2", + "mov r5, r2", + "mov r6, r2", + "mov r7, r2", + "mov r8, r2", + "mov r9, r2", + "1:", + "subs r1, r1, #32", + "stmge r0!, {{r2-r9}}", + "bgt 1b", + "pop {{r4-r9}}", + inlateout("r0") dest, + inlateout("r1") byte_count, + in("r2") reg2, + in("r3") reg3, + ); + } + } + + #[allow(unused_assignments)] + unsafe { + core::arch::asm!( + // check for 4 remaining words. + "tst r1, #0b10000", + "stmne r0!, {{r2, r3}}", + "stmne r0!, {{r2, r3}}", + // check for 2 and/or 1 remaining words + "lsls {trash}, r1, #29", + "stmcs r0!, {{r2, r3}}", + "strmi r2, [r0], #4", + // set halfword and/or byte + "lsls {trash}, r1, #29", + "stmcs r0!, {{r2, r3}}", + "strmi r2, [r0], #4", + trash = out(reg) _, + inlateout("r0") dest, + in("r1") byte_count, + in("r2") reg2, + in("r3") reg3, + options(nostack) + ); + } +} + +/// This function is provided only for API completeness, because in some cases +/// the compiler might automatically generate a call to this function. +#[inline] +#[instruction_set(arm::a32)] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memset8( + dest: *mut u32, byte_count: usize, byte: i32, +) { + unsafe { __aeabi_memset4(dest.cast(), byte_count, byte) } +} + +/// Write a value to all bytes in the region. +/// +/// This is the `libc` version of a memory set. This implementation just calls +/// [`__aeabi_memset`] with the argument order swapped around. Prefer a direct +/// call to that function if possible. +/// +/// This function is provided only for API completeness, because in some cases +/// the compiler might automatically generate a call to this function. +/// +/// * **Returns:** The `dest` pointer. +#[inline] +#[instruction_set(arm::a32)] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn memset( + dest: *mut u8, byte: i32, byte_count: usize, +) -> *mut u8 { + unsafe { __aeabi_memset(dest, byte_count, byte) }; + dest +} diff --git a/src/video.rs b/src/video.rs index ff11b9b3..96292739 100644 --- a/src/video.rs +++ b/src/video.rs @@ -2,6 +2,8 @@ use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; +use crate::{mem::_bulk_set_util, mmio::MODE3_VRAM}; + /// A color value. /// /// This is a bit-packed linear RGB color value with 5 bits per channel: @@ -240,3 +242,14 @@ pub struct Tile4bpp(pub [u32; 8]); #[derive(Clone, Copy, Default)] #[repr(transparent)] pub struct Tile8bpp(pub [u32; 16]); + +/// Clears the Mode 3 bitmap background to the given color. +#[inline] +pub fn mode3_clear_to(color: Color) { + let c: u32 = color.0 as u32 | ((color.0 as u32) << 16); + // Safety: the mode 3 base address is aligned to 4, and valid to write for the + // given number of bytes. + unsafe { + _bulk_set_util(MODE3_VRAM.as_usize() as *mut u32, 260 * 140 * 2, c, c) + }; +} From f01c3c221d854c40c3c4ebd0912b8c696021a19e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 17:36:14 -0600 Subject: [PATCH 36/89] restore mgba module --- {backup/src => src}/mgba.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {backup/src => src}/mgba.rs (100%) diff --git a/backup/src/mgba.rs b/src/mgba.rs similarity index 100% rename from backup/src/mgba.rs rename to src/mgba.rs From 7244b3fefb9e3d95cc588c116dc70994feed707c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 19:15:12 -0600 Subject: [PATCH 37/89] why doesn't the green paddle move? --- .cargo/config.toml | 1 + examples/paddle_ball.rs | 12 +-- src/asm_runtime.rs | 6 ++ src/dma.rs | 6 +- src/lib.rs | 4 +- src/mem/clear.rs | 22 ------ src/mem/mod.rs | 6 +- src/mem/set.rs | 161 ---------------------------------------- src/mmio.rs | 33 +++++++- src/panic_handlers.rs | 12 +++ src/video.rs | 48 +++++++++--- 11 files changed, 100 insertions(+), 211 deletions(-) delete mode 100644 src/mem/clear.rs delete mode 100644 src/mem/set.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 99678165..ac3738f1 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,6 +7,7 @@ build-std = ["core"] [target.thumbv4t-none-eabi] runner = "mgba-qt" rustflags = [ + "-Zub-checks=no", "-Clinker=arm-none-eabi-ld", "-Clink-arg=-Tlinker_scripts/mono_boot.ld", ] diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 16336376..1b5be741 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -5,19 +5,19 @@ #![no_std] #![no_main] +#![allow(unused)] use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - mem::__aeabi_memclr, mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, - video::{mode3_clear_to, Color, DisplayControl, DisplayStatus}, + video::{Color, DisplayControl, DisplayStatus, Mode3}, IrqBits, KeyInput, }; -const SCREEN_WIDTH: u16 = 240; -const SCREEN_HEIGHT: u16 = 160; +const SCREEN_WIDTH: u16 = Mode3::WIDTH; +const SCREEN_HEIGHT: u16 = Mode3::HEIGHT; const PADDLE_WIDTH: u16 = 4; const PADDLE_HEIGHT: u16 = 20; @@ -106,7 +106,7 @@ static SPRITE_POSITIONS: [GbaCell; 6] = [ GbaCell::new(0), ]; -gba::panic_handler!(empty_loop); +gba::panic_handler!(mgba_log_error); #[no_mangle] fn main() -> ! { @@ -143,7 +143,7 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - mode3_clear_to(Color::BLACK); + Mode3.dma3_clear_to(Color::BLACK); draw_rect( SPRITE_POSITIONS[0].read(), diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 5cc28103..30ce08df 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -105,6 +105,7 @@ core::arch::global_asm! { ".space 0xE0", "1:", + // reg setup. "mov r12, #0x04000000", "add r3, r12, #0xD4", @@ -117,6 +118,11 @@ core::arch::global_asm! { "ldr r1, =0x4317", "strh r1, [r0]", + // Request mGBA logging be activated. + "ldr r0, =0x04FFF780", + "ldr r1, =0xC0DE", + "strh r1, [r0]", + /* iwram copy */ "ldr r0, =_iwram_word_copy_count", when!(("r0" != "#0") { diff --git a/src/dma.rs b/src/dma.rs index a7b7dd0d..e4db17da 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -83,7 +83,7 @@ impl DmaControl { pub const fn with_repeat(self, repeat: bool) -> Self { Self(u16_with_bit(9, self.0, repeat)) } - /// If the DMA unit should transfer `u32` data (otherwise it's `u16) + /// If the DMA unit should transfer `u32` data (otherwise it's `u16`) #[inline] pub const fn with_u32_transfer(self, u32: bool) -> Self { Self(u16_with_bit(10, self.0, u32)) @@ -179,7 +179,7 @@ pub enum DmaStart { /// * The two regions must not overlap. /// * `count` must not be 0. #[inline(never)] -pub unsafe fn dma3_u32_copy(src: *const u32, dest: *mut u32, count: usize) { +pub unsafe fn dma3_copy_u32(src: *const u32, dest: *mut u32, count: usize) { on_gba_or_unimplemented!( const CONTROL: DmaControl = DmaControl::new().with_u32_transfer(true).with_enabled(true); @@ -213,6 +213,6 @@ pub fn dma3_copy_tile4( // The requirements for the source to be readable and the destination to be // writable are satisfied by the data types of the input values. unsafe { - dma3_u32_copy(src.as_ptr().cast(), dest.as_mut_ptr().cast(), src.len() * 8) + dma3_copy_u32(src.as_ptr().cast(), dest.as_mut_ptr().cast(), src.len() * 8) }; } diff --git a/src/lib.rs b/src/lib.rs index cbc43ac8..0385eda7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] +#![allow(unused_mut)] #![cfg_attr(not(feature = "on_gba"), allow(unused))] -#![warn(missing_docs)] +//#![warn(missing_docs)] #![warn(unsafe_op_in_unsafe_fn)] #![cfg_attr(feature = "doc_cfg", feature(doc_cfg))] @@ -58,6 +59,7 @@ pub mod dma; pub mod gba_cell; pub mod gba_fixed; pub mod mem; +pub mod mgba; pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; diff --git a/src/mem/clear.rs b/src/mem/clear.rs deleted file mode 100644 index dc8f689f..00000000 --- a/src/mem/clear.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::{__aeabi_memset, _bulk_set_util}; - -#[inline] -#[instruction_set(arm::a32)] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memclr8(dest: *mut u32, byte_count: usize) { - unsafe { _bulk_set_util(dest, byte_count, 0, 0) } -} - -#[inline] -#[instruction_set(arm::a32)] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memclr4(dest: *mut u32, byte_count: usize) { - unsafe { _bulk_set_util(dest, byte_count, 0, 0) } -} - -#[inline] -#[instruction_set(arm::a32)] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memclr(dest: *mut u8, byte_count: usize) { - unsafe { __aeabi_memset(dest.cast(), byte_count, 0) } -} diff --git a/src/mem/mod.rs b/src/mem/mod.rs index 727674c0..ffcc0923 100644 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -1,9 +1,7 @@ +#![allow(missing_docs)] + //! Low-level memory manipulation functions. -mod clear; mod copy; -mod set; -pub use clear::*; pub use copy::*; -pub use set::*; diff --git a/src/mem/set.rs b/src/mem/set.rs deleted file mode 100644 index acf4e417..00000000 --- a/src/mem/set.rs +++ /dev/null @@ -1,161 +0,0 @@ -/// Sets all bytes in the region to the `byte` given. -/// -/// Because of historical reasons, the byte is passed in as an `i32`, but only -/// the lowest 8 bits are used. -/// -/// ## Safety -/// * `dest` must be valid to write to for `byte_count` bytes. -#[instruction_set(arm::a32)] -#[link_section = ".iwram.__aeabi_memset"] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memset( - mut dest: *mut u8, mut byte_count: usize, byte: i32, -) { - if byte_count >= 8 { - let byte: u8 = byte as u8; - let byte: u16 = (byte as u16) << 8 | (byte as u16); - let byte: u32 = (byte as u32) << 16 | (byte as u32); - - if (dest as usize & 1) != 0 { - unsafe { dest.write_volatile(byte as u8) }; - dest = unsafe { dest.add(1) }; - byte_count -= 1; - } - - let mut dest = dest.cast::(); - debug_assert!(dest.is_aligned()); - - if (dest as usize & 0b10) != 0 { - unsafe { dest.write_volatile(byte as u16) }; - dest = unsafe { dest.add(1) }; - byte_count -= 2; - } - - let dest = dest.cast::(); - debug_assert!(dest.is_aligned()); - - let byte_r2 = byte; - let byte_r3 = byte; - unsafe { _bulk_set_util(dest, byte_count, byte_r2, byte_r3) }; - } else { - let byte = byte as u8; - for _ in 0..byte_count { - unsafe { dest.write_volatile(byte) }; - dest = unsafe { dest.add(1) }; - } - } -} - -/// Like [`__aeabi_memset`], but for a known-aligned pointer. -/// -/// ## Safety -/// * `dest` must be valid to write to for `byte_count` bytes. -/// * The `dest` must be aligned to 4. -#[inline] -#[instruction_set(arm::a32)] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memset4( - dest: *mut u32, byte_count: usize, byte: i32, -) { - debug_assert!(dest.is_aligned()); - if byte_count >= 8 { - let byte: u8 = byte as u8; - let byte: u16 = (byte as u16) << 8 | (byte as u16); - let byte: u32 = (byte as u32) << 16 | (byte as u32); - unsafe { _bulk_set_util(dest, byte_count, byte, byte) }; - } else { - let mut dest = dest.cast::(); - let byte = byte as u8; - for _ in 0..byte_count { - unsafe { dest.write_volatile(byte) }; - dest = unsafe { dest.add(1) }; - } - } -} - -/// Sets `reg2` and `reg3` all across the destination. -/// ## Safety -/// * `dest` must be aligned and valid to write for `byte_count` bytes. -#[instruction_set(arm::a32)] -#[link_section = ".iwram._bulk_set_util"] -pub(crate) unsafe extern "C" fn _bulk_set_util( - mut dest: *mut u32, mut byte_count: usize, reg2: u32, reg3: u32, -) { - if byte_count >= 32 { - unsafe { - core::arch::asm!( - "push {{r4-r9}}", - "mov r4, r2", - "mov r5, r2", - "mov r6, r2", - "mov r7, r2", - "mov r8, r2", - "mov r9, r2", - "1:", - "subs r1, r1, #32", - "stmge r0!, {{r2-r9}}", - "bgt 1b", - "pop {{r4-r9}}", - inlateout("r0") dest, - inlateout("r1") byte_count, - in("r2") reg2, - in("r3") reg3, - ); - } - } - - #[allow(unused_assignments)] - unsafe { - core::arch::asm!( - // check for 4 remaining words. - "tst r1, #0b10000", - "stmne r0!, {{r2, r3}}", - "stmne r0!, {{r2, r3}}", - // check for 2 and/or 1 remaining words - "lsls {trash}, r1, #29", - "stmcs r0!, {{r2, r3}}", - "strmi r2, [r0], #4", - // set halfword and/or byte - "lsls {trash}, r1, #29", - "stmcs r0!, {{r2, r3}}", - "strmi r2, [r0], #4", - trash = out(reg) _, - inlateout("r0") dest, - in("r1") byte_count, - in("r2") reg2, - in("r3") reg3, - options(nostack) - ); - } -} - -/// This function is provided only for API completeness, because in some cases -/// the compiler might automatically generate a call to this function. -#[inline] -#[instruction_set(arm::a32)] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memset8( - dest: *mut u32, byte_count: usize, byte: i32, -) { - unsafe { __aeabi_memset4(dest.cast(), byte_count, byte) } -} - -/// Write a value to all bytes in the region. -/// -/// This is the `libc` version of a memory set. This implementation just calls -/// [`__aeabi_memset`] with the argument order swapped around. Prefer a direct -/// call to that function if possible. -/// -/// This function is provided only for API completeness, because in some cases -/// the compiler might automatically generate a call to this function. -/// -/// * **Returns:** The `dest` pointer. -#[inline] -#[instruction_set(arm::a32)] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn memset( - dest: *mut u8, byte: i32, byte_count: usize, -) -> *mut u8 { - unsafe { __aeabi_memset(dest, byte_count, byte) }; - dest -} diff --git a/src/mmio.rs b/src/mmio.rs index 24e23175..ebf3bbd9 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -9,7 +9,8 @@ use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided}; use crate::{ dma::DmaControl, - video::{Color, DisplayControl, DisplayStatus, Tile4bpp}, + mgba::MgbaMessageLevel, + video::{Color, DisplayControl, DisplayStatus, Mode3, Tile4bpp}, IrqBits, KeyInput, }; @@ -24,6 +25,8 @@ type SOGBA = voladdress::Unsafe; type PlainAddr = VolAddress; /// Read-only addr type RoAddr = VolAddress; +/// Write-only addr +type WoAddr = VolAddress; /// Display Control setting. /// @@ -166,6 +169,23 @@ pub const IF: PlainAddr = unsafe { VolAddress::new(0x0400_0202) }; /// interrupts actually being enabled/disabled. In practice, it doesn't matter. pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; +// mGBA Logging + +/// The buffer to put logging messages into. +/// +/// The first 0 in the buffer is the end of each message. +pub const MGBA_LOG_BUFFER: VolBlock = + unsafe { VolBlock::new(0x04FF_F600) }; + +/// Write to this each time you want to reset a message (it also resets the +/// buffer). +pub const MGBA_LOG_SEND: WoAddr = + unsafe { VolAddress::new(0x04FF_F700) }; + +/// Allows you to attempt to activate mGBA logging. +pub const MGBA_LOG_ENABLE: PlainAddr = + unsafe { VolAddress::new(0x04FF_F780) }; + /// The backdrop color is the color shown when no *other* element is displayed /// in a given pixel. pub const BACKDROP_COLOR: PlainAddr = @@ -187,11 +207,16 @@ pub const VRAM_BG_TILE4: VolBlock = pub const VRAM_BG_TILE8: VolBlock = unsafe { VolBlock::new(0x0600_0000) }; -/// The VRAM's view in Video Mode 3. +/// The VRAM's view in Video Mode 3 (240 x 160). /// /// Each location is a direct color value. -pub const MODE3_VRAM: VolGrid2d = - unsafe { VolGrid2d::new(0x0600_0000) }; +pub const MODE3_VRAM: VolGrid2d< + Color, + SOGBA, + SOGBA, + { Mode3::WIDTH as usize }, + { Mode3::HEIGHT as usize }, +> = unsafe { VolGrid2d::new(0x0600_0000) }; /// The VRAM's view in Video Mode 4. /// diff --git a/src/panic_handlers.rs b/src/panic_handlers.rs index e32a3ce0..a8e491ad 100644 --- a/src/panic_handlers.rs +++ b/src/panic_handlers.rs @@ -1,5 +1,7 @@ //! Various panic handler functions that you might find useful. +use crate::mgba::{MgbaBufferedLogger, MgbaMessageLevel}; + /// Declares one of the functions in the /// [`panic_handlers`](crate::panic_handlers) module to be the handler for your /// program. @@ -24,3 +26,13 @@ macro_rules! panic_handler { pub fn empty_loop(_: &core::panic::PanicInfo) -> ! { loop {} } + +/// Attempts to log the info to the mGBA debug output at the "error" level. +#[inline] +pub fn mgba_log_error(info: &core::panic::PanicInfo) -> ! { + use core::fmt::Write; + if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Error) { + writeln!(logger, "{info}").ok(); + } + loop {} +} diff --git a/src/video.rs b/src/video.rs index 96292739..77bcbafa 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,8 +1,17 @@ //! +use core::{ffi::c_void, ptr::addr_of}; + use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; -use crate::{mem::_bulk_set_util, mmio::MODE3_VRAM}; +use crate::{ + asm_runtime::nop, + dma::{DmaControl, DmaSrcAddr}, + mmio::{ + DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, DMA3_TRANSFER_COUNT, + MODE3_VRAM, + }, +}; /// A color value. /// @@ -243,13 +252,32 @@ pub struct Tile4bpp(pub [u32; 8]); #[repr(transparent)] pub struct Tile8bpp(pub [u32; 16]); -/// Clears the Mode 3 bitmap background to the given color. -#[inline] -pub fn mode3_clear_to(color: Color) { - let c: u32 = color.0 as u32 | ((color.0 as u32) << 16); - // Safety: the mode 3 base address is aligned to 4, and valid to write for the - // given number of bytes. - unsafe { - _bulk_set_util(MODE3_VRAM.as_usize() as *mut u32, 260 * 140 * 2, c, c) - }; +pub struct Mode3; +impl Mode3 { + pub const WIDTH: u16 = 240; + pub const HEIGHT: u16 = 160; + + /// Clears the Mode 3 bitmap background to the given color. + pub fn dma3_clear_to(self, color: Color) { + let c: u32 = color.0 as u32 | ((color.0 as u32) << 16); + let src: *const c_void = addr_of!(c).cast(); + unsafe { + // clear any previous setting + DMA3_CONTROL.write(DmaControl::new()); + // configure and run + debug_assert!((src as usize) < 0x0800_0000); + DMA3_SOURCE.write(src); + DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as _); + DMA3_TRANSFER_COUNT.write(Self::WIDTH * Self::HEIGHT / 2); + DMA3_CONTROL.write( + DmaControl::new() + .with_u32_transfer(true) + .with_src_addr(DmaSrcAddr::Fixed) + .with_enabled(true), + ); + nop(); + nop(); + nop(); + }; + } } From b22a3926474a715680664e91655c499438966ca1 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 19:28:16 -0600 Subject: [PATCH 38/89] okay, in this state it runs the game. --- examples/paddle_ball.rs | 30 ++++++++++++++++++++++++------ src/asm_runtime.rs | 6 ------ src/lib.rs | 9 ++------- src/mmio.rs | 33 ++++----------------------------- src/panic_handlers.rs | 12 ------------ src/video.rs | 41 ----------------------------------------- 6 files changed, 30 insertions(+), 101 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 1b5be741..7f419d36 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -7,17 +7,23 @@ #![no_main] #![allow(unused)] +use core::ptr::addr_of; + use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, + dma::{DmaControl, DmaSrcAddr}, gba_cell::GbaCell, - mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, - video::{Color, DisplayControl, DisplayStatus, Mode3}, + mmio::{ + DISPCNT, DISPSTAT, DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, + DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, OBJ_PALRAM, + }, + video::{Color, DisplayControl, DisplayStatus}, IrqBits, KeyInput, }; -const SCREEN_WIDTH: u16 = Mode3::WIDTH; -const SCREEN_HEIGHT: u16 = Mode3::HEIGHT; +const SCREEN_WIDTH: u16 = 240; +const SCREEN_HEIGHT: u16 = 160; const PADDLE_WIDTH: u16 = 4; const PADDLE_HEIGHT: u16 = 20; @@ -106,7 +112,7 @@ static SPRITE_POSITIONS: [GbaCell; 6] = [ GbaCell::new(0), ]; -gba::panic_handler!(mgba_log_error); +gba::panic_handler!(empty_loop); #[no_mangle] fn main() -> ! { @@ -143,7 +149,19 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - Mode3.dma3_clear_to(Color::BLACK); + unsafe { + let color = OBJ_PALRAM.index(0).read(); + let c: u32 = color.0 as u32 | (color.0 as u32) << 16; + DMA3_SOURCE.write(addr_of!(c).cast()); + DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as _); + DMA3_TRANSFER_COUNT.write(SCREEN_WIDTH * SCREEN_HEIGHT / 2); + DMA3_CONTROL.write( + DmaControl::new() + .with_src_addr(DmaSrcAddr::Fixed) + .with_u32_transfer(true) + .with_enabled(true), + ); + } draw_rect( SPRITE_POSITIONS[0].read(), diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 30ce08df..5cc28103 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -105,7 +105,6 @@ core::arch::global_asm! { ".space 0xE0", "1:", - // reg setup. "mov r12, #0x04000000", "add r3, r12, #0xD4", @@ -118,11 +117,6 @@ core::arch::global_asm! { "ldr r1, =0x4317", "strh r1, [r0]", - // Request mGBA logging be activated. - "ldr r0, =0x04FFF780", - "ldr r1, =0xC0DE", - "strh r1, [r0]", - /* iwram copy */ "ldr r0, =_iwram_word_copy_count", when!(("r0" != "#0") { diff --git a/src/lib.rs b/src/lib.rs index 0385eda7..e171b2dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] -#![allow(unused_mut)] #![cfg_attr(not(feature = "on_gba"), allow(unused))] -//#![warn(missing_docs)] +#![warn(missing_docs)] #![warn(unsafe_op_in_unsafe_fn)] #![cfg_attr(feature = "doc_cfg", feature(doc_cfg))] @@ -47,9 +46,7 @@ macro_rules! on_gba_or_unimplemented { $($token_tree)* } #[cfg(not(feature="on_gba"))] - { - unimplemented!() - } + unimplemented!() } } @@ -58,8 +55,6 @@ pub mod bios; pub mod dma; pub mod gba_cell; pub mod gba_fixed; -pub mod mem; -pub mod mgba; pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; diff --git a/src/mmio.rs b/src/mmio.rs index ebf3bbd9..24e23175 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -9,8 +9,7 @@ use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided}; use crate::{ dma::DmaControl, - mgba::MgbaMessageLevel, - video::{Color, DisplayControl, DisplayStatus, Mode3, Tile4bpp}, + video::{Color, DisplayControl, DisplayStatus, Tile4bpp}, IrqBits, KeyInput, }; @@ -25,8 +24,6 @@ type SOGBA = voladdress::Unsafe; type PlainAddr = VolAddress; /// Read-only addr type RoAddr = VolAddress; -/// Write-only addr -type WoAddr = VolAddress; /// Display Control setting. /// @@ -169,23 +166,6 @@ pub const IF: PlainAddr = unsafe { VolAddress::new(0x0400_0202) }; /// interrupts actually being enabled/disabled. In practice, it doesn't matter. pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; -// mGBA Logging - -/// The buffer to put logging messages into. -/// -/// The first 0 in the buffer is the end of each message. -pub const MGBA_LOG_BUFFER: VolBlock = - unsafe { VolBlock::new(0x04FF_F600) }; - -/// Write to this each time you want to reset a message (it also resets the -/// buffer). -pub const MGBA_LOG_SEND: WoAddr = - unsafe { VolAddress::new(0x04FF_F700) }; - -/// Allows you to attempt to activate mGBA logging. -pub const MGBA_LOG_ENABLE: PlainAddr = - unsafe { VolAddress::new(0x04FF_F780) }; - /// The backdrop color is the color shown when no *other* element is displayed /// in a given pixel. pub const BACKDROP_COLOR: PlainAddr = @@ -207,16 +187,11 @@ pub const VRAM_BG_TILE4: VolBlock = pub const VRAM_BG_TILE8: VolBlock = unsafe { VolBlock::new(0x0600_0000) }; -/// The VRAM's view in Video Mode 3 (240 x 160). +/// The VRAM's view in Video Mode 3. /// /// Each location is a direct color value. -pub const MODE3_VRAM: VolGrid2d< - Color, - SOGBA, - SOGBA, - { Mode3::WIDTH as usize }, - { Mode3::HEIGHT as usize }, -> = unsafe { VolGrid2d::new(0x0600_0000) }; +pub const MODE3_VRAM: VolGrid2d = + unsafe { VolGrid2d::new(0x0600_0000) }; /// The VRAM's view in Video Mode 4. /// diff --git a/src/panic_handlers.rs b/src/panic_handlers.rs index a8e491ad..e32a3ce0 100644 --- a/src/panic_handlers.rs +++ b/src/panic_handlers.rs @@ -1,7 +1,5 @@ //! Various panic handler functions that you might find useful. -use crate::mgba::{MgbaBufferedLogger, MgbaMessageLevel}; - /// Declares one of the functions in the /// [`panic_handlers`](crate::panic_handlers) module to be the handler for your /// program. @@ -26,13 +24,3 @@ macro_rules! panic_handler { pub fn empty_loop(_: &core::panic::PanicInfo) -> ! { loop {} } - -/// Attempts to log the info to the mGBA debug output at the "error" level. -#[inline] -pub fn mgba_log_error(info: &core::panic::PanicInfo) -> ! { - use core::fmt::Write; - if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Error) { - writeln!(logger, "{info}").ok(); - } - loop {} -} diff --git a/src/video.rs b/src/video.rs index 77bcbafa..ff11b9b3 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,18 +1,7 @@ //! -use core::{ffi::c_void, ptr::addr_of}; - use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; -use crate::{ - asm_runtime::nop, - dma::{DmaControl, DmaSrcAddr}, - mmio::{ - DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, DMA3_TRANSFER_COUNT, - MODE3_VRAM, - }, -}; - /// A color value. /// /// This is a bit-packed linear RGB color value with 5 bits per channel: @@ -251,33 +240,3 @@ pub struct Tile4bpp(pub [u32; 8]); #[derive(Clone, Copy, Default)] #[repr(transparent)] pub struct Tile8bpp(pub [u32; 16]); - -pub struct Mode3; -impl Mode3 { - pub const WIDTH: u16 = 240; - pub const HEIGHT: u16 = 160; - - /// Clears the Mode 3 bitmap background to the given color. - pub fn dma3_clear_to(self, color: Color) { - let c: u32 = color.0 as u32 | ((color.0 as u32) << 16); - let src: *const c_void = addr_of!(c).cast(); - unsafe { - // clear any previous setting - DMA3_CONTROL.write(DmaControl::new()); - // configure and run - debug_assert!((src as usize) < 0x0800_0000); - DMA3_SOURCE.write(src); - DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as _); - DMA3_TRANSFER_COUNT.write(Self::WIDTH * Self::HEIGHT / 2); - DMA3_CONTROL.write( - DmaControl::new() - .with_u32_transfer(true) - .with_src_addr(DmaSrcAddr::Fixed) - .with_enabled(true), - ); - nop(); - nop(); - nop(); - }; - } -} From 0339c3b36bc4e79fff08022b782522d634586e56 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 19:30:15 -0600 Subject: [PATCH 39/89] we should only set the instruction set on the GBA. --- src/mem/copy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mem/copy.rs b/src/mem/copy.rs index 5c05dfe0..07bfd625 100644 --- a/src/mem/copy.rs +++ b/src/mem/copy.rs @@ -11,9 +11,9 @@ /// * The two regions must either be *entirely* disjoint or *entirely* /// overlapping. Partial overlap is not allowed. #[inline] -#[instruction_set(arm::a32)] #[link_section = ".iwram.__aeabi_memcpy1"] #[cfg_attr(feature = "no_mangle_memcpy", no_mangle)] +#[cfg_addr(feature = "on_gba", instruction_set(arm::a32))] pub unsafe extern "C" fn __aeabi_memcpy1( dest: *mut u8, src: *const u8, byte_count: usize, ) { From 0498d6cf0b2b817b21f646b69e343fa9d460aa1a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 19:35:26 -0600 Subject: [PATCH 40/89] put out small copy loop back into the crate source tree. --- src/lib.rs | 1 + src/mem/copy.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e171b2dc..6c173651 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,7 @@ pub mod bios; pub mod dma; pub mod gba_cell; pub mod gba_fixed; +pub mod mem; pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; diff --git a/src/mem/copy.rs b/src/mem/copy.rs index 07bfd625..71c72af1 100644 --- a/src/mem/copy.rs +++ b/src/mem/copy.rs @@ -13,7 +13,7 @@ #[inline] #[link_section = ".iwram.__aeabi_memcpy1"] #[cfg_attr(feature = "no_mangle_memcpy", no_mangle)] -#[cfg_addr(feature = "on_gba", instruction_set(arm::a32))] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] pub unsafe extern "C" fn __aeabi_memcpy1( dest: *mut u8, src: *const u8, byte_count: usize, ) { From f56796fbe64dc7773e80fbf5f552e92b82141d2a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 26 May 2024 19:53:50 -0600 Subject: [PATCH 41/89] update dependency --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d3a59803..4002beae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ critical-section = { version = "1.1.2", features = [ ], optional = true } bytemuck = { version = "1.16.0", optional = true } fixed = { version = "1.27.0", default-features = false, optional = true } -bracer = "0.2.1" +bracer = "0.3.0" [profile.dev] opt-level = 3 From 63ddcdf8217e84bd407eebe68e8601e85529b559 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:45:16 -0600 Subject: [PATCH 42/89] Delete more old files. --- backup/src/bios.rs | 318 ------------------------ backup/src/builtin_art/cga_8x8_thick.rs | 228 ----------------- backup/src/builtin_art/mod.rs | 7 - backup/src/dma.rs | 164 ------------ backup/src/gba_cell.rs | 248 ------------------ backup/src/interrupts.rs | 49 ---- backup/src/keys.rs | 163 ------------ backup/src/lib.rs | 159 ------------ backup/src/macros.rs | 144 ----------- backup/src/mmio.rs | 277 --------------------- backup/src/{video => }/obj.rs | 0 backup/src/prelude.rs | 18 -- backup/src/video/mod.rs | 317 ----------------------- 13 files changed, 2092 deletions(-) delete mode 100644 backup/src/bios.rs delete mode 100644 backup/src/builtin_art/cga_8x8_thick.rs delete mode 100644 backup/src/builtin_art/mod.rs delete mode 100644 backup/src/dma.rs delete mode 100644 backup/src/gba_cell.rs delete mode 100644 backup/src/interrupts.rs delete mode 100644 backup/src/keys.rs delete mode 100644 backup/src/lib.rs delete mode 100644 backup/src/macros.rs delete mode 100644 backup/src/mmio.rs rename backup/src/{video => }/obj.rs (100%) delete mode 100644 backup/src/prelude.rs delete mode 100644 backup/src/video/mod.rs diff --git a/backup/src/bios.rs b/backup/src/bios.rs deleted file mode 100644 index 0690512b..00000000 --- a/backup/src/bios.rs +++ /dev/null @@ -1,318 +0,0 @@ -#![allow(non_snake_case)] -#![allow(clippy::missing_safety_doc)] - -//! The GBA's BIOS provides limited built-in utility functions. -//! -//! BIOS functions are accessed with an `swi` instruction to perform a software -//! interrupt. This means that there's a *significant* overhead for a BIOS call -//! (tens of cycles) compared to a normal function call (3 cycles, or even none -//! of the function ends up inlined). Despite this higher cost, some bios -//! functions are useful enough to justify the overhead. - -use crate::{fixed::i16fx14, interrupts::IrqBits}; - -// Note(Lokathor): All `swi` calls will preserve the flags. You should generally -// not use any other inline-asm options with `swi` calls. - -/// `0x00`: Software Reset. -/// -/// This clears the BIOS portion of IWRAM (the top `0x200` bytes), resets the -/// SVC, IRQ, and SYS stack pointers to their defaults, then performs a `bx r14` -/// to go to an address based on what's written to the byte at `0x0300_7FFA`: -/// * zero: `0x0800_0000` (ROM) -/// * non-zero: `0x0200_0000` (IWRAM). -/// -/// (Note: the target address is determined *before* clearing the top of IWRAM.) -#[inline] -#[instruction_set(arm::t32)] -pub fn SoftReset() -> ! { - unsafe { - core::arch::asm! { - "swi #0x00", - options(noreturn), - } - }; -} - -/// `0x04`: Waits for a specific interrupt type(s) to happen. -/// -/// Pauses the CPU until any of the interrupt types set in `target_irqs` to -/// occur. This can create a significant savings of the battery while you're -/// waiting, so use this function when possible. -/// -/// **Important:** This function forces [`IME`](crate::mmio::IME) on. -/// -/// Your interrupt handler (if any) will be run before this function returns. -/// -/// If none of the interrupts specified in `target_irqs` are properly configured -/// to fire then this function will loop forever without returning. -/// -/// This function uses a special BIOS variable to track what interrupts have -/// occured recently. -/// * If `ignore_existing` is set, then any previous interrupts (since -/// `IntrWait` was last called) that match `target_irqs` are *ignored* and -/// this function will wait for a new target interrupt to occur. -/// * Otherwise, any previous interrupts that match `target_irqs` will cause the -/// function to return immediately without waiting for a new interrupt. -#[inline] -#[instruction_set(arm::t32)] -pub fn IntrWait(ignore_existing: bool, target_irqs: IrqBits) { - unsafe { - core::arch::asm! { - "swi #0x04", - inout("r0") ignore_existing as u32 => _, - inout("r1") target_irqs.to_u16() => _, - out("r3") _, - options(preserves_flags), - } - }; -} - -/// `0x05`: Builtin shorthand for [`IntrWait(true, IrqBits::VBLANK)`](IntrWait) -#[inline] -#[instruction_set(arm::t32)] -pub fn VBlankIntrWait() { - unsafe { - core::arch::asm! { - "swi #0x05", - out("r0") _, - out("r1") _, - out("r3") _, - options(preserves_flags), - } - }; -} - -/// `0x09`: Arc tangent. -/// -/// * **Returns:** The output is in the range +/- `pi/2`, but accuracy is worse -/// outside of +/- `pi/4`. -#[inline] -#[instruction_set(arm::t32)] -pub fn ArcTan(theta: i16fx14) -> i16fx14 { - let mut i = theta.into_raw(); - unsafe { - core::arch::asm! { - "swi #0x09", - inout("r0") i, - out("r1") _, - out("r3") _, - options(pure, nomem, preserves_flags), - } - }; - i16fx14::from_raw(i) -} - -/// `0x0A`: The "2-argument arctangent" ([atan2][wp-atan2]). -/// -/// [wp-atan2]: https://en.wikipedia.org/wiki/Atan2 -/// -/// * **Returns:** The angle of the input vector, with `u16::MAX` being -/// equivalent to `2pi`. -#[inline] -#[instruction_set(arm::t32)] -pub fn ArcTan2(x: i16fx14, y: i16fx14) -> u16 { - let x = x.into_raw(); - let y = y.into_raw(); - let output: u16; - unsafe { - core::arch::asm! { - "swi #0x0A", - inout("r0") x => output, - inout("r1") y => _, - out("r3") _, - options(pure, nomem, preserves_flags), - } - }; - output -} - -/// Used to provide info to a call of the [`BitUnPack`] function. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] -pub struct BitUnpackInfo { - /// Number of bytes in the source buffer - pub src_byte_len: u16, - /// Bits per source element: 1, 2, 4, or 8. - pub src_elem_width: u8, - /// Bits per destination element: 1, 2, 4, 8, 16, or 32. - pub dest_elem_width: u8, - /// Bits `0..=30` are the offset value added to all non-zero elements. - /// - /// If bit `31` is set then offset value is *also* added to zero elements. - pub offset_and_touch_zero: u32, -} - -/// `0x10`: Copy data from `src` to `dest` while increasing the bit depth of the -/// elements copied. -/// -/// * This reads one byte at a time from `src`. Each source byte holds 1 or more -/// source elements, depending on the source bit depth you specify. Elements -/// within a byte are packed from low bit to high bit. -/// * Each non-zero source element has the offset added to it. If the source -/// element is zero and the "touch zero" flag is set, then that source element -/// will also have the offset added to it. This creates a destination element. -/// * Destination elements are collected into the output `u32` buffer one at a -/// time, from low bit to high bit. If a source element plus the offset -/// produces a value larger than the destination element bit size this will -/// corrupt any following destination elements within the buffer. When the -/// buffer has 32 bits held then it's written to the destination pointer. -/// * When the source byte read has no more source elements remaining the source -/// pointer will advance and `src_byte_len` will go down by 1. When -/// `src_byte_len` goes to 0 the function's main loop will break and return. -/// If there was partial output in the `u32` buffer when the function's -/// primary loop ends this data will be lost. -/// -/// ## Safety -/// * The `info` provided must correctly describe the data. -/// * `src` must be readable for the number of **bytes** specified -/// * `dest` must be writable for the number of **words** that the source -/// buffer, source depth, and destination depth will total up to. -/// * `dest` must be 4 byte aligned. -#[inline] -#[instruction_set(arm::t32)] -pub unsafe fn BitUnPack(src: *const u8, dest: *mut u32, info: &BitUnpackInfo) { - core::arch::asm! { - "swi #0x10", - inout("r0") src => _, - inout("r1") dest => _, - inout("r2") info => _, - out("r3") _, - options(preserves_flags), - } -} - -/// `0x11`: Decompress LZ77 data from `src` to `dest` using 8-bit writes. -/// -/// * The `src` is the LZ77 header and data, and must start aligned to 4. -/// * The `dest` pointer is written 8 bits at a time, meaning that this function -/// is **not** VRAM compatible. -/// -/// ## The GBA's LZ77 Format -/// * Data header (32bit) -/// * Bit 0-7: Magic number `0b0001_0000` -/// * Bit 8-31: Byte count of *decompressed* data -/// -/// Repeat below. Each Flag Byte followed by eight Blocks. -/// * Flag data (8bit) -/// * Bit 0-7: block type bits for the next 8 blocks, MSB first. -/// * Block Type 0 (Uncompressed): Literal byte. -/// * Bit 0-7: one byte to copy directly to the output -/// * Block Type 1 (Compressed): Repeated sequence. Copies `N+3` bytes from -/// `dest-delta-1` back in the output sequence to `dest`. The GBATEK docs call -/// the delta value "disp", presumably for "displacement". -/// * Bit 0-3: high 4 bits of `delta` -/// * Bit 4-7: `N` -/// * Bit 8-15: low 8 bits of `delta` -#[inline] -#[instruction_set(arm::t32)] -pub unsafe fn LZ77UnCompReadNormalWrite8bit(src: *const u8, dest: *mut u8) { - core::arch::asm! { - "swi #0x11", - inout("r0") src => _, - inout("r1") dest => _, - out("r3") _, - options(preserves_flags), - } -} - -/// `0x12`: Decompress LZ77 data from `src` to `dest` using 16-bit writes. -/// -/// * The `src` is the LZ77 header and data, and must start aligned to 4. -/// * The `dest` pointer is written 16 bits at a time, so it must have align 2. -/// -/// See [`LZ77UnCompReadNormalWrite16bit`] for a description of the LZ77 format -/// used. -#[inline] -#[instruction_set(arm::t32)] -pub unsafe fn LZ77UnCompReadNormalWrite16bit(src: *const u8, dest: *mut u16) { - core::arch::asm! { - "swi #0x12", - inout("r0") src => _, - inout("r1") dest => _, - out("r3") _, - options(preserves_flags), - } -} - -/// `0x13`: Decompress huffman encoded data. -/// -/// * `src` points to the header and data (must be aligned to 4). -/// * `dest` points to the output buffer (must be aligned to 4). -/// -/// ## Data Format -/// -/// * `header` (32bit) -/// * Bits 0-3: bits per data element (normally 4 or 8). -/// * Bits 4-7: must be 2 -/// * Bits 8-31: size of decompressed data in *bytes* -/// * `tree_size` (8bit) -/// * Bits 0-7: `tree_table/2 - 1` (aka the offset to `compressed_bitstream`) -/// * `tree_table` (list of 8bit nodes, starting with the root node) -/// * Root Node and Non-Data-Child Nodes are: -/// * Bits 0-5: Offset to next child node. -/// * Next child node0 is at `(CurrentAddr AND NOT 1)+Offset*2+2` -/// * Next child node1 is at `(CurrentAddr AND NOT 1)+Offset*2+2+1` -/// * Bit 6: Node1 End Flag (1=Next child node is data) -/// * Bit 7: Node0 End Flag (1=Next child node is data) -/// * Data nodes are (when End Flag was set in parent node): -/// * Bits 0-7: Data element (upper bits should be zero when elements are -/// less than 8 bits) -/// * `compressed_bitstream` (stored in units of 32bits) -/// * Bit 0-31: Node Bits (high bit to low bit) (0=Node0, 1=Node1) -#[inline] -#[instruction_set(arm::t32)] -pub unsafe fn HuffUnCompReadNormal(src: *const u8, dest: *mut u32) { - core::arch::asm! { - "swi #0x13", - inout("r0") src => _, - inout("r1") dest => _, - out("r3") _, - options(preserves_flags), - } -} - -/// `0x14`: Decompress run-length encoded data (8-bit writes). -/// -/// * `src` points to the header and data (must be aligned to 4). -/// * `dest` points to the output buffer. -/// -/// ## Data Format -/// * `header` (32bit) -/// * Bits 0-7: magic number `0b0011_0000` -/// * Bit: 8-31: Size of decompressed data in *bytes* -/// * Repeat below. Each Flag Byte followed by one or more Data Bytes. -/// * Flag data (8bit) -/// * Bits 0-6: Expanded Data Length (uncompressed N-1, compressed N-3) -/// * Bit 7: Flag (0=uncompressed, 1=compressed) -/// * Data Byte(s): N uncompressed bytes, or 1 byte repeated N times -#[inline] -#[instruction_set(arm::t32)] -pub unsafe fn RLUnCompReadNormalWrite8bit(src: *const u8, dest: *mut u8) { - core::arch::asm! { - "swi #0x14", - inout("r0") src => _, - inout("r1") dest => _, - out("r3") _, - options(preserves_flags), - } -} - -/// `0x15`: Decompress run-length encoded data (16-bit writes). -/// -/// * `src` points to the header and data (must be aligned to 4). -/// * `dest` points to the output buffer. -/// -/// ## Data Format -/// * See [`RLUnCompReadNormalWrite8bit`] -#[inline] -#[instruction_set(arm::t32)] -pub unsafe fn RLUnCompReadNormalWrite16bit(src: *const u8, dest: *mut u16) { - core::arch::asm! { - "swi #0x15", - inout("r0") src => _, - inout("r1") dest => _, - out("r3") _, - options(preserves_flags), - } -} diff --git a/backup/src/builtin_art/cga_8x8_thick.rs b/backup/src/builtin_art/cga_8x8_thick.rs deleted file mode 100644 index ce6918e8..00000000 --- a/backup/src/builtin_art/cga_8x8_thick.rs +++ /dev/null @@ -1,228 +0,0 @@ -use core::mem::size_of_val; - -use voladdress::{Safe, VolRegion}; - -use crate::video::{Tile4, Tile8}; - -macro_rules! glyph { - ($name:ident = $id:expr) => { - pub const $name: u8 = $id; - }; -} - -#[derive(Debug, Clone, Copy)] -pub struct Cga8x8Thick; - -impl Cga8x8Thick { - // 0x0? - glyph!(NULL = 0x00); - glyph!(FACE = 0x01); - glyph!(FACE_INVERSE = 0x02); - glyph!(HEART = 0x03); - glyph!(DIAMOND = 0x04); - glyph!(CLUB = 0x05); - glyph!(SPADE = 0x06); - glyph!(BULLET = 0x07); - glyph!(BULLET_INVERSE = 0x08); - glyph!(CIRCLE = 0x09); - glyph!(CIRCLE_INVERSE = 0x0A); - glyph!(MALE = 0x0B); - glyph!(FEMALE = 0x0C); - glyph!(NOTE = 0x0D); - glyph!(NOTE_DOUBLE = 0x0E); - glyph!(SOLAR = 0x0F); - - // 0x1? - glyph!(POINTER_RIGHT = 0x10); - glyph!(POINTER_LEFT = 0x11); - glyph!(ARROW_UP_DOWN = 0x12); - glyph!(BANG_DOUBLE = 0x13); - glyph!(PARAGRAPH = 0x14); - glyph!(SECTION = 0x15); - glyph!(UNDERLINE_THICK = 0x16); - glyph!(ARROW_UP_DOWN_UNDERLINED = 0x17); - glyph!(ARROW_UP = 0x18); - glyph!(ARROW_DOWN = 0x19); - glyph!(ARROW_RIGHT = 0x1A); - glyph!(ARROW_LEFT = 0x1B); - glyph!(RIGHT_ANGLE = 0x1C); - glyph!(ARROW_LEFT_RIGHT = 0x1D); - glyph!(POINTER_UP = 0x1E); - glyph!(POINTER_DOWN = 0x1F); - - // Box drawing - glyph!(BOX_VERTICAL = 0xB3); - glyph!(BOX_HORIZONTAL = 0xC4); - glyph!(BOX_UPPER_RIGHT = 0xBF); - glyph!(BOX_UPPER_LEFT = 0xDA); - glyph!(BOX_LOWER_RIGHT = 0xD9); - glyph!(BOX_LOWER_LEFT = 0xC0); - - /// Bit unpacks the data (4bpp depth) to the location given. - /// - /// * `offset_and_touch_zero`: Works like the [`BitUnpackInfo`] field. By - /// default you should usually pass 0 here. - /// - /// ## Panics - /// * Requires at least 256 elements of space within the region. - #[inline] - #[cfg(feature = "on_gba")] - pub fn bitunpack_4bpp( - self, b: VolRegion, offset_and_touch_zero: u32, - ) { - assert!(b.len() >= 256); - let src = CGA_8X8_THICK.as_ptr(); - let dest = b.index(0).as_usize() as *mut u32; - let info = crate::bios::BitUnpackInfo { - src_byte_len: size_of_val(&CGA_8X8_THICK) as u16, - src_elem_width: 1, - dest_elem_width: 4, - offset_and_touch_zero, - }; - unsafe { crate::bios::BitUnPack(src.cast(), dest, &info) }; - } - - /// Bit unpacks the data (8bpp depth) to the location given. - /// - /// * `offset_and_touch_zero`: Works like the [`BitUnpackInfo`] field. By - /// default you should usually pass 0 here. - /// - /// ## Panics - /// * Requires at least 256 elements of space within the region. - #[inline] - #[cfg(feature = "on_gba")] - pub fn bitunpack_8bpp( - self, b: VolRegion, offset_and_touch_zero: u32, - ) { - assert!(b.len() >= 256); - let src = CGA_8X8_THICK.as_ptr(); - let dest = b.index(0).as_usize() as *mut u32; - let info = crate::bios::BitUnpackInfo { - src_byte_len: size_of_val(&CGA_8X8_THICK) as u16, - src_elem_width: 1, - dest_elem_width: 8, - offset_and_touch_zero, - }; - unsafe { crate::bios::BitUnPack(src.cast(), dest, &info) }; - } -} - -/// The CGA [Code Page 437][cp437] type face, with thick lines. -/// -/// There's 256 tiles, packed down to 1bpp. -/// -/// To easily decompress this you can use the [`Cga8x8Thick`] type, which -/// provides a safe helper method: -/// -/// ```no_run -/// # use gba::prelude::*; -/// Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP, 0); -/// ``` -/// -/// I am not a lawyer, but type faces are not protected by copyright in the USA. -/// The copyright status of font faces [varies by country][wp]. If it matters, -/// CGA was first released in 1981. This 8x8 version comes from the [Dwarf -/// Fortress Tileset Repository][df-tiles]. -/// -/// [cp437]: https://en.wikipedia.org/wiki/Code_page_437 -/// -/// [wp]: -/// https://en.wikipedia.org/wiki/Intellectual_property_protection_of_typefaces -/// -/// [df-tiles]: https://dwarffortresswiki.org/Tileset_repository#8.C3.978 -pub static CGA_8X8_THICK: [u32; 512] = [ - // Note(Lokathor): I generated this by: - // - // 1) converting the type face image from an RGB PNG to Indexed Color PNG - // using GIMP (any other image editor works too). - // 2) running `grit CGA8x8thick-indexed.png -gB1` to output an assembly file - // full of the compressed data `.word` entries. - // https://www.coranac.com/projects/grit/ - // 3) copying all those words into this array. - 0x00000000, 0x00000000, 0x81A5817E, 0x7E8199BD, 0xFFDBFF7E, 0x7EFFE7C3, - 0x7F7F7F36, 0x00081C3E, 0x7F3E1C08, 0x00081C3E, 0x7F1C3E1C, 0x1C086B7F, - 0x3E1C0808, 0x1C083E7F, 0x3C180000, 0x0000183C, 0xC3E7FFFF, 0xFFFFE7C3, - 0x42663C00, 0x003C6642, 0xBD99C3FF, 0xFFC399BD, 0xBEF0E0F0, 0x1E333333, - 0x6666663C, 0x187E183C, 0x0CFCCCFC, 0x070F0E0C, 0xC6FEC6FE, 0x0367E6C6, - 0xE73CDB18, 0x18DB3CE7, 0x7F1F0701, 0x0001071F, 0x7F7C7040, 0x0040707C, - 0x187E3C18, 0x183C7E18, 0x66666666, 0x00660066, 0xDEDBDBFE, 0x00D8D8D8, - 0x361CC67C, 0x1E331C36, 0x00000000, 0x007E7E7E, 0x187E3C18, 0xFF183C7E, - 0x187E3C18, 0x00181818, 0x18181818, 0x00183C7E, 0x7F301800, 0x00001830, - 0x7F060C00, 0x00000C06, 0x03030000, 0x00007F03, 0xFF662400, 0x00002466, - 0x7E3C1800, 0x0000FFFF, 0x7EFFFF00, 0x0000183C, 0x00000000, 0x00000000, - 0x0C1E1E0C, 0x000C000C, 0x00363636, 0x00000000, 0x367F3636, 0x0036367F, - 0x1E033E0C, 0x000C1F30, 0x18336300, 0x0063660C, 0x6E1C361C, 0x006E333B, - 0x00030606, 0x00000000, 0x06060C18, 0x00180C06, 0x18180C06, 0x00060C18, - 0xFF3C6600, 0x0000663C, 0x3F0C0C00, 0x00000C0C, 0x00000000, 0x060C0C00, - 0x3F000000, 0x00000000, 0x00000000, 0x000C0C00, 0x0C183060, 0x00010306, - 0x7B73633E, 0x003E676F, 0x0C0C0E0C, 0x003F0C0C, 0x1C30331E, 0x003F3306, - 0x1C30331E, 0x001E3330, 0x33363C38, 0x0078307F, 0x301F033F, 0x001E3330, - 0x1F03061C, 0x001E3333, 0x1830333F, 0x000C0C0C, 0x1E33331E, 0x001E3333, - 0x3E33331E, 0x000E1830, 0x000C0C00, 0x000C0C00, 0x000C0C00, 0x060C0C00, - 0x03060C18, 0x00180C06, 0x003F0000, 0x00003F00, 0x30180C06, 0x00060C18, - 0x1830331E, 0x000C000C, 0x7B7B633E, 0x001E037B, 0x33331E0C, 0x0033333F, - 0x3E66663F, 0x003F6666, 0x0303663C, 0x003C6603, 0x6666361F, 0x001F3666, - 0x1E16467F, 0x007F4616, 0x1E16467F, 0x000F0616, 0x0303663C, 0x007C6673, - 0x3F333333, 0x00333333, 0x0C0C0C1E, 0x001E0C0C, 0x30303078, 0x001E3333, - 0x1E366667, 0x00676636, 0x0606060F, 0x007F6646, 0x7F7F7763, 0x0063636B, - 0x7B6F6763, 0x00636373, 0x6363361C, 0x001C3663, 0x3E66663F, 0x000F0606, - 0x3333331E, 0x00381E3B, 0x3E66663F, 0x00676636, 0x0C06331E, 0x001E3318, - 0x0C0C2D3F, 0x001E0C0C, 0x33333333, 0x003F3333, 0x33333333, 0x000C1E33, - 0x6B636363, 0x0063777F, 0x1C366363, 0x0063361C, 0x1E333333, 0x001E0C0C, - 0x1831637F, 0x007F664C, 0x0606061E, 0x001E0606, 0x180C0603, 0x00406030, - 0x1818181E, 0x001E1818, 0x63361C08, 0x00000000, 0x00000000, 0xFF000000, - 0x00180C0C, 0x00000000, 0x301E0000, 0x006E333E, 0x3E060607, 0x003B6666, - 0x331E0000, 0x001E3303, 0x3E303038, 0x006E3333, 0x331E0000, 0x001E033F, - 0x0F06361C, 0x000F0606, 0x336E0000, 0x1F303E33, 0x6E360607, 0x00676666, - 0x0C0E000C, 0x001E0C0C, 0x30300030, 0x1E333330, 0x36660607, 0x0067361E, - 0x0C0C0C0E, 0x001E0C0C, 0x7F330000, 0x00636B7F, 0x331F0000, 0x00333333, - 0x331E0000, 0x001E3333, 0x663B0000, 0x0F063E66, 0x336E0000, 0x78303E33, - 0x6E3B0000, 0x000F0666, 0x033E0000, 0x001F301E, 0x0C3E0C08, 0x00182C0C, - 0x33330000, 0x006E3333, 0x33330000, 0x000C1E33, 0x6B630000, 0x00367F7F, - 0x36630000, 0x0063361C, 0x33330000, 0x1F303E33, 0x193F0000, 0x003F260C, - 0x070C0C38, 0x00380C0C, 0x00181818, 0x00181818, 0x380C0C07, 0x00070C0C, - 0x00003B6E, 0x00000000, 0x361C0800, 0x007F6363, 0x3303331E, 0x1E30181E, - 0x33003300, 0x007E3333, 0x331E0038, 0x001E033F, 0x603CC37E, 0x00FC667C, - 0x301E0033, 0x007E333E, 0x301E0007, 0x007E333E, 0x301E0C0C, 0x007E333E, - 0x031E0000, 0x1C301E03, 0x663CC37E, 0x003C067E, 0x331E0033, 0x001E033F, - 0x331E0007, 0x001E033F, 0x0C0E0033, 0x001E0C0C, 0x181C633E, 0x003C1818, - 0x0C0E0007, 0x001E0C0C, 0x63361C63, 0x0063637F, 0x1E000C0C, 0x00333F33, - 0x063F0038, 0x003F061E, 0x30FE0000, 0x00FE33FE, 0x7F33367C, 0x00733333, - 0x1E00331E, 0x001E3333, 0x1E003300, 0x001E3333, 0x1E000700, 0x001E3333, - 0x3300331E, 0x007E3333, 0x33000700, 0x007E3333, 0x33003300, 0x1F303E33, - 0x663C18C3, 0x00183C66, 0x33330033, 0x001E3333, 0x037E1818, 0x18187E03, - 0x0F26361C, 0x003F6706, 0x3F1E3333, 0x0C0C3F0C, 0x5F33331F, 0xE363F363, - 0x3C18D870, 0x0E1B1818, 0x301E0038, 0x007E333E, 0x0C0E001C, 0x001E0C0C, - 0x1E003800, 0x001E3333, 0x33003800, 0x007E3333, 0x1F001F00, 0x00333333, - 0x3733003F, 0x00333B3F, 0x7C36363C, 0x00007E00, 0x1C36361C, 0x00003E00, - 0x060C000C, 0x001E3303, 0x3F000000, 0x00000303, 0x3F000000, 0x00003030, - 0x7B3363C3, 0xF03366CC, 0xDB3363C3, 0xC0F3F6EC, 0x18001818, 0x00181818, - 0x3366CC00, 0x0000CC66, 0xCC663300, 0x00003366, 0x11441144, 0x11441144, - 0x55AA55AA, 0x55AA55AA, 0x77DBEEDB, 0x77DBEEDB, 0x18181818, 0x18181818, - 0x18181818, 0x1818181F, 0x181F1818, 0x1818181F, 0x6C6C6C6C, 0x6C6C6C6F, - 0x00000000, 0x6C6C6C7F, 0x181F0000, 0x1818181F, 0x606F6C6C, 0x6C6C6C6F, - 0x6C6C6C6C, 0x6C6C6C6C, 0x607F0000, 0x6C6C6C6F, 0x606F6C6C, 0x0000007F, - 0x6C6C6C6C, 0x0000007F, 0x181F1818, 0x0000001F, 0x00000000, 0x1818181F, - 0x18181818, 0x000000F8, 0x18181818, 0x000000FF, 0x00000000, 0x181818FF, - 0x18181818, 0x181818F8, 0x00000000, 0x000000FF, 0x18181818, 0x181818FF, - 0x18F81818, 0x181818F8, 0x6C6C6C6C, 0x6C6C6CEC, 0x0CEC6C6C, 0x000000FC, - 0x0CFC0000, 0x6C6C6CEC, 0x00EF6C6C, 0x000000FF, 0x00FF0000, 0x6C6C6CEF, - 0x0CEC6C6C, 0x6C6C6CEC, 0x00FF0000, 0x000000FF, 0x00EF6C6C, 0x6C6C6CEF, - 0x00FF1818, 0x000000FF, 0x6C6C6C6C, 0x000000FF, 0x00FF0000, 0x181818FF, - 0x00000000, 0x6C6C6CFF, 0x6C6C6C6C, 0x000000FC, 0x18F81818, 0x000000F8, - 0x18F80000, 0x181818F8, 0x00000000, 0x6C6C6CFC, 0x6C6C6C6C, 0x6C6C6CFF, - 0x18FF1818, 0x181818FF, 0x18181818, 0x0000001F, 0x00000000, 0x181818F8, - 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x0F0F0F0F, 0x0F0F0F0F, - 0xF0F0F0F0, 0xF0F0F0F0, 0xFFFFFFFF, 0x00000000, 0x3B6E0000, 0x006E3B13, - 0x1F331E00, 0x03031F33, 0x03333F00, 0x00030303, 0x36367F00, 0x00363636, - 0x0C06333F, 0x003F3306, 0x1B7E0000, 0x000E1B1B, 0x66666600, 0x03063E66, - 0x183B6E00, 0x00181818, 0x331E0C3F, 0x3F0C1E33, 0x7F63361C, 0x001C3663, - 0x6363361C, 0x00773636, 0x3E180C38, 0x001E3333, 0xDB7E0000, 0x00007EDB, - 0xDB7E3060, 0x03067EDB, 0x1F03061C, 0x001C0603, 0x3333331E, 0x00333333, - 0x3F003F00, 0x00003F00, 0x0C3F0C0C, 0x003F000C, 0x0C180C06, 0x003F0006, - 0x0C060C18, 0x003F0018, 0x18D8D870, 0x18181818, 0x18181818, 0x0E1B1B18, - 0x3F000C0C, 0x000C0C00, 0x003B6E00, 0x00003B6E, 0x1C36361C, 0x00000000, - 0x18000000, 0x00000018, 0x00000000, 0x00000018, 0x303030F0, 0x383C3637, - 0x3636361E, 0x00000036, 0x060C180E, 0x0000001E, 0x3C3C0000, 0x00003C3C, - 0x00000000, 0x00000000, -]; diff --git a/backup/src/builtin_art/mod.rs b/backup/src/builtin_art/mod.rs deleted file mode 100644 index 6b05e56e..00000000 --- a/backup/src/builtin_art/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! This module provides some basic art assets. -//! -//! It's mostly intended for the crate's examples, but you can use it yourself -//! too if you find it pleasing. - -mod cga_8x8_thick; -pub use cga_8x8_thick::*; diff --git a/backup/src/dma.rs b/backup/src/dma.rs deleted file mode 100644 index d0b3a763..00000000 --- a/backup/src/dma.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! Module for interfacing with the GBA's Direct Memory Access units. -//! -//! The GBA has four DMA units, numbered from 0 to 3. They can be used for -//! extremely efficient memory transfers, and they can also be set to -//! automatically transfer in response to select events. -//! -//! Whenever a DMA unit is active, the CPU does not operate at all. Not even -//! hardware interrupts will occur while a DMA is running. The interrupt will -//! instead happen after the DMA transfer is done. When it's critical that an -//! interrupt be handled exactly on time (such as when using serial interrupts) -//! then you should avoid any large DMA transfers. -//! -//! In any situation when more than one DMA unit would be active at the same -//! time, the lower-numbered DMA unit runs first. -//! -//! Each DMA unit is controlled by 4 different MMIO addresses, as follows -//! (replace `x` with the DMA unit's number): -//! * `DMAx_SRC` and `DMAx_DEST`: source and destination address. DMA 0 can only -//! use internal memory, DMA 1 and 2 can read from the gamepak but not write -//! to it, and DMA 3 can even write to the gamepak (when the gamepak itself -//! supports that). In all cases, SRAM cannot be accessed. The addresses of a -//! transfer should always be aliged to the element size. -//! * `DMAx_COUNT`: Number of elements to transfer. The number of elements is -//! either a 14-bit (DMA 0/1/2) or 16-bit (DMA3) number. If the count is set -//! to 0 then the transfer will instead copy one more than the normal maximum -//! of that number's range (DMA 0/1/2: 16_384, DMA 3: 65_536). -//! * `DMAx_CONTROL`: Configuration bits for the transfer, see [`DmaControl`]. -//! -//! ## Safety -//! -//! The DMA units are the least safe part of the GBA and should be used with -//! caution. -//! -//! Because Rust doesn't have a fully precise memory model, and because LLVM is -//! a little fuzzy about the limits of what a volatile address access can do, -//! you are advised to **not** use DMA to alter any memory that is part of -//! Rust's compilation (stack variables, static variables, etc). -//! -//! You are advised to only use the DMA units to transfer data into VRAM, -//! PALRAM, OAM, and MMIO controls (eg: the FIFO sound buffers). -//! -//! In the future the situation may improve. - -use crate::macros::{pub_const_fn_new_zeroed, u16_bool_field, u16_enum_field}; - -/// Sets the change in destination address after each transfer. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u16)] -pub enum DestAddrControl { - /// Increases the address by one element (`addr = addr.add(1)`) - #[default] - Increment = 0 << 5, - /// Decreases the address by one element (`addr = addr.sub(1)`) - Decrement = 1 << 5, - /// The address does not change. - Fixed = 2 << 5, - /// The address increases by one element per transfer, and also returns to - /// its initial value when the DMA unit restarts. - IncReload = 3 << 5, -} - -/// Sets the change in source address after each transfer. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u16)] -pub enum SrcAddrControl { - /// Increases the address by one element (`addr = addr.add(1)`) - #[default] - Increment = 0 << 7, - /// Decreases the address by one element (`addr = addr.sub(1)`) - Decrement = 1 << 7, - /// The address does not change. - Fixed = 2 << 7, -} - -/// When the DMA unit should start doing work. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u16)] -pub enum DmaStartTime { - /// The DMA unit should start immediately. - /// - /// When this is used, there is actually a 2 CPU cycle delay before the - /// transfer begins. - #[default] - Immediate = 0 << 12, - /// Transfer when vertical blank starts. - VBlank = 1 << 12, - /// Transfer when horizontal blank starts. - HBlank = 2 << 12, - /// Transfer at a special time according to which DMA unit you use this with: - /// * 0: The `Special` start time is illegal to use with DMA0. - /// * 1 or 2: When the associated sound FIFO buffer is empty - /// * 3: Video capture. - Special = 3 << 12, -} - -/// DMA control configuration. -/// -/// * `dest_addr_control`: How the destination address changes per element -/// transferred. -/// * `src_addr_control`: How the source address changes per element -/// transferred. -/// * `repeat`: If the DMA should automatically trigger again at the next start -/// time (vblank, hblank, or special). Caution: if you use `repeat` in -/// combination with the `Immediate` start time then the DMA will run over and -/// over and lock up the system. -/// * `transfer_32bit`: When set the DMA will transfer in 32-bit elements. -/// Otherwise, it will transfer in 16-bit elements. In general, you should -/// always transfer using 32-bit units when possible. -/// * `start_time`: When the DMA unit should begin a transfer. -/// * `irq_after`: If the end of the DMA transfer should send a hardware -/// interrupt. -/// * `enabled`: If the DMA unit is active. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct DmaControl(u16); -impl DmaControl { - pub_const_fn_new_zeroed!(); - u16_enum_field!( - 5 - 6: DestAddrControl, - dest_addr_control, - with_dest_addr_control - ); - u16_enum_field!( - 7 - 8: SrcAddrControl, - src_addr_control, - with_src_addr_control - ); - u16_bool_field!(9, repeat, with_repeat); - u16_bool_field!(10, transfer_32bit, with_transfer_32bit); - u16_enum_field!(12 - 13: DmaStartTime, start_time, with_start_time); - u16_bool_field!(14, irq_after, with_irq_after); - u16_bool_field!(15, enabled, with_enabled); - - /// Unwrap this value into its raw `u16` form. - #[inline] - #[must_use] - pub const fn to_u16(self) -> u16 { - self.0 - } -} - -/// Uses `stm` to set all parts of a DMA as a single instruction. -/// -/// * `dma_id` is 0, 1, 2, or 3 (this is debug asserted). -/// * `src` address for the transfer -/// * `dest` address for the transfer -/// * `count_ctrl` is the count in the low half and control in the upper half -#[inline] -#[allow(dead_code)] -// we may make this pub in the future, until then this is basically a note -unsafe fn stm_dma( - dma_id: usize, src: *const u8, dest: *mut u8, count_ctrl: u32, -) { - debug_assert!(dma_id < 4); - let dma_addr = 0x0400_00B0 + dma_id * 0xC; - core::arch::asm!( - "stm r0, {{r1, r2, r3}}", - in("r0") dma_addr, - in("r1") src, - in("r2") dest, - in("r3") count_ctrl, - options(nostack, preserves_flags) - ); -} diff --git a/backup/src/gba_cell.rs b/backup/src/gba_cell.rs deleted file mode 100644 index 0ef7cd33..00000000 --- a/backup/src/gba_cell.rs +++ /dev/null @@ -1,248 +0,0 @@ -//! A GBA-specific "cell" type that allows safe global mutable data. -//! -//! Most importantly, data stored in a [`GbaCell`] can be safely shared between -//! the main program and the interrupt handler. -//! -//! All you have to do is declare a static `GbaCell`: -//! -//! ``` -//! static THE_COLOR: GbaCell = GbaCell::new(Color::new()); -//! ``` -//! -//! And then you can use the [`read`](GbaCell::read) and -//! [`write`](GbaCell::write) methods to interact with the data: -//! -//! ``` -//! # static THE_COLOR: GbaCell = GbaCell::new(Color::new()); -//! let old_color = THE_COLOR.read(); -//! -//! THE_COLOR.write(Color::default()); -//! ``` - -use core::{ - cell::UnsafeCell, - fmt::Debug, - mem::{align_of, size_of}, - num::{NonZeroI16, NonZeroI32, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU8}, - panic::RefUnwindSafe, -}; - -use crate::{ - fixed::Fixed, - interrupts::IrqFn, - keys::{KeyControl, KeyInput}, - video::Color, -}; - -/// A GBA-specific wrapper around Rust's [`UnsafeCell`](core::cell::UnsafeCell) -/// type. -/// -/// Supports any data type that implements the [`GbaCellSafe`] marker trait. -/// -/// ## Safety Logic -/// -/// * LLVM thinks that ARMv4T only supports atomic operations via special atomic -/// support library functions. This is true for the "complex" atomic ops like -/// "fetch-add", but for individual load or store ops this is overkill. -/// * If you directly write an Acquire/load, Release/store, or a Relaxed op with -/// an associated `compiler_fence`, then LLVM does generate correct code. -/// However, it will have very sub-optimal performance. LLVM will generate -/// calls to the mythical atomic support library, when it should just directly -/// use an `ldr` or `str` instruction. -/// * In response to this LLVM nonsense, the `GbaCell` type just uses inline -/// assembly to perform all accesses to the contained data. -/// * When LLVM sees inline assembly, it is forced to defensively act as if the -/// inline assembly might have done *anything* legally possible using the -/// pointer and value provided to the inline assembly. This includes that the -/// inline assembly *might* call the atomic support library to access the -/// pointer's data using an atomic load or store. So LLVM has to treat the -/// inline assembly as an atomic sync point. -/// * However, inside the inline asm block we actually just use the single load -/// or store op that we wanted. -#[repr(transparent)] -pub struct GbaCell(UnsafeCell); -impl Debug for GbaCell -where - T: GbaCellSafe + Debug, -{ - #[inline] - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - ::fmt(&self.read(), f) - } -} -impl Default for GbaCell -where - T: GbaCellSafe + Default, -{ - #[inline] - #[must_use] - fn default() -> Self { - Self::new(T::default()) - } -} -impl Clone for GbaCell -where - T: GbaCellSafe + Default, -{ - #[inline] - #[must_use] - fn clone(&self) -> Self { - Self::new(self.read()) - } -} -unsafe impl Send for GbaCell {} -unsafe impl Sync for GbaCell {} -impl RefUnwindSafe for GbaCell {} -impl GbaCell -where - T: GbaCellSafe, -{ - /// Wraps a value in a new `GbaCell`. - #[inline] - #[must_use] - pub const fn new(val: T) -> Self { - Self(UnsafeCell::new(val)) - } - - /// Gets a pointer to the inner data. - /// - /// The rules for this pointer work just like with [`UnsafeCell`]. - #[inline] - #[must_use] - pub const fn get_ptr(&self) -> *mut T { - self.0.get() - } - - /// Reads the value. - #[inline] - #[must_use] - pub fn read(&self) -> T { - match (size_of::(), align_of::()) { - (4, 4) => unsafe { - let val: u32; - core::arch::asm!( - "ldr {r}, [{addr}]", - r = lateout(reg) val, - addr = in(reg) self.get_ptr(), - options(readonly, preserves_flags, nostack) - ); - core::mem::transmute_copy(&val) - }, - (2, 2) => unsafe { - let val: u16; - core::arch::asm!( - "ldrh {r}, [{addr}]", - r = lateout(reg) val, - addr = in(reg) self.get_ptr(), - options(readonly, preserves_flags, nostack) - ); - core::mem::transmute_copy(&val) - }, - (1, 1) => unsafe { - let val: u8; - core::arch::asm!( - "ldrb {r}, [{addr}]", - r = lateout(reg) val, - addr = in(reg) self.get_ptr(), - options(readonly, preserves_flags, nostack) - ); - core::mem::transmute_copy(&val) - }, - _ => { - unimplemented!() - } - } - } - - /// Writes a new value. - #[inline] - pub fn write(&self, val: T) { - match (size_of::(), align_of::()) { - (4, 4) => unsafe { - let u: u32 = core::mem::transmute_copy(&val); - core::arch::asm!( - "str {val}, [{addr}]", - val = in(reg) u, - addr = in(reg) self.get_ptr(), - options(preserves_flags, nostack) - ) - }, - (2, 2) => unsafe { - let u: u16 = core::mem::transmute_copy(&val); - core::arch::asm!( - "strh {val}, [{addr}]", - val = in(reg) u, - addr = in(reg) self.get_ptr(), - options(preserves_flags, nostack) - ) - }, - (1, 1) => unsafe { - let u: u8 = core::mem::transmute_copy(&val); - core::arch::asm!( - "strb {val}, [{addr}]", - val = in(reg) u, - addr = in(reg) self.get_ptr(), - options(preserves_flags, nostack) - ) - }, - _ => { - unimplemented!() - } - } - } -} - -/// Marker trait bound for the methods of [`GbaCell`]. -/// -/// When a type implements this trait it indicates that the type can be loaded -/// from a pointer in a single instruction. Also it can be stored to a pointer -/// in a single instruction. -/// -/// The exact pair of load/store instructions used will depend on the type's -/// size (`ldr`/`str`, `ldrh`/`strh`, or `ldrb`/`strb`). -/// -/// ## Safety -/// The type must fit in a single register and have an alignment equal to its -/// size. Generally that means it should be one of: -/// -/// * an 8, 16, or 32 bit integer -/// * a function pointer -/// * a data pointer to a sized type -/// * an optional non-null pointer (to function or sized data) -/// * a `repr(transparent)` newtype over one of the above -pub unsafe trait GbaCellSafe: Copy {} - -// Note(Lokathor): It would be nice if this impl list could be kept sorted, but -// it's not necessary to do so. - -// Note(Lokathor): This list is very incomplete! It's just what I thought would -// be most useful right away. More types (eg: other fn pointer types) should be -// added as necessary. - -unsafe impl GbaCellSafe for bool {} -unsafe impl GbaCellSafe for char {} -unsafe impl GbaCellSafe for Color {} -unsafe impl GbaCellSafe for i16 {} -unsafe impl GbaCellSafe for i32 {} -unsafe impl GbaCellSafe for i8 {} -unsafe impl GbaCellSafe for KeyInput {} -unsafe impl GbaCellSafe for KeyControl {} -unsafe impl GbaCellSafe for NonZeroI16 {} -unsafe impl GbaCellSafe for NonZeroI32 {} -unsafe impl GbaCellSafe for NonZeroI8 {} -unsafe impl GbaCellSafe for NonZeroU16 {} -unsafe impl GbaCellSafe for NonZeroU32 {} -unsafe impl GbaCellSafe for NonZeroU8 {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for Option {} -unsafe impl GbaCellSafe for u16 {} -unsafe impl GbaCellSafe for u32 {} -unsafe impl GbaCellSafe for u8 {} -unsafe impl GbaCellSafe for Fixed {} diff --git a/backup/src/interrupts.rs b/backup/src/interrupts.rs deleted file mode 100644 index 7de2f6ef..00000000 --- a/backup/src/interrupts.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::macros::{pub_const_fn_new_zeroed, u16_bool_field}; - -/// A function you want called during an interrupt. -pub type IrqFn = unsafe extern "C" fn(IrqBits); - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct IrqBits(u16); -impl IrqBits { - pub_const_fn_new_zeroed!(); - u16_bool_field!(0, vblank, with_vblank); - u16_bool_field!(1, hblank, with_hblank); - u16_bool_field!(2, vcounter, with_vcounter); - u16_bool_field!(3, timer0, with_timer0); - u16_bool_field!(4, timer1, with_timer1); - u16_bool_field!(5, timer2, with_timer2); - u16_bool_field!(6, timer3, with_timer3); - u16_bool_field!(7, serial, with_serial); - u16_bool_field!(8, dma0, with_dma0); - u16_bool_field!(9, dma1, with_dma1); - u16_bool_field!(10, dma2, with_dma2); - u16_bool_field!(11, dma3, with_dma3); - u16_bool_field!(12, keypad, with_keypad); - u16_bool_field!(13, gamepak, with_gamepak); - - pub const VBLANK: Self = Self::new().with_vblank(true); - pub const HBLANK: Self = Self::new().with_hblank(true); - pub const VCOUNTER: Self = Self::new().with_vcounter(true); - pub const TIMER0: Self = Self::new().with_timer0(true); - pub const TIMER1: Self = Self::new().with_timer1(true); - pub const TIMER2: Self = Self::new().with_timer2(true); - pub const TIMER3: Self = Self::new().with_timer3(true); - pub const SERIAL: Self = Self::new().with_serial(true); - pub const DMA0: Self = Self::new().with_dma0(true); - pub const DMA1: Self = Self::new().with_dma1(true); - pub const DMA2: Self = Self::new().with_dma2(true); - pub const DMA3: Self = Self::new().with_dma3(true); - pub const KEYPAD: Self = Self::new().with_keypad(true); - pub const GAMEPAK: Self = Self::new().with_gamepak(true); - - #[inline] - #[must_use] - pub const fn to_u16(self) -> u16 { - self.0 - } -} - -// TODO: might want to support bit ops. But it's not super important right now -// since they can't be implented as const traits yet anyway. diff --git a/backup/src/keys.rs b/backup/src/keys.rs deleted file mode 100644 index 9b14da4b..00000000 --- a/backup/src/keys.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! Module for interfacing with the device's button inputs. -//! -//! The GBA has two primary face buttons (A and B), two secondary face buttons -//! (Select and Start), a 4-way directional pad ("D-pad"), and two shoulder -//! buttons (L and R). -//! -//! To get the state of all the buttons just read from -//! [`KEYINPUT`](crate::mmio::KEYINPUT). For consistency, you should usually -//! read the buttons only once per frame. Then use that same data for all user -//! input considerations across that entire frame. Otherwise, small fluctuations -//! in pressure can cause inconsistencies in the reading during a frame. -//! -//! In addition to simply providing inputs, the buttons can also trigger a -//! hardware interrupt. Set the desired set of buttons that will trigger a key -//! interrupt with [`KEYCNT`](crate::mmio::KEYCNT), and when that button -//! combination is pressed the key interrupt will be fired. Key interrupts -//! aren't a good fit for standard inputs, but as a way to provide a single -//! extra special input it works okay. For example, this is generally how games -//! with a "soft reset" button combination do that. The key interrupt handler -//! sets a "reset requested" flag when the key interrupt occurs, and then the -//! main game loop checks the flag each frame and performs a soft reset instead -//! of the normal game simulation when the flag is set. - -use crate::macros::{pub_const_fn_new_zeroed, u16_bool_field}; -use core::ops; - -/// [`KEYINPUT`](crate::prelude::KEYINPUT): Key input data. -/// -/// Each key on the GBA is represented by a single bit within this value, so all -/// button state can be captured in the same moment. The `input.getter()` method -/// for each button will return `true` if that button was held down at the time -/// of reading this input data. -/// -/// Generally you should read `KEYINPUT` just once per frame, and then use that -/// data for the entire frame's computation. If you read `KEYINPUT` each time -/// you need to check a particular button for input then small variations in -/// button contact can cause confusing frame inputs. For example, a particular -/// button reads as pressed at one part of a frame and then released later -/// during the same frame. Reading the input only once per frame is a simple -/// form of "debouncing" that will help maintain consistency in the program. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct KeyInput(u16); -impl KeyInput { - pub const fn new() -> Self { - Self(0xFFFF) - } - u16_bool_field!(inverted 0, a, with_a); - u16_bool_field!(inverted 1, b, with_b); - u16_bool_field!(inverted 2, select, with_select); - u16_bool_field!(inverted 3, start, with_start); - u16_bool_field!(inverted 4, right, with_right); - u16_bool_field!(inverted 5, left, with_left); - u16_bool_field!(inverted 6, up, with_up); - u16_bool_field!(inverted 7, down, with_down); - u16_bool_field!(inverted 8, r, with_r); - u16_bool_field!(inverted 9, l, with_l); - - /// Unwraps the input into its raw `u16` form. - /// - /// Internally this type uses a "low-active" convention: A bit is 0 when the - /// key is *pressed*, and 1 when a key is *released*. The accessor methods - /// handle this automatically, but when unwrapping the value into a `u16` you - /// may need to consider this yourself. - #[inline] - #[must_use] - pub const fn to_u16(self) -> u16 { - self.0 - } -} -impl From for u16 { - #[inline] - #[must_use] - fn from(value: KeyInput) -> Self { - value.to_u16() - } -} -impl From for KeyInput { - #[inline] - #[must_use] - fn from(value: u16) -> Self { - Self(value) - } -} -impl ops::BitAnd for KeyInput { - type Output = Self; - - fn bitand(self, other: Self) -> Self { - Self(!(!self.to_u16() & !other.to_u16())) - } -} -impl ops::BitAndAssign for KeyInput { - fn bitand_assign(&mut self, other: Self) { - *self = *self & other; - } -} -impl ops::BitOr for KeyInput { - type Output = Self; - - fn bitor(self, other: Self) -> Self { - Self(!(!self.to_u16() | !other.to_u16())) - } -} -impl ops::BitOrAssign for KeyInput { - fn bitor_assign(&mut self, other: Self) { - *self = *self | other; - } -} -impl ops::BitXor for KeyInput { - type Output = Self; - - fn bitxor(self, other: Self) -> Self { - Self(!(!self.to_u16() ^ !other.to_u16())) - } -} -impl ops::BitXorAssign for KeyInput { - fn bitxor_assign(&mut self, other: Self) { - *self = *self ^ other; - } -} -impl ops::Not for KeyInput { - type Output = Self; - - fn not(self) -> Self { - Self(!self.to_u16()) - } -} - -/// [`KEYCNT`](crate::prelude::KEYCNT): Determines when a key interrupt will be -/// sent. -/// -/// A key interrupt can be sent if `irq_enabled` is set and the correct buttons -/// are pushed. This *should not* be used for normal user input. For normal -/// input you should just read [`KEYINPUT`](crate::prelude::KEYINPUT). Instead, -/// the key interrupts are primarily used for breaking the CPU out of Halt -/// state. -/// -/// * If `irq_all` is `true` then *all* buttons enabled must be pressed at once. -/// * Otherwise *any* buttons enabled can be pressed to trigger the interrupt. -/// -/// As with all interrupts, the key interrupt must also be set to be received in -/// the [`IE`](crate::prelude::IE) control, and interrupts must be enabled with -/// the [`IME`](crate::prelude::IME) control, or the key interrupt won't -/// actually be triggered even when `irq_enabled` is set. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct KeyControl(u16); -impl KeyControl { - pub_const_fn_new_zeroed!(); - u16_bool_field!(0, a, with_a); - u16_bool_field!(1, b, with_b); - u16_bool_field!(2, select, with_select); - u16_bool_field!(3, start, with_start); - u16_bool_field!(4, right, with_right); - u16_bool_field!(5, left, with_left); - u16_bool_field!(6, up, with_up); - u16_bool_field!(7, down, with_down); - u16_bool_field!(8, r, with_r); - u16_bool_field!(9, l, with_l); - - u16_bool_field!(14, irq_enabled, with_irq_enabled); - u16_bool_field!(15, irq_all, with_irq_all); -} diff --git a/backup/src/lib.rs b/backup/src/lib.rs deleted file mode 100644 index 4cd643c8..00000000 --- a/backup/src/lib.rs +++ /dev/null @@ -1,159 +0,0 @@ -#![no_std] -#![feature(asm_const)] -#![feature(naked_functions)] -#![warn(clippy::missing_inline_in_public_items)] -#![allow(clippy::let_and_return)] -#![allow(clippy::result_unit_err)] -//#![warn(missing_docs)] - -//! A crate for GBA development. -//! -//! ## How To Make Your Own GBA Project Using This Crate -//! -//! This will require the use of Nightly Rust. Any recent-ish version of Nightly -//! should be fine. -//! -//! [arm-download]: -//! https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain -//! -//! * **Get The ARM Binutils:** You'll need the ARM version of the GNU binutils -//! in your path, specifically the linker (`arm-none-eabi-ld`). Linux folks -//! can use the package manager. Mac and Windows folks can use the [ARM -//! Website][arm-download]. -//! * **Run `rustup component add rust-src`:** This makes rustup keep the -//! standard library source code on hand, which is necessary for `build-std` -//! to work. -//! * **Create A `.cargo/config.toml`:** You'll want to set up a file to provide -//! all the right default settings so that a basic `cargo build` and `cargo -//! run` will "just work". Something like the following is what you probably -//! want. -//! -//! ```toml -//! [build] -//! target = "thumbv4t-none-eabi" -//! -//! [unstable] -//! build-std = ["core"] -//! -//! [target.thumbv4t-none-eabi] -//! runner = "mgba-qt" -//! rustflags = ["-Clink-arg=-Tlinker_scripts/mono_boot.ld"] -//! ``` -//! -//! * **Make Your Executables:** At this point you can make a `bin` or an -//! `example` file. Every executable will need to be `#![no_std]` and -//! `#![no_main]`. They will also need a `#[panic_handler]` defined, as well -//! as a `#[no_mangle] extern "C" fn main() -> ! {}` function, which is what -//! the assembly runtime will call to start your Rust program after it fully -//! initializes the system. The C ABI must be used because Rust's own ABI is -//! not stable. -//! -//! ```rust -//! #![no_std] -//! #![no_main] -//! -//! #[panic_handler] -//! fn panic_handler(_: &core::panic::PanicInfo) -> ! { -//! loop {} -//! } -//! -//! #[no_mangle] -//! extern "C" fn main() -> ! { -//! loop {} -//! } -//! ``` -//! -//! * **Optional: Use `objcopy` and `gbafix`:** The `cargo build` will produce -//! ELF files, which mGBA can run directly. If you want to run your program on -//! real hardware you'll need to first `objcopy` the raw binary out of the ELF -//! into its own file, then Use `gbafix` to give an appropriate header to the -//! file. `objcopy` is part of the ARM binutils you already installed, it -//! should be named `arm-none-eabi-objcopy`. You can get `gbafix` through -//! cargo: `cargo install gbafix`. -//! -//! ## Other GBA-related Crates -//! -//! This crate provides an API to interact with the GBA that is safe, but with -//! minimal restrictions on what components can be changed when. If you'd like -//! an API where the borrow checker provides stronger control over component -//! access then the [agb](https://docs.rs/agb) crate might be what you want. -//! -//! ## Safety -//! -//! All safety considerations for the crate assume that you're building for the -//! `thumbv4t-none-eabi` or `armv4t-none-eabi` targets, using the provided -//! linker script, and then running the code on a GBA. While it's possible to -//! break any of these assumptions, if you do that some or all of the code -//! provided by this crate may become unsound. - -mod macros; - -pub mod asm_runtime; -pub mod bios; -pub mod builtin_art; -#[cfg(feature = "critical-section")] -mod critical_section; -pub mod dma; -pub mod fixed; -pub mod gba_cell; -pub mod interrupts; -pub mod keys; -pub mod mem_fns; -pub mod mgba; -pub mod mmio; -pub mod prelude; -pub mod random; -pub mod sound; -pub mod timers; -pub mod video; - -/// Wraps a value to be aligned to a minimum of 4. -/// -/// If the size of the value held is already a multiple of 4 then this will be -/// the same size as the wrapped value. Otherwise the compiler will add -/// sufficient padding bytes on the end to make the size a multiple of 4. -#[derive(Debug)] -#[repr(C, align(4))] -pub struct Align4(pub T); - -impl Align4<[u8; N]> { - /// Views these bytes as a slice of `u32` - /// ## Panics - /// * If the number of bytes isn't a multiple of 4 - #[inline] - #[must_use] - pub fn as_u32_slice(&self) -> &[u32] { - assert!(self.0.len() % 4 == 0); - // Safety: our struct is aligned to 4, so the pointer will already be - // aligned, we only need to check the length - unsafe { - let data: *const u8 = self.0.as_ptr(); - let len: usize = self.0.len(); - core::slice::from_raw_parts(data.cast::(), len / 4) - } - } - - /// Views these bytes as a slice of `u16` - /// ## Panics - /// * If the number of bytes isn't a multiple of 2 - #[inline] - #[must_use] - pub fn as_u16_slice(&self) -> &[u16] { - assert!(self.0.len() % 2 == 0); - // Safety: our struct is aligned to 4, so the pointer will already be - // aligned, we only need to check the length - unsafe { - let data: *const u8 = self.0.as_ptr(); - let len: usize = self.0.len(); - core::slice::from_raw_parts(data.cast::(), len / 2) - } - } -} - -/// Works like [`include_bytes!`], but the value is wrapped in [`Align4`]. -#[macro_export] -macro_rules! include_aligned_bytes { - ($file:expr $(,)?) => {{ - Align4(*include_bytes!($file)) - }}; -} diff --git a/backup/src/macros.rs b/backup/src/macros.rs deleted file mode 100644 index fee6a660..00000000 --- a/backup/src/macros.rs +++ /dev/null @@ -1,144 +0,0 @@ -#![allow(unused_macros)] -#![allow(unused_imports)] - -macro_rules! pub_const_fn_new_zeroed { - () => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn new() -> Self { - Self(0) - } - }; -} -pub(crate) use pub_const_fn_new_zeroed; - -macro_rules! u16_bool_field { - ($bit:expr, $get:ident, $with:ident) => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $get(self) -> bool { - bitfrob::u16_get_bit($bit, self.0) - } - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $with(self, b: bool) -> Self { - Self(bitfrob::u16_with_bit($bit, self.0, b)) - } - }; - (inverted $bit:expr, $get:ident, $with:ident) => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $get(self) -> bool { - !bitfrob::u16_get_bit($bit, self.0) - } - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $with(self, b: bool) -> Self { - Self(bitfrob::u16_with_bit($bit, self.0, !b)) - } - }; -} -pub(crate) use u16_bool_field; - -macro_rules! u16_enum_field { - ($low:literal - $high:literal : $t:ty, $get:ident, $with:ident) => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $get(self) -> $t { - unsafe { - core::mem::transmute::(bitfrob::u16_get_region( - $low, $high, self.0, - )) - } - } - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $with(self, val: $t) -> Self { - Self(bitfrob::u16_with_region($low, $high, self.0, val as u16)) - } - }; -} -pub(crate) use u16_enum_field; - -macro_rules! u16_int_field { - ($low:literal - $high:literal, $get:ident, $with:ident) => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $get(self) -> u16 { - bitfrob::u16_get_value($low, $high, self.0) - } - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $with(self, val: u16) -> Self { - Self(bitfrob::u16_with_value($low, $high, self.0, val)) - } - }; -} -pub(crate) use u16_int_field; - -macro_rules! u8_bool_field { - ($bit:expr, $get:ident, $with:ident) => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $get(self) -> bool { - bitfrob::u8_get_bit($bit, self.0) - } - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $with(self, b: bool) -> Self { - Self(bitfrob::u8_with_bit($bit, self.0, b)) - } - }; -} -pub(crate) use u8_bool_field; - -macro_rules! u8_enum_field { - ($low:literal - $high:literal : $t:ty, $get:ident, $with:ident) => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $get(self) -> $t { - unsafe { - core::mem::transmute::(bitfrob::u8_get_region( - $low, $high, self.0, - )) - } - } - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $with(self, val: $t) -> Self { - Self(bitfrob::u8_with_region($low, $high, self.0, val as u8)) - } - }; -} -pub(crate) use u8_enum_field; - -macro_rules! u8_int_field { - ($low:literal - $high:literal, $get:ident, $with:ident) => { - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $get(self) -> u8 { - bitfrob::u8_get_value($low, $high, self.0) - } - #[inline] - #[must_use] - #[allow(missing_docs)] - pub const fn $with(self, val: u8) -> Self { - Self(bitfrob::u8_with_value($low, $high, self.0, val)) - } - }; -} -pub(crate) use u8_int_field; diff --git a/backup/src/mmio.rs b/backup/src/mmio.rs deleted file mode 100644 index a392bb27..00000000 --- a/backup/src/mmio.rs +++ /dev/null @@ -1,277 +0,0 @@ -#![cfg_attr(rustfmt, rustfmt::skip)] - -//! Contains all the MMIO address definitions for the GBA's components. -//! -//! This module contains *only* the MMIO addresses. The data type definitions -//! for each MMIO control value are stored in the appropriate other modules such -//! as [`video`](crate::video), [`interrupts`](crate::interrupts), etc. -//! -//! In general, the docs for each address are quite short. If you want to -//! understand how a subsystem of the GBA works, you should read the docs for -//! that system's module, and the data type used by the address. -//! -//! The GBATEK names (and thus mGBA names) are used for the MMIO addresses by -//! default. However, in some cases (eg: sound) the GBATEK naming is excessively -//! cryptic, and so new names have been created. Whenever a new name is used, -//! the GBATEK name is still listed as a doc alias for that address. If -//! necessary you can just search the GBATEK name in the rustdoc search bar and -//! the search results will show you the new name. -//! -//! ## Safety -//! -//! The MMIO declarations and wrapper types in this module **must not** be used -//! outside of a GBA. The read and write safety of each address are declared -//! assuming that code is running on a GBA. On any other platform, the -//! declarations are simply incorrect. - -use core::{ffi::c_void, mem::size_of}; -use crate::prelude::*; -pub use bitfrob::u8x2; -pub use voladdress::{Safe, Unsafe, VolAddress, VolBlock, VolSeries, VolGrid2dStrided, VolGrid2d}; - -// Note(Lokathor): This macro lets us stick each address at the start of the -// definition, which lets us easily keep each declaration in address order. -macro_rules! def_mmio { - ($addr:literal = $name:ident : $t:ty $(; $comment:expr )?) => { - // redirect a call **without** an alias list to just pass an empty alias list - def_mmio!($addr = $name/[]: $t $(; $comment)? ); - }; - ($addr:literal = $name:ident / [ $( $alias:literal ),* ]: $t:ty $(; $comment:expr )?) => { - $(#[doc = $comment])? - $(#[doc(alias = $alias)])* - #[allow(missing_docs)] - pub const $name: $t = unsafe { <$t>::new($addr) }; - }; -} - -// Video - -def_mmio!(0x0400_0000 = DISPCNT: VolAddress; "Display Control"); -def_mmio!(0x0400_0004 = DISPSTAT: VolAddress; "Display Status"); -def_mmio!(0x0400_0006 = VCOUNT: VolAddress; "Vertical Counter"); - -def_mmio!(0x0400_0008 = BG0CNT: VolAddress; "Background 0 Control"); -def_mmio!(0x0400_000A = BG1CNT: VolAddress; "Background 1 Control"); -def_mmio!(0x0400_000C = BG2CNT: VolAddress; "Background 2 Control"); -def_mmio!(0x0400_000E = BG3CNT: VolAddress; "Background 3 Control"); - -def_mmio!(0x0400_0010 = BG0HOFS: VolAddress; "Background 0 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_0012 = BG0VOFS: VolAddress; "Background 0 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_0014 = BG1HOFS: VolAddress; "Background 1 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_0016 = BG1VOFS: VolAddress; "Background 1 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_0018 = BG2HOFS: VolAddress; "Background 2 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_001A = BG2VOFS: VolAddress; "Background 2 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_001C = BG3HOFS: VolAddress; "Background 3 Horizontal Offset (9-bit, text mode)"); -def_mmio!(0x0400_001E = BG3VOFS: VolAddress; "Background 3 Vertical Offset (9-bit, text mode)"); - -def_mmio!(0x0400_0020 = BG2PA: VolAddress; "Background 2 Param A (affine mode)"); -def_mmio!(0x0400_0022 = BG2PB: VolAddress; "Background 2 Param B (affine mode)"); -def_mmio!(0x0400_0024 = BG2PC: VolAddress; "Background 2 Param C (affine mode)"); -def_mmio!(0x0400_0026 = BG2PD: VolAddress; "Background 2 Param D (affine mode)"); -def_mmio!(0x0400_0028 = BG2X/["BG2X_L", "BG2X_H"]: VolAddress; "Background 2 X Reference Point (affine/bitmap modes)"); -def_mmio!(0x0400_002C = BG2Y/["BG2Y_L", "BG2Y_H"]: VolAddress; "Background 2 Y Reference Point (affine/bitmap modes)"); - -def_mmio!(0x0400_0030 = BG3PA: VolAddress; "Background 3 Param A (affine mode)"); -def_mmio!(0x0400_0032 = BG3PB: VolAddress; "Background 3 Param B (affine mode)"); -def_mmio!(0x0400_0034 = BG3PC: VolAddress; "Background 3 Param C (affine mode)"); -def_mmio!(0x0400_0036 = BG3PD: VolAddress; "Background 3 Param D (affine mode)"); -def_mmio!(0x0400_0038 = BG3X/["BG3X_L", "BG3X_H"]: VolAddress; "Background 3 X Reference Point (affine/bitmap modes)"); -def_mmio!(0x0400_003C = BG3Y/["BG3Y_L", "BG3Y_H"]: VolAddress; "Background 3 Y Reference Point (affine/bitmap modes)"); - -def_mmio!(0x0400_0040 = WIN0H: VolAddress; "Window 0 Horizontal: high=left, low=(right+1)"); -def_mmio!(0x0400_0042 = WIN1H: VolAddress; "Window 1 Horizontal: high=left, low=(right+1)"); -def_mmio!(0x0400_0044 = WIN0V: VolAddress; "Window 0 Vertical: high=top, low=(bottom+1)"); -def_mmio!(0x0400_0046 = WIN1V: VolAddress; "Window 1 Vertical: high=top, low=(bottom+1)"); -def_mmio!(0x0400_0048 = WININ: VolAddress; "Controls the inside Windows 0 and 1"); -def_mmio!(0x0400_004A = WINOUT: VolAddress; "Controls inside the object window and outside of windows"); - -def_mmio!(0x0400_004C = MOSAIC: VolAddress; "Sets the intensity of all mosaic effects"); -def_mmio!(0x0400_0050 = BLDCNT: VolAddress; "Sets color blend effects"); -def_mmio!(0x0400_0052 = BLDALPHA: VolAddress;"Sets EVA(low) and EVB(high) alpha blend coefficients, allows `0..=16`, in 1/16th units"); -def_mmio!(0x0400_0054 = BLDY: VolAddress;"Sets EVY brightness blend coefficient, allows `0..=16`, in 1/16th units"); - -// Sound - -def_mmio!(0x0400_0060 = TONE1_SWEEP/["SOUND1CNT_L","NR10"]: VolAddress; "Tone 1 Sweep"); -def_mmio!(0x0400_0062 = TONE1_PATTERN/["SOUND1CNT_H","NR11","NR12"]: VolAddress; "Tone 1 Duty/Len/Envelope"); -def_mmio!(0x0400_0064 = TONE1_FREQUENCY/["SOUND1CNT_X","NR13","NR14"]: VolAddress; "Tone 1 Frequency/Control"); - -def_mmio!(0x0400_0068 = TONE2_PATTERN/["SOUND2CNT_L","NR21","NR22"]: VolAddress; "Tone 2 Duty/Len/Envelope"); -def_mmio!(0x0400_006C = TONE2_FREQUENCY/["SOUND2CNT_H","NR23","NR24"]: VolAddress; "Tone 2 Frequency/Control"); - -def_mmio!(0x0400_0070 = WAVE_BANK/["SOUND3CNT_L","NR30"]: VolAddress; "Wave banking controls"); -def_mmio!(0x0400_0072 = WAVE_LEN_VOLUME/["SOUND3CNT_H","NR31","NR32"]: VolAddress; "Wave Length/Volume"); -def_mmio!(0x0400_0074 = WAVE_FREQ/["SOUND3CNT_X","NR33","NR34"]: VolAddress; "Wave Frequency/Control"); - -def_mmio!(0x0400_0078 = NOISE_LEN_ENV/["SOUND4CNT_L","NR41","NR42"]: VolAddress; "Noise Length/Envelope"); -def_mmio!(0x0400_007C = NOISE_FREQ/["SOUND4CNT_H","NR43","NR44"]: VolAddress; "Noise Frequency/Control"); - -def_mmio!(0x0400_0080 = LEFT_RIGHT_VOLUME/["SOUNDCNT_L","NR50","NR51"]: VolAddress;"Left/Right sound control (but GBAs only have one speaker each)."); -def_mmio!(0x0400_0082 = SOUND_MIX/["SOUNDCNT_H"]: VolAddress;"Mixes sound sources out to the left and right"); -def_mmio!(0x0400_0084 = SOUND_ENABLED/["SOUNDCNT_X"]: VolAddress;"Sound active flags (r), as well as the sound primary enable (rw)."); -def_mmio!(0x0400_0088 = SOUNDBIAS: VolAddress;"Provides a bias to set the 'middle point' of sound output."); - -def_mmio!(0x0400_0090 = WAVE_RAM/["WAVE_RAM0_L","WAVE_RAM0_H","WAVE_RAM1_L","WAVE_RAM1_H","WAVE_RAM2_L","WAVE_RAM2_H","WAVE_RAM3_L","WAVE_RAM3_H"]: VolBlock; "Wave memory, `u4`, plays MSB/LSB per byte."); -def_mmio!(0x0400_00A0 = FIFO_A/["FIFO_A_L", "FIFO_A_H"]: VolAddress; "Pushes 4 `i8` samples into the Sound A buffer.\n\nThe buffer is 32 bytes max, playback is LSB first."); -def_mmio!(0x0400_00A4 = FIFO_B/["FIFO_B_L", "FIFO_B_H"]: VolAddress; "Pushes 4 `i8` samples into the Sound B buffer.\n\nThe buffer is 32 bytes max, playback is LSB first."); - -// DMA - -def_mmio!(0x0400_00B0 = DMA0_SRC/["DMA0SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA0 Source Address (internal memory only)"); -def_mmio!(0x0400_00B4 = DMA0_DEST/["DMA0DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA0 Destination Address (internal memory only)"); -def_mmio!(0x0400_00B8 = DMA0_COUNT/["DMA0CNT_L"]: VolAddress; "DMA0 Transfer Count (14-bit, 0=max)"); -def_mmio!(0x0400_00BA = DMA0_CONTROL/["DMA0_CNT_H"]: VolAddress; "DMA0 Control Bits"); - -def_mmio!(0x0400_00BC = DMA1_SRC/["DMA1SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA1 Source Address (non-SRAM memory)"); -def_mmio!(0x0400_00C0 = DMA1_DEST/["DMA1DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA1 Destination Address (internal memory only)"); -def_mmio!(0x0400_00C4 = DMA1_COUNT/["DMA1CNT_L"]: VolAddress; "DMA1 Transfer Count (14-bit, 0=max)"); -def_mmio!(0x0400_00C6 = DMA1_CONTROL/["DMA1_CNT_H"]: VolAddress; "DMA1 Control Bits"); - -def_mmio!(0x0400_00C8 = DMA2_SRC/["DMA2SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA2 Source Address (non-SRAM memory)"); -def_mmio!(0x0400_00CC = DMA2_DEST/["DMA2DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA2 Destination Address (internal memory only)"); -def_mmio!(0x0400_00D0 = DMA2_COUNT/["DMA2CNT_L"]: VolAddress; "DMA2 Transfer Count (14-bit, 0=max)"); -def_mmio!(0x0400_00D2 = DMA2_CONTROL/["DMA2_CNT_H"]: VolAddress; "DMA2 Control Bits"); - -def_mmio!(0x0400_00D4 = DMA3_SRC/["DMA3SAD"]: VolAddress<*const c_void, (), Unsafe>; "DMA3 Source Address (non-SRAM memory)"); -def_mmio!(0x0400_00D8 = DMA3_DEST/["DMA3DAD"]: VolAddress<*mut c_void, (), Unsafe>; "DMA3 Destination Address (non-SRAM memory)"); -def_mmio!(0x0400_00DC = DMA3_COUNT/["DMA3CNT_L"]: VolAddress; "DMA3 Transfer Count (16-bit, 0=max)"); -def_mmio!(0x0400_00DE = DMA3_CONTROL/["DMA3_CNT_H"]: VolAddress; "DMA3 Control Bits"); - -// Timers - -def_mmio!(0x0400_0100 = TIMER0_COUNT/["TM0CNT_L"]: VolAddress; "Timer 0 Count read"); -def_mmio!(0x0400_0100 = TIMER0_RELOAD/["TM0CNT_L"]: VolAddress; "Timer 0 Reload write"); -def_mmio!(0x0400_0102 = TIMER0_CONTROL/["TM0CNT_H"]: VolAddress; "Timer 0 control"); - -def_mmio!(0x0400_0104 = TIMER1_COUNT/["TM1CNT_L"]: VolAddress; "Timer 1 Count read"); -def_mmio!(0x0400_0104 = TIMER1_RELOAD/["TM1CNT_L"]: VolAddress; "Timer 1 Reload write"); -def_mmio!(0x0400_0106 = TIMER1_CONTROL/["TM1CNT_H"]: VolAddress; "Timer 1 control"); - -def_mmio!(0x0400_0108 = TIMER2_COUNT/["TM2CNT_L"]: VolAddress; "Timer 2 Count read"); -def_mmio!(0x0400_0108 = TIMER2_RELOAD/["TM2CNT_L"]: VolAddress; "Timer 2 Reload write"); -def_mmio!(0x0400_010A = TIMER2_CONTROL/["TM2CNT_H"]: VolAddress; "Timer 2 control"); - -def_mmio!(0x0400_010C = TIMER3_COUNT/["TM3CNT_L"]: VolAddress; "Timer 3 Count read"); -def_mmio!(0x0400_010C = TIMER3_RELOAD/["TM3CNT_L"]: VolAddress; "Timer 3 Reload write"); -def_mmio!(0x0400_010E = TIMER3_CONTROL/["TM3CNT_H"]: VolAddress; "Timer 3 control"); - -// Serial (part 1) - -def_mmio!(0x0400_0120 = SIODATA32: VolAddress); -def_mmio!(0x0400_0120 = SIOMULTI0: VolAddress); -def_mmio!(0x0400_0122 = SIOMULTI1: VolAddress); -def_mmio!(0x0400_0124 = SIOMULTI2: VolAddress); -def_mmio!(0x0400_0126 = SIOMULTI3: VolAddress); -def_mmio!(0x0400_0128 = SIOCNT: VolAddress); -def_mmio!(0x0400_012A = SIOMLT_SEND: VolAddress); -def_mmio!(0x0400_012A = SIODATA8: VolAddress); - -// Keys - -def_mmio!(0x0400_0130 = KEYINPUT: VolAddress; "Key state data."); -def_mmio!(0x0400_0132 = KEYCNT: VolAddress; "Key control to configure the key interrupt."); - -// Serial (part 2) - -def_mmio!(0x0400_0134 = RCNT: VolAddress); -def_mmio!(0x0400_0140 = JOYCNT: VolAddress); -def_mmio!(0x0400_0150 = JOY_RECV: VolAddress); -def_mmio!(0x0400_0154 = JOY_TRANS: VolAddress); -def_mmio!(0x0400_0158 = JOYSTAT: VolAddress); - -// Interrupts - -def_mmio!(0x0400_0200 = IE: VolAddress; "Interrupts Enabled: sets which interrupts will be accepted when a subsystem fires an interrupt"); -def_mmio!(0x0400_0202 = IF: VolAddress; "Interrupts Flagged: reads which interrupts are pending, writing bit(s) will clear a pending interrupt."); -def_mmio!(0x0400_0204 = WAITCNT: VolAddress; "Wait state control for interfacing with the ROM.\n\nThis can make reading the ROM give garbage when it's mis-configured!"); -def_mmio!(0x0400_0208 = IME: VolAddress; "Interrupt Master Enable: Allows turning on/off all interrupts with a single access."); - -// mGBA Logging - -def_mmio!(0x04FF_F600 = MGBA_LOG_BUFFER: VolBlock; "The buffer to put logging messages into.\n\nThe first 0 in the buffer is the end of each message."); -def_mmio!(0x04FF_F700 = MGBA_LOG_SEND: VolAddress; "Write to this each time you want to reset a message (it also resets the buffer)."); -def_mmio!(0x04FF_F780 = MGBA_LOG_ENABLE: VolAddress; "Allows you to attempt to activate mGBA logging."); - -// Palette RAM (PALRAM) - -def_mmio!(0x0500_0000 = BACKDROP_COLOR: VolAddress; "Color that's shown when no BG or OBJ draws to a pixel"); -def_mmio!(0x0500_0000 = BG_PALETTE: VolBlock; "Background tile palette entries."); -def_mmio!(0x0500_0200 = OBJ_PALETTE: VolBlock; "Object tile palette entries."); - -#[inline] -#[must_use] -#[cfg_attr(feature="track_caller", track_caller)] -pub const fn bg_palbank(bank: usize) -> VolBlock { - let u = BG_PALETTE.index(bank * 16).as_usize(); - unsafe { VolBlock::new(u) } -} -#[inline] -#[must_use] -#[cfg_attr(feature="track_caller", track_caller)] -pub const fn obj_palbank(bank: usize) -> VolBlock { - let u = OBJ_PALETTE.index(bank * 16).as_usize(); - unsafe { VolBlock::new(u) } -} - -// Video RAM (VRAM) - -/// The VRAM byte offset per screenblock index. -/// -/// This is the same for all background types and sizes. -pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; - -/// The size of the background tile region of VRAM. -/// -/// Background tile index use will work between charblocks, but not past the end -/// of BG tile memory into OBJ tile memory. -pub const BG_TILE_REGION_SIZE: usize = 64 * 1_024; - -def_mmio!(0x0600_0000 = CHARBLOCK0_4BPP: VolBlock; "Charblock 0, 4bpp view (512 tiles)."); -def_mmio!(0x0600_4000 = CHARBLOCK1_4BPP: VolBlock; "Charblock 1, 4bpp view (512 tiles)."); -def_mmio!(0x0600_8000 = CHARBLOCK2_4BPP: VolBlock; "Charblock 2, 4bpp view (512 tiles)."); -def_mmio!(0x0600_C000 = CHARBLOCK3_4BPP: VolBlock; "Charblock 3, 4bpp view (512 tiles)."); - -def_mmio!(0x0600_0000 = CHARBLOCK0_8BPP: VolBlock; "Charblock 0, 8bpp view (256 tiles)."); -def_mmio!(0x0600_4000 = CHARBLOCK1_8BPP: VolBlock; "Charblock 1, 8bpp view (256 tiles)."); -def_mmio!(0x0600_8000 = CHARBLOCK2_8BPP: VolBlock; "Charblock 2, 8bpp view (256 tiles)."); -def_mmio!(0x0600_C000 = CHARBLOCK3_8BPP: VolBlock; "Charblock 3, 8bpp view (256 tiles)."); - -def_mmio!(0x0600_0000 = TEXT_SCREENBLOCKS: VolGrid2dStrided; "Text mode screenblocks."); - -def_mmio!(0x0600_0000 = AFFINE0_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 0)."); - -def_mmio!(0x0600_0000 = AFFINE1_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 1)."); - -def_mmio!(0x0600_0000 = AFFINE2_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 2)."); - -def_mmio!(0x0600_0000 = AFFINE3_SCREENBLOCKS: VolGrid2dStrided; "Affine screenblocks (size 3)."); - -def_mmio!(0x0600_0000 = VIDEO3_VRAM: VolGrid2d; "Video mode 3 bitmap"); - -def_mmio!(0x0600_0000 = VIDEO4_VRAM: VolGrid2dStrided; "Video mode 4 palette maps (frames 0 and 1). Each entry is two palette indexes."); - -def_mmio!(0x0600_0000 = VIDEO5_VRAM: VolGrid2dStrided; "Video mode 5 bitmaps (frames 0 and 1)."); - -def_mmio!(0x0601_0000 = OBJ_TILES: VolBlock; "Object tiles. In video modes 3, 4, and 5 only indices 512..=1023 are available."); - -// Object Attribute Memory (OAM) - -def_mmio!(0x0700_0000 = OBJ_ATTR0: VolSeries()}>; "Object attributes 0."); -def_mmio!(0x0700_0002 = OBJ_ATTR1: VolSeries()}>; "Object attributes 1."); -def_mmio!(0x0700_0004 = OBJ_ATTR2: VolSeries()}>; "Object attributes 2."); - -def_mmio!(0x0700_0000 = OBJ_ATTR_ALL: VolSeries()}>; "Object attributes (all in one)."); - -def_mmio!(0x0700_0006 = AFFINE_PARAM_A: VolSeries()}>; "Affine parameters A."); -def_mmio!(0x0700_000E = AFFINE_PARAM_B: VolSeries()}>; "Affine parameters B."); -def_mmio!(0x0700_0016 = AFFINE_PARAM_C: VolSeries()}>; "Affine parameters C."); -def_mmio!(0x0700_001E = AFFINE_PARAM_D: VolSeries()}>; "Affine parameters D."); - -// Cartridge IO port -// https://problemkaputt.de/gbatek.htm#gbacartioportgpio -def_mmio!(0x0800_00C4 = IO_PORT_DATA: VolAddress; "I/O port data"); -def_mmio!(0x0800_00C6 = IO_PORT_DIRECTION: VolAddress; "I/O port direction"); -def_mmio!(0x0800_00C8 = IO_PORT_CONTROL: VolAddress; "I/O port control"); diff --git a/backup/src/video/obj.rs b/backup/src/obj.rs similarity index 100% rename from backup/src/video/obj.rs rename to backup/src/obj.rs diff --git a/backup/src/prelude.rs b/backup/src/prelude.rs deleted file mode 100644 index 45b30dc5..00000000 --- a/backup/src/prelude.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! A module that just re-exports all the other modules of the crate. - -#[cfg(feature = "on_gba")] -pub use crate::{ - asm_runtime::*, bios::*, dma::*, gba_cell::*, mgba::*, mmio::*, -}; - -pub use crate::{ - builtin_art::*, - fixed::*, - include_aligned_bytes, - interrupts::*, - keys::*, - sound::*, - timers::*, - video::{obj::*, *}, - Align4, -}; diff --git a/backup/src/video/mod.rs b/backup/src/video/mod.rs deleted file mode 100644 index 866b22a1..00000000 --- a/backup/src/video/mod.rs +++ /dev/null @@ -1,317 +0,0 @@ -//! Module to control the GBA's screen. -//! -//! # Video Basics -//! -//! To configure the screen's display, you should first decide on the -//! [`DisplayControl`] value that you want to set to the -//! [`DISPCNT`](crate::mmio::DISPCNT) register. This configures several things, -//! but most importantly it determines the [`VideoMode`] for the display to use. -//! -//! The GBA has four Background layers. Depending on the current video mode, -//! different background layers will be available for use in either "text", -//! "affine", or "bitmap" mode. -//! -//! In addition to the background layers, there's also an "OBJ" layer. This -//! allows the display of a number of "objects", which can move independently of -//! any background. Generally, one or more objects will be used to display the -//! "sprites" within a game. Because there isn't an exact 1:1 mapping between -//! sprites and objects, these docs will attempt to only talk about objects. -//! -//! ## Color, Bit Depth, and Palettes -//! -//! [Color] values on the GBA are 5-bits-per-channel RGB values. They're always -//! bit-packed and aligned to 2, so think of them as being like a `u16`. -//! -//! Because of the GBA's limited memory, most images don't use direct color (one -//! color per pixel). Instead they use indexed color (one *palette index* per -//! pixel). Indexed image data can be 4-bits-per-pixel (4bpp) or -//! 8-bits-per-pixel (8bpp). In either case, the color values themselves are -//! stored in the PALRAM region. The PALRAM contains the [`BG_PALETTE`] and -//! [`OBJ_PALETTE`], which hold the color values for backgrounds and objects -//! respectively. Both palettes have 256 slots. The palettes are always indexed -//! with 8 bits total, but *how* those bits are determined depends on the bit -//! depth of the image: -//! * Things drawing with 8bpp image data index into the full range of the -//! palette directly. -//! * Things drawing with 4bpp image data will also have a "palbank" setting. -//! The palbank acts as the upper 4 bits of the index, selecting which block -//! of 16 palette entries the that thing will be able to use. Then each 4-bit -//! pixel within the image indexes within the palbank. -//! -//! In both 8bpp and 4bpp modes, if a particular pixel's index value is 0 then -//! that pixel is instead considered transparent. So 8bpp images can use 255 -//! colors (+ transparent), and 4bpp images can use 15 colors (+ transparent). -//! Each background layer and each object can individually be set to display -//! with either 4bpp or 8bpp mode. -//! -//! ## Tiles, Screenblocks, and Charblocks -//! -//! The basic unit of the GBA's hardware graphics support is a "tile". -//! Regardless of their bit depth, a tile is always an 8x8 area. This means that -//! they're either 32 bytes (4bpp) or 64 bytes (8bpp). Since VRAM starts aligned -//! to 4, and since both size tiles are a multiple of 4 bytes in size, we model -//! tile data as being arrays of `u32` rather than arrays of `u8`. Having the -//! data stay aligned to 4 within the ROM gives a significant speed gain when -//! copying tiles from ROM into VRAM. -//! -//! The layout of tiles within a background is defined by a "screenblock". -//! * Text backgrounds use a fixed 32x32 size screenblock, with larger -//! backgrounds using more than one screenblock. Each [TextEntry] value in the -//! screenblock has a tile index (10-bit), bits for horizontal flip and -//! vertical flip, and a palbank value. If the background is not in 4bpp mode -//! the palbank value is simply ignored. -//! * Affine backgrounds always have a single screenblock each, and the size of -//! the screenblock itself changes with the background's size (from 16x16 to -//! 128x128, in powers of 2). Each entry in an affine screenblock is just a -//! `u8` tile index, with no special options. Affine backgrounds can't use -//! 4bpp color, and they also can't flip tiles on a per-tile basis. -//! -//! A background's screenblock is selected by an index (5-bit). The indexes go -//! in 2,048 byte (2k) jumps. This is exactly the size of a text screenblock, -//! but doesn't precisely match the size of any of the affine screenblocks. -//! -//! Because tile indexes can only be so large, there are also "charblocks". This -//! offsets all of the tile index values that the background uses, allowing you -//! to make better use of all of the VRAM. The charblock value provides a 16,384 -//! byte (16k) offset, and can be in the range `0..=3`. -//! -//! ## Priority -//! -//! When more than one thing would be drawn to the same pixel, there's a -//! priority system that determines which pixel is actually drawn. -//! * Priority values are always 2-bit, the range `0..=3`. The priority acts -//! like the sorting index, or you could also think of it as the distance from -//! the viewer. Things with a *lower* priority number are *closer* to the -//! viewer, and so they'll be what's drawn. -//! * Objects always draw over top a same-priority background. -//! * Lower indexed objects get drawn when two objects have the same priority. -//! * Lower numbered backgrounds get drawn when two backgrounds have the same -//! priority. -//! -//! There's also one hardware bug that can occur: when there's two objects and -//! their the priority and index wouldn't sort them the same (eg: a lower index -//! number object has a higher priority number), if a background is *also* -//! between the two objects, then the object that's supposed to be behind the -//! background will instead appear through the background where the two objects -//! overlap. This might never happen to you, but if it does, the "fix" is to -//! sort your object entries so that any lower priority objects are also the -//! lower index objects. - -use crate::macros::{ - pub_const_fn_new_zeroed, u16_bool_field, u16_enum_field, u16_int_field, -}; -#[allow(unused_imports)] -use crate::prelude::*; - -pub mod obj; - -/// An RGB555 color value (packed into `u16`). -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct Color(pub u16); -#[allow(clippy::unusual_byte_groupings)] -#[allow(missing_docs)] -impl Color { - pub const BLACK: Color = Color(0b0_00000_00000_00000); - pub const RED: Color = Color(0b0_00000_00000_11111); - pub const GREEN: Color = Color(0b0_00000_11111_00000); - pub const YELLOW: Color = Color(0b0_00000_11111_11111); - pub const BLUE: Color = Color(0b0_11111_00000_00000); - pub const MAGENTA: Color = Color(0b0_11111_00000_11111); - pub const CYAN: Color = Color(0b0_11111_11111_00000); - pub const WHITE: Color = Color(0b0_11111_11111_11111); - - pub_const_fn_new_zeroed!(); - u16_int_field!(0 - 4, red, with_red); - u16_int_field!(5 - 9, green, with_green); - u16_int_field!(10 - 14, blue, with_blue); - - /// Constructs a new color value from the given channel values. - #[inline] - #[must_use] - pub const fn from_rgb(r: u16, g: u16, b: u16) -> Self { - Self(r & 0b11111 | (g & 0b11111) << 5 | (b & 0b11111) << 10) - } -} - -/// The video mode controls how each background layer will operate. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u16)] -pub enum VideoMode { - /// All four background layers use text mode. - #[default] - _0 = 0, - /// BG0 and BG1 are text mode, while BG2 is affine. BG3 is unavailable. - _1 = 1, - /// BG2 and BG3 are affine. BG0 and BG1 are unavailable. - _2 = 2, - /// BG2 is a single full color bitmap. - _3 = 3, - /// BG2 holds two 8bpp indexmaps, and you can flip between. - _4 = 4, - /// BG2 holds two full color bitmaps of reduced size (only 160x128), and you - /// can flip between. - _5 = 5, -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct DisplayControl(u16); -impl DisplayControl { - pub_const_fn_new_zeroed!(); - u16_enum_field!(0 - 2: VideoMode, video_mode, with_video_mode); - u16_bool_field!(4, show_frame1, with_show_frame1); - u16_bool_field!(5, hblank_oam_free, with_hblank_oam_free); - u16_bool_field!(6, obj_vram_1d, with_obj_vram_1d); - u16_bool_field!(7, forced_blank, with_forced_blank); - u16_bool_field!(8, show_bg0, with_show_bg0); - u16_bool_field!(9, show_bg1, with_show_bg1); - u16_bool_field!(10, show_bg2, with_show_bg2); - u16_bool_field!(11, show_bg3, with_show_bg3); - u16_bool_field!(12, show_obj, with_show_obj); - u16_bool_field!(13, enable_win0, with_enable_win0); - u16_bool_field!(14, enable_win1, with_enable_win1); - u16_bool_field!(15, enable_obj_win, with_enable_obj_win); -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct DisplayStatus(u16); -impl DisplayStatus { - pub_const_fn_new_zeroed!(); - u16_bool_field!(0, currently_vblank, with_currently_vblank); - u16_bool_field!(1, currently_hblank, with_currently_hblank); - u16_bool_field!(2, currently_vcount, with_currently_vcount); - u16_bool_field!(3, irq_vblank, with_irq_vblank); - u16_bool_field!(4, irq_hblank, with_irq_hblank); - u16_bool_field!(5, irq_vcount, with_irq_vcount); - u16_int_field!(8 - 15, vcount_setting, with_vcount_setting); -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct BackgroundControl(u16); -impl BackgroundControl { - pub_const_fn_new_zeroed!(); - u16_int_field!(0 - 1, priority, with_priority); - u16_int_field!(2 - 3, charblock, with_charblock); - u16_bool_field!(6, mosaic, with_mosaic); - u16_bool_field!(7, bpp8, with_bpp8); - u16_int_field!(8 - 12, screenblock, with_screenblock); - u16_bool_field!(13, is_affine_wrapping, with_is_affine_wrapping); - u16_int_field!(14 - 15, size, with_size); -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct WindowInside(u16); -impl WindowInside { - pub_const_fn_new_zeroed!(); - u16_bool_field!(0, win0_bg0, with_win0_bg0); - u16_bool_field!(1, win0_bg1, with_win0_bg1); - u16_bool_field!(2, win0_bg2, with_win0_bg2); - u16_bool_field!(3, win0_bg3, with_win0_bg3); - u16_bool_field!(4, win0_obj, with_win0_obj); - u16_bool_field!(5, win0_effect, with_win0_effect); - u16_bool_field!(8, win1_bg0, with_win1_bg0); - u16_bool_field!(9, win1_bg1, with_win1_bg1); - u16_bool_field!(10, win1_bg2, with_win1_bg2); - u16_bool_field!(11, win1_bg3, with_win1_bg3); - u16_bool_field!(12, win1_obj, with_win1_obj); - u16_bool_field!(13, win1_effect, with_win1_effect); -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct WindowOutside(u16); -impl WindowOutside { - pub_const_fn_new_zeroed!(); - u16_bool_field!(0, outside_bg0, with_outside_bg0); - u16_bool_field!(1, outside_bg1, with_outside_bg1); - u16_bool_field!(2, outside_bg2, with_outside_bg2); - u16_bool_field!(3, outside_bg3, with_outside_bg3); - u16_bool_field!(4, outside_obj, with_outside_obj); - u16_bool_field!(5, outside_effect, with_outside_effect); - u16_bool_field!(8, obj_win_bg0, with_obj_win_bg0); - u16_bool_field!(9, obj_win_bg1, with_obj_win_bg1); - u16_bool_field!(10, obj_win_bg2, with_obj_win_bg2); - u16_bool_field!(11, obj_win_bg3, with_obj_win_bg3); - u16_bool_field!(12, obj_win_obj, with_obj_win_obj); - u16_bool_field!(13, obj_win_effect, with_obj_win_effect); -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct Mosaic(u16); -impl Mosaic { - pub_const_fn_new_zeroed!(); - u16_int_field!(0 - 3, bg_h_extra, with_bg_h_extra); - u16_int_field!(4 - 7, bg_v_extra, with_bg_v_extra); - u16_int_field!(8 - 11, obj_h_extra, with_obj_h_extra); - u16_int_field!(12 - 15, obj_v_extra, with_obj_v_extra); -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(u16)] -pub enum ColorEffectMode { - #[default] - NoEffect = 0 << 6, - AlphaBlend = 1 << 6, - Brighten = 2 << 6, - Darken = 3 << 6, -} - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct BlendControl(u16); -impl BlendControl { - pub_const_fn_new_zeroed!(); - u16_bool_field!(0, target1_bg0, with_target1_bg0); - u16_bool_field!(1, target1_bg1, with_target1_bg1); - u16_bool_field!(2, target1_bg2, with_target1_bg2); - u16_bool_field!(3, target1_bg3, with_target1_bg3); - u16_bool_field!(4, target1_obj, with_target1_obj); - u16_bool_field!(5, target1_backdrop, with_target1_backdrop); - u16_enum_field!(6 - 7: ColorEffectMode, mode, with_mode); - u16_bool_field!(8, target2_bg0, with_target2_bg0); - u16_bool_field!(9, target2_bg1, with_target2_bg1); - u16_bool_field!(10, target2_bg2, with_target2_bg2); - u16_bool_field!(11, target2_bg3, with_target2_bg3); - u16_bool_field!(12, target2_obj, with_target2_obj); - u16_bool_field!(13, target2_backdrop, with_target2_backdrop); -} - -/// Data for a 4-bit-per-pixel tile. -pub type Tile4 = [u32; 8]; - -/// Data for an 8-bit-per-pixel tile. -pub type Tile8 = [u32; 16]; - -/// An entry within a tile mode tilemap. -/// -/// * `tile` is the index of the tile, offset from the `charblock` that the -/// background is using. This is a 10-bit value, so indexes are in the range -/// `0..=1023`. You *cannot* index past the end of background VRAM into object -/// VRAM (it just won't draw properly), but you *can* index past the end of -/// one charblock into the next charblock. -/// * `hflip` If you want the tile horizontally flipped. -/// * `vflip` If you want the tile vertically flipped. -/// * `palbank` sets the palbank for this tile. If the background is in 8bpp -/// mode this has no effect. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct TextEntry(u16); -impl TextEntry { - pub_const_fn_new_zeroed!(); - u16_int_field!(0 - 9, tile, with_tile); - u16_bool_field!(10, hflip, with_hflip); - u16_bool_field!(11, vflip, with_vflip); - u16_int_field!(12 - 15, palbank, with_palbank); - - /// Shorthand for `TextEntry::new().with_tile(id)` - #[inline] - #[must_use] - pub const fn from_tile(id: u16) -> Self { - Self(id & 0b11_1111_1111) - } -} From 66340b4947c549be9ff756232e66e714c070f26a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:45:38 -0600 Subject: [PATCH 43/89] mgba logging --- src/lib.rs | 1 + src/mgba.rs | 70 +++++++++++++++++++++++++++++++++++++---------------- src/mmio.rs | 22 +++++++++++++++++ 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6c173651..d6ac8969 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub mod dma; pub mod gba_cell; pub mod gba_fixed; pub mod mem; +pub mod mgba; pub mod mmio; pub mod panic_handlers; pub mod per_project_setup; diff --git a/src/mgba.rs b/src/mgba.rs index bec167b9..2c0387f3 100644 --- a/src/mgba.rs +++ b/src/mgba.rs @@ -1,10 +1,8 @@ //! Lets you interact with the mGBA debug output buffer. //! //! This buffer works as a "standard output" sort of interface: -//! * First `use core::fmt::Write;` so that the [`Write`](core::fmt::Write) -//! trait is in scope. //! * Try to make a logger with `MgbaBufferedLogger::try_new(log_level)`. -//! * Use the `write!` macro to write data into the logger. +//! * Use the `writeln!` macro to write data into the logger. //! * The logger will automatically flush itself (using the log level you set) //! when the buffer is full, on a newline, and when it's dropped. //! @@ -14,15 +12,6 @@ //! isn't available. You can also call [`mgba_logging_available`] directly to //! check if mGBA logging is possible. //! -//! ```no_run -//! # use gba::prelude::*; -//! use core::fmt::Write; -//! let log_level = MgbaMessageLevel::Debug; -//! if let Ok(logger) = MgbaBufferedLogger::try_new(log_level) { -//! writeln!(logger, "hello").ok(); -//! } -//! ``` -//! //! ## Fine Details //! Even when the program is running within mGBA, the [`MGBA_LOG_ENABLE`] //! address needs to be written with the [`MGBA_LOGGING_ENABLE_REQUEST`] value @@ -43,13 +32,19 @@ use crate::mmio::{MGBA_LOG_BUFFER, MGBA_LOG_ENABLE, MGBA_LOG_SEND}; +/// This is what you write to [`MGBA_LOG_ENABLE`] to signal to the emulator that +/// you want to do logging. pub const MGBA_LOGGING_ENABLE_REQUEST: u16 = 0xC0DE; +/// If [`MGBA_LOG_ENABLE`] says this value when you read it, then mGBA logging +/// is available. pub const MGBA_LOGGING_ENABLE_RESPONSE: u16 = 0x1DEA; -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +/// The logging level of a message sent out to the mGBA logging interface. +#[derive(Debug, Default, Clone, Copy)] #[repr(u16)] -pub enum MgbaMessageLevel { +#[allow(missing_docs)] +pub enum MgbaLogLevel { /// Warning! This causes mGBA to halt emulation! Fatal = 0x100, Error = 0x101, @@ -62,29 +57,62 @@ pub enum MgbaMessageLevel { /// Returns if mGBA logging is possible. #[inline] pub fn mgba_logging_available() -> bool { - // the `__start` function writes the request, so here we just check success. + // the `_start` function writes the request, so here we just check success. MGBA_LOG_ENABLE.read() == MGBA_LOGGING_ENABLE_RESPONSE } -pub struct MgbaBufferedLogger { +/// A logger for sending out messages to the mGBA debug log interface. +/// +/// This logger has all the methods for [`writeln!`] to work without you needing +/// to import the [`core::fmt::Write`] trait manually. +pub struct MgbaLogger { byte_count: u8, - pub message_level: MgbaMessageLevel, + /// The current message level of the logger. + /// + /// All messages sent out by the logger will use this logging level. + pub message_level: MgbaLogLevel, } -impl MgbaBufferedLogger { +impl MgbaLogger { + /// Tries to make a new logger. + /// + /// There's only actually one logger on the system, but this doesn't do + /// anything to ensure exclusive access. #[inline] - pub fn try_new(message_level: MgbaMessageLevel) -> Result { + pub fn try_new(message_level: MgbaLogLevel) -> Result { if mgba_logging_available() { Ok(Self { byte_count: 0, message_level }) } else { Err(()) } } + + /// As [`core::fmt::Write::write_str`] + #[inline] + pub fn write_str(&mut self, s: &str) -> core::fmt::Result { + ::write_str(self, s) + } + + /// As [`core::fmt::Write::write_char`] + #[inline] + pub fn write_char(&mut self, c: char) -> core::fmt::Result { + ::write_char(self, c) + } + + /// As [`core::fmt::Write::write_fmt`] + #[inline] + pub fn write_fmt( + &mut self, args: core::fmt::Arguments<'_>, + ) -> core::fmt::Result { + ::write_fmt(self, args) + } + + #[inline] fn flush(&mut self) { MGBA_LOG_SEND.write(self.message_level); self.byte_count = 0; } } -impl Drop for MgbaBufferedLogger { +impl Drop for MgbaLogger { #[inline] fn drop(&mut self) { if self.byte_count != 0 { @@ -92,7 +120,7 @@ impl Drop for MgbaBufferedLogger { } } } -impl core::fmt::Write for MgbaBufferedLogger { +impl core::fmt::Write for MgbaLogger { #[inline] fn write_str(&mut self, s: &str) -> core::fmt::Result { for b in s.as_bytes().iter().copied() { diff --git a/src/mmio.rs b/src/mmio.rs index 24e23175..e26a39d6 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -9,6 +9,7 @@ use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided}; use crate::{ dma::DmaControl, + mgba::MgbaLogLevel, video::{Color, DisplayControl, DisplayStatus, Tile4bpp}, IrqBits, KeyInput, }; @@ -24,6 +25,8 @@ type SOGBA = voladdress::Unsafe; type PlainAddr = VolAddress; /// Read-only addr type RoAddr = VolAddress; +/// Write-only addr +type WoAddr = VolAddress; /// Display Control setting. /// @@ -166,6 +169,25 @@ pub const IF: PlainAddr = unsafe { VolAddress::new(0x0400_0202) }; /// interrupts actually being enabled/disabled. In practice, it doesn't matter. pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; +/// The buffer to put logging messages into. +/// +/// The first `\0` in the buffer is the end of each message. +pub const MGBA_LOG_BUFFER: VolBlock = + unsafe { VolBlock::new(0x04FF_F600) }; + +/// Write to this each time you want to send out the current buffer content. +/// +/// It also resets the buffer content. +pub const MGBA_LOG_SEND: WoAddr = + unsafe { VolAddress::new(0x04FFF700) }; + +/// Allows you to enable/disable mGBA logging. +/// +/// This is enabled by default by the assembly runtime, so you don't normally +/// need to touch this. +pub const MGBA_LOG_ENABLE: PlainAddr = + unsafe { VolAddress::new(0x04FF_F780) }; + /// The backdrop color is the color shown when no *other* element is displayed /// in a given pixel. pub const BACKDROP_COLOR: PlainAddr = From 750ab268f416d74f7091e908774f0ceba44fc27e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:45:51 -0600 Subject: [PATCH 44/89] update the runtime --- src/asm_runtime.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 5cc28103..4ba5bb7c 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -117,9 +117,14 @@ core::arch::global_asm! { "ldr r1, =0x4317", "strh r1, [r0]", + // activate mGBA logging, when available + "ldr r0, =0x04FFF780", + "ldr r1, =0xC0DE", + "strh r1, [r0]", + /* iwram copy */ "ldr r0, =_iwram_word_copy_count", - when!(("r0" != "#0") { + when!(("r0" != "#0")[1] { "ldr r1, =_iwram_position_in_rom", "str r1, [r3]", // src "ldr r1, =_iwram_start", @@ -131,7 +136,7 @@ core::arch::global_asm! { /* ewram copy */ "ldr r4, =_ewram_word_copy_count", - when!(("r4" != "#0") { + when!(("r4" != "#0")[1] { "ldr r1, =_ewram_position_in_rom", "str r1, [r3]", "ldr r1, =_ewram_start", @@ -143,7 +148,7 @@ core::arch::global_asm! { /* bss zero */ "ldr r4, =_bss_word_clear_count", - when!(("r4" != "#0") { + when!(("r4" != "#0")[1] { "ldr r0, =_bss_start", "mov r2, #0", "2:", @@ -200,7 +205,7 @@ core::arch::global_asm! { // Get the user handler fn pointer, call it if non-null. "ldr r12, ={USER_IRQ_HANDLER}", "ldr r12, [r12]", - when!(("r12" != "#0") { + when!(("r12" != "#0")[1] { "mov r0, r1", // we need to save `lr`, and we need to save an even number of registers // to keep the stack aligned to 8 for the C ABI, so we'll also save `r0`, From 2f0481c97228ea9e33379c9b99d792c40a1cb720 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:46:05 -0600 Subject: [PATCH 45/89] a new kind of panic handler. --- src/panic_handlers.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/panic_handlers.rs b/src/panic_handlers.rs index e32a3ce0..10e5ca27 100644 --- a/src/panic_handlers.rs +++ b/src/panic_handlers.rs @@ -1,5 +1,7 @@ //! Various panic handler functions that you might find useful. +use crate::mgba::{MgbaLogLevel, MgbaLogger}; + /// Declares one of the functions in the /// [`panic_handlers`](crate::panic_handlers) module to be the handler for your /// program. @@ -24,3 +26,12 @@ macro_rules! panic_handler { pub fn empty_loop(_: &core::panic::PanicInfo) -> ! { loop {} } + +/// Writes the panic message to the mGBA logger, then does an empty `loop`. +#[inline] +pub fn mgba_log_err(info: &core::panic::PanicInfo) -> ! { + if let Ok(mut logger) = MgbaLogger::try_new(MgbaLogLevel::Error) { + writeln!(logger, "PANIC: {info}").ok(); + } + loop {} +} From 0aefd69c3543f8ec8be6a3cb68dd5ed6aaa443aa Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:46:54 -0600 Subject: [PATCH 46/89] update the demo --- examples/basic_keyinput.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/basic_keyinput.rs b/examples/basic_keyinput.rs index 73f8c21a..6756a18d 100644 --- a/examples/basic_keyinput.rs +++ b/examples/basic_keyinput.rs @@ -1,15 +1,19 @@ #![no_std] #![no_main] +#![allow(unused)] + +use core::ptr::{addr_of, addr_of_mut}; use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, + mgba::{MgbaLogLevel, MgbaLogger}, mmio::{BACKDROP_COLOR, DISPCNT, DISPSTAT, IE, IME, KEYINPUT}, video::{Color, DisplayControl, DisplayStatus}, IrqBits, }; -gba::panic_handler!(empty_loop); +gba::panic_handler!(mgba_log_err); #[no_mangle] pub extern "C" fn main() -> ! { From 306f8f52b3d907c713516cda49d9190994b025f9 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:47:11 -0600 Subject: [PATCH 47/89] update the demo --- examples/paddle_ball.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 7f419d36..8eb6c04a 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -5,7 +5,6 @@ #![no_std] #![no_main] -#![allow(unused)] use core::ptr::addr_of; @@ -63,17 +62,18 @@ impl Ball { } fn update(&mut self, paddle1: &Paddle, paddle2: &Paddle) { + // top/bottom bounce if self.y <= 0 || self.y + BALL_SIZE >= SCREEN_HEIGHT { self.dy = -self.dy; } + // paddle bounce if self.x + BALL_SIZE >= paddle1.x && self.x <= paddle1.x + PADDLE_WIDTH && self.y + BALL_SIZE >= paddle1.y && self.y <= paddle1.y + PADDLE_HEIGHT { self.dx = -self.dx; - self.dy = -self.dy; } if self.x + BALL_SIZE >= paddle2.x @@ -82,9 +82,9 @@ impl Ball { && self.y <= paddle2.y + PADDLE_HEIGHT { self.dx = -self.dx; - self.dy = -self.dy; } + // off the edges if self.x + BALL_SIZE <= 1 + BALL_SIZE { self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2; self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2; @@ -98,6 +98,7 @@ impl Ball { self.dx = -1; self.dy = 1; } + self.x = (self.x as i16 + self.dx) as u16; self.y = (self.y as i16 + self.dy) as u16; } From 88ee89ee5adc005dbd0ad125a687b1d832fa233c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:47:37 -0600 Subject: [PATCH 48/89] add memory set functions. --- src/mem/mod.rs | 4 +- src/mem/set.rs | 173 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 src/mem/set.rs diff --git a/src/mem/mod.rs b/src/mem/mod.rs index ffcc0923..c41d797d 100644 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -1,7 +1,7 @@ -#![allow(missing_docs)] - //! Low-level memory manipulation functions. mod copy; +mod set; pub use copy::*; +pub use set::*; diff --git a/src/mem/set.rs b/src/mem/set.rs new file mode 100644 index 00000000..984cf92b --- /dev/null +++ b/src/mem/set.rs @@ -0,0 +1,173 @@ +/// The core "memory setting" function. +/// +/// * `dest` is the destination pointer. +/// * `byte_count` is the number of bytes to write. +/// * `r2` and `r3` should hold the desired byte, repeated into all four bytes +/// of each `u32` value. +/// +/// ## Safety +/// * `dest` must be aligned to 4 and writable for `byte_count` bytes. +#[inline(never)] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +#[cfg_attr(feature = "on_gba", link_section = ".iwram._bulk_set_util")] +pub unsafe extern "C" fn bulk_memory_set( + mut dest: *mut u32, mut byte_count: usize, r2: u32, r3: u32, +) { + on_gba_or_unimplemented!( + // debug assert alignment, but if we call the method for this is generates a + // stupid huge amount of code. + debug_assert_eq!(dest as usize & 0b11, 0); + + // Note(Lokathor): We need a threshold for how many bytes is the minimum for + // doing the `stm` loop. It requires a big push/pop to get enough usable + // registers, and then setting up those registers as well. That's all + // somewhat costly, so we don't want the threshold too low. The current + // threshold of 32 bytes is essentially an arbitrary one, it's the size of + // one 4bpp tile. + if byte_count >= 32 { + unsafe { + core::arch::asm!( + "push {{r4-r9}}", + "mov r4, r2", + "mov r5, r2", + "mov r6, r2", + "mov r7, r2", + "mov r8, r2", + "mov r9, r2", + "1:", + "subs r1, r1, #32", + "stmge r0!, {{r2-r9}}", + "bgt 1b", + "pop {{r4-r9}}", + + inout("r0") dest, + inout("r1") byte_count, + in("r2") r2, + in("r3") r3, + ); + } + } + + unsafe { + core::arch::asm!( + // set 4 words + "tst r1, #0b10000", + "stmne r0!, {{r2, r3}}", + "stmne r0!, {{r2, r3}}", + + // set 2 and/or 1 words + "lsls r12, r1, #29", + "stmcs r0!, {{r2, r3}}", + "strmi r2, [r0], #4", + + // set halfword and/or byte + "lsls r12, r1, #31", + "strhcs r2, [r0], #2", + "strbmi r2, [r0], #1", + + inout("r0") dest => _, + inout("r1") byte_count => _, + in("r2") r2, + in("r3") r3, + options(nostack) + ) + }; + ); +} + +/// AEABI-styled memory set. +/// +/// ## Safety +/// * `dest` must be aligned to 4 and writable for `byte_count` bytes. +#[inline] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memset4( + dest: *mut u32, byte_count: usize, byte: i32, +) { + let byte8 = byte as u8; + let byte16 = byte8 as u16 | (byte8 as u16) << 8; + let byte32 = byte16 as u32 | (byte16 as u32) << 16; + + debug_assert_eq!(dest as usize & 0b11, 0); + unsafe { bulk_memory_set(dest.cast(), byte_count, byte32, byte32) }; +} + +/// AEABI-styled memory set. +/// +/// ## Safety +/// * `dest` must be aligned to 8 and writable for `byte_count` bytes. +#[inline] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memset8( + dest: *mut u32, byte_count: usize, byte: i32, +) { + debug_assert_eq!(dest as usize & 0b111, 0); + unsafe { __aeabi_memset4(dest.cast(), byte_count, byte) } +} + +/// AEABI-styled memory set. +/// +/// ## Safety +/// * `dest` must be writable for `byte_count` bytes. +#[inline] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn __aeabi_memset( + mut dest: *mut u8, mut byte_count: usize, byte: i32, +) { + if byte_count == 0 { + return; + } + let byte8 = byte as u8; + let byte16 = byte8 as u16 | (byte8 as u16) << 8; + let byte32 = byte16 as u32 | (byte16 as u32) << 16; + + // We only get fancy if the requested span is sufficiently large. + if byte_count >= 8 { + if (dest as usize) & 0b1 != 0 { + debug_assert!(byte_count >= 1); + unsafe { dest.write_volatile(byte8) }; + dest = unsafe { dest.add(1) }; + byte_count -= 1; + if byte_count == 0 { + return; + } + } + let mut dest: *mut u16 = dest.cast(); + debug_assert_eq!(dest as usize & 0b1, 0); + + if (dest as usize) & 0b10 != 0 { + debug_assert!(byte_count >= 2); + unsafe { dest.write_volatile(byte16) }; + dest = unsafe { dest.add(1) }; + byte_count -= 2; + if byte_count == 0 { + return; + } + } + let dest: *mut u32 = dest.cast(); + debug_assert_eq!(dest as usize & 0b11, 0); + + unsafe { bulk_memory_set(dest.cast(), byte_count, byte32, byte32) }; + } else { + for _ in 0..byte_count { + unsafe { dest.write_volatile(byte8) }; + dest = unsafe { dest.add(1) }; + } + } +} + +/// `libc`-style memory set. +/// +/// Don't ever call this function. Literally you don't ever want to directly +/// call this function. It's **always** slightly more costly than just calling +/// [`__aeabi_memset`] directly. This function is provided only because the +/// compiler will occasionally directly insert calls to `memset`, and so this is +/// needed for compatibility. +#[inline] +#[cfg_attr(feature = "no_mangle_memset", no_mangle)] +pub unsafe extern "C" fn memset( + dest: *mut u8, byte: i32, byte_count: usize, +) -> *mut u8 { + unsafe { __aeabi_memset(dest, byte_count, byte) }; + dest +} From 9a37ec026c9ceb09b825fda1fb0c85760bb5c41b Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:48:13 -0600 Subject: [PATCH 49/89] minor rename. --- src/mem/set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mem/set.rs b/src/mem/set.rs index 984cf92b..cfef4811 100644 --- a/src/mem/set.rs +++ b/src/mem/set.rs @@ -9,7 +9,7 @@ /// * `dest` must be aligned to 4 and writable for `byte_count` bytes. #[inline(never)] #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] -#[cfg_attr(feature = "on_gba", link_section = ".iwram._bulk_set_util")] +#[cfg_attr(feature = "on_gba", link_section = ".iwram.bulk_memory_set")] pub unsafe extern "C" fn bulk_memory_set( mut dest: *mut u32, mut byte_count: usize, r2: u32, r3: u32, ) { From 926933923cd4cfa381a41fbfa3fafe0be60f517c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 01:56:26 -0600 Subject: [PATCH 50/89] use on_gba_or_unimplemented where necessary --- src/mgba.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/mgba.rs b/src/mgba.rs index 2c0387f3..3aef358a 100644 --- a/src/mgba.rs +++ b/src/mgba.rs @@ -57,8 +57,10 @@ pub enum MgbaLogLevel { /// Returns if mGBA logging is possible. #[inline] pub fn mgba_logging_available() -> bool { - // the `_start` function writes the request, so here we just check success. - MGBA_LOG_ENABLE.read() == MGBA_LOGGING_ENABLE_RESPONSE + on_gba_or_unimplemented!( + // the `_start` function writes the request, so here we just check success. + return MGBA_LOG_ENABLE.read() == MGBA_LOGGING_ENABLE_RESPONSE; + ); } /// A logger for sending out messages to the mGBA debug log interface. @@ -108,8 +110,10 @@ impl MgbaLogger { #[inline] fn flush(&mut self) { - MGBA_LOG_SEND.write(self.message_level); - self.byte_count = 0; + on_gba_or_unimplemented!( + MGBA_LOG_SEND.write(self.message_level); + self.byte_count = 0; + ); } } impl Drop for MgbaLogger { @@ -123,18 +127,20 @@ impl Drop for MgbaLogger { impl core::fmt::Write for MgbaLogger { #[inline] fn write_str(&mut self, s: &str) -> core::fmt::Result { - for b in s.as_bytes().iter().copied() { - if b == b'\n' { - self.flush(); - } else { - MGBA_LOG_BUFFER.index(self.byte_count as usize).write(b); - if self.byte_count == u8::MAX { + on_gba_or_unimplemented!( + for b in s.as_bytes().iter().copied() { + if b == b'\n' { self.flush(); } else { - self.byte_count += 1; + MGBA_LOG_BUFFER.index(self.byte_count as usize).write(b); + if self.byte_count == u8::MAX { + self.flush(); + } else { + self.byte_count += 1; + } } } - } - Ok(()) + return Ok(()); + ); } } From 7fe46b6449087e01a34819143739572ccacf0c7e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 04:26:26 -0600 Subject: [PATCH 51/89] move backup files to not be burried. --- backup/{src => }/asm_runtime.rs | 0 backup/{src => }/fixed.rs | 0 backup/{src => }/mem_fns.rs | 0 backup/{src => }/random.rs | 0 backup/{src => }/sound.rs | 0 backup/{src => }/timers.rs | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename backup/{src => }/asm_runtime.rs (100%) rename backup/{src => }/fixed.rs (100%) rename backup/{src => }/mem_fns.rs (100%) rename backup/{src => }/random.rs (100%) rename backup/{src => }/sound.rs (100%) rename backup/{src => }/timers.rs (100%) diff --git a/backup/src/asm_runtime.rs b/backup/asm_runtime.rs similarity index 100% rename from backup/src/asm_runtime.rs rename to backup/asm_runtime.rs diff --git a/backup/src/fixed.rs b/backup/fixed.rs similarity index 100% rename from backup/src/fixed.rs rename to backup/fixed.rs diff --git a/backup/src/mem_fns.rs b/backup/mem_fns.rs similarity index 100% rename from backup/src/mem_fns.rs rename to backup/mem_fns.rs diff --git a/backup/src/random.rs b/backup/random.rs similarity index 100% rename from backup/src/random.rs rename to backup/random.rs diff --git a/backup/src/sound.rs b/backup/sound.rs similarity index 100% rename from backup/src/sound.rs rename to backup/sound.rs diff --git a/backup/src/timers.rs b/backup/timers.rs similarity index 100% rename from backup/src/timers.rs rename to backup/timers.rs From 869562b3b02af7afd6232e1e5adc4ba5922b3f84 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 04:29:21 -0600 Subject: [PATCH 52/89] a working objects demo. --- backup/examples/game.rs | 209 ------------------ .../objects.rs | 54 ++--- src/lib.rs | 1 + src/mmio.rs | 98 +++++++- {backup/src => src}/obj.rs | 138 +++++++++--- src/sample_art.rs | 126 +++++++++++ src/video.rs | 86 +++++++ 7 files changed, 450 insertions(+), 262 deletions(-) delete mode 100644 backup/examples/game.rs rename backup/examples/game_vblank_draw.rs => examples/objects.rs (84%) rename {backup/src => src}/obj.rs (53%) diff --git a/backup/examples/game.rs b/backup/examples/game.rs deleted file mode 100644 index 9e51d6aa..00000000 --- a/backup/examples/game.rs +++ /dev/null @@ -1,209 +0,0 @@ -#![no_std] -#![no_main] - -use gba::prelude::*; - -#[panic_handler] -fn panic_handler(info: &core::panic::PanicInfo) -> ! { - #[cfg(debug_assertions)] - if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) { - use core::fmt::Write; - writeln!(logger, "{info}").ok(); - } - loop {} -} - -#[derive(Debug, Clone, Copy, Default)] -struct Position { - x: u16, - y: u16, -} -#[derive(Debug, Clone, Copy, Default)] -struct Rect { - x: u16, - y: u16, - w: u16, - h: u16, -} -impl Rect { - fn intersect(self, other: Self) -> bool { - self.x < other.x + other.w - && self.x + self.w > other.x - && self.y < other.y + other.h - && self.h + self.y > other.y - } - - fn iter_tiles(self) -> impl Iterator { - let y_range_incl = (self.y / 8)..=((self.y + self.h - 1) / 8); - let x_range_incl = (self.x / 8)..=((self.x + self.w - 1) / 8); - y_range_incl - .map(move |y_index| { - x_range_incl.clone().map(move |x_index| (x_index, y_index)) - }) - .flatten() - } -} - -#[no_mangle] -extern "C" fn main() -> ! { - // game simulation setup - let mut creatures = [Position::default(); 5]; - creatures[0].x = 11; - creatures[0].y = 14; - // - creatures[1].x = 44; - creatures[1].y = 38; - creatures[2].x = 100; - creatures[2].y = 23; - creatures[3].x = 14; - creatures[3].y = 101; - creatures[4].x = 72; - creatures[4].y = 59; - - // indexing with `[y][x]` - let mut world = [[0_u8; 32]; 32]; - for i in 0..32 { - world[0][i] = Cga8x8Thick::BOX_HORIZONTAL; - world[19][i] = Cga8x8Thick::BOX_HORIZONTAL; - world[i][0] = Cga8x8Thick::BOX_VERTICAL; - world[i][29] = Cga8x8Thick::BOX_VERTICAL; - } - world[0][0] = Cga8x8Thick::BOX_UPPER_LEFT; - world[0][29] = Cga8x8Thick::BOX_UPPER_RIGHT; - world[19][0] = Cga8x8Thick::BOX_LOWER_LEFT; - world[19][29] = Cga8x8Thick::BOX_LOWER_RIGHT; - world[1][3] = b'B'; - world[2][3] = b'G'; - world[3][3] = b'0'; - - // interrupt configuration - DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true)); - IE.write(IrqBits::VBLANK); - IME.write(true); - - // bg - BG_PALETTE.index(1).write(Color::MAGENTA); - // obj - let colors = - [Color::CYAN, Color::GREEN, Color::RED, Color::BLUE, Color::YELLOW]; - for (pal, color) in colors.iter().enumerate() { - obj_palbank(pal).index(1).write(*color); - } - - Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP.as_region(), 0); - Cga8x8Thick.bitunpack_4bpp(OBJ_TILES.as_region(), 0); - - BG0CNT.write(BackgroundControl::new().with_screenblock(8)); - let screenblock = TEXT_SCREENBLOCKS.get_frame(8).unwrap(); - for y in 0..32 { - let row = screenblock.get_row(y).unwrap(); - for (x, addr) in row.iter().enumerate() { - let te = TextEntry::new().with_tile(world[y][x] as u16); - addr.write(te); - } - } - - let no_display = ObjAttr0::new().with_style(ObjDisplayStyle::NotDisplayed); - OBJ_ATTR0.iter().skip(creatures.len()).for_each(|va| va.write(no_display)); - - DISPCNT.write(DisplayControl::new().with_show_obj(true).with_show_bg0(true)); - - let mut l_was_pressed = false; - let mut r_was_pressed = false; - - loop { - // wait for vblank - VBlankIntrWait(); - - // update graphics MMIO - for (i, (creature_pos, attr_addr)) in - creatures.iter().zip(OBJ_ATTR_ALL.iter()).enumerate() - { - let mut obj = ObjAttr::new(); - obj.set_x(creature_pos.x); - obj.set_y(creature_pos.y); - obj.set_tile_id(1); - obj.set_palbank(i as u16); - attr_addr.write(obj); - } - - // handle input - let keys = KEYINPUT.read(); - if keys.l() && !l_was_pressed { - creatures.rotate_left(1); - } - if keys.r() && !r_was_pressed { - creatures.rotate_right(1); - } - l_was_pressed = keys.l(); - r_was_pressed = keys.r(); - - // the way we handle movement here is per-direction. If you're against a - // wall and you press a diagonal then one axis will progress while the other - // will be halted by the wall. This makes the player slide along the wall - // when bumping into walls. - let (player, enemies) = match &mut creatures { - [player, enemies @ ..] => (player, enemies), - }; - if keys.up() { - let new_p = Position { x: player.x, y: player.y - 1 }; - let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - let terrain_clear = new_r - .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); - let enemy_clear = enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }); - if terrain_clear && enemy_clear { - *player = new_p; - } - } - if keys.down() { - let new_p = Position { x: player.x, y: player.y + 1 }; - let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - let terrain_clear = new_r - .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); - let enemy_clear = enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }); - if terrain_clear && enemy_clear { - *player = new_p; - } - } - if keys.left() { - let new_p = Position { x: player.x - 1, y: player.y }; - let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - let terrain_clear = new_r - .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); - let enemy_clear = enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }); - if terrain_clear && enemy_clear { - *player = new_p; - } - } - if keys.right() { - let new_p = Position { x: player.x + 1, y: player.y }; - let new_r = Rect { x: new_p.x, y: new_p.y, w: 8, h: 8 }; - let terrain_clear = new_r - .iter_tiles() - .all(|(tx, ty)| allows_movement(world[ty as usize][tx as usize])); - let enemy_clear = enemies.iter().all(|enemy| { - let enemy_r = Rect { x: enemy.x, y: enemy.y, w: 8, h: 8 }; - !new_r.intersect(enemy_r) - }); - if terrain_clear && enemy_clear { - *player = new_p; - } - } - } -} - -const fn allows_movement(u: u8) -> bool { - u == 0 || u == b' ' || u == u8::MAX -} diff --git a/backup/examples/game_vblank_draw.rs b/examples/objects.rs similarity index 84% rename from backup/examples/game_vblank_draw.rs rename to examples/objects.rs index 443a6894..abb6459c 100644 --- a/backup/examples/game_vblank_draw.rs +++ b/examples/objects.rs @@ -1,17 +1,21 @@ #![no_std] #![no_main] -use gba::prelude::*; - -#[panic_handler] -fn panic_handler(info: &core::panic::PanicInfo) -> ! { - #[cfg(debug_assertions)] - if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) { - use core::fmt::Write; - writeln!(logger, "{info}").ok(); - } - loop {} -} +use gba::{ + asm_runtime::USER_IRQ_HANDLER, + bios::VBlankIntrWait, + gba_cell::GbaCell, + mmio::{ + obj_palbank, BG0CNT, BG_PALRAM, DISPCNT, DISPSTAT, IE, IME, KEYINPUT, + OBJ_ATTR0, OBJ_ATTR_ALL, TEXT_SCREENBLOCKS, VRAM_BG_TILE4, VRAM_OBJ_TILE4, + }, + obj::{ObjAttr, ObjAttr0, ObjDisplayStyle}, + sample_art::{decompress_cga_face_to_vram_4bpp, Cga}, + video::{BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry}, + IrqBits, +}; + +gba::panic_handler!(mgba_log_err); #[derive(Debug, Clone, Copy, Default)] struct Position { @@ -88,27 +92,27 @@ extern "C" fn main() -> ! { // indexing with `[y][x]` let mut world = [[0_u8; 32]; 32]; for i in 0..32 { - world[0][i] = Cga8x8Thick::BOX_HORIZONTAL; - world[19][i] = Cga8x8Thick::BOX_HORIZONTAL; - world[i][0] = Cga8x8Thick::BOX_VERTICAL; - world[i][29] = Cga8x8Thick::BOX_VERTICAL; + world[0][i] = Cga::LEFT_RIGHT; + world[19][i] = Cga::LEFT_RIGHT; + world[i][0] = Cga::UP_DOWN; + world[i][29] = Cga::UP_DOWN; } - world[0][0] = Cga8x8Thick::BOX_UPPER_LEFT; - world[0][29] = Cga8x8Thick::BOX_UPPER_RIGHT; - world[19][0] = Cga8x8Thick::BOX_LOWER_LEFT; - world[19][29] = Cga8x8Thick::BOX_LOWER_RIGHT; + world[0][0] = Cga::DOWN_RIGHT; + world[0][29] = Cga::LEFT_DOWN; + world[19][0] = Cga::UP_RIGHT; + world[19][29] = Cga::UP_LEFT; world[1][3] = b'B'; world[2][3] = b'G'; world[3][3] = b'0'; // interrupt configuration - RUST_IRQ_HANDLER.write(Some(irq_handler)); - DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true)); + USER_IRQ_HANDLER.write(Some(irq_handler)); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); IE.write(IrqBits::VBLANK); IME.write(true); // bg - BG_PALETTE.index(1).write(Color::MAGENTA); + BG_PALRAM.index(1).write(Color::MAGENTA); // obj let colors = [Color::CYAN, Color::GREEN, Color::RED, Color::BLUE, Color::YELLOW]; @@ -116,8 +120,8 @@ extern "C" fn main() -> ! { obj_palbank(pal).index(1).write(*color); } - Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP.as_region(), 0); - Cga8x8Thick.bitunpack_4bpp(OBJ_TILES.as_region(), 0); + decompress_cga_face_to_vram_4bpp(VRAM_BG_TILE4.as_region()); + decompress_cga_face_to_vram_4bpp(VRAM_OBJ_TILE4.as_region()); BG0CNT.write(BackgroundControl::new().with_screenblock(8)); let screenblock = TEXT_SCREENBLOCKS.get_frame(8).unwrap(); @@ -132,7 +136,7 @@ extern "C" fn main() -> ! { let no_display = ObjAttr0::new().with_style(ObjDisplayStyle::NotDisplayed); OBJ_ATTR0.iter().skip(creatures.len()).for_each(|va| va.write(no_display)); - DISPCNT.write(DisplayControl::new().with_show_obj(true).with_show_bg0(true)); + DISPCNT.write(DisplayControl::new().with_objects(true).with_bg0(true)); let mut l_was_pressed = false; let mut r_was_pressed = false; diff --git a/src/lib.rs b/src/lib.rs index d6ac8969..18370350 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,7 @@ pub mod gba_fixed; pub mod mem; pub mod mgba; pub mod mmio; +pub mod obj; pub mod panic_handlers; pub mod per_project_setup; pub mod per_system_setup; diff --git a/src/mmio.rs b/src/mmio.rs index e26a39d6..98953ec4 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -5,12 +5,16 @@ use core::ffi::c_void; use bitfrob::u8x2; #[allow(unused_imports)] use voladdress::VolAddress; -use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided}; +use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided, VolSeries}; use crate::{ dma::DmaControl, mgba::MgbaLogLevel, - video::{Color, DisplayControl, DisplayStatus, Tile4bpp}, + obj::{ObjAttr, ObjAttr0, ObjAttr1, ObjAttr2}, + video::{ + BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry, + Tile4bpp, + }, IrqBits, KeyInput, }; @@ -49,6 +53,22 @@ pub const DISPSTAT: PlainAddr = /// Values of 160 to 227 indicate that a vertical blank line is happening. pub const VCOUNT: RoAddr = unsafe { VolAddress::new(0x0400_0006) }; +/// Background 0 controls +pub const BG0CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_0008) }; + +/// Background 1 controls +pub const BG1CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_000A) }; + +/// Background 2 controls +pub const BG2CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_000C) }; + +/// Background 3 controls +pub const BG3CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_000E) }; + /// Source address for DMA3. /// /// The correct pointer type depends on the transfer mode used. @@ -199,7 +219,24 @@ pub const BG_PALRAM: VolBlock = /// Palette data for the objects. pub const OBJ_PALRAM: VolBlock = - unsafe { VolBlock::new(0x0500_0000) }; + unsafe { VolBlock::new(0x0500_0200) }; + +/// Gets the block for a specific palbank. +/// +/// ## Panics +/// * If the `bank` requested is 16 or greater this will panic. +#[inline] +#[must_use] +#[cfg_attr(feature = "track_caller", track_caller)] +pub const fn obj_palbank(bank: usize) -> VolBlock { + let u = OBJ_PALRAM.index(bank * 16).as_usize(); + unsafe { VolBlock::new(u) } +} + +/// The VRAM byte offset per screenblock index. +/// +/// This is the same for all background types and sizes. +pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; /// The VRAM's background tile view, using 4bpp tiles. pub const VRAM_BG_TILE4: VolBlock = @@ -209,6 +246,25 @@ pub const VRAM_BG_TILE4: VolBlock = pub const VRAM_BG_TILE8: VolBlock = unsafe { VolBlock::new(0x0600_0000) }; +/// The text mode screenblocks. +pub const TEXT_SCREENBLOCKS: VolGrid2dStrided< + TextEntry, + SOGBA, + SOGBA, + 32, + 32, + 32, + SCREENBLOCK_INDEX_OFFSET, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The VRAM's object tile view, using 4bpp tiles. +pub const VRAM_OBJ_TILE4: VolBlock = + unsafe { VolBlock::new(0x0601_0000) }; + +/// The VRAM's object tile view, using 8bpp tiles. +pub const VRAM_OBJ_TILE8: VolBlock = + unsafe { VolBlock::new(0x0601_0000) }; + /// The VRAM's view in Video Mode 3. /// /// Each location is a direct color value. @@ -243,3 +299,39 @@ pub const MODE5_VRAM: VolGrid2dStrided< 2, 0xA000, > = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The combined object attributes. +pub const OBJ_ATTR_ALL: VolSeries< + ObjAttr, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000) }; + +/// The object 0th attributes. +pub const OBJ_ATTR0: VolSeries< + ObjAttr0, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000) }; + +/// The object 1st attributes. +pub const OBJ_ATTR1: VolSeries< + ObjAttr1, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000 + 2) }; + +/// The object 2nd attributes. +pub const OBJ_ATTR2: VolSeries< + ObjAttr2, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000 + 4) }; diff --git a/backup/src/obj.rs b/src/obj.rs similarity index 53% rename from backup/src/obj.rs rename to src/obj.rs index 4f97982c..f5fadd7b 100644 --- a/backup/src/obj.rs +++ b/src/obj.rs @@ -31,13 +31,13 @@ //! not using don't appear on the screen. Otherwise, you'll end up with //! un-configured objects appearing in the upper left corner of the display. -use super::*; +use bitfrob::{u16_with_bit, u16_with_region, u16_with_value}; /// How the object should be displayed. /// /// Bit 9 of Attr0 changes meaning depending on Bit 8, so this merges the two /// bits into a single property. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, Default)] #[repr(u16)] pub enum ObjDisplayStyle { /// The default, non-affine display @@ -52,7 +52,7 @@ pub enum ObjDisplayStyle { } /// What special effect the object interacts with -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, Default)] #[repr(u16)] pub enum ObjEffectMode { /// The default, no special effect interaction @@ -76,7 +76,7 @@ pub enum ObjEffectMode { /// | 1 | 16x16 | 32x8 | 8x32 | /// | 2 | 32x32 | 32x16 | 16x32 | /// | 3 | 64x64 | 64x32 | 32x64 | -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, Default)] #[repr(u16)] #[allow(missing_docs)] pub enum ObjShape { @@ -87,48 +87,136 @@ pub enum ObjShape { } /// Object Attributes, field 0 of the entry. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, Default)] #[repr(transparent)] pub struct ObjAttr0(u16); impl ObjAttr0 { - pub_const_fn_new_zeroed!(); - u16_int_field!(0 - 7, y, with_y); - u16_enum_field!(8 - 9: ObjDisplayStyle, style, with_style); - u16_enum_field!(10 - 11: ObjEffectMode, mode, with_mode); - u16_bool_field!(12, mosaic, with_mosaic); - u16_bool_field!(13, bpp8, with_bpp8); - u16_enum_field!(14 - 15: ObjShape, shape, with_shape); + /// A new blank attr 0. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Sets the `y` position of this object + #[inline] + pub const fn with_y(self, y: u16) -> Self { + Self(u16_with_value(0, 7, self.0, y as u16)) + } + /// The object's display styling. + #[inline] + pub const fn with_style(self, style: ObjDisplayStyle) -> Self { + Self(u16_with_region(8, 9, self.0, style as u16)) + } + /// The special effect mode of the object, if any. + #[inline] + pub const fn with_effect(self, effect: ObjEffectMode) -> Self { + Self(u16_with_region(10, 11, self.0, effect as u16)) + } + /// If the object should use the mosaic effect. + #[inline] + pub const fn with_mosaic(self, mosaic: bool) -> Self { + Self(u16_with_bit(12, self.0, mosaic)) + } + /// If the object draws using 8-bits-per-pixel. + #[inline] + pub const fn with_bpp8(self, bpp8: bool) -> Self { + Self(u16_with_bit(13, self.0, bpp8)) + } + /// The object's shape + #[inline] + pub const fn with_shape(self, shape: ObjShape) -> Self { + Self(u16_with_region(14, 15, self.0, shape as u16)) + } } /// Object Attributes, field 1 of the entry. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, Default)] #[repr(transparent)] pub struct ObjAttr1(u16); impl ObjAttr1 { - pub_const_fn_new_zeroed!(); - u16_int_field!(0 - 8, x, with_x); - u16_int_field!(9 - 13, affine_index, with_affine_index); - u16_bool_field!(12, hflip, with_hflip); - u16_bool_field!(13, vflip, with_vflip); - u16_int_field!(14 - 15, size, with_size); + /// A new blank attr 1. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Sets the `x` position of this object + #[inline] + pub const fn with_x(self, x: u16) -> Self { + Self(u16_with_value(0, 8, self.0, x as u16)) + } + /// The affine index of the object. + #[inline] + pub const fn with_affine_index(self, index: u16) -> Self { + Self(u16_with_value(9, 13, self.0, index as u16)) + } + /// If the object is horizontally flipped + #[inline] + pub const fn with_hflip(self, hflip: bool) -> Self { + Self(u16_with_bit(12, self.0, hflip)) + } + /// If the object is vertically flipped + #[inline] + pub const fn with_vflip(self, vflip: bool) -> Self { + Self(u16_with_bit(13, self.0, vflip)) + } + /// The object's size + /// + /// The size you set here, combined with the shape of the object, determines + /// the object's actual area. + /// + /// | Size | Square| Horizontal| Vertical| + /// |:-:|:-:|:-:|:-:| + /// | 0 | 8x8 | 16x8 | 8x16 | + /// | 1 | 16x16 | 32x8 | 8x32 | + /// | 2 | 32x32 | 32x16 | 16x32 | + /// | 3 | 64x64 | 64x32 | 32x64 | + #[inline] + pub const fn with_size(self, size: u16) -> Self { + Self(u16_with_value(14, 15, self.0, size as u16)) + } } /// Object Attributes, field 2 of the entry. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, Default)] #[repr(transparent)] pub struct ObjAttr2(u16); impl ObjAttr2 { - pub_const_fn_new_zeroed!(); - u16_int_field!(0 - 9, tile_id, with_tile_id); - u16_int_field!(10 - 11, priority, with_priority); - u16_int_field!(12 - 15, palbank, with_palbank); + /// A new blank attr 2. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// The base tile id of the object. + /// + /// All other tiles in the object are automatically selected using the + /// following tiles, according to if + /// [`with_obj_vram_1d`][crate::video::DisplayControl::with_obj_vram_1d] it + /// set or not. + #[inline] + pub const fn with_tile_id(self, id: u16) -> Self { + Self(u16_with_value(0, 9, self.0, id as u16)) + } + /// Sets the object's priority sorting. + /// + /// Lower priority objects are closer to the viewer, and will appear in front + /// other objects that have *higher* priority, and in front of backgrounds of + /// *equal or higher* priority. If two objects have the same priority, the + /// lower index object is shown. + #[inline] + pub const fn with_priority(self, priority: u16) -> Self { + Self(u16_with_value(10, 11, self.0, priority as u16)) + } + /// Sets the palbank value of this object. + #[inline] + pub const fn with_palbank(self, palbank: u16) -> Self { + Self(u16_with_value(12, 15, self.0, palbank as u16)) + } } /// Object Attributes. /// /// The fields of this struct are all `pub` so that you can simply alter them as /// you wish. Some "setter" methods are also provided as a shorthand. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, Default)] #[repr(C)] pub struct ObjAttr(pub ObjAttr0, pub ObjAttr1, pub ObjAttr2); #[allow(missing_docs)] diff --git a/src/sample_art.rs b/src/sample_art.rs index e56fcb9c..a6015680 100644 --- a/src/sample_art.rs +++ b/src/sample_art.rs @@ -24,6 +24,132 @@ pub fn decompress_cga_face_to_vram_4bpp(dest: VolRegion) { } } +/// An empty type that just serves as a namespace for constants. +pub struct Cga; +#[allow(missing_docs)] +impl Cga { + // + pub const BLANK: u8 = 0x00; + pub const EMPTY_FACE: u8 = 0x01; + pub const FULL_FACE: u8 = 0x02; + pub const HEART: u8 = 0x03; + pub const DIAMOND: u8 = 0x04; + pub const CLUB: u8 = 0x05; + pub const SPADE: u8 = 0x06; + pub const CENTER_DOT: u8 = 0x07; + pub const INVERT_CENTER_DOT: u8 = 0x08; + pub const RING: u8 = 0x09; + pub const INVERT_RING: u8 = 0x0A; + pub const MALE: u8 = 0x0B; + pub const FEMALE: u8 = 0x0C; + pub const MUSIC_NOTE: u8 = 0x0D; + pub const DOUBLE_MUSIC_NOTE: u8 = 0x0E; + pub const STAR: u8 = 0x0F; + // + pub const TRIANGLE_RIGHT: u8 = 0x10; + pub const TRIANGLE_LEFT: u8 = 0x11; + pub const ARROW_UP_DOWN: u8 = 0x12; + pub const DOUBLE_EXCLAMATION: u8 = 0x13; + pub const PARAGRAPH: u8 = 0x14; + pub const SUBSECTION: u8 = 0x15; + pub const UNDERLINE: u8 = 0x16; + pub const ARROW_UP_DOWN_UNDERLINED: u8 = 0x17; + pub const ARROW_UP: u8 = 0x18; + pub const ARROW_DOWN: u8 = 0x19; + pub const ARROW_RIGHT: u8 = 0x1A; + pub const ARROW_LEFT: u8 = 0x1B; + pub const CORNER_THING: u8 = 0x1C; + pub const ARROW_LEFT_RIGHT: u8 = 0x1D; + pub const TRIANGLE_UP: u8 = 0x1E; + pub const TRIANGLE_DOWN: u8 = 0x1F; + // + pub const DELETE: u8 = 0x7F; + // + pub const QUARTER_COVER: u8 = 0xB0; + pub const HALF_COVER: u8 = 0xB1; + pub const THREE_QUARTER_COVER: u8 = 0xB2; + pub const UP_DOWN: u8 = 0xB3; + pub const UP_LEFT_DOWN: u8 = 0xB4; + pub const UP_LEFT2_DOWN: u8 = 0xB5; + pub const UP2_LEFT_DOWN2: u8 = 0xB6; + pub const LEFT_DOWN2: u8 = 0xB7; + pub const LEFT2_DOWN: u8 = 0xB8; + pub const UP2_LEFT2_DOWN2: u8 = 0xB9; + pub const UP2_DOWN2: u8 = 0xBA; + pub const LEFT2_DOWN2: u8 = 0xBB; + pub const UP2_LEFT2: u8 = 0xBC; + pub const UP2_LEFT: u8 = 0xBD; + pub const UP_LEFT2: u8 = 0xBE; + pub const LEFT_DOWN: u8 = 0xBF; + // + pub const UP_RIGHT: u8 = 0xC0; + pub const UP_LEFT_RIGHT: u8 = 0xC1; + pub const LEFT_DOWN_RIGHT: u8 = 0xC2; + pub const UP_DOWN_RIGHT: u8 = 0xC3; + pub const LEFT_RIGHT: u8 = 0xC4; + pub const UP_LEFT_DOWN_RIGHT: u8 = 0xC5; + pub const UP_DOWN_RIGHT2: u8 = 0xC6; + pub const UP2_DOWN2_RIGHT: u8 = 0xC7; + pub const UP2_RIGHT2: u8 = 0xC8; + pub const DOWN2_RIGHT2: u8 = 0xC9; + pub const UP2_LEFT2_RIGHT2: u8 = 0xCA; + pub const LEFT2_DOWN2_RIGHT2: u8 = 0xCB; + pub const UP2_DOWN2_RIGHT2: u8 = 0xCC; + pub const LEFT2_RIGHT2: u8 = 0xCD; + pub const UP2_LEFT2_DOWN2_RIGHT2: u8 = 0xCE; + pub const UP_LEFT2_RIGHT2: u8 = 0xCF; + // + pub const UP2_LEFT_RIGHT: u8 = 0xD0; + pub const LEFT2_DOWN_RIGHT2: u8 = 0xD1; + pub const LEFT_DOWN2_RIGHT: u8 = 0xD2; + pub const UP2_RIGHT: u8 = 0xD3; + pub const UP_RIGHT2: u8 = 0xD4; + pub const DOWN_RIGHT2: u8 = 0xD5; + pub const DOWN2_RIGHT: u8 = 0xD6; + pub const UP2_RIGHT_DOWN2_LEFT: u8 = 0xD7; + pub const UP_LEFT2_DOWN_RIGHT2: u8 = 0xD8; + pub const UP_LEFT: u8 = 0xD9; + pub const DOWN_RIGHT: u8 = 0xDA; + pub const SOLID: u8 = 0xDB; + pub const HALF_BOTTOM: u8 = 0xDC; + pub const HALF_LEFT: u8 = 0xDD; + pub const HALF_RIGHT: u8 = 0xDE; + pub const HALF_TOP: u8 = 0xDF; + // + pub const ALPHA: u8 = 0xE0; + pub const BETA: u8 = 0xE1; + pub const GAMMA: u8 = 0xE2; + pub const PI: u8 = 0xE3; + pub const SIGMA: u8 = 0xE4; + pub const SIGMA_LOWERCASE: u8 = 0xE5; + pub const MU_LOWERCASE: u8 = 0xE6; + pub const GAMMA_LOWERCASE: u8 = 0xE7; + pub const PHI: u8 = 0xE8; + pub const THETA: u8 = 0xE9; + pub const OMEGA: u8 = 0xEA; + pub const DELTA_LOWERCASE: u8 = 0xEB; + pub const INFINITY: u8 = 0xEC; + pub const NOT_INFINITY: u8 = 0xED; + pub const EPSILON: u8 = 0xEE; + pub const HOOP: u8 = 0xEF; + // + pub const TRIPLE_LINES: u8 = 0xF0; + pub const PLUS_UNDERLINED: u8 = 0xF1; + pub const GREATER_THAN_UNDERLINED: u8 = 0xF2; + pub const LESS_THAN_UNDERLINED: u8 = 0xF3; + pub const HOOK_HIGH: u8 = 0xF4; + pub const HOOK_LOW: u8 = 0xF5; + pub const DIVISION: u8 = 0xF6; + pub const APPROXIMATE: u8 = 0xF7; + pub const DEGREE: u8 = 0xF8; + pub const CENTER_SQUARE: u8 = 0xF9; + pub const CENTER_HALF_SQUARE: u8 = 0xFA; + pub const SQUARE_ROOT: u8 = 0xFB; + pub const N_EXPONENT: u8 = 0xFC; + pub const SQUARED: u8 = 0xFD; + pub const LARGE_CENTER_SQUARE: u8 = 0xFE; +} + static CGA_8X8_THICK_LZ77: [u32; 421] = [ 0x00200010, 0xF0000030, 0x10059001, 0x42011111, 0x01060001, 0x20101001, 0x111E1107, 0x01100110, 0x1B101A00, 0x73111F10, 0x10010011, 0x00081019, diff --git a/src/video.rs b/src/video.rs index ff11b9b3..fe3f5561 100644 --- a/src/video.rs +++ b/src/video.rs @@ -211,6 +211,92 @@ impl DisplayStatus { } } +/// Background layer control data. +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct BackgroundControl(u16); +impl BackgroundControl { + /// Makes a new, empty value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Sets the background's priority sorting. + /// + /// Lower priority backgrounds are closer to the viewer, and will appear in + /// front other objects that have *higher* priority, and in front of + /// backgrounds of *higher* priority. If two backgrounds have the same + /// priority the lower numbered background is shown. + #[inline] + pub const fn with_priority(self, priority: u8) -> Self { + Self(u16_with_value(0, 1, self.0, priority as u16)) + } + /// The base charblock value for this background. + #[inline] + pub const fn with_charblock(self, charblock: u8) -> Self { + Self(u16_with_value(2, 3, self.0, charblock as u16)) + } + /// If this background uses the mosaic effect. + #[inline] + pub const fn with_mosaic(self, mosaic: bool) -> Self { + Self(u16_with_bit(6, self.0, mosaic)) + } + /// Sets the background to 8-bits-per-pixel + #[inline] + pub const fn with_bpp8(self, bpp8: bool) -> Self { + Self(u16_with_bit(7, self.0, bpp8)) + } + /// Sets the screenblock which lays out these tiles. + #[inline] + pub const fn with_screenblock(self, screenblock: u8) -> Self { + Self(u16_with_value(8, 12, self.0, screenblock as u16)) + } + /// If affine pixels that go out of the background's area + #[inline] + pub const fn with_affine_wrap(self, wrap: bool) -> Self { + Self(u16_with_bit(13, self.0, wrap)) + } + /// The background's size. + #[inline] + pub const fn with_size(self, size: u8) -> Self { + Self(u16_with_value(14, 15, self.0, size as u16)) + } +} + +/// Textual tile mode entry. +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct TextEntry(u16); +impl TextEntry { + /// Makes a new, empty value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// The tile ID + #[inline] + pub const fn with_tile(self, id: u16) -> Self { + Self(u16_with_value(0, 9, self.0, id)) + } + /// If the tile should be horizontally flipped + #[inline] + pub const fn with_hflip(self, hflip: bool) -> Self { + Self(u16_with_bit(10, self.0, hflip)) + } + /// If the tile should be vertically flipped. + #[inline] + pub const fn with_vflip(self, vflip: bool) -> Self { + Self(u16_with_bit(11, self.0, vflip)) + } + /// The palbank for this tile. + /// + /// Only used if the background is set for 4bpp. + #[inline] + pub const fn with_palbank(self, palbank: u16) -> Self { + Self(u16_with_value(12, 15, self.0, palbank)) + } +} + /// Data for a 4-bit-per-pixel tile. /// /// The tile is 8 pixels wide and 8 pixels tall. Each pixel is 4 bits, giving an From e10c6170e49e195beeac27eae4195b6d1abc0db1 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 11:58:57 -0600 Subject: [PATCH 53/89] pull more stuff in from backup. --- backup/asm_runtime.rs | 185 -------------------- backup/fixed.rs | 358 -------------------------------------- src/lib.rs | 1 + {backup => src}/random.rs | 10 +- 4 files changed, 8 insertions(+), 546 deletions(-) delete mode 100644 backup/asm_runtime.rs delete mode 100644 backup/fixed.rs rename {backup => src}/random.rs (93%) diff --git a/backup/asm_runtime.rs b/backup/asm_runtime.rs deleted file mode 100644 index 0de3dcf0..00000000 --- a/backup/asm_runtime.rs +++ /dev/null @@ -1,185 +0,0 @@ -// For now, the division fns can just keep living here. - -/// Returns 0 in `r0`, while placing the `numerator` into `r1`. -/// -/// This is written in that slightly strange way so that `div` function and -/// `divmod` functions can share the same code path. -/// -/// See: [__aeabi_idiv0][aeabi-division-by-zero] -/// -/// [aeabi-division-by-zero]: https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#division-by-zero -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -// this should literally never get called for real, so we leave it in ROM -extern "C" fn __aeabi_idiv0(numerator: i32) -> i32 { - unsafe { - core::arch::asm!( - // this comment stops rustfmt from making this a one-liner - "mov r1, r0", - "mov r0, #0", - "bx lr", - options(noreturn) - ) - } -} - -/// Returns `u32 / u32` -/// -/// This implementation is *not* the fastest possible division, but it is -/// extremely compact. -/// -/// See: [__aeabi_uidiv][aeabi-integer-32-32-division] -/// -/// [aeabi-integer-32-32-division]: -/// https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#integer-32-32-32-division-functions -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.uidiv"] -extern "C" fn __aeabi_uidiv(numerator: u32, denominator: u32) -> u32 { - // Note(Lokathor): Other code in this module relies on being able to call this - // function without affecting r12, so any future implementations of this code - // **must not** destroy r12. - unsafe { - core::arch::asm!( - // Check for divide by 0 - "cmp r1, #0", - "beq {__aeabi_idiv0}", - // r3(shifted_denom) = denom - "mov r3, r1", - // while shifted_denom < (num>>1): shifted_denom =<< 1; - "cmp r3, r0, lsr #1", - "2:", - "lslls r3, r3, #1", - "cmp r3, r0, lsr #1", - "bls 2b", - // r0=quot(init 0), r1=denom, r2=num, r3=shifted_denom - "mov r2, r0", - "mov r0, #0", - // subtraction loop - "3:", - "cmp r2, r3", - "subcs r2, r2, r3", - "adc r0, r0, r0", - "mov r3, r3, lsr #1", - "cmp r3, r1", - "bcs 3b", - "bx lr", - __aeabi_idiv0 = sym __aeabi_idiv0, - options(noreturn) - ) - } -} - -/// Returns `i32 / i32` -/// -/// See: [__aeabi_idiv][aeabi-integer-32-32-division] -/// -/// [aeabi-integer-32-32-division]: -/// https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#integer-32-32-32-division-functions -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.idiv"] -extern "C" fn __aeabi_idiv(numerator: i32, denominator: i32) -> u32 { - unsafe { - core::arch::asm!( - // determine if `numerator` and `denominator` are the same sign - "eor r12, r1, r0", - // convert both values to their unsigned absolute value. - "cmp r0, #0", - "rsblt r0, r0, #0", - "cmp r1, #0", - "rsclt r1, r1, #0", - bracer::with_pushed_registers!("{{lr}}", { - // divide them using `u32` division (this will check for divide by 0) - "bl {__aeabi_uidiv}", - }), - // if they started as different signs, flip the output's sign. - "cmp r12, #0", - "rsblt r0, r0, #0", - "bx lr", - __aeabi_uidiv = sym __aeabi_uidiv, - options(noreturn) - ) - } -} - -/// Returns `(u32 / u32, u32 % u32)` in `(r0, r1)`. -/// -/// The `u64` return value is a mild lie that gets Rust to grab up both the `r0` -/// and `r1` values when the function returns. If you transmute the return value -/// into `[u32; 2]` then you can separate the two parts of the return value, and -/// it will have no runtime cost. -/// -/// See: [__aeabi_uidivmod][aeabi-integer-32-32-division] -/// -/// [aeabi-integer-32-32-division]: -/// https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#integer-32-32-32-division-functions -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.uidivmod"] -extern "C" fn __aeabi_uidivmod(numerator: u32, denominator: u32) -> u64 { - unsafe { - core::arch::asm!( - // We need to save *both* input args until after the uidiv call. One of - // them can be saved in `r12` because we know our uidiv doesn't actually - // touch `r12`, while the other will be pushed onto the stack along with - // `lr`. Since the function's output will be in `r0`, we push/pop `r1`. - "mov r12, r0", - bracer::with_pushed_registers!("{{r1, lr}}", { - "bl {__aeabi_uidiv}", - }), - // Now r0 holds the `quot`, and we use it along with the input args to - // calculate the `rem`. - "mul r2, r0, r1", - "sub r1, r12, r2", - "bx lr", - __aeabi_uidiv = sym __aeabi_uidiv, - options(noreturn) - ) - } -} - -/// Returns `(i32 / i32, i32 % i32)` in `(r0, r1)`. -/// -/// The `u64` return value is a mild lie that gets Rust to grab up both the `r0` -/// and `r1` values when the function returns. If you transmute the return value -/// into `[i32; 2]` then you can separate the two parts of the return value, and -/// it will have no runtime cost. -/// -/// See: [__aeabi_idivmod][aeabi-integer-32-32-division] -/// -/// [aeabi-integer-32-32-division]: -/// https://github.com/ARM-software/abi-aa/blob/main/rtabi32/rtabi32.rst#integer-32-32-32-division-functions -#[naked] -#[no_mangle] -#[instruction_set(arm::a32)] -#[link_section = ".iwram.aeabi.idivmod"] -extern "C" fn __aeabi_idivmod(numerator: i32, denominator: i32) -> u64 { - unsafe { - core::arch::asm!( - bracer::with_pushed_registers!("{{r4, r5, lr}}", { - // store old numerator then make it the unsigned absolute - "movs r4, r0", - "rsblt r0, r0, #0", - // store old denominator then make it the unsigned absolute - "movs r5, r1", - "rsblt r1, r1, #0", - // divmod using unsigned. - "bl {__aeabi_uidivmod}", - // if signs started opposite, quot becomes negative - "eors r12, r4, r5", - "rsblt r0, r0, #0", - // if numerator started negative, rem is negative - "cmp r4, #0", - "rsblt r1, r1, #0", - }), - "bx lr", - __aeabi_uidivmod = sym __aeabi_uidivmod, - options(noreturn) - ) - } -} diff --git a/backup/fixed.rs b/backup/fixed.rs deleted file mode 100644 index f2aa64a9..00000000 --- a/backup/fixed.rs +++ /dev/null @@ -1,358 +0,0 @@ -use core::ops::*; - -/// `i16` with 8 bits of fixed-point fraction. -/// -/// This is used by the affine matrix entries. -#[allow(non_camel_case_types)] -pub type i16fx8 = Fixed; - -/// `i16` with 14 bits of fixed-point fraction. -/// -/// This is used by the [`ArcTan`](crate::bios::ArcTan) and -/// [`ArcTan2`](crate::bios::ArcTan2) BIOS functions. -#[allow(non_camel_case_types)] -pub type i16fx14 = Fixed; - -/// `i32` with 8 bits of fixed-point fraction. -/// -/// This is used by the background reference point entries. -#[allow(non_camel_case_types)] -pub type i32fx8 = Fixed; - -/// A [fixed-point][wp-fp] number. This transparently wraps an integer with a -/// const generic for how many bits are fractional. -/// -/// [wp-fp]: https://en.wikipedia.org/wiki/Fixed-point_arithmetic -/// -/// * This type is generic, but the `I` type is intended to be a signed or -/// unsigned integer of a fixed bit size: `i8`, `i16`, `i32`, `u8`, `u16`, or -/// `u32`. This type is *not* semver supported to work with any other `I` -/// type. If it does work for other types of `I`, that's on accident. -/// * The `B` value is the number of bits that form the fractional part. It -/// should be *less than* the number of bits in the integer's type. Multiply -/// and divide ops need to shift the value by `B`, and so if `B` is greater -/// than or equal to the integer's size the op will panic. -#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct Fixed(I); - -macro_rules! impl_trait_op_unit { - ($t:ty, $trait:ident, $op:ident) => { - impl $trait for Fixed<$t, B> { - type Output = Self; - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - fn $op(self) -> Self::Output { - Self::$op(self) - } - } - }; -} -macro_rules! impl_trait_op_self_rhs { - ($t:ty, $trait:ident, $op:ident) => { - impl $trait for Fixed<$t, B> { - type Output = Self; - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - fn $op(self, rhs: Self) -> Self::Output { - Self::$op(self, rhs) - } - } - }; -} -macro_rules! impl_trait_op_assign_self_rhs { - ($t:ty, $trait:ident, $op:ident, $op_assign:ident) => { - impl $trait for Fixed<$t, B> { - #[inline] - #[cfg_attr(feature = "track_caller", track_caller)] - fn $op_assign(&mut self, rhs: Self) { - *self = self.$op(rhs); - } - } - }; -} -macro_rules! impl_shift_self_u32 { - ($t:ty, $trait:ident, $op:ident) => { - impl $trait for Fixed<$t, B> { - type Output = Self; - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - fn $op(self, rhs: u32) -> Self::Output { - Self::$op(self, rhs) - } - } - }; -} -macro_rules! impl_shift_assign_self_u32 { - ($t:ty, $trait:ident, $op:ident, $op_assign:ident) => { - impl $trait for Fixed<$t, B> { - #[inline] - #[cfg_attr(feature = "track_caller", track_caller)] - fn $op_assign(&mut self, rhs: u32) { - *self = self.$op(rhs); - } - } - }; -} - -macro_rules! impl_common_fixed_ops { - ($t:ty) => { - impl Fixed<$t, B> { - /// Shifts the value left by `B`, wrapping it into the range of this Fixed - /// type. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn wrapping_from(i: $t) -> Self { - Self(i << B) - } - - /// Makes a `Fixed` directly from a raw inner value (no shift). - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn from_raw(i: $t) -> Self { - Self(i) - } - - /// Unwraps the inner value directly into the base type (no shift). - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn into_raw(self) -> $t { - self.0 - } - - /// Bitwise Not. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn not(self) -> Self { - Self(!self.0) - } - - /// Addition. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn add(self, rhs: Self) -> Self { - Self(self.0 + rhs.0) - } - - /// Subtraction. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn sub(self, rhs: Self) -> Self { - Self(self.0 - rhs.0) - } - - /// Remainder. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn rem(self, rhs: Self) -> Self { - Self(self.0 % rhs.0) - } - - /// Bitwise AND. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn bitand(self, rhs: Self) -> Self { - Self(self.0 & rhs.0) - } - - /// Bitwise OR. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn bitor(self, rhs: Self) -> Self { - Self(self.0 | rhs.0) - } - - /// Bitwise XOR. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn bitxor(self, rhs: Self) -> Self { - Self(self.0 ^ rhs.0) - } - - /// Bit-shift Left. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn shl(self, rhs: u32) -> Self { - Self(self.0 << rhs) - } - - /// Bit-shift Right. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn shr(self, rhs: u32) -> Self { - Self(self.0 >> rhs) - } - } - impl_trait_op_unit!($t, Not, not); - impl_trait_op_self_rhs!($t, Add, add); - impl_trait_op_self_rhs!($t, Sub, sub); - impl_trait_op_self_rhs!($t, Mul, mul); - impl_trait_op_self_rhs!($t, Div, div); - impl_trait_op_self_rhs!($t, Rem, rem); - impl_trait_op_self_rhs!($t, BitAnd, bitand); - impl_trait_op_self_rhs!($t, BitOr, bitor); - impl_trait_op_self_rhs!($t, BitXor, bitxor); - impl_shift_self_u32!($t, Shl, shl); - impl_shift_self_u32!($t, Shr, shr); - impl_trait_op_assign_self_rhs!($t, AddAssign, add, add_assign); - impl_trait_op_assign_self_rhs!($t, SubAssign, sub, sub_assign); - impl_trait_op_assign_self_rhs!($t, MulAssign, mul, mul_assign); - impl_trait_op_assign_self_rhs!($t, DivAssign, div, div_assign); - impl_trait_op_assign_self_rhs!($t, RemAssign, rem, rem_assign); - impl_trait_op_assign_self_rhs!($t, BitAndAssign, bitand, bitand_assign); - impl_trait_op_assign_self_rhs!($t, BitOrAssign, bitor, bitor_assign); - impl_trait_op_assign_self_rhs!($t, BitXorAssign, bitxor, bitxor_assign); - impl_shift_assign_self_u32!($t, ShlAssign, shl, shl_assign); - impl_shift_assign_self_u32!($t, ShrAssign, shr, shr_assign); - }; -} -impl_common_fixed_ops!(i8); -impl_common_fixed_ops!(i16); -impl_common_fixed_ops!(i32); -impl_common_fixed_ops!(u8); -impl_common_fixed_ops!(u16); -impl_common_fixed_ops!(u32); - -macro_rules! impl_signed_fixed_ops { - ($t:ty, $unsigned:ty) => { - impl Fixed<$t, B> { - /// Negate. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn neg(self) -> Self { - Self(-self.0) - } - - /// If the number is negative or not. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn is_negative(self) -> bool { - self.0 < 0 - } - - /// Multiply. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn mul(self, rhs: Self) -> Self { - let raw = (self.0 as i32) * (rhs.0 as i32); - Self((raw >> B) as $t) - } - - /// Divide. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn div(self, rhs: Self) -> Self { - let m = (self.0 as i32) * (1 << B); - let d = m / (rhs.0 as i32); - Self(d as $t) - } - - /// Fractional part of the value. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn fract(self) -> Self { - let frac_mask = (<$unsigned>::MAX >> (<$t>::BITS - B)); - Self((self.0.unsigned_abs() & frac_mask) as $t) - } - - /// Whole part of the value. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn trunc(self) -> Self { - Self(((self.0.unsigned_abs() >> B) << B) as $t) - } - } - impl_trait_op_unit!($t, Neg, neg); - impl core::fmt::Debug for Fixed<$t, B> { - #[inline] - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - let whole: $t = self.trunc().into_raw() >> B; - let fract: $t = self.fract().into_raw(); - let divisor: $t = 1 << B; - if self.is_negative() { - let whole = whole.unsigned_abs(); - write!(f, "-({whole}+{fract}/{divisor})") - } else { - write!(f, "{whole}+{fract}/{divisor}") - } - } - } - }; -} -impl_signed_fixed_ops!(i8, u8); -impl_signed_fixed_ops!(i16, u16); -impl_signed_fixed_ops!(i32, u32); - -macro_rules! impl_unsigned_fixed_ops { - ($t:ty) => { - impl Fixed<$t, B> { - /// Multiply. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn mul(self, rhs: Self) -> Self { - let raw = (self.0 as u32) * (rhs.0 as u32); - Self((raw >> B) as $t) - } - - /// Divide. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn div(self, rhs: Self) -> Self { - let m = (self.0 as u32) * (1 << B); - let d = m / (rhs.0 as u32); - Self(d as $t) - } - - /// Fractional part of the value. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn fract(self) -> Self { - Self(self.0 & (<$t>::MAX >> (<$t>::BITS - B))) - } - - /// Whole part of the value. - #[inline] - #[must_use] - #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn trunc(self) -> Self { - Self(self.0 & (<$t>::MAX << B)) - } - } - impl core::fmt::Debug for Fixed<$t, B> { - #[inline] - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - let whole: $t = self.trunc().into_raw() >> B; - let fract: $t = self.fract().into_raw(); - let divisor: $t = 1 << B; - write!(f, "{whole}+{fract}/{divisor}") - } - } - }; -} -impl_unsigned_fixed_ops!(u8); -impl_unsigned_fixed_ops!(u16); -impl_unsigned_fixed_ops!(u32); diff --git a/src/lib.rs b/src/lib.rs index 18370350..ebf0047b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ pub mod obj; pub mod panic_handlers; pub mod per_project_setup; pub mod per_system_setup; +pub mod random; pub mod sample_art; pub mod video; diff --git a/backup/random.rs b/src/random.rs similarity index 93% rename from backup/random.rs rename to src/random.rs index 83e7be2d..4b01eb8c 100644 --- a/backup/random.rs +++ b/src/random.rs @@ -1,6 +1,10 @@ -// Note(Lokathor): We have a generic LCG type below, but for now we can hide the -// process of having to pick what multiplier and increment to use behind a -// newtype that selects some default constants. +//! Randomization routines that will work well enough on the GBA. +//! +//! Randomization is basically about a trade off between time taken to produce +//! each next output and the quality of that output. Most modern randomization +//! libraries do 64-bit randomization and aim to be extremely unpredictable. +//! That's basically overkill on the GBA. The random generators here are 32-bit, +//! and they are relatively simple while maintaining reasonable output. /// A [Linear Congruential Generator][wp-lcg] with 32-bits of output. /// From fed279b18dc360367b2a0029d2b30037623f590c Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 27 May 2024 13:10:55 -0600 Subject: [PATCH 54/89] we should log the panic errors, if they occur. --- examples/paddle_ball.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 8eb6c04a..16d268a2 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -113,7 +113,7 @@ static SPRITE_POSITIONS: [GbaCell; 6] = [ GbaCell::new(0), ]; -gba::panic_handler!(empty_loop); +gba::panic_handler!(mgba_log_err); #[no_mangle] fn main() -> ! { From 354730c2617ba1c2903286d979c70c89e93fc2f6 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Wed, 29 May 2024 16:57:31 -0600 Subject: [PATCH 55/89] this change causes the crash. --- examples/paddle_ball.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 16d268a2..9a282a89 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -13,6 +13,7 @@ use gba::{ bios::VBlankIntrWait, dma::{DmaControl, DmaSrcAddr}, gba_cell::GbaCell, + mem::bulk_memory_set, mmio::{ DISPCNT, DISPSTAT, DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, OBJ_PALRAM, @@ -150,6 +151,7 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { + /* unsafe { let color = OBJ_PALRAM.index(0).read(); let c: u32 = color.0 as u32 | (color.0 as u32) << 16; @@ -163,6 +165,16 @@ extern "C" fn draw_sprites(_bits: IrqBits) { .with_enabled(true), ); } + // */ + unsafe { + let dest = MODE3_VRAM.as_usize() as *mut u32; + let byte_count = SCREEN_WIDTH as usize + * SCREEN_HEIGHT as usize + * core::mem::size_of::(); + let r2 = 0; + let r3 = 0; + bulk_memory_set(dest, byte_count, r2, r3); + } draw_rect( SPRITE_POSITIONS[0].read(), From 2aa0f2d283058e52db73d4bf65b1918942b452f8 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Wed, 29 May 2024 17:23:17 -0600 Subject: [PATCH 56/89] this bulk set fn is more "obviously" correct, but the game still crashes. --- examples/paddle_ball.rs | 5 +-- src/mem/set.rs | 82 ++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 53 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 9a282a89..68b30f0b 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -1,8 +1,8 @@ +#![allow(unused_imports)] /* * Made by Evan Goemer * Discord: @evangoemer */ - #![no_std] #![no_main] @@ -172,8 +172,7 @@ extern "C" fn draw_sprites(_bits: IrqBits) { * SCREEN_HEIGHT as usize * core::mem::size_of::(); let r2 = 0; - let r3 = 0; - bulk_memory_set(dest, byte_count, r2, r3); + bulk_memory_set(dest, byte_count, r2); } draw_rect( diff --git a/src/mem/set.rs b/src/mem/set.rs index cfef4811..6c6023af 100644 --- a/src/mem/set.rs +++ b/src/mem/set.rs @@ -2,8 +2,7 @@ /// /// * `dest` is the destination pointer. /// * `byte_count` is the number of bytes to write. -/// * `r2` and `r3` should hold the desired byte, repeated into all four bytes -/// of each `u32` value. +/// * `x` holds the desired word to write across all of the bytes. /// /// ## Safety /// * `dest` must be aligned to 4 and writable for `byte_count` bytes. @@ -11,67 +10,50 @@ #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] #[cfg_attr(feature = "on_gba", link_section = ".iwram.bulk_memory_set")] pub unsafe extern "C" fn bulk_memory_set( - mut dest: *mut u32, mut byte_count: usize, r2: u32, r3: u32, + mut dest: *mut u32, byte_count: usize, x: u32, ) { on_gba_or_unimplemented!( // debug assert alignment, but if we call the method for this is generates a // stupid huge amount of code. debug_assert_eq!(dest as usize & 0b11, 0); - // Note(Lokathor): We need a threshold for how many bytes is the minimum for - // doing the `stm` loop. It requires a big push/pop to get enough usable - // registers, and then setting up those registers as well. That's all - // somewhat costly, so we don't want the threshold too low. The current - // threshold of 32 bytes is essentially an arbitrary one, it's the size of - // one 4bpp tile. - if byte_count >= 32 { + let (mut blocks, mut spare) = (byte_count / 32, byte_count % 32); + + while blocks > 0 { unsafe { core::arch::asm!( - "push {{r4-r9}}", - "mov r4, r2", - "mov r5, r2", - "mov r6, r2", - "mov r7, r2", - "mov r8, r2", - "mov r9, r2", - "1:", - "subs r1, r1, #32", - "stmge r0!, {{r2-r9}}", - "bgt 1b", - "pop {{r4-r9}}", + "stm r0!, {{r2,r3,r4,r5,r7,r8,r9,r10}}", inout("r0") dest, - inout("r1") byte_count, - in("r2") r2, - in("r3") r3, + in("r2") x, + in("r3") x, + in("r4") x, + in("r5") x, + in("r7") x, + in("r8") x, + in("r9") x, + in("r10") x, + options(nostack), ); - } + }; + blocks -= 1; } - unsafe { - core::arch::asm!( - // set 4 words - "tst r1, #0b10000", - "stmne r0!, {{r2, r3}}", - "stmne r0!, {{r2, r3}}", + debug_assert!(spare < 32); - // set 2 and/or 1 words - "lsls r12, r1, #29", - "stmcs r0!, {{r2, r3}}", - "strmi r2, [r0], #4", - - // set halfword and/or byte - "lsls r12, r1, #31", - "strhcs r2, [r0], #2", - "strbmi r2, [r0], #1", + while spare > 4 { + unsafe { dest.write_volatile(x) }; + dest = unsafe { dest.add(1) }; + spare -= 4; + } - inout("r0") dest => _, - inout("r1") byte_count => _, - in("r2") r2, - in("r3") r3, - options(nostack) - ) - }; + let mut dest = dest.cast::(); + let x = x as u8; + while spare > 0 { + unsafe { dest.write_volatile(x) }; + dest = unsafe { dest.add(1) }; + spare -= 1; + } ); } @@ -89,7 +71,7 @@ pub unsafe extern "C" fn __aeabi_memset4( let byte32 = byte16 as u32 | (byte16 as u32) << 16; debug_assert_eq!(dest as usize & 0b11, 0); - unsafe { bulk_memory_set(dest.cast(), byte_count, byte32, byte32) }; + unsafe { bulk_memory_set(dest.cast(), byte_count, byte32) }; } /// AEABI-styled memory set. @@ -147,7 +129,7 @@ pub unsafe extern "C" fn __aeabi_memset( let dest: *mut u32 = dest.cast(); debug_assert_eq!(dest as usize & 0b11, 0); - unsafe { bulk_memory_set(dest.cast(), byte_count, byte32, byte32) }; + unsafe { bulk_memory_set(dest.cast(), byte_count, byte32) }; } else { for _ in 0..byte_count { unsafe { dest.write_volatile(byte8) }; From 534a4c53573be66eb598b432163a8a2c14b08f62 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 30 May 2024 09:04:51 -0600 Subject: [PATCH 57/89] This makes the program run again. --- examples/paddle_ball.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 68b30f0b..2a6ff831 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -198,6 +198,7 @@ extern "C" fn draw_sprites(_bits: IrqBits) { ); } +#[inline(never)] fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { for i in 0..width { for j in 0..height { From aad5ddd0258870bfac62921d00833438ec094ce6 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 30 May 2024 19:43:18 -0600 Subject: [PATCH 58/89] docs on setting the CPU --- .cargo/config.toml | 1 + src/per_project_setup.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index ac3738f1..cbaf2061 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -10,4 +10,5 @@ rustflags = [ "-Zub-checks=no", "-Clinker=arm-none-eabi-ld", "-Clink-arg=-Tlinker_scripts/mono_boot.ld", + "-Ctarget-cpu=arm7tdmi", ] diff --git a/src/per_project_setup.rs b/src/per_project_setup.rs index 101f3780..b40ce39a 100644 --- a/src/per_project_setup.rs +++ b/src/per_project_setup.rs @@ -28,6 +28,12 @@ //! `rustflags` argument with `-Clink-arg=-T`, using the script's path //! relative to our project root. //! +//! You can also set `-Ctarget-cpu=arm7tdmi`, because that is the specific CPU +//! of the GBA, though I'm not sure that LLVM really does much with the +//! information. In my very limited testing, I didn't see any different code +//! generated when setting the CPU compared to not doing it, but I do it just in +//! case it helps somehow, because it doesn't hurt. +//! //! [github_script]: //! https://github.com/rust-console/gba/blob/main/linker_scripts/mono_boot.ld //! @@ -44,6 +50,7 @@ //! rustflags = [ //! "-Clinker=arm-none-eabi-ld", //! "-Clink-arg=-Tlinker_scripts/mono_boot.ld", +//! "-Ctarget-cpu=arm7tdmi", //! ] //! ``` //! From 692a8b2f6c830c8d62aab815104d81c147bb6290 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Thu, 30 May 2024 21:51:02 -0600 Subject: [PATCH 59/89] Mode3::clear_to is a very fast and easily-proven-correct clear routine. --- examples/paddle_ball.rs | 41 +++++------------------------ src/video.rs | 58 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 2a6ff831..6e942a51 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -1,24 +1,15 @@ -#![allow(unused_imports)] -/* - * Made by Evan Goemer - * Discord: @evangoemer - */ #![no_std] #![no_main] -use core::ptr::addr_of; +//! Made by Evan Goemer, Discord: @evangoemer use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, - dma::{DmaControl, DmaSrcAddr}, gba_cell::GbaCell, mem::bulk_memory_set, - mmio::{ - DISPCNT, DISPSTAT, DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, - DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, OBJ_PALRAM, - }, - video::{Color, DisplayControl, DisplayStatus}, + mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, + video::{Color, DisplayControl, DisplayStatus, Mode3}, IrqBits, KeyInput, }; @@ -151,29 +142,7 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - /* - unsafe { - let color = OBJ_PALRAM.index(0).read(); - let c: u32 = color.0 as u32 | (color.0 as u32) << 16; - DMA3_SOURCE.write(addr_of!(c).cast()); - DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as _); - DMA3_TRANSFER_COUNT.write(SCREEN_WIDTH * SCREEN_HEIGHT / 2); - DMA3_CONTROL.write( - DmaControl::new() - .with_src_addr(DmaSrcAddr::Fixed) - .with_u32_transfer(true) - .with_enabled(true), - ); - } - // */ - unsafe { - let dest = MODE3_VRAM.as_usize() as *mut u32; - let byte_count = SCREEN_WIDTH as usize - * SCREEN_HEIGHT as usize - * core::mem::size_of::(); - let r2 = 0; - bulk_memory_set(dest, byte_count, r2); - } + Mode3.clear_to(Color::BLACK); draw_rect( SPRITE_POSITIONS[0].read(), @@ -198,6 +167,8 @@ extern "C" fn draw_sprites(_bits: IrqBits) { ); } +// we out-line this because otherwise `draw_sprites` overloads the stack when it +// holds 3 copies of this function. #[inline(never)] fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { for i in 0..width { diff --git a/src/video.rs b/src/video.rs index fe3f5561..fefec616 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,4 +1,4 @@ -//! +//! Module for screen-related types and functions. use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; @@ -326,3 +326,59 @@ pub struct Tile4bpp(pub [u32; 8]); #[derive(Clone, Copy, Default)] #[repr(transparent)] pub struct Tile8bpp(pub [u32; 16]); + +/// A zero-sized type that gives a namespace for Mode 3 related things. +#[derive(Clone, Copy)] +pub struct Mode3; +impl Mode3 { + /// Width, in pixels, of the Mode 3 bitmap. + pub const WIDTH: usize = 240; + + /// Height, in pixels, of the Mode 3 bitmap. + pub const HEIGHT: usize = 160; + + /// The size, in bytes, of the Mode 3 bitmap. + pub const BYTES: usize = + Self::WIDTH * Self::HEIGHT * core::mem::size_of::(); + + /// Clears the entire bitmap to a color of your choosing. + #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] + #[cfg_attr(feature = "on_gba", link_section = ".iwram.mode3.clear_to")] + pub fn clear_to(self, color: Color) { + on_gba_or_unimplemented!(unsafe { + let x: u32 = color.0 as u32 | ((color.0 as u32) << 16); + // now we spam out that `u32`, 10 stm per loop, 8 times per stm. + core::arch::asm!( + "1:", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "subs {count}, {count}, #1", + "bne 1b", + + // The assembler will give us a warning (that we can't easily disable) + // if the reg_list for `stm` doesn't give the registers in order from + // low to high, so we just manually pick registers. The count register + // and the pointer register can be anything else. + in("r0") x, + in("r1") x, + in("r2") x, + in("r3") x, + in("r4") x, + in("r5") x, + in("r7") x, + in("r8") x, + count = inout(reg) 240 => _, + ptr = inout(reg) crate::mmio::MODE3_VRAM.as_usize() => _, + options(nostack), + ) + }); + } +} From be231174f2649d2b62c06dac47e650902f0e46be Mon Sep 17 00:00:00 2001 From: Lokathor Date: Fri, 31 May 2024 01:27:04 -0600 Subject: [PATCH 60/89] added rectangle fill code with less bounds checks required. --- examples/paddle_ball.rs | 17 +++------------ src/video.rs | 48 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 6e942a51..83df6429 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -144,21 +144,21 @@ fn main() -> ! { extern "C" fn draw_sprites(_bits: IrqBits) { Mode3.clear_to(Color::BLACK); - draw_rect( + Mode3.fill_rect_clipped( SPRITE_POSITIONS[0].read(), SPRITE_POSITIONS[1].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::RED, ); - draw_rect( + Mode3.fill_rect_clipped( SPRITE_POSITIONS[2].read(), SPRITE_POSITIONS[3].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::GREEN, ); - draw_rect( + Mode3.fill_rect_clipped( SPRITE_POSITIONS[4].read(), SPRITE_POSITIONS[5].read(), BALL_SIZE, @@ -166,14 +166,3 @@ extern "C" fn draw_sprites(_bits: IrqBits) { Color::CYAN, ); } - -// we out-line this because otherwise `draw_sprites` overloads the stack when it -// holds 3 copies of this function. -#[inline(never)] -fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { - for i in 0..width { - for j in 0..height { - MODE3_VRAM.index((x + i) as usize, (y + j) as usize).write(color); - } - } -} diff --git a/src/video.rs b/src/video.rs index fefec616..f7b76a68 100644 --- a/src/video.rs +++ b/src/video.rs @@ -2,6 +2,8 @@ use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; +use crate::mmio::MODE3_VRAM; + /// A color value. /// /// This is a bit-packed linear RGB color value with 5 bits per channel: @@ -332,14 +334,17 @@ pub struct Tile8bpp(pub [u32; 16]); pub struct Mode3; impl Mode3 { /// Width, in pixels, of the Mode 3 bitmap. - pub const WIDTH: usize = 240; + pub const WIDTH_USIZE: usize = 240; /// Height, in pixels, of the Mode 3 bitmap. - pub const HEIGHT: usize = 160; + pub const HEIGHT_USIZE: usize = 160; + + /// The size, in bytes, of one scanline of the Mode 3 bitmap. + pub const BYTES_PER_ROW: usize = + core::mem::size_of::<[Color; Mode3::WIDTH_USIZE]>(); - /// The size, in bytes, of the Mode 3 bitmap. - pub const BYTES: usize = - Self::WIDTH * Self::HEIGHT * core::mem::size_of::(); + /// The size, in bytes, of the whole Mode 3 bitmap. + pub const BYTES_TOTAL: usize = Self::BYTES_PER_ROW * Self::HEIGHT_USIZE; /// Clears the entire bitmap to a color of your choosing. #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] @@ -381,4 +386,37 @@ impl Mode3 { ) }); } + + /// Fills the given rectangle, clipped to the bounds of the bitmap. + #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] + pub fn fill_rect_clipped( + self, x: u16, y: u16, width: u16, height: u16, color: Color, + ) { + on_gba_or_unimplemented!( + let x_start = x.min(Self::WIDTH_USIZE as u16); + let x_end = x.saturating_add(width).min(Self::WIDTH_USIZE as u16); + let x_count = x_end - x_start; + let y_start = y.min(Self::HEIGHT_USIZE as u16); + let y_end = y.saturating_add(height).min(Self::HEIGHT_USIZE as u16); + let y_count = y_end - y_start; + // base + let mut p = MODE3_VRAM.as_usize() as *mut Color; + // go to start y + p = unsafe { p.byte_add(Self::BYTES_PER_ROW * (y_start as u16 as usize)) }; + // go to start x + p = unsafe { p.add(x_start as u16 as usize) }; + let mut y_remaining = y_count; + while y_remaining > 0 { + let mut within_row = p; + let mut x_remaining = x_count; + while x_remaining > 0 { + unsafe { within_row.write_volatile(color) }; + within_row = unsafe { within_row.add(1) }; + x_remaining -= 1; + } + p = unsafe { p.byte_add(Self::BYTES_PER_ROW) }; + y_remaining -= 1; + } + ); + } } From ed4d8a9f09046b5a955534cf3703f9b3d5426c28 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 12:47:45 -0600 Subject: [PATCH 61/89] we'll have to do memset later, these impls are hopelessly inefficient --- src/mem/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mem/mod.rs b/src/mem/mod.rs index c41d797d..88e71630 100644 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -1,7 +1,5 @@ //! Low-level memory manipulation functions. mod copy; -mod set; pub use copy::*; -pub use set::*; From 948aa8e28bcfd8653ca67afa0e202462b71b4442 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 13:11:12 -0600 Subject: [PATCH 62/89] update memory fns, and fix docs links --- examples/paddle_ball.rs | 3 +- src/bios.rs | 44 ++++++++++++ src/dma.rs | 6 +- src/mem/copy.rs | 37 ++++++++++ src/mem/set.rs | 155 ---------------------------------------- src/mgba.rs | 2 +- src/mmio.rs | 11 ++- src/obj.rs | 6 +- src/sample_art.rs | 4 +- src/video.rs | 4 +- 10 files changed, 98 insertions(+), 174 deletions(-) delete mode 100644 src/mem/set.rs diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 83df6429..90face57 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -7,8 +7,7 @@ use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - mem::bulk_memory_set, - mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, + mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT}, video::{Color, DisplayControl, DisplayStatus, Mode3}, IrqBits, KeyInput, }; diff --git a/src/bios.rs b/src/bios.rs index 19215072..a03e040e 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -64,6 +64,50 @@ pub fn VBlankIntrWait() { ); } +/// `0x09`: Arc tangent. +/// +/// * **Returns:** The output is in the range +/- `pi/2`, but accuracy is worse +/// outside of +/- `pi/4`. +#[inline] +#[instruction_set(arm::t32)] +pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { + let mut i = theta.into_raw(); + unsafe { + core::arch::asm! { + "swi #0x09", + inout("r0") i, + out("r1") _, + out("r3") _, + options(pure, nomem, preserves_flags), + } + }; + crate::i16fx14::from_raw(i) +} + +/// `0x0A`: The "2-argument arctangent" ([atan2][wp-atan2]). +/// +/// [wp-atan2]: https://en.wikipedia.org/wiki/Atan2 +/// +/// * **Returns:** The angle of the input vector, with `u16::MAX` being +/// equivalent to `2pi`. +#[inline] +#[instruction_set(arm::t32)] +pub fn ArcTan2(x: crate::i16fx14, y: crate::i16fx14) -> u16 { + let x = x.into_raw(); + let y = y.into_raw(); + let output: u16; + unsafe { + core::arch::asm! { + "swi #0x0A", + inout("r0") x => output, + inout("r1") y => _, + out("r3") _, + options(pure, nomem, preserves_flags), + } + }; + output +} + /// `0x12` TODO: document this more #[inline] #[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] diff --git a/src/dma.rs b/src/dma.rs index e4db17da..8dcf776c 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -47,7 +47,7 @@ use voladdress::{Safe, VolRegion}; use crate::{ mmio::{DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, DMA3_TRANSFER_COUNT}, - video::Tile4bpp, + video::Tile4, }; /// Controls the activity of a DMA unit. @@ -201,9 +201,7 @@ pub unsafe fn dma3_copy_u32(src: *const u32, dest: *mut u32, count: usize) { /// ## Panics /// * The `src` and `dest` must have the same length. #[inline] -pub fn dma3_copy_tile4( - src: &[Tile4bpp], dest: VolRegion, -) { +pub fn dma3_copy_tile4(src: &[Tile4], dest: VolRegion) { assert_eq!(src.len(), dest.len()); if src.len() == 0 { return; diff --git a/src/mem/copy.rs b/src/mem/copy.rs index 71c72af1..fa88473d 100644 --- a/src/mem/copy.rs +++ b/src/mem/copy.rs @@ -32,3 +32,40 @@ pub unsafe extern "C" fn __aeabi_memcpy1( } }); } + +/// Copies eight `u32` at a time to `dest` from `src` +/// +/// Particularly, this is the size of one [`Tile4`][crate::video::Tile4], half a +/// [`Tile8`][crate::video::Tile8], or one complete palbank of +/// [`Color`][crate::video::Color] values. +/// +/// ## Safety +/// * As with all copying routines, the source must be readable for the size you +/// specify, and the destination must be writable for the size you specify. +/// * Both pointers must be aligned to 4. +#[link_section = ".iwram.copy_u32x8_unchecked"] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +pub unsafe fn copy_u32x8_unchecked( + mut dest: *mut u32, mut src: *const u32, mut count: usize, +) { + while count > 0 { + on_gba_or_unimplemented!(unsafe { + core::arch::asm!( + "ldm {src}!, {{r3,r4,r5,r7, r8,r9,r10,r12}}", + "stm {dest}!, {{r3,r4,r5,r7, r8,r9,r10,r12}}", + dest = inout(reg) dest, + src = inout(reg) src, + out("r3") _, + out("r4") _, + out("r5") _, + out("r7") _, + out("r8") _, + out("r9") _, + out("r10") _, + out("r12") _, + options(nostack) + ) + }); + count -= 1; + } +} diff --git a/src/mem/set.rs b/src/mem/set.rs deleted file mode 100644 index 6c6023af..00000000 --- a/src/mem/set.rs +++ /dev/null @@ -1,155 +0,0 @@ -/// The core "memory setting" function. -/// -/// * `dest` is the destination pointer. -/// * `byte_count` is the number of bytes to write. -/// * `x` holds the desired word to write across all of the bytes. -/// -/// ## Safety -/// * `dest` must be aligned to 4 and writable for `byte_count` bytes. -#[inline(never)] -#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] -#[cfg_attr(feature = "on_gba", link_section = ".iwram.bulk_memory_set")] -pub unsafe extern "C" fn bulk_memory_set( - mut dest: *mut u32, byte_count: usize, x: u32, -) { - on_gba_or_unimplemented!( - // debug assert alignment, but if we call the method for this is generates a - // stupid huge amount of code. - debug_assert_eq!(dest as usize & 0b11, 0); - - let (mut blocks, mut spare) = (byte_count / 32, byte_count % 32); - - while blocks > 0 { - unsafe { - core::arch::asm!( - "stm r0!, {{r2,r3,r4,r5,r7,r8,r9,r10}}", - - inout("r0") dest, - in("r2") x, - in("r3") x, - in("r4") x, - in("r5") x, - in("r7") x, - in("r8") x, - in("r9") x, - in("r10") x, - options(nostack), - ); - }; - blocks -= 1; - } - - debug_assert!(spare < 32); - - while spare > 4 { - unsafe { dest.write_volatile(x) }; - dest = unsafe { dest.add(1) }; - spare -= 4; - } - - let mut dest = dest.cast::(); - let x = x as u8; - while spare > 0 { - unsafe { dest.write_volatile(x) }; - dest = unsafe { dest.add(1) }; - spare -= 1; - } - ); -} - -/// AEABI-styled memory set. -/// -/// ## Safety -/// * `dest` must be aligned to 4 and writable for `byte_count` bytes. -#[inline] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memset4( - dest: *mut u32, byte_count: usize, byte: i32, -) { - let byte8 = byte as u8; - let byte16 = byte8 as u16 | (byte8 as u16) << 8; - let byte32 = byte16 as u32 | (byte16 as u32) << 16; - - debug_assert_eq!(dest as usize & 0b11, 0); - unsafe { bulk_memory_set(dest.cast(), byte_count, byte32) }; -} - -/// AEABI-styled memory set. -/// -/// ## Safety -/// * `dest` must be aligned to 8 and writable for `byte_count` bytes. -#[inline] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memset8( - dest: *mut u32, byte_count: usize, byte: i32, -) { - debug_assert_eq!(dest as usize & 0b111, 0); - unsafe { __aeabi_memset4(dest.cast(), byte_count, byte) } -} - -/// AEABI-styled memory set. -/// -/// ## Safety -/// * `dest` must be writable for `byte_count` bytes. -#[inline] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn __aeabi_memset( - mut dest: *mut u8, mut byte_count: usize, byte: i32, -) { - if byte_count == 0 { - return; - } - let byte8 = byte as u8; - let byte16 = byte8 as u16 | (byte8 as u16) << 8; - let byte32 = byte16 as u32 | (byte16 as u32) << 16; - - // We only get fancy if the requested span is sufficiently large. - if byte_count >= 8 { - if (dest as usize) & 0b1 != 0 { - debug_assert!(byte_count >= 1); - unsafe { dest.write_volatile(byte8) }; - dest = unsafe { dest.add(1) }; - byte_count -= 1; - if byte_count == 0 { - return; - } - } - let mut dest: *mut u16 = dest.cast(); - debug_assert_eq!(dest as usize & 0b1, 0); - - if (dest as usize) & 0b10 != 0 { - debug_assert!(byte_count >= 2); - unsafe { dest.write_volatile(byte16) }; - dest = unsafe { dest.add(1) }; - byte_count -= 2; - if byte_count == 0 { - return; - } - } - let dest: *mut u32 = dest.cast(); - debug_assert_eq!(dest as usize & 0b11, 0); - - unsafe { bulk_memory_set(dest.cast(), byte_count, byte32) }; - } else { - for _ in 0..byte_count { - unsafe { dest.write_volatile(byte8) }; - dest = unsafe { dest.add(1) }; - } - } -} - -/// `libc`-style memory set. -/// -/// Don't ever call this function. Literally you don't ever want to directly -/// call this function. It's **always** slightly more costly than just calling -/// [`__aeabi_memset`] directly. This function is provided only because the -/// compiler will occasionally directly insert calls to `memset`, and so this is -/// needed for compatibility. -#[inline] -#[cfg_attr(feature = "no_mangle_memset", no_mangle)] -pub unsafe extern "C" fn memset( - dest: *mut u8, byte: i32, byte_count: usize, -) -> *mut u8 { - unsafe { __aeabi_memset(dest, byte_count, byte) }; - dest -} diff --git a/src/mgba.rs b/src/mgba.rs index 3aef358a..41bbbe86 100644 --- a/src/mgba.rs +++ b/src/mgba.rs @@ -8,7 +8,7 @@ //! //! Logging is not always available. Obviously the mGBA output buffer can't be //! used if the game isn't running within the mGBA emulator. -//! [`MgbaBufferedLogger::try_new`] will fail to make a logger when logging +//! [`MgbaLogger::try_new`] will fail to make a logger when logging //! isn't available. You can also call [`mgba_logging_available`] directly to //! check if mGBA logging is possible. //! diff --git a/src/mmio.rs b/src/mmio.rs index 98953ec4..60876f6c 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -12,8 +12,7 @@ use crate::{ mgba::MgbaLogLevel, obj::{ObjAttr, ObjAttr0, ObjAttr1, ObjAttr2}, video::{ - BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry, - Tile4bpp, + BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry, Tile4, }, IrqBits, KeyInput, }; @@ -239,11 +238,11 @@ pub const fn obj_palbank(bank: usize) -> VolBlock { pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; /// The VRAM's background tile view, using 4bpp tiles. -pub const VRAM_BG_TILE4: VolBlock = +pub const VRAM_BG_TILE4: VolBlock = unsafe { VolBlock::new(0x0600_0000) }; /// The VRAM's background tile view, using 8bpp tiles. -pub const VRAM_BG_TILE8: VolBlock = +pub const VRAM_BG_TILE8: VolBlock = unsafe { VolBlock::new(0x0600_0000) }; /// The text mode screenblocks. @@ -258,11 +257,11 @@ pub const TEXT_SCREENBLOCKS: VolGrid2dStrided< > = unsafe { VolGrid2dStrided::new(0x0600_0000) }; /// The VRAM's object tile view, using 4bpp tiles. -pub const VRAM_OBJ_TILE4: VolBlock = +pub const VRAM_OBJ_TILE4: VolBlock = unsafe { VolBlock::new(0x0601_0000) }; /// The VRAM's object tile view, using 8bpp tiles. -pub const VRAM_OBJ_TILE8: VolBlock = +pub const VRAM_OBJ_TILE8: VolBlock = unsafe { VolBlock::new(0x0601_0000) }; /// The VRAM's view in Video Mode 3. diff --git a/src/obj.rs b/src/obj.rs index f5fadd7b..17c754f0 100644 --- a/src/obj.rs +++ b/src/obj.rs @@ -20,8 +20,10 @@ //! [ObjAttr2]. //! //! When you've got an object's data configured how you want, use either the -//! [`OBJ_ATTR_ALL`] control (to write all fields at once) or the [`OBJ_ATTR0`], -//! [`OBJ_ATTR1`], and/or [`OBJ_ATTR2`] controls (to write just some of the +//! [`OBJ_ATTR_ALL`][crate::mmio::OBJ_ATTR_ALL] control (to write all fields at +//! once) or the [`OBJ_ATTR0`][crate::mmio::OBJ_ATTR0], +//! [`OBJ_ATTR1`][crate::mmio::OBJ_ATTR1], and/or +//! [`OBJ_ATTR2`][crate::mmio::OBJ_ATTR2] controls (to write just some of the //! fields). //! //! **Note:** When the GBA first boots, the object layer will be off but the diff --git a/src/sample_art.rs b/src/sample_art.rs index a6015680..a96007bb 100644 --- a/src/sample_art.rs +++ b/src/sample_art.rs @@ -4,7 +4,7 @@ use voladdress::{Safe, VolRegion}; -use crate::{bios::LZ77UnCompReadNormalWrite16bit, video::Tile4bpp}; +use crate::{bios::LZ77UnCompReadNormalWrite16bit, video::Tile4}; /// Decompresses the sample CGA font face into VRAM. /// @@ -12,7 +12,7 @@ use crate::{bios::LZ77UnCompReadNormalWrite16bit, video::Tile4bpp}; /// /// ## Panics /// The provided destination needs to be at least 256 tiles, or this will panic. -pub fn decompress_cga_face_to_vram_4bpp(dest: VolRegion) { +pub fn decompress_cga_face_to_vram_4bpp(dest: VolRegion) { assert!(dest.len() >= 256); // TODO: we should allow inputting *where* in VRAM the tiles go, instead of // always using the VRAM base address. diff --git a/src/video.rs b/src/video.rs index f7b76a68..3668367c 100644 --- a/src/video.rs +++ b/src/video.rs @@ -312,7 +312,7 @@ impl TextEntry { /// expected to manipulate particular pixels within a tile at runtime. #[derive(Clone, Copy, Default)] #[repr(transparent)] -pub struct Tile4bpp(pub [u32; 8]); +pub struct Tile4(pub [u32; 8]); /// Data for a 8-bit-per-pixel tile. /// @@ -327,7 +327,7 @@ pub struct Tile4bpp(pub [u32; 8]); /// expected to manipulate particular pixels within a tile at runtime. #[derive(Clone, Copy, Default)] #[repr(transparent)] -pub struct Tile8bpp(pub [u32; 16]); +pub struct Tile8(pub [u32; 16]); /// A zero-sized type that gives a namespace for Mode 3 related things. #[derive(Clone, Copy)] From 2555648dd074651f408655160b0837d25b3cef4a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 14:09:15 -0600 Subject: [PATCH 63/89] update the asm runtime handler to call the user fn with the cpu in system mode --- src/asm_runtime.rs | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 4ba5bb7c..e3df7651 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -171,18 +171,32 @@ core::arch::global_asm! { } } +// This "default" IRQ handler +// * Assumes that `IntrWait` support is desired. +// * Calls the user handler function in System mode. +// * Does NOT support nested interrupts at all. #[cfg(feature = "on_gba")] core::arch::global_asm! { put_fn_in_section!(".iwram.text._asm_runtime_irq_handler"), ".global _asm_runtime_irq_handler", force_a32!{ "_asm_runtime_irq_handler:", - - // At function entry: - // * r0: holds 0x0400_0000 - // // We're allowed to use the usual C ABI registers. + // Note(Lokathor): So the trouble here is that we want to 16-bit access + // BASE+0x200 and also BASE-8. Immediate offsets for 16-bit access can't be + // that large of a range. So we'll have to actually do an `add` or `sub` to + // put the correct offset from our base pointer into a register. Also, our + // base pointer is in `r0`, but we need to pass the bits to the user + // function in `r0` when we do that call. The compromise here is that we'll + // keep our base address in `r0`, manipulate the bits in `r1`, and then if + // we're actually doing a call to a user fn we can move the bits down to + // `r0` after we don't need the base pointer anymore. This seems to be the + // way that has the least register shuffling. + + // Assumed: + // * r0: 0x0400_0000 (set by the BIOS) + // handle MMIO interrupt system "add r12, r0, #0x200", // 16-bit access offsets can't be too big "ldr r1, [r12]", // IE_IF.read32() @@ -190,16 +204,16 @@ core::arch::global_asm! { "strh r1, [r12, #2]", // write IF // Now: - // * r0: holds 0x0400_0000 + // * r0: 0x0400_0000 // * r1: irq bits - // handle BIOS interrupt system + // handle BIOS IntrWait system "ldrh r2, [r0, #-8]", // read the `has_occurred` flags "orr r2, r2, r1", // activate the new bits, if any "strh r2, [r0, #-8]", // update the value // Now: - // * r0: holds 0x0400_0000 + // * r0: 0x0400_0000 // * r1: irq bits // Get the user handler fn pointer, call it if non-null. @@ -207,12 +221,13 @@ core::arch::global_asm! { "ldr r12, [r12]", when!(("r12" != "#0")[1] { "mov r0, r1", - // we need to save `lr`, and we need to save an even number of registers - // to keep the stack aligned to 8 for the C ABI, so we'll also save `r0`, - // though it's not actually of use. - "push {{r0, lr}}", + a32_read_spsr_to!("r3"), + "push {{r3, lr}}", + a32_set_cpu_control!(System, irq_masked = true, fiq_masked = true), a32_fake_blx!("r12"), - "pop {{r0, lr}}", + a32_set_cpu_control!(IRQ, irq_masked = false, fiq_masked = false), + "pop {{r3, lr}}", + a32_write_spsr_from!("r3"), }), // return to the BIOS From 59cdb7223a8286d319c75f5930f25ad41227015f Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 14:09:36 -0600 Subject: [PATCH 64/89] delete these cargo features which no longer apply --- Cargo.toml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4002beae..49203f5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,13 +19,6 @@ doc_cfg = [] # inlined (meaning `Location` is passed via the stack). This is only needed for # debugging, and so it's off by default. track_caller = [] -# Applies the `no_mangle` attribute to the `memcpy` family of functions, causing -# them to override the same intrinsics from the `compiler_builtins` crate. When -# using this you will need to set cargo's `build-std-features` list to include -# "compiler-builtins-weak-intrinsics" -no_mangle_memcpy = [] -# As above, but for `memset` and `memclr` fns. -no_mangle_memset = [] [dependencies] voladdress = "1.4.0" @@ -35,7 +28,7 @@ critical-section = { version = "1.1.2", features = [ ], optional = true } bytemuck = { version = "1.16.0", optional = true } fixed = { version = "1.27.0", default-features = false, optional = true } -bracer = "0.3.0" +bracer = "0.3.1" [profile.dev] opt-level = 3 From 36870c2c9a76e8a23a54573fcd3fc63bbe40ccf4 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 14:10:28 -0600 Subject: [PATCH 65/89] delete the cfg that doesn't matter --- src/mem/copy.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mem/copy.rs b/src/mem/copy.rs index fa88473d..bc382b15 100644 --- a/src/mem/copy.rs +++ b/src/mem/copy.rs @@ -12,7 +12,6 @@ /// overlapping. Partial overlap is not allowed. #[inline] #[link_section = ".iwram.__aeabi_memcpy1"] -#[cfg_attr(feature = "no_mangle_memcpy", no_mangle)] #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] pub unsafe extern "C" fn __aeabi_memcpy1( dest: *mut u8, src: *const u8, byte_count: usize, From deae8eadba2b705fc005271d2ddc51c9dcd88c89 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 14:40:09 -0600 Subject: [PATCH 66/89] make u32x8 use less stack, and add more docs and notes --- src/mem/copy.rs | 75 +++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/mem/copy.rs b/src/mem/copy.rs index bc382b15..4c3e47f4 100644 --- a/src/mem/copy.rs +++ b/src/mem/copy.rs @@ -1,22 +1,22 @@ -/// `u8` copy between exclusive regions. +/// Copies `u8` at a time between exclusive regions. /// /// * This will *always* copy one byte at a time, and the code is always stored /// in IWRAM, making it suitable for use with SRAM memory. /// /// ## Safety -/// * If `byte_count` is zero then the pointers are not used at all, and they -/// can be any value. -/// * If `byte_count` is non-zero then: -/// * Both pointers must be valid for the number of bytes given. -/// * The two regions must either be *entirely* disjoint or *entirely* -/// overlapping. Partial overlap is not allowed. -#[inline] -#[link_section = ".iwram.__aeabi_memcpy1"] +/// * As with all copying routines, the source must be readable for the size you +/// specify, and the destination must be writable for the size you specify. +/// * The regions must not overlap. #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] -pub unsafe extern "C" fn __aeabi_memcpy1( +#[cfg_attr(feature = "on_gba", link_section = ".iwram.copy_u8_unchecked")] +pub unsafe extern "C" fn copy_u8_unchecked( dest: *mut u8, src: *const u8, byte_count: usize, ) { on_gba_or_unimplemented!(unsafe { + // Note(Lokathor): This loop setup assumes that the `byte_count` is usually + // greater than 0, and so subtracts first and then does a conditional + // load/store pair if the value (after subtracting) is greater than or equal + // to 0 (meaning that the value before the subtract *was* 1 or more). core::arch::asm! { "1:", "subs {count}, {count}, #1", @@ -32,7 +32,7 @@ pub unsafe extern "C" fn __aeabi_memcpy1( }); } -/// Copies eight `u32` at a time to `dest` from `src` +/// Copies `[u32; 8]` sized blocks, to `dest` from `src` /// /// Particularly, this is the size of one [`Tile4`][crate::video::Tile4], half a /// [`Tile8`][crate::video::Tile8], or one complete palbank of @@ -42,29 +42,38 @@ pub unsafe extern "C" fn __aeabi_memcpy1( /// * As with all copying routines, the source must be readable for the size you /// specify, and the destination must be writable for the size you specify. /// * Both pointers must be aligned to 4. -#[link_section = ".iwram.copy_u32x8_unchecked"] +/// * The regions must not overlap. #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +#[cfg_attr(feature = "on_gba", link_section = ".iwram.copy_u32x8_unchecked")] pub unsafe fn copy_u32x8_unchecked( - mut dest: *mut u32, mut src: *const u32, mut count: usize, + dest: *mut [u32; 8], src: *const [u32; 8], count: usize, ) { - while count > 0 { - on_gba_or_unimplemented!(unsafe { - core::arch::asm!( - "ldm {src}!, {{r3,r4,r5,r7, r8,r9,r10,r12}}", - "stm {dest}!, {{r3,r4,r5,r7, r8,r9,r10,r12}}", - dest = inout(reg) dest, - src = inout(reg) src, - out("r3") _, - out("r4") _, - out("r5") _, - out("r7") _, - out("r8") _, - out("r9") _, - out("r10") _, - out("r12") _, - options(nostack) - ) - }); - count -= 1; - } + on_gba_or_unimplemented!(unsafe { + // Note(Lokathor): Same loop logic as `copy_u8_unchecked`, we're just + // processing bigger chunks of data at a time. + core::arch::asm!( + "1:", + "subs {count}, {count}, #1", + "ldmge {src}!, {{r3,r4,r5,r7, r8,r9,r12,lr}}", + "stmge {dest}!, {{r3,r4,r5,r7, r8,r9,r12,lr}}", + "bgt 1b", + + count = inout(reg) count => _, + dest = inout(reg) dest => _, + src = inout(reg) src => _, + out("r3") _, + out("r4") _, + out("r5") _, + out("r7") _, + out("r8") _, + out("r9") _, + out("r12") _, + // Note(Lokathor): LLVM will always put `lr` on the stack as part of the + // push/pop for the function, even if we don't use `lr`, so we might as + // well use `lr`, because if we use a different register (such as `r10`) + // that would only add to the amount of push/pop LLVM does. + out("lr") _, + options(nostack) + ) + }); } From ab424c8d84f1380a3aca0d516092f682e63841c1 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 23:29:33 -0600 Subject: [PATCH 67/89] rename gba_fixed methods to match the `fixed` crate's method names for the same operation. --- src/bios.rs | 8 ++++---- src/gba_fixed.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bios.rs b/src/bios.rs index a03e040e..a6a74fd8 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -71,7 +71,7 @@ pub fn VBlankIntrWait() { #[inline] #[instruction_set(arm::t32)] pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { - let mut i = theta.into_raw(); + let mut i = theta.to_bits(); unsafe { core::arch::asm! { "swi #0x09", @@ -81,7 +81,7 @@ pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { options(pure, nomem, preserves_flags), } }; - crate::i16fx14::from_raw(i) + crate::i16fx14::from_bits(i) } /// `0x0A`: The "2-argument arctangent" ([atan2][wp-atan2]). @@ -93,8 +93,8 @@ pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { #[inline] #[instruction_set(arm::t32)] pub fn ArcTan2(x: crate::i16fx14, y: crate::i16fx14) -> u16 { - let x = x.into_raw(); - let y = y.into_raw(); + let x = x.to_bits(); + let y = y.to_bits(); let output: u16; unsafe { core::arch::asm! { diff --git a/src/gba_fixed.rs b/src/gba_fixed.rs index b118469c..808825d4 100644 --- a/src/gba_fixed.rs +++ b/src/gba_fixed.rs @@ -119,7 +119,7 @@ macro_rules! impl_common_fixed_ops { #[inline] #[must_use] #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn from_raw(i: $t) -> Self { + pub const fn from_bits(i: $t) -> Self { Self(i) } @@ -127,7 +127,7 @@ macro_rules! impl_common_fixed_ops { #[inline] #[must_use] #[cfg_attr(feature = "track_caller", track_caller)] - pub const fn into_raw(self) -> $t { + pub const fn to_bits(self) -> $t { self.0 } From 35018e6033fa7b95057b491c0d1194d9820f149a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 23:33:24 -0600 Subject: [PATCH 68/89] bios fns should be consistent about on_gba_or_unimplemented usage --- src/bios.rs | 68 ++++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/bios.rs b/src/bios.rs index a6a74fd8..8341bb13 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -34,34 +34,30 @@ use crate::IrqBits; #[inline] #[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] pub fn IntrWait(ignore_existing: bool, target_irqs: IrqBits) { - on_gba_or_unimplemented!( - unsafe { - core::arch::asm! { - "swi #0x04", - inout("r0") ignore_existing as u32 => _, - inout("r1") target_irqs.0 => _, - out("r3") _, - options(preserves_flags), - } - }; - ); + on_gba_or_unimplemented!(unsafe { + core::arch::asm! { + "swi #0x04", + inout("r0") ignore_existing as u32 => _, + inout("r1") target_irqs.0 => _, + out("r3") _, + options(preserves_flags), + } + }); } /// `0x05`: Builtin shorthand for [`IntrWait(true, IrqBits::VBLANK)`](IntrWait) #[inline] #[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] pub fn VBlankIntrWait() { - on_gba_or_unimplemented!( - unsafe { - core::arch::asm! { - "swi #0x05", - out("r0") _, - out("r1") _, - out("r3") _, - options(preserves_flags), - } - }; - ); + on_gba_or_unimplemented!(unsafe { + core::arch::asm! { + "swi #0x05", + out("r0") _, + out("r1") _, + out("r3") _, + options(preserves_flags), + } + }); } /// `0x09`: Arc tangent. @@ -72,7 +68,7 @@ pub fn VBlankIntrWait() { #[instruction_set(arm::t32)] pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { let mut i = theta.to_bits(); - unsafe { + on_gba_or_unimplemented!(unsafe { core::arch::asm! { "swi #0x09", inout("r0") i, @@ -80,7 +76,7 @@ pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { out("r3") _, options(pure, nomem, preserves_flags), } - }; + }); crate::i16fx14::from_bits(i) } @@ -96,7 +92,7 @@ pub fn ArcTan2(x: crate::i16fx14, y: crate::i16fx14) -> u16 { let x = x.to_bits(); let y = y.to_bits(); let output: u16; - unsafe { + on_gba_or_unimplemented!(unsafe { core::arch::asm! { "swi #0x0A", inout("r0") x => output, @@ -104,7 +100,7 @@ pub fn ArcTan2(x: crate::i16fx14, y: crate::i16fx14) -> u16 { out("r3") _, options(pure, nomem, preserves_flags), } - }; + }); output } @@ -112,15 +108,13 @@ pub fn ArcTan2(x: crate::i16fx14, y: crate::i16fx14) -> u16 { #[inline] #[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] pub unsafe fn LZ77UnCompReadNormalWrite16bit(src: *const u32, dst: *mut u16) { - on_gba_or_unimplemented!( - unsafe { - core::arch::asm! { - "swi #0x12", - inout("r0") src => _, - inout("r1") dst => _, - out("r3") _, - options(preserves_flags), - } - }; - ); + on_gba_or_unimplemented!(unsafe { + core::arch::asm! { + "swi #0x12", + inout("r0") src => _, + inout("r1") dst => _, + out("r3") _, + options(preserves_flags), + } + }); } From 5e6d9f1dfae8c5710faaeb65329e8ced45584a5a Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 2 Jun 2024 23:46:26 -0600 Subject: [PATCH 69/89] macros --- src/gba_fixed.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gba_fixed.rs b/src/gba_fixed.rs index 808825d4..1925755c 100644 --- a/src/gba_fixed.rs +++ b/src/gba_fixed.rs @@ -292,14 +292,14 @@ macro_rules! impl_signed_fixed_ops { impl core::fmt::Debug for Fixed<$t, B> { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - let whole: $t = self.trunc().into_raw() >> B; - let fract: $t = self.fract().into_raw(); + let whole: $t = self.trunc().to_bits() >> B; + let fract: $t = self.fract().to_bits(); let divisor: $t = 1 << B; if self.is_negative() { let whole = whole.unsigned_abs(); - write!(f, "-({whole}+{fract}/{divisor})") + write!(f, "-({whole}+({fract}/{divisor}))") } else { - write!(f, "{whole}+{fract}/{divisor}") + write!(f, "{whole}+({fract}/{divisor})") } } } @@ -350,10 +350,10 @@ macro_rules! impl_unsigned_fixed_ops { impl core::fmt::Debug for Fixed<$t, B> { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - let whole: $t = self.trunc().into_raw() >> B; - let fract: $t = self.fract().into_raw(); + let whole: $t = self.trunc().to_bits() >> B; + let fract: $t = self.fract().to_bits(); let divisor: $t = 1 << B; - write!(f, "{whole}+{fract}/{divisor}") + write!(f, "{whole}+({fract}/{divisor})") } } }; From effab6fabc36a2f46b5870ba1c8b7fbd342fac60 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 00:00:32 -0600 Subject: [PATCH 70/89] finish out the IrqBits type setters --- src/lib.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ebf0047b..047c8700 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -315,10 +315,88 @@ impl IrqBits { u16_get_bit(13, self.0) } - /// Set if vblank triggers an interrupt. + /// Set the vblank bit. #[inline] #[must_use] pub const fn with_vblank(self, vblank: bool) -> Self { Self(u16_with_bit(0, self.0, vblank)) } + /// Set the hblank bit. + #[inline] + #[must_use] + pub const fn with_hblank(self, hblank: bool) -> Self { + Self(u16_with_bit(1, self.0, hblank)) + } + /// Set the vcount bit. + #[inline] + #[must_use] + pub const fn with_vcount(self, vcount: bool) -> Self { + Self(u16_with_bit(2, self.0, vcount)) + } + /// Set the timer0 bit. + #[inline] + #[must_use] + pub const fn with_timer0(self, timer0: bool) -> Self { + Self(u16_with_bit(3, self.0, timer0)) + } + /// Set the timer1 bit. + #[inline] + #[must_use] + pub const fn with_timer1(self, timer1: bool) -> Self { + Self(u16_with_bit(4, self.0, timer1)) + } + /// Set the timer2 bit. + #[inline] + #[must_use] + pub const fn with_timer2(self, timer2: bool) -> Self { + Self(u16_with_bit(5, self.0, timer2)) + } + /// Set the timer3 bit. + #[inline] + #[must_use] + pub const fn with_timer3(self, timer3: bool) -> Self { + Self(u16_with_bit(6, self.0, timer3)) + } + /// Set the serial bit. + #[inline] + #[must_use] + pub const fn with_serial(self, serial: bool) -> Self { + Self(u16_with_bit(7, self.0, serial)) + } + /// Set the dma0 bit. + #[inline] + #[must_use] + pub const fn with_dma0(self, dma0: bool) -> Self { + Self(u16_with_bit(8, self.0, dma0)) + } + /// Set the dma1 bit. + #[inline] + #[must_use] + pub const fn with_dma1(self, dma1: bool) -> Self { + Self(u16_with_bit(9, self.0, dma1)) + } + /// Set the dma2 bit. + #[inline] + #[must_use] + pub const fn with_dma2(self, dma2: bool) -> Self { + Self(u16_with_bit(10, self.0, dma2)) + } + /// Set the dma3 bit. + #[inline] + #[must_use] + pub const fn with_dma3(self, dma3: bool) -> Self { + Self(u16_with_bit(11, self.0, dma3)) + } + /// Set the keypad bit. + #[inline] + #[must_use] + pub const fn with_keypad(self, keypad: bool) -> Self { + Self(u16_with_bit(12, self.0, keypad)) + } + /// Set the gamepak bit. + #[inline] + #[must_use] + pub const fn with_gamepak(self, gamepak: bool) -> Self { + Self(u16_with_bit(13, self.0, gamepak)) + } } From d5400b7a3f10ab7304aeb9cd249cf4bbacb29a0f Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 00:07:09 -0600 Subject: [PATCH 71/89] break up the definitions into separate files for easier finding. --- src/mmio/mod.rs | 38 +++++ src/{mmio.rs => mmio/peripheral_controls.rs} | 161 +------------------ src/mmio/video_memory.rs | 129 +++++++++++++++ 3 files changed, 168 insertions(+), 160 deletions(-) create mode 100644 src/mmio/mod.rs rename src/{mmio.rs => mmio/peripheral_controls.rs} (59%) create mode 100644 src/mmio/video_memory.rs diff --git a/src/mmio/mod.rs b/src/mmio/mod.rs new file mode 100644 index 00000000..3166b5f4 --- /dev/null +++ b/src/mmio/mod.rs @@ -0,0 +1,38 @@ +//! Definitions for Memory-mapped IO (hardware control). + +use core::ffi::c_void; + +use bitfrob::u8x2; +#[allow(unused_imports)] +use voladdress::VolAddress; +use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided, VolSeries}; + +use crate::{ + dma::DmaControl, + mgba::MgbaLogLevel, + obj::{ObjAttr, ObjAttr0, ObjAttr1, ObjAttr2}, + video::{ + BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry, Tile4, + }, + IrqBits, KeyInput, +}; + +/// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` +/// cargo feature. +#[cfg(feature = "on_gba")] +type SOGBA = voladdress::Safe; +#[cfg(not(feature = "on_gba"))] +type SOGBA = voladdress::Unsafe; + +/// Responds "normally" to read/write, just holds a setting +type PlainAddr = VolAddress; +/// Read-only addr +type RoAddr = VolAddress; +/// Write-only addr +type WoAddr = VolAddress; + +mod peripheral_controls; +mod video_memory; + +pub use peripheral_controls::*; +pub use video_memory::*; diff --git a/src/mmio.rs b/src/mmio/peripheral_controls.rs similarity index 59% rename from src/mmio.rs rename to src/mmio/peripheral_controls.rs index 60876f6c..de0bbc95 100644 --- a/src/mmio.rs +++ b/src/mmio/peripheral_controls.rs @@ -1,35 +1,4 @@ -//! Definitions for Memory-mapped IO (hardware control). - -use core::ffi::c_void; - -use bitfrob::u8x2; -#[allow(unused_imports)] -use voladdress::VolAddress; -use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided, VolSeries}; - -use crate::{ - dma::DmaControl, - mgba::MgbaLogLevel, - obj::{ObjAttr, ObjAttr0, ObjAttr1, ObjAttr2}, - video::{ - BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry, Tile4, - }, - IrqBits, KeyInput, -}; - -/// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` -/// cargo feature. -#[cfg(feature = "on_gba")] -type SOGBA = voladdress::Safe; -#[cfg(not(feature = "on_gba"))] -type SOGBA = voladdress::Unsafe; - -/// Responds "normally" to read/write, just holds a setting -type PlainAddr = VolAddress; -/// Read-only addr -type RoAddr = VolAddress; -/// Write-only addr -type WoAddr = VolAddress; +use super::*; /// Display Control setting. /// @@ -206,131 +175,3 @@ pub const MGBA_LOG_SEND: WoAddr = /// need to touch this. pub const MGBA_LOG_ENABLE: PlainAddr = unsafe { VolAddress::new(0x04FF_F780) }; - -/// The backdrop color is the color shown when no *other* element is displayed -/// in a given pixel. -pub const BACKDROP_COLOR: PlainAddr = - unsafe { VolAddress::new(0x0500_0000) }; - -/// Palette data for the backgrounds -pub const BG_PALRAM: VolBlock = - unsafe { VolBlock::new(0x0500_0000) }; - -/// Palette data for the objects. -pub const OBJ_PALRAM: VolBlock = - unsafe { VolBlock::new(0x0500_0200) }; - -/// Gets the block for a specific palbank. -/// -/// ## Panics -/// * If the `bank` requested is 16 or greater this will panic. -#[inline] -#[must_use] -#[cfg_attr(feature = "track_caller", track_caller)] -pub const fn obj_palbank(bank: usize) -> VolBlock { - let u = OBJ_PALRAM.index(bank * 16).as_usize(); - unsafe { VolBlock::new(u) } -} - -/// The VRAM byte offset per screenblock index. -/// -/// This is the same for all background types and sizes. -pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; - -/// The VRAM's background tile view, using 4bpp tiles. -pub const VRAM_BG_TILE4: VolBlock = - unsafe { VolBlock::new(0x0600_0000) }; - -/// The VRAM's background tile view, using 8bpp tiles. -pub const VRAM_BG_TILE8: VolBlock = - unsafe { VolBlock::new(0x0600_0000) }; - -/// The text mode screenblocks. -pub const TEXT_SCREENBLOCKS: VolGrid2dStrided< - TextEntry, - SOGBA, - SOGBA, - 32, - 32, - 32, - SCREENBLOCK_INDEX_OFFSET, -> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; - -/// The VRAM's object tile view, using 4bpp tiles. -pub const VRAM_OBJ_TILE4: VolBlock = - unsafe { VolBlock::new(0x0601_0000) }; - -/// The VRAM's object tile view, using 8bpp tiles. -pub const VRAM_OBJ_TILE8: VolBlock = - unsafe { VolBlock::new(0x0601_0000) }; - -/// The VRAM's view in Video Mode 3. -/// -/// Each location is a direct color value. -pub const MODE3_VRAM: VolGrid2d = - unsafe { VolGrid2d::new(0x0600_0000) }; - -/// The VRAM's view in Video Mode 4. -/// -/// Each location is a pair of palette indexes into the background palette. -/// Because the VRAM can't be written with a single byte, we have to work with -/// this in units of [`u8x2`]. It's annoying, I know. -pub const MODE4_VRAM: VolGrid2dStrided< - u8x2, - SOGBA, - SOGBA, - { 240 / 2 }, - 160, - 2, - 0xA000, -> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; - -/// The VRAM's view in Video Mode 5. -/// -/// Each location is a direct color value, but there's a lower image size to -/// allow for two frames. -pub const MODE5_VRAM: VolGrid2dStrided< - Color, - SOGBA, - SOGBA, - 160, - 128, - 2, - 0xA000, -> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; - -/// The combined object attributes. -pub const OBJ_ATTR_ALL: VolSeries< - ObjAttr, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000) }; - -/// The object 0th attributes. -pub const OBJ_ATTR0: VolSeries< - ObjAttr0, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000) }; - -/// The object 1st attributes. -pub const OBJ_ATTR1: VolSeries< - ObjAttr1, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000 + 2) }; - -/// The object 2nd attributes. -pub const OBJ_ATTR2: VolSeries< - ObjAttr2, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000 + 4) }; diff --git a/src/mmio/video_memory.rs b/src/mmio/video_memory.rs new file mode 100644 index 00000000..a0d6e8bf --- /dev/null +++ b/src/mmio/video_memory.rs @@ -0,0 +1,129 @@ +use super::*; + +/// The backdrop color is the color shown when no *other* element is displayed +/// in a given pixel. +pub const BACKDROP_COLOR: PlainAddr = + unsafe { VolAddress::new(0x0500_0000) }; + +/// Palette data for the backgrounds +pub const BG_PALRAM: VolBlock = + unsafe { VolBlock::new(0x0500_0000) }; + +/// Palette data for the objects. +pub const OBJ_PALRAM: VolBlock = + unsafe { VolBlock::new(0x0500_0200) }; + +/// Gets the block for a specific palbank. +/// +/// ## Panics +/// * If the `bank` requested is 16 or greater this will panic. +#[inline] +#[must_use] +#[cfg_attr(feature = "track_caller", track_caller)] +pub const fn obj_palbank(bank: usize) -> VolBlock { + let u = OBJ_PALRAM.index(bank * 16).as_usize(); + unsafe { VolBlock::new(u) } +} + +/// The VRAM byte offset per screenblock index. +/// +/// This is the same for all background types and sizes. +pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; + +/// The VRAM's background tile view, using 4bpp tiles. +pub const VRAM_BG_TILE4: VolBlock = + unsafe { VolBlock::new(0x0600_0000) }; + +/// The VRAM's background tile view, using 8bpp tiles. +pub const VRAM_BG_TILE8: VolBlock = + unsafe { VolBlock::new(0x0600_0000) }; + +/// The text mode screenblocks. +pub const TEXT_SCREENBLOCKS: VolGrid2dStrided< + TextEntry, + SOGBA, + SOGBA, + 32, + 32, + 32, + SCREENBLOCK_INDEX_OFFSET, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The VRAM's object tile view, using 4bpp tiles. +pub const VRAM_OBJ_TILE4: VolBlock = + unsafe { VolBlock::new(0x0601_0000) }; + +/// The VRAM's object tile view, using 8bpp tiles. +pub const VRAM_OBJ_TILE8: VolBlock = + unsafe { VolBlock::new(0x0601_0000) }; + +/// The VRAM's view in Video Mode 3. +/// +/// Each location is a direct color value. +pub const MODE3_VRAM: VolGrid2d = + unsafe { VolGrid2d::new(0x0600_0000) }; + +/// The VRAM's view in Video Mode 4. +/// +/// Each location is a pair of palette indexes into the background palette. +/// Because the VRAM can't be written with a single byte, we have to work with +/// this in units of [`u8x2`]. It's annoying, I know. +pub const MODE4_VRAM: VolGrid2dStrided< + u8x2, + SOGBA, + SOGBA, + { 240 / 2 }, + 160, + 2, + 0xA000, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The VRAM's view in Video Mode 5. +/// +/// Each location is a direct color value, but there's a lower image size to +/// allow for two frames. +pub const MODE5_VRAM: VolGrid2dStrided< + Color, + SOGBA, + SOGBA, + 160, + 128, + 2, + 0xA000, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The combined object attributes. +pub const OBJ_ATTR_ALL: VolSeries< + ObjAttr, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000) }; + +/// The object 0th attributes. +pub const OBJ_ATTR0: VolSeries< + ObjAttr0, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000) }; + +/// The object 1st attributes. +pub const OBJ_ATTR1: VolSeries< + ObjAttr1, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000 + 2) }; + +/// The object 2nd attributes. +pub const OBJ_ATTR2: VolSeries< + ObjAttr2, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000 + 4) }; From 7376e8b0d4a8eda4910ef0d0f31ed9651e622fc7 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 00:11:50 -0600 Subject: [PATCH 72/89] we won't accept `self` for a ZST method if the ZST isn't guarding anything, we'll just use associated functions. --- examples/paddle_ball.rs | 8 ++++---- src/video.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 90face57..15e8e665 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -141,23 +141,23 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - Mode3.clear_to(Color::BLACK); + Mode3::clear_to(Color::BLACK); - Mode3.fill_rect_clipped( + Mode3::fill_rect_clipped( SPRITE_POSITIONS[0].read(), SPRITE_POSITIONS[1].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::RED, ); - Mode3.fill_rect_clipped( + Mode3::fill_rect_clipped( SPRITE_POSITIONS[2].read(), SPRITE_POSITIONS[3].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::GREEN, ); - Mode3.fill_rect_clipped( + Mode3::fill_rect_clipped( SPRITE_POSITIONS[4].read(), SPRITE_POSITIONS[5].read(), BALL_SIZE, diff --git a/src/video.rs b/src/video.rs index 3668367c..bd984458 100644 --- a/src/video.rs +++ b/src/video.rs @@ -349,7 +349,7 @@ impl Mode3 { /// Clears the entire bitmap to a color of your choosing. #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] #[cfg_attr(feature = "on_gba", link_section = ".iwram.mode3.clear_to")] - pub fn clear_to(self, color: Color) { + pub fn clear_to(color: Color) { on_gba_or_unimplemented!(unsafe { let x: u32 = color.0 as u32 | ((color.0 as u32) << 16); // now we spam out that `u32`, 10 stm per loop, 8 times per stm. @@ -390,7 +390,7 @@ impl Mode3 { /// Fills the given rectangle, clipped to the bounds of the bitmap. #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] pub fn fill_rect_clipped( - self, x: u16, y: u16, width: u16, height: u16, color: Color, + x: u16, y: u16, width: u16, height: u16, color: Color, ) { on_gba_or_unimplemented!( let x_start = x.min(Self::WIDTH_USIZE as u16); From d3fdb9368b52063bbdda6c37d31123098b9ca878 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 00:22:13 -0600 Subject: [PATCH 73/89] limit `instruction_set` use --- src/bios.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bios.rs b/src/bios.rs index 8341bb13..0fd90883 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -65,7 +65,7 @@ pub fn VBlankIntrWait() { /// * **Returns:** The output is in the range +/- `pi/2`, but accuracy is worse /// outside of +/- `pi/4`. #[inline] -#[instruction_set(arm::t32)] +#[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { let mut i = theta.to_bits(); on_gba_or_unimplemented!(unsafe { @@ -87,7 +87,7 @@ pub fn ArcTan(theta: crate::i16fx14) -> crate::i16fx14 { /// * **Returns:** The angle of the input vector, with `u16::MAX` being /// equivalent to `2pi`. #[inline] -#[instruction_set(arm::t32)] +#[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] pub fn ArcTan2(x: crate::i16fx14, y: crate::i16fx14) -> u16 { let x = x.to_bits(); let y = y.to_bits(); From e4024d6330e008ffa5d8da8f9171a193f8ad7744 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 01:25:47 -0600 Subject: [PATCH 74/89] weird bug --- dump.bat | 16 ++++++----- examples/timer.rs | 43 +++++++++++++++++++++++++++++ src/lib.rs | 1 + src/mmio/peripheral_controls.rs | 40 +++++++++++++++++++++++++++ src/timers.rs | 49 +++++++++++++++++++++++++++++++++ 5 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 examples/timer.rs create mode 100644 src/timers.rs diff --git a/dump.bat b/dump.bat index 1996735e..8b90c30c 100644 --- a/dump.bat +++ b/dump.bat @@ -1,15 +1,17 @@ cargo build --examples -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/basic_keyinput >target/ex-basic_keyinput.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/basic_keyinput >target/ex-basic_keyinput.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/do_nothing >target/ex-do_nothing.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/do_nothing >target/ex-do_nothing.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode0 >target/ex-mode0.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/mode0 >target/ex-mode0.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode3 >target/ex-mode3.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/mode3 >target/ex-mode3.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode4 >target/ex-mode4.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/mode4 >target/ex-mode4.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/mode5 >target/ex-mode5.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/mode5 >target/ex-mode5.txt -arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std target/thumbv4t-none-eabi/debug/examples/paddle_ball >target/ex-paddle_ball.txt +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/paddle_ball >target/ex-paddle_ball.txt + +arm-none-eabi-objdump --headers --disassemble --demangle --architecture=armv4t --no-show-raw-insn -Mreg-names-std --visualize-jumps target/thumbv4t-none-eabi/debug/examples/timer >target/ex-timer.txt diff --git a/examples/timer.rs b/examples/timer.rs new file mode 100644 index 00000000..a2886d35 --- /dev/null +++ b/examples/timer.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +use gba::{ + asm_runtime::USER_IRQ_HANDLER, + bios::VBlankIntrWait, + gba_cell::GbaCell, + mmio::{BACKDROP_COLOR, DISPCNT, DISPSTAT, IE, IME, TIMER0_CONTROL}, + timers::{CpusPerTick, TimerControl}, + video::{Color, DisplayControl, DisplayStatus}, + IrqBits, +}; + +static SECONDS: GbaCell = GbaCell::new(0); + +gba::panic_handler!(mgba_log_err); + +#[no_mangle] +extern "C" fn main() -> ! { + USER_IRQ_HANDLER.write(Some(irq_handler)); + IE.write(IrqBits::new().with_vblank(true).with_timer0(true)); + IME.write(true); + DISPSTAT.write(DisplayStatus::new().with_vblank_irq(true)); + + TIMER0_CONTROL.write( + TimerControl::new() + .with_enabled(true) + .with_send_irq(true) + // .with_cpus_per_tick(CpusPerTick::_64), + ); + + DISPCNT.write(DisplayControl::new().with_bg_mode(3)); + loop { + VBlankIntrWait(); + BACKDROP_COLOR.write(Color(SECONDS.read() as u16)); + } +} + +extern "C" fn irq_handler(bits: IrqBits) { + if bits.timer0() { + SECONDS.write(SECONDS.read().wrapping_add(1)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 047c8700..a04d5b14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,7 @@ pub mod per_project_setup; pub mod per_system_setup; pub mod random; pub mod sample_art; +pub mod timers; pub mod video; #[cfg(feature = "critical-section")] diff --git a/src/mmio/peripheral_controls.rs b/src/mmio/peripheral_controls.rs index de0bbc95..41681721 100644 --- a/src/mmio/peripheral_controls.rs +++ b/src/mmio/peripheral_controls.rs @@ -1,3 +1,5 @@ +use crate::timers::TimerControl; + use super::*; /// Display Control setting. @@ -125,6 +127,44 @@ pub const DMA3_TRANSFER_COUNT: VolAddress = pub const DMA3_CONTROL: VolAddress = unsafe { VolAddress::new(0x0400_00DE) }; +/// Timer0's current counter value. +pub const TIMER0_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0100) }; +/// Timer1's current counter value. +pub const TIMER1_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0104) }; +/// Timer2's current counter value. +pub const TIMER2_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0108) }; +/// Timer3's current counter value. +pub const TIMER3_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_010C) }; + +/// The value for Timer0 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER0_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0100) }; +/// The value for Timer1 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER1_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0104) }; +/// The value for Timer2 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER2_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0108) }; +/// The value for Timer3 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER3_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_010C) }; + +/// Control bits for Timer 0. +pub const TIMER0_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_0102) }; + +/// Control bits for Timer 1. +pub const TIMER1_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_0106) }; + +/// Control bits for Timer 2. +pub const TIMER2_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_010A) }; + +/// Control bits for Timer 3. +pub const TIMER3_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_010E) }; + /// Key Input (read-only). /// /// Gives the low-active button state of all system buttons. diff --git a/src/timers.rs b/src/timers.rs new file mode 100644 index 00000000..96626411 --- /dev/null +++ b/src/timers.rs @@ -0,0 +1,49 @@ +//! Timer related data types. + +use bitfrob::{u8_with_bit, u8_with_region}; + +/// Control bits for one of the GBA's four timers. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct TimerControl(u8); +impl TimerControl { + /// A new, zeroed value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// The number of CPU cycles per timer tick. + #[inline] + pub const fn with_cpus_per_tick(self, cpus: CpusPerTick) -> Self { + Self(u8_with_region(0, 1, self.0, cpus as u8)) + } + /// If the timer should *only* tick when the lower-number timer overflows. + /// + /// * When set, this **overrides** the `cpus_per_tick` value and Timer N will + /// instead tick once per overflow of Timer (N-1). + /// * This has no effect for Timer 0, since it has no lower numbered timer. + #[inline] + pub const fn cascade_ticks(self, cascade: bool) -> Self { + Self(u8_with_bit(2, self.0, cascade)) + } + /// If an overflow of this timer should send an interrupt. + #[inline] + pub const fn with_send_irq(self, irq: bool) -> Self { + Self(u8_with_bit(6, self.0, irq)) + } + /// If this timer is enabled. + #[inline] + pub const fn with_enabled(self, enabled: bool) -> Self { + Self(u8_with_bit(7, self.0, enabled)) + } +} + +/// How many CPU cycles per timer tick. +#[repr(u8)] +#[allow(missing_docs)] +pub enum CpusPerTick { + _1 = 0, + _64 = 1, + _256 = 2, + _1024 = 3, +} From f842a0af094c6495bbb90ae0567a3554c1517499 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 01:50:18 -0600 Subject: [PATCH 75/89] whoops we gotta keep those masked off. --- src/asm_runtime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index e3df7651..d39b9984 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -225,7 +225,7 @@ core::arch::global_asm! { "push {{r3, lr}}", a32_set_cpu_control!(System, irq_masked = true, fiq_masked = true), a32_fake_blx!("r12"), - a32_set_cpu_control!(IRQ, irq_masked = false, fiq_masked = false), + a32_set_cpu_control!(IRQ, irq_masked = true, fiq_masked = true), "pop {{r3, lr}}", a32_write_spsr_from!("r3"), }), From 21e7e990c0658df4c74bd5b48a8fb6da4af76ada Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 02:27:29 -0600 Subject: [PATCH 76/89] this makes the asm runtime robust against bad emulators that don't set r0 before calling the assembly handler, which apparently exist. --- src/asm_runtime.rs | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index d39b9984..0d319de4 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -183,44 +183,21 @@ core::arch::global_asm! { "_asm_runtime_irq_handler:", // We're allowed to use the usual C ABI registers. - // Note(Lokathor): So the trouble here is that we want to 16-bit access - // BASE+0x200 and also BASE-8. Immediate offsets for 16-bit access can't be - // that large of a range. So we'll have to actually do an `add` or `sub` to - // put the correct offset from our base pointer into a register. Also, our - // base pointer is in `r0`, but we need to pass the bits to the user - // function in `r0` when we do that call. The compromise here is that we'll - // keep our base address in `r0`, manipulate the bits in `r1`, and then if - // we're actually doing a call to a user fn we can move the bits down to - // `r0` after we don't need the base pointer anymore. This seems to be the - // way that has the least register shuffling. - - // Assumed: - // * r0: 0x0400_0000 (set by the BIOS) - + /* A fox wizard told me how to do this one */ // handle MMIO interrupt system - "add r12, r0, #0x200", // 16-bit access offsets can't be too big - "ldr r1, [r12]", // IE_IF.read32() - "and r1, r1, r1, LSR #16", // IE & IF - "strh r1, [r12, #2]", // write IF - - // Now: - // * r0: 0x0400_0000 - // * r1: irq bits - + "mov r12, 0x04000000", // load r12 with a 1 cycle value + "ldr r0, [r12, #0x200]!", // load IE_IF with r12 writeback + "and r0, r0, r0, LSR #16", // bits = IE & IF + "strh r0, [r12, #2]", // write16 to just IF // handle BIOS IntrWait system - "ldrh r2, [r0, #-8]", // read the `has_occurred` flags - "orr r2, r2, r1", // activate the new bits, if any - "strh r2, [r0, #-8]", // update the value - - // Now: - // * r0: 0x0400_0000 - // * r1: irq bits + "ldr r1, [r12, #-0x208]!", // load BIOS_IF_?? with r12 writeback + "orr r1, r1, r0", // mark `bits` as `has_occurred` + "strh r1, [r12]", // write16 to just BIOS_IF // Get the user handler fn pointer, call it if non-null. "ldr r12, ={USER_IRQ_HANDLER}", "ldr r12, [r12]", when!(("r12" != "#0")[1] { - "mov r0, r1", a32_read_spsr_to!("r3"), "push {{r3, lr}}", a32_set_cpu_control!(System, irq_masked = true, fiq_masked = true), From 2092429fe0586611fa009d012f983d116e166acb Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 02:30:02 -0600 Subject: [PATCH 77/89] rename the global, and also make the demo less visually intense --- examples/timer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/timer.rs b/examples/timer.rs index a2886d35..5de911bb 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -11,7 +11,7 @@ use gba::{ IrqBits, }; -static SECONDS: GbaCell = GbaCell::new(0); +static OVERFLOWS: GbaCell = GbaCell::new(0); gba::panic_handler!(mgba_log_err); @@ -26,18 +26,18 @@ extern "C" fn main() -> ! { TimerControl::new() .with_enabled(true) .with_send_irq(true) - // .with_cpus_per_tick(CpusPerTick::_64), + .with_cpus_per_tick(CpusPerTick::_64), ); DISPCNT.write(DisplayControl::new().with_bg_mode(3)); loop { VBlankIntrWait(); - BACKDROP_COLOR.write(Color(SECONDS.read() as u16)); + BACKDROP_COLOR.write(Color(OVERFLOWS.read() as u16)); } } extern "C" fn irq_handler(bits: IrqBits) { if bits.timer0() { - SECONDS.write(SECONDS.read().wrapping_add(1)); + OVERFLOWS.write(OVERFLOWS.read().wrapping_add(1)); } } From 57f5f3afc0934b9bf7aa82574f24a76793bed629 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 12:09:32 -0600 Subject: [PATCH 78/89] ditch the mmio module --- examples/basic_keyinput.rs | 8 +- examples/mode0.rs | 8 +- examples/mode3.rs | 8 +- examples/mode4.rs | 9 +- examples/mode5.rs | 8 +- examples/objects.rs | 15 +- examples/paddle_ball.rs | 6 +- examples/timer.rs | 9 +- src/asm_runtime.rs | 8 +- src/bios.rs | 2 +- src/dma.rs | 98 +++++++- src/gba_cell.rs | 2 +- src/irq.rs | 214 ++++++++++++++++++ src/keys.rs | 99 ++++++++ src/lib.rs | 294 ++---------------------- src/mgba.rs | 23 +- src/mmio/mod.rs | 38 ---- src/mmio/peripheral_controls.rs | 217 ------------------ src/mmio/video_memory.rs | 129 ----------- src/obj.rs | 250 -------------------- src/timers.rs | 39 ++++ src/video.rs | 388 +++++++++++++++++++++++++++++++- 22 files changed, 918 insertions(+), 954 deletions(-) create mode 100644 src/irq.rs create mode 100644 src/keys.rs delete mode 100644 src/mmio/mod.rs delete mode 100644 src/mmio/peripheral_controls.rs delete mode 100644 src/mmio/video_memory.rs delete mode 100644 src/obj.rs diff --git a/examples/basic_keyinput.rs b/examples/basic_keyinput.rs index 6756a18d..bc584be3 100644 --- a/examples/basic_keyinput.rs +++ b/examples/basic_keyinput.rs @@ -7,10 +7,12 @@ use core::ptr::{addr_of, addr_of_mut}; use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, + irq::{IrqBits, IE, IME}, + keys::KEYINPUT, mgba::{MgbaLogLevel, MgbaLogger}, - mmio::{BACKDROP_COLOR, DISPCNT, DISPSTAT, IE, IME, KEYINPUT}, - video::{Color, DisplayControl, DisplayStatus}, - IrqBits, + video::{ + Color, DisplayControl, DisplayStatus, BACKDROP_COLOR, DISPCNT, DISPSTAT, + }, }; gba::panic_handler!(mgba_log_err); diff --git a/examples/mode0.rs b/examples/mode0.rs index ec1c91d6..937375d6 100644 --- a/examples/mode0.rs +++ b/examples/mode0.rs @@ -3,10 +3,12 @@ use gba::{ bios::VBlankIntrWait, - mmio::{BG_PALRAM, DISPCNT, DISPSTAT, IE, IME, VRAM_BG_TILE4}, + irq::{IrqBits, IE, IME}, sample_art::decompress_cga_face_to_vram_4bpp, - video::{Color, DisplayControl, DisplayStatus}, - IrqBits, + video::{ + Color, DisplayControl, DisplayStatus, BG_PALRAM, DISPCNT, DISPSTAT, + VRAM_BG_TILE4, + }, }; gba::panic_handler!(empty_loop); diff --git a/examples/mode3.rs b/examples/mode3.rs index e5dd132f..95daa2d8 100644 --- a/examples/mode3.rs +++ b/examples/mode3.rs @@ -3,9 +3,11 @@ use gba::{ bios::VBlankIntrWait, - mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, - video::{Color, DisplayControl, DisplayStatus}, - IrqBits, + irq::{IrqBits, IE, IME}, + keys::KEYINPUT, + video::{ + Color, DisplayControl, DisplayStatus, DISPCNT, DISPSTAT, MODE3_VRAM, + }, }; gba::panic_handler!(empty_loop); diff --git a/examples/mode4.rs b/examples/mode4.rs index 8766d56f..d1ff8ee9 100644 --- a/examples/mode4.rs +++ b/examples/mode4.rs @@ -3,9 +3,12 @@ use gba::{ bios::VBlankIntrWait, - mmio::{BG_PALRAM, DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE4_VRAM}, - video::{Color, DisplayControl, DisplayStatus}, - IrqBits, + irq::{IrqBits, IE, IME}, + keys::KEYINPUT, + video::{ + Color, DisplayControl, DisplayStatus, BG_PALRAM, DISPCNT, DISPSTAT, + MODE4_VRAM, + }, }; gba::panic_handler!(empty_loop); diff --git a/examples/mode5.rs b/examples/mode5.rs index 89a5f7a5..25987fe6 100644 --- a/examples/mode5.rs +++ b/examples/mode5.rs @@ -3,9 +3,11 @@ use gba::{ bios::VBlankIntrWait, - mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE5_VRAM}, - video::{Color, DisplayControl, DisplayStatus}, - IrqBits, + irq::{IrqBits, IE, IME}, + keys::KEYINPUT, + video::{ + Color, DisplayControl, DisplayStatus, DISPCNT, DISPSTAT, MODE5_VRAM, + }, }; gba::panic_handler!(empty_loop); diff --git a/examples/objects.rs b/examples/objects.rs index abb6459c..96dfdfaf 100644 --- a/examples/objects.rs +++ b/examples/objects.rs @@ -5,14 +5,15 @@ use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - mmio::{ - obj_palbank, BG0CNT, BG_PALRAM, DISPCNT, DISPSTAT, IE, IME, KEYINPUT, - OBJ_ATTR0, OBJ_ATTR_ALL, TEXT_SCREENBLOCKS, VRAM_BG_TILE4, VRAM_OBJ_TILE4, - }, - obj::{ObjAttr, ObjAttr0, ObjDisplayStyle}, + irq::{IrqBits, IE, IME}, + keys::KEYINPUT, sample_art::{decompress_cga_face_to_vram_4bpp, Cga}, - video::{BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry}, - IrqBits, + video::{ + obj_palbank, BackgroundControl, Color, DisplayControl, DisplayStatus, + ObjAttr, ObjAttr0, ObjDisplayStyle, TextEntry, BG0CNT, BG_PALRAM, DISPCNT, + DISPSTAT, OBJ_ATTR0, OBJ_ATTR_ALL, TEXT_SCREENBLOCKS, VRAM_BG_TILE4, + VRAM_OBJ_TILE4, + }, }; gba::panic_handler!(mgba_log_err); diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 15e8e665..416f19e9 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -7,9 +7,9 @@ use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT}, - video::{Color, DisplayControl, DisplayStatus, Mode3}, - IrqBits, KeyInput, + irq::{IrqBits, IE, IME}, + keys::{KeyInput, KEYINPUT}, + video::{Color, DisplayControl, DisplayStatus, Mode3, DISPCNT, DISPSTAT}, }; const SCREEN_WIDTH: u16 = 240; diff --git a/examples/timer.rs b/examples/timer.rs index 5de911bb..b4bed9ad 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -5,10 +5,11 @@ use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - mmio::{BACKDROP_COLOR, DISPCNT, DISPSTAT, IE, IME, TIMER0_CONTROL}, - timers::{CpusPerTick, TimerControl}, - video::{Color, DisplayControl, DisplayStatus}, - IrqBits, + irq::{IrqBits, IE, IME}, + timers::{CpusPerTick, TimerControl, TIMER0_CONTROL}, + video::{ + Color, DisplayControl, DisplayStatus, BACKDROP_COLOR, DISPCNT, DISPSTAT, + }, }; static OVERFLOWS: GbaCell = GbaCell::new(0); diff --git a/src/asm_runtime.rs b/src/asm_runtime.rs index 0d319de4..3d12d215 100644 --- a/src/asm_runtime.rs +++ b/src/asm_runtime.rs @@ -6,7 +6,8 @@ // cargo feature enabled, and so they should all have the `track_caller` // attribute set whenever the `on_gba` feature is *disabled* -use crate::{gba_cell::GbaCell, IrqBits}; +use crate::gba_cell::GbaCell; + use bracer::*; /// Inserts a `nop` instruction. @@ -215,5 +216,6 @@ core::arch::global_asm! { /// The user-provided interrupt request handler function. #[cfg(feature = "on_gba")] -pub static USER_IRQ_HANDLER: GbaCell> = - GbaCell::new(None); +pub static USER_IRQ_HANDLER: GbaCell< + Option, +> = GbaCell::new(None); diff --git a/src/bios.rs b/src/bios.rs index 0fd90883..89b52ebd 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -9,7 +9,7 @@ //! BIOS functions have a rather high calls overhead compared to a normal //! foreign function. -use crate::IrqBits; +use crate::irq::IrqBits; /// `0x04`: Waits for a specific interrupt type(s) to happen. /// diff --git a/src/dma.rs b/src/dma.rs index 8dcf776c..fb42413d 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -42,13 +42,13 @@ //! Using the DMA units is equivalent to playing around with raw pointers. It //! must be handled very carefully, or memory corruption can occur. +use core::ffi::c_void; + use bitfrob::{u16_with_bit, u16_with_region}; -use voladdress::{Safe, VolRegion}; +use video::Tile4; +use voladdress::{Safe, Unsafe, VolRegion}; -use crate::{ - mmio::{DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, DMA3_TRANSFER_COUNT}, - video::Tile4, -}; +use super::*; /// Controls the activity of a DMA unit. #[derive(Debug, Clone, Copy, Default)] @@ -214,3 +214,91 @@ pub fn dma3_copy_tile4(src: &[Tile4], dest: VolRegion) { dma3_copy_u32(src.as_ptr().cast(), dest.as_mut_ptr().cast(), src.len() * 8) }; } + +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA0_SOURCE: VolAddress<*const c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00B0) }; + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA0_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00B4) }; + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x4000` +pub const DMA0_TRANSFER_COUNT: VolAddress = + unsafe { VolAddress::new(0x0400_00B8) }; + +/// DMA3 Control Bits. +pub const DMA0_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00BA) }; + +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA1_SOURCE: VolAddress<*const c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00BC) }; + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA1_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00C0) }; + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x4000` +pub const DMA1_TRANSFER_COUNT: VolAddress = + unsafe { VolAddress::new(0x0400_00C4) }; + +/// DMA3 Control Bits. +pub const DMA1_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00C6) }; + +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA2_SOURCE: VolAddress<*const c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00C8) }; + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA2_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00CC) }; + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x4000` +pub const DMA2_TRANSFER_COUNT: VolAddress = + unsafe { VolAddress::new(0x0400_00D0) }; + +/// DMA3 Control Bits. +pub const DMA2_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00D2) }; + +/// Source address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA3_SOURCE: VolAddress<*const c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00D4) }; + +/// Destination address for DMA3. +/// +/// The correct pointer type depends on the transfer mode used. +pub const DMA3_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = + unsafe { VolAddress::new(0x0400_00D8) }; + +/// The number of transfers desired. +/// +/// A value of 0 indicates the maximum number of transfers: `0x1_0000` +pub const DMA3_TRANSFER_COUNT: VolAddress = + unsafe { VolAddress::new(0x0400_00DC) }; + +/// DMA3 Control Bits. +pub const DMA3_CONTROL: VolAddress = + unsafe { VolAddress::new(0x0400_00DE) }; diff --git a/src/gba_cell.rs b/src/gba_cell.rs index ba8f9b44..c5400d0a 100644 --- a/src/gba_cell.rs +++ b/src/gba_cell.rs @@ -6,7 +6,7 @@ use core::{ ptr::NonNull, }; -use crate::{video::Color, IrqBits, KeyInput}; +use crate::{irq::IrqBits, keys::KeyInput, video::Color}; /// Marker trait bound for the methods of [`GbaCell`]. /// diff --git a/src/irq.rs b/src/irq.rs new file mode 100644 index 00000000..5c7bef47 --- /dev/null +++ b/src/irq.rs @@ -0,0 +1,214 @@ +//! Hardware interrupt handling + +use super::*; + +/// Interrupt bit flags. +#[derive(Clone, Copy, Default)] +#[repr(transparent)] +pub struct IrqBits(pub u16); +impl IrqBits { + /// The vblank bit. + pub const VBLANK: Self = Self::new().with_vblank(true); + + /// Makes a new, empty value. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Vertical-blank + #[inline] + #[must_use] + pub const fn vblank(self) -> bool { + u16_get_bit(0, self.0) + } + /// Horizontal-blank + #[inline] + #[must_use] + pub const fn hblank(self) -> bool { + u16_get_bit(1, self.0) + } + /// Vertical-counter match + #[inline] + #[must_use] + pub const fn vcount(self) -> bool { + u16_get_bit(2, self.0) + } + /// Timer 0 overflow + #[inline] + #[must_use] + pub const fn timer0(self) -> bool { + u16_get_bit(3, self.0) + } + /// Timer 1 overflow + #[inline] + #[must_use] + pub const fn timer1(self) -> bool { + u16_get_bit(4, self.0) + } + /// Timer 2 overflow + #[inline] + #[must_use] + pub const fn timer2(self) -> bool { + u16_get_bit(5, self.0) + } + /// Timer 3 overflow + #[inline] + #[must_use] + pub const fn timer3(self) -> bool { + u16_get_bit(6, self.0) + } + /// Serial port communication + #[inline] + #[must_use] + pub const fn serial(self) -> bool { + u16_get_bit(7, self.0) + } + /// DMA 0 complete + #[inline] + #[must_use] + pub const fn dma0(self) -> bool { + u16_get_bit(8, self.0) + } + /// DMA 1 complete + #[inline] + #[must_use] + pub const fn dma1(self) -> bool { + u16_get_bit(9, self.0) + } + /// DMA 2 complete + #[inline] + #[must_use] + pub const fn dma2(self) -> bool { + u16_get_bit(10, self.0) + } + /// DMA 3 complete + #[inline] + #[must_use] + pub const fn dma3(self) -> bool { + u16_get_bit(11, self.0) + } + /// Keypad match + #[inline] + #[must_use] + pub const fn keypad(self) -> bool { + u16_get_bit(12, self.0) + } + /// Game pak + #[inline] + #[must_use] + pub const fn gamepak(self) -> bool { + u16_get_bit(13, self.0) + } + + /// Set the vblank bit. + #[inline] + #[must_use] + pub const fn with_vblank(self, vblank: bool) -> Self { + Self(u16_with_bit(0, self.0, vblank)) + } + /// Set the hblank bit. + #[inline] + #[must_use] + pub const fn with_hblank(self, hblank: bool) -> Self { + Self(u16_with_bit(1, self.0, hblank)) + } + /// Set the vcount bit. + #[inline] + #[must_use] + pub const fn with_vcount(self, vcount: bool) -> Self { + Self(u16_with_bit(2, self.0, vcount)) + } + /// Set the timer0 bit. + #[inline] + #[must_use] + pub const fn with_timer0(self, timer0: bool) -> Self { + Self(u16_with_bit(3, self.0, timer0)) + } + /// Set the timer1 bit. + #[inline] + #[must_use] + pub const fn with_timer1(self, timer1: bool) -> Self { + Self(u16_with_bit(4, self.0, timer1)) + } + /// Set the timer2 bit. + #[inline] + #[must_use] + pub const fn with_timer2(self, timer2: bool) -> Self { + Self(u16_with_bit(5, self.0, timer2)) + } + /// Set the timer3 bit. + #[inline] + #[must_use] + pub const fn with_timer3(self, timer3: bool) -> Self { + Self(u16_with_bit(6, self.0, timer3)) + } + /// Set the serial bit. + #[inline] + #[must_use] + pub const fn with_serial(self, serial: bool) -> Self { + Self(u16_with_bit(7, self.0, serial)) + } + /// Set the dma0 bit. + #[inline] + #[must_use] + pub const fn with_dma0(self, dma0: bool) -> Self { + Self(u16_with_bit(8, self.0, dma0)) + } + /// Set the dma1 bit. + #[inline] + #[must_use] + pub const fn with_dma1(self, dma1: bool) -> Self { + Self(u16_with_bit(9, self.0, dma1)) + } + /// Set the dma2 bit. + #[inline] + #[must_use] + pub const fn with_dma2(self, dma2: bool) -> Self { + Self(u16_with_bit(10, self.0, dma2)) + } + /// Set the dma3 bit. + #[inline] + #[must_use] + pub const fn with_dma3(self, dma3: bool) -> Self { + Self(u16_with_bit(11, self.0, dma3)) + } + /// Set the keypad bit. + #[inline] + #[must_use] + pub const fn with_keypad(self, keypad: bool) -> Self { + Self(u16_with_bit(12, self.0, keypad)) + } + /// Set the gamepak bit. + #[inline] + #[must_use] + pub const fn with_gamepak(self, gamepak: bool) -> Self { + Self(u16_with_bit(13, self.0, gamepak)) + } +} + +/// Interrupts Enabled. +/// +/// When any sub-system is set to "send" interrupts, that interrupt type must +/// *also* be configured here or it won't actually be "received" by the CPU. +pub const IE: PlainAddr = unsafe { VolAddress::new(0x0400_0200) }; + +/// Interrupts Flagged. +/// +/// These are the interrupts that are pending, and haven't been handled. Clear a +/// pending interrupt by writing an [`IrqBits`] value with that bit enabled. The +/// assembly runtime handles this automatically, so you don't normally need to +/// interact with `IF` at all. +pub const IF: PlainAddr = unsafe { VolAddress::new(0x0400_0202) }; + +/// Interrupt Master Enable +/// +/// * When this is set to `true`, hardware interrupts that are flagged will +/// immediately run the interrupt handler. +/// * When this is `false`, any interrupt events that are flagged will be left +/// pending until this is again set to `true`. +/// +/// This defaults to `false`. +/// +/// Technically there's a two CPU cycle delay between this being written and +/// interrupts actually being enabled/disabled. In practice, it doesn't matter. +pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; diff --git a/src/keys.rs b/src/keys.rs new file mode 100644 index 00000000..8790236c --- /dev/null +++ b/src/keys.rs @@ -0,0 +1,99 @@ +//! Keypad (button) reading. + +use super::*; + +/// Keypad input state. +#[derive(Clone, Copy, Default)] +#[repr(transparent)] +pub struct KeyInput(pub u16); +impl KeyInput { + /// If `a` is pressed (left primary button) + #[inline] + #[must_use] + pub const fn a(self) -> bool { + !bitfrob::u16_get_bit(0, self.0) + } + /// If `b` is pressed (right primary button) + #[inline] + #[must_use] + pub const fn b(self) -> bool { + !bitfrob::u16_get_bit(1, self.0) + } + /// If `select` is pressed (lower/left secondary button) + #[inline] + #[must_use] + pub const fn select(self) -> bool { + !bitfrob::u16_get_bit(2, self.0) + } + /// If `start` is pressed (upper/right secondary button) + #[inline] + #[must_use] + pub const fn start(self) -> bool { + !bitfrob::u16_get_bit(3, self.0) + } + /// If `right` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn right(self) -> bool { + !bitfrob::u16_get_bit(4, self.0) + } + /// If `left` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn left(self) -> bool { + !bitfrob::u16_get_bit(5, self.0) + } + /// If `up` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn up(self) -> bool { + !bitfrob::u16_get_bit(6, self.0) + } + /// If `down` is pressed (d-pad) + #[inline] + #[must_use] + pub const fn down(self) -> bool { + !bitfrob::u16_get_bit(7, self.0) + } + /// If `r` is pressed (right shoulder button) + #[inline] + #[must_use] + pub const fn r(self) -> bool { + !bitfrob::u16_get_bit(8, self.0) + } + /// If `l` is pressed (left shoulder button) + #[inline] + #[must_use] + pub const fn l(self) -> bool { + !bitfrob::u16_get_bit(9, self.0) + } + /// Delta X of the d-pad. right +1, left -1. + #[inline] + #[must_use] + pub const fn dx(self) -> i8 { + if self.right() { + 1 + } else if self.left() { + -1 + } else { + 0 + } + } + /// Delta Y of the d-pad. up +1, down -1. + #[inline] + #[must_use] + pub const fn dy(self) -> i8 { + if self.up() { + 1 + } else if self.down() { + -1 + } else { + 0 + } + } +} + +/// Key Input (read-only). +/// +/// Gives the low-active button state of all system buttons. +pub const KEYINPUT: RoAddr = unsafe { VolAddress::new(0x0400_0130) }; diff --git a/src/lib.rs b/src/lib.rs index a04d5b14..5b885888 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,7 @@ //! * [Per Project Setup][`per_project_setup`] use bitfrob::{u16_get_bit, u16_with_bit}; +use voladdress::VolAddress; macro_rules! on_gba_or_unimplemented { ($($token_tree:tt)*) => { @@ -55,10 +56,10 @@ pub mod bios; pub mod dma; pub mod gba_cell; pub mod gba_fixed; +pub mod irq; +pub mod keys; pub mod mem; pub mod mgba; -pub mod mmio; -pub mod obj; pub mod panic_handlers; pub mod per_project_setup; pub mod per_system_setup; @@ -67,6 +68,20 @@ pub mod sample_art; pub mod timers; pub mod video; +/// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` +/// cargo feature. +#[cfg(feature = "on_gba")] +type SOGBA = voladdress::Safe; +#[cfg(not(feature = "on_gba"))] +type SOGBA = voladdress::Unsafe; + +/// Responds "normally" to read/write, just holds a setting +type PlainAddr = VolAddress; +/// Read-only addr +type RoAddr = VolAddress; +/// Write-only addr +type WoAddr = VolAddress; + #[cfg(feature = "critical-section")] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "critical-section")))] pub mod critical_section; @@ -126,278 +141,3 @@ pub type i16fx14 = crate::gba_fixed::Fixed; #[cfg(not(feature = "fixed"))] #[allow(non_camel_case_types)] pub type i32fx8 = crate::gba_fixed::Fixed; - -/// Keypad input state. -#[derive(Clone, Copy, Default)] -#[repr(transparent)] -pub struct KeyInput(pub u16); -impl KeyInput { - /// If `a` is pressed (left primary button) - #[inline] - #[must_use] - pub const fn a(self) -> bool { - !bitfrob::u16_get_bit(0, self.0) - } - /// If `b` is pressed (right primary button) - #[inline] - #[must_use] - pub const fn b(self) -> bool { - !bitfrob::u16_get_bit(1, self.0) - } - /// If `select` is pressed (lower/left secondary button) - #[inline] - #[must_use] - pub const fn select(self) -> bool { - !bitfrob::u16_get_bit(2, self.0) - } - /// If `start` is pressed (upper/right secondary button) - #[inline] - #[must_use] - pub const fn start(self) -> bool { - !bitfrob::u16_get_bit(3, self.0) - } - /// If `right` is pressed (d-pad) - #[inline] - #[must_use] - pub const fn right(self) -> bool { - !bitfrob::u16_get_bit(4, self.0) - } - /// If `left` is pressed (d-pad) - #[inline] - #[must_use] - pub const fn left(self) -> bool { - !bitfrob::u16_get_bit(5, self.0) - } - /// If `up` is pressed (d-pad) - #[inline] - #[must_use] - pub const fn up(self) -> bool { - !bitfrob::u16_get_bit(6, self.0) - } - /// If `down` is pressed (d-pad) - #[inline] - #[must_use] - pub const fn down(self) -> bool { - !bitfrob::u16_get_bit(7, self.0) - } - /// If `r` is pressed (right shoulder button) - #[inline] - #[must_use] - pub const fn r(self) -> bool { - !bitfrob::u16_get_bit(8, self.0) - } - /// If `l` is pressed (left shoulder button) - #[inline] - #[must_use] - pub const fn l(self) -> bool { - !bitfrob::u16_get_bit(9, self.0) - } - /// Delta X of the d-pad. right +1, left -1. - #[inline] - #[must_use] - pub const fn dx(self) -> i8 { - if self.right() { - 1 - } else if self.left() { - -1 - } else { - 0 - } - } - /// Delta Y of the d-pad. up +1, down -1. - #[inline] - #[must_use] - pub const fn dy(self) -> i8 { - if self.up() { - 1 - } else if self.down() { - -1 - } else { - 0 - } - } -} - -/// Interrupt bit flags. -#[derive(Clone, Copy, Default)] -#[repr(transparent)] -pub struct IrqBits(u16); -impl IrqBits { - /// The vblank bit. - pub const VBLANK: Self = Self::new().with_vblank(true); - - /// Makes a new, empty value. - #[inline] - pub const fn new() -> Self { - Self(0) - } - /// Vertical-blank - #[inline] - #[must_use] - pub const fn vblank(self) -> bool { - u16_get_bit(0, self.0) - } - /// Horizontal-blank - #[inline] - #[must_use] - pub const fn hblank(self) -> bool { - u16_get_bit(1, self.0) - } - /// Vertical-counter match - #[inline] - #[must_use] - pub const fn vcount(self) -> bool { - u16_get_bit(2, self.0) - } - /// Timer 0 overflow - #[inline] - #[must_use] - pub const fn timer0(self) -> bool { - u16_get_bit(3, self.0) - } - /// Timer 1 overflow - #[inline] - #[must_use] - pub const fn timer1(self) -> bool { - u16_get_bit(4, self.0) - } - /// Timer 2 overflow - #[inline] - #[must_use] - pub const fn timer2(self) -> bool { - u16_get_bit(5, self.0) - } - /// Timer 3 overflow - #[inline] - #[must_use] - pub const fn timer3(self) -> bool { - u16_get_bit(6, self.0) - } - /// Serial port communication - #[inline] - #[must_use] - pub const fn serial(self) -> bool { - u16_get_bit(7, self.0) - } - /// DMA 0 complete - #[inline] - #[must_use] - pub const fn dma0(self) -> bool { - u16_get_bit(8, self.0) - } - /// DMA 1 complete - #[inline] - #[must_use] - pub const fn dma1(self) -> bool { - u16_get_bit(9, self.0) - } - /// DMA 2 complete - #[inline] - #[must_use] - pub const fn dma2(self) -> bool { - u16_get_bit(10, self.0) - } - /// DMA 3 complete - #[inline] - #[must_use] - pub const fn dma3(self) -> bool { - u16_get_bit(11, self.0) - } - /// Keypad match - #[inline] - #[must_use] - pub const fn keypad(self) -> bool { - u16_get_bit(12, self.0) - } - /// Game pak - #[inline] - #[must_use] - pub const fn gamepak(self) -> bool { - u16_get_bit(13, self.0) - } - - /// Set the vblank bit. - #[inline] - #[must_use] - pub const fn with_vblank(self, vblank: bool) -> Self { - Self(u16_with_bit(0, self.0, vblank)) - } - /// Set the hblank bit. - #[inline] - #[must_use] - pub const fn with_hblank(self, hblank: bool) -> Self { - Self(u16_with_bit(1, self.0, hblank)) - } - /// Set the vcount bit. - #[inline] - #[must_use] - pub const fn with_vcount(self, vcount: bool) -> Self { - Self(u16_with_bit(2, self.0, vcount)) - } - /// Set the timer0 bit. - #[inline] - #[must_use] - pub const fn with_timer0(self, timer0: bool) -> Self { - Self(u16_with_bit(3, self.0, timer0)) - } - /// Set the timer1 bit. - #[inline] - #[must_use] - pub const fn with_timer1(self, timer1: bool) -> Self { - Self(u16_with_bit(4, self.0, timer1)) - } - /// Set the timer2 bit. - #[inline] - #[must_use] - pub const fn with_timer2(self, timer2: bool) -> Self { - Self(u16_with_bit(5, self.0, timer2)) - } - /// Set the timer3 bit. - #[inline] - #[must_use] - pub const fn with_timer3(self, timer3: bool) -> Self { - Self(u16_with_bit(6, self.0, timer3)) - } - /// Set the serial bit. - #[inline] - #[must_use] - pub const fn with_serial(self, serial: bool) -> Self { - Self(u16_with_bit(7, self.0, serial)) - } - /// Set the dma0 bit. - #[inline] - #[must_use] - pub const fn with_dma0(self, dma0: bool) -> Self { - Self(u16_with_bit(8, self.0, dma0)) - } - /// Set the dma1 bit. - #[inline] - #[must_use] - pub const fn with_dma1(self, dma1: bool) -> Self { - Self(u16_with_bit(9, self.0, dma1)) - } - /// Set the dma2 bit. - #[inline] - #[must_use] - pub const fn with_dma2(self, dma2: bool) -> Self { - Self(u16_with_bit(10, self.0, dma2)) - } - /// Set the dma3 bit. - #[inline] - #[must_use] - pub const fn with_dma3(self, dma3: bool) -> Self { - Self(u16_with_bit(11, self.0, dma3)) - } - /// Set the keypad bit. - #[inline] - #[must_use] - pub const fn with_keypad(self, keypad: bool) -> Self { - Self(u16_with_bit(12, self.0, keypad)) - } - /// Set the gamepak bit. - #[inline] - #[must_use] - pub const fn with_gamepak(self, gamepak: bool) -> Self { - Self(u16_with_bit(13, self.0, gamepak)) - } -} diff --git a/src/mgba.rs b/src/mgba.rs index 41bbbe86..2614bf9c 100644 --- a/src/mgba.rs +++ b/src/mgba.rs @@ -30,7 +30,9 @@ //! logs at that message level and also implicitly zeroes the message buffer so //! that it's ready for the next message. -use crate::mmio::{MGBA_LOG_BUFFER, MGBA_LOG_ENABLE, MGBA_LOG_SEND}; +use voladdress::VolBlock; + +use super::*; /// This is what you write to [`MGBA_LOG_ENABLE`] to signal to the emulator that /// you want to do logging. @@ -144,3 +146,22 @@ impl core::fmt::Write for MgbaLogger { ); } } + +/// The buffer to put logging messages into. +/// +/// The first `\0` in the buffer is the end of each message. +pub const MGBA_LOG_BUFFER: VolBlock = + unsafe { VolBlock::new(0x04FF_F600) }; + +/// Write to this each time you want to send out the current buffer content. +/// +/// It also resets the buffer content. +pub const MGBA_LOG_SEND: WoAddr = + unsafe { VolAddress::new(0x04FFF700) }; + +/// Allows you to enable/disable mGBA logging. +/// +/// This is enabled by default by the assembly runtime, so you don't normally +/// need to touch this. +pub const MGBA_LOG_ENABLE: PlainAddr = + unsafe { VolAddress::new(0x04FF_F780) }; diff --git a/src/mmio/mod.rs b/src/mmio/mod.rs deleted file mode 100644 index 3166b5f4..00000000 --- a/src/mmio/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Definitions for Memory-mapped IO (hardware control). - -use core::ffi::c_void; - -use bitfrob::u8x2; -#[allow(unused_imports)] -use voladdress::VolAddress; -use voladdress::{Unsafe, VolBlock, VolGrid2d, VolGrid2dStrided, VolSeries}; - -use crate::{ - dma::DmaControl, - mgba::MgbaLogLevel, - obj::{ObjAttr, ObjAttr0, ObjAttr1, ObjAttr2}, - video::{ - BackgroundControl, Color, DisplayControl, DisplayStatus, TextEntry, Tile4, - }, - IrqBits, KeyInput, -}; - -/// "safe on GBA", which is either Safe or Unsafe according to the `on_gba` -/// cargo feature. -#[cfg(feature = "on_gba")] -type SOGBA = voladdress::Safe; -#[cfg(not(feature = "on_gba"))] -type SOGBA = voladdress::Unsafe; - -/// Responds "normally" to read/write, just holds a setting -type PlainAddr = VolAddress; -/// Read-only addr -type RoAddr = VolAddress; -/// Write-only addr -type WoAddr = VolAddress; - -mod peripheral_controls; -mod video_memory; - -pub use peripheral_controls::*; -pub use video_memory::*; diff --git a/src/mmio/peripheral_controls.rs b/src/mmio/peripheral_controls.rs deleted file mode 100644 index 41681721..00000000 --- a/src/mmio/peripheral_controls.rs +++ /dev/null @@ -1,217 +0,0 @@ -use crate::timers::TimerControl; - -use super::*; - -/// Display Control setting. -/// -/// This sets what background mode is active, as well as various related -/// details. -/// -/// Unlike most MMIO, this doesn't have an "all 0" state at boot. The -/// `forced_blank` bit it left set by the BIOS's startup routine. -pub const DISPCNT: PlainAddr = - unsafe { VolAddress::new(0x0400_0000) }; - -/// Display Status setting. -/// -/// Gives info on the display state, and controls display-based interrupts. -pub const DISPSTAT: PlainAddr = - unsafe { VolAddress::new(0x0400_0004) }; - -/// The current scanline that the display is working on. -/// -/// Values of 160 to 227 indicate that a vertical blank line is happening. -pub const VCOUNT: RoAddr = unsafe { VolAddress::new(0x0400_0006) }; - -/// Background 0 controls -pub const BG0CNT: PlainAddr = - unsafe { VolAddress::new(0x0400_0008) }; - -/// Background 1 controls -pub const BG1CNT: PlainAddr = - unsafe { VolAddress::new(0x0400_000A) }; - -/// Background 2 controls -pub const BG2CNT: PlainAddr = - unsafe { VolAddress::new(0x0400_000C) }; - -/// Background 3 controls -pub const BG3CNT: PlainAddr = - unsafe { VolAddress::new(0x0400_000E) }; - -/// Source address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA0_SOURCE: VolAddress<*const c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00B0) }; - -/// Destination address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA0_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00B4) }; - -/// The number of transfers desired. -/// -/// A value of 0 indicates the maximum number of transfers: `0x4000` -pub const DMA0_TRANSFER_COUNT: VolAddress = - unsafe { VolAddress::new(0x0400_00B8) }; - -/// DMA3 Control Bits. -pub const DMA0_CONTROL: VolAddress = - unsafe { VolAddress::new(0x0400_00BA) }; - -/// Source address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA1_SOURCE: VolAddress<*const c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00BC) }; - -/// Destination address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA1_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00C0) }; - -/// The number of transfers desired. -/// -/// A value of 0 indicates the maximum number of transfers: `0x4000` -pub const DMA1_TRANSFER_COUNT: VolAddress = - unsafe { VolAddress::new(0x0400_00C4) }; - -/// DMA3 Control Bits. -pub const DMA1_CONTROL: VolAddress = - unsafe { VolAddress::new(0x0400_00C6) }; - -/// Source address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA2_SOURCE: VolAddress<*const c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00C8) }; - -/// Destination address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA2_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00CC) }; - -/// The number of transfers desired. -/// -/// A value of 0 indicates the maximum number of transfers: `0x4000` -pub const DMA2_TRANSFER_COUNT: VolAddress = - unsafe { VolAddress::new(0x0400_00D0) }; - -/// DMA3 Control Bits. -pub const DMA2_CONTROL: VolAddress = - unsafe { VolAddress::new(0x0400_00D2) }; - -/// Source address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA3_SOURCE: VolAddress<*const c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00D4) }; - -/// Destination address for DMA3. -/// -/// The correct pointer type depends on the transfer mode used. -pub const DMA3_DESTINATION: VolAddress<*mut c_void, (), Unsafe> = - unsafe { VolAddress::new(0x0400_00D8) }; - -/// The number of transfers desired. -/// -/// A value of 0 indicates the maximum number of transfers: `0x1_0000` -pub const DMA3_TRANSFER_COUNT: VolAddress = - unsafe { VolAddress::new(0x0400_00DC) }; - -/// DMA3 Control Bits. -pub const DMA3_CONTROL: VolAddress = - unsafe { VolAddress::new(0x0400_00DE) }; - -/// Timer0's current counter value. -pub const TIMER0_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0100) }; -/// Timer1's current counter value. -pub const TIMER1_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0104) }; -/// Timer2's current counter value. -pub const TIMER2_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0108) }; -/// Timer3's current counter value. -pub const TIMER3_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_010C) }; - -/// The value for Timer0 to reload on overflow on when the `start` bit is newly -/// set. -pub const TIMER0_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0100) }; -/// The value for Timer1 to reload on overflow on when the `start` bit is newly -/// set. -pub const TIMER1_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0104) }; -/// The value for Timer2 to reload on overflow on when the `start` bit is newly -/// set. -pub const TIMER2_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0108) }; -/// The value for Timer3 to reload on overflow on when the `start` bit is newly -/// set. -pub const TIMER3_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_010C) }; - -/// Control bits for Timer 0. -pub const TIMER0_CONTROL: PlainAddr = - unsafe { VolAddress::new(0x0400_0102) }; - -/// Control bits for Timer 1. -pub const TIMER1_CONTROL: PlainAddr = - unsafe { VolAddress::new(0x0400_0106) }; - -/// Control bits for Timer 2. -pub const TIMER2_CONTROL: PlainAddr = - unsafe { VolAddress::new(0x0400_010A) }; - -/// Control bits for Timer 3. -pub const TIMER3_CONTROL: PlainAddr = - unsafe { VolAddress::new(0x0400_010E) }; - -/// Key Input (read-only). -/// -/// Gives the low-active button state of all system buttons. -pub const KEYINPUT: RoAddr = unsafe { VolAddress::new(0x0400_0130) }; - -/// Interrupts Enabled. -/// -/// When any sub-system is set to "send" interrupts, that interrupt type must -/// *also* be configured here or it won't actually be "received" by the CPU. -pub const IE: PlainAddr = unsafe { VolAddress::new(0x0400_0200) }; - -/// Interrupts Flagged. -/// -/// These are the interrupts that are pending, and haven't been handled. Clear a -/// pending interrupt by writing an [`IrqBits`] value with that bit enabled. The -/// assembly runtime handles this automatically, so you don't normally need to -/// interact with `IF` at all. -pub const IF: PlainAddr = unsafe { VolAddress::new(0x0400_0202) }; - -/// Interrupt Master Enable -/// -/// * When this is set to `true`, hardware interrupts that are flagged will -/// immediately run the interrupt handler. -/// * When this is `false`, any interrupt events that are flagged will be left -/// pending until this is again set to `true`. -/// -/// This defaults to `false`. -/// -/// Technically there's a two CPU cycle delay between this being written and -/// interrupts actually being enabled/disabled. In practice, it doesn't matter. -pub const IME: PlainAddr = unsafe { VolAddress::new(0x0400_0208) }; - -/// The buffer to put logging messages into. -/// -/// The first `\0` in the buffer is the end of each message. -pub const MGBA_LOG_BUFFER: VolBlock = - unsafe { VolBlock::new(0x04FF_F600) }; - -/// Write to this each time you want to send out the current buffer content. -/// -/// It also resets the buffer content. -pub const MGBA_LOG_SEND: WoAddr = - unsafe { VolAddress::new(0x04FFF700) }; - -/// Allows you to enable/disable mGBA logging. -/// -/// This is enabled by default by the assembly runtime, so you don't normally -/// need to touch this. -pub const MGBA_LOG_ENABLE: PlainAddr = - unsafe { VolAddress::new(0x04FF_F780) }; diff --git a/src/mmio/video_memory.rs b/src/mmio/video_memory.rs deleted file mode 100644 index a0d6e8bf..00000000 --- a/src/mmio/video_memory.rs +++ /dev/null @@ -1,129 +0,0 @@ -use super::*; - -/// The backdrop color is the color shown when no *other* element is displayed -/// in a given pixel. -pub const BACKDROP_COLOR: PlainAddr = - unsafe { VolAddress::new(0x0500_0000) }; - -/// Palette data for the backgrounds -pub const BG_PALRAM: VolBlock = - unsafe { VolBlock::new(0x0500_0000) }; - -/// Palette data for the objects. -pub const OBJ_PALRAM: VolBlock = - unsafe { VolBlock::new(0x0500_0200) }; - -/// Gets the block for a specific palbank. -/// -/// ## Panics -/// * If the `bank` requested is 16 or greater this will panic. -#[inline] -#[must_use] -#[cfg_attr(feature = "track_caller", track_caller)] -pub const fn obj_palbank(bank: usize) -> VolBlock { - let u = OBJ_PALRAM.index(bank * 16).as_usize(); - unsafe { VolBlock::new(u) } -} - -/// The VRAM byte offset per screenblock index. -/// -/// This is the same for all background types and sizes. -pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; - -/// The VRAM's background tile view, using 4bpp tiles. -pub const VRAM_BG_TILE4: VolBlock = - unsafe { VolBlock::new(0x0600_0000) }; - -/// The VRAM's background tile view, using 8bpp tiles. -pub const VRAM_BG_TILE8: VolBlock = - unsafe { VolBlock::new(0x0600_0000) }; - -/// The text mode screenblocks. -pub const TEXT_SCREENBLOCKS: VolGrid2dStrided< - TextEntry, - SOGBA, - SOGBA, - 32, - 32, - 32, - SCREENBLOCK_INDEX_OFFSET, -> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; - -/// The VRAM's object tile view, using 4bpp tiles. -pub const VRAM_OBJ_TILE4: VolBlock = - unsafe { VolBlock::new(0x0601_0000) }; - -/// The VRAM's object tile view, using 8bpp tiles. -pub const VRAM_OBJ_TILE8: VolBlock = - unsafe { VolBlock::new(0x0601_0000) }; - -/// The VRAM's view in Video Mode 3. -/// -/// Each location is a direct color value. -pub const MODE3_VRAM: VolGrid2d = - unsafe { VolGrid2d::new(0x0600_0000) }; - -/// The VRAM's view in Video Mode 4. -/// -/// Each location is a pair of palette indexes into the background palette. -/// Because the VRAM can't be written with a single byte, we have to work with -/// this in units of [`u8x2`]. It's annoying, I know. -pub const MODE4_VRAM: VolGrid2dStrided< - u8x2, - SOGBA, - SOGBA, - { 240 / 2 }, - 160, - 2, - 0xA000, -> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; - -/// The VRAM's view in Video Mode 5. -/// -/// Each location is a direct color value, but there's a lower image size to -/// allow for two frames. -pub const MODE5_VRAM: VolGrid2dStrided< - Color, - SOGBA, - SOGBA, - 160, - 128, - 2, - 0xA000, -> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; - -/// The combined object attributes. -pub const OBJ_ATTR_ALL: VolSeries< - ObjAttr, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000) }; - -/// The object 0th attributes. -pub const OBJ_ATTR0: VolSeries< - ObjAttr0, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000) }; - -/// The object 1st attributes. -pub const OBJ_ATTR1: VolSeries< - ObjAttr1, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000 + 2) }; - -/// The object 2nd attributes. -pub const OBJ_ATTR2: VolSeries< - ObjAttr2, - SOGBA, - SOGBA, - 128, - { core::mem::size_of::<[i16; 4]>() }, -> = unsafe { VolSeries::new(0x0700_0000 + 4) }; diff --git a/src/obj.rs b/src/obj.rs deleted file mode 100644 index 17c754f0..00000000 --- a/src/obj.rs +++ /dev/null @@ -1,250 +0,0 @@ -//! Module for object (OBJ) entry data. -//! -//! The GBA's object drawing allows for hardware drawing that is independent of -//! the background layers. Another common term for objects is "sprites", but -//! within the GBA community they're called objects, so this crate calls them -//! objects too. -//! -//! The GBA has 128 object entries within the Object Attribute Memory (OAM) -//! region. The object entries are also interspersed with the memory for the -//! affine entries, so OAM should not be thought of as being an array of just -//! one or the other types of data. -//! -//! A few of the GBA's controls will affect all objects at once, particularly -//! the Display Control (which can control if the objects are visible at all), -//! but in general each object can be controlled independently. -//! -//! Each object entry consists of a number of bit-packed "attributes". The -//! object's attributes are stored in three 16-bit fields. The [ObjAttr] struct -//! has one field for each 16-bit group of attributes: [ObjAttr0], [ObjAttr1], -//! [ObjAttr2]. -//! -//! When you've got an object's data configured how you want, use either the -//! [`OBJ_ATTR_ALL`][crate::mmio::OBJ_ATTR_ALL] control (to write all fields at -//! once) or the [`OBJ_ATTR0`][crate::mmio::OBJ_ATTR0], -//! [`OBJ_ATTR1`][crate::mmio::OBJ_ATTR1], and/or -//! [`OBJ_ATTR2`][crate::mmio::OBJ_ATTR2] controls (to write just some of the -//! fields). -//! -//! **Note:** When the GBA first boots, the object layer will be off but the -//! object entries in OAM will *not* be set to prevent individual objects from -//! being displayed. Before enabling the object layer you should generally set -//! the [ObjDisplayStyle] of all [ObjAttr0] fields so that any objects you're -//! not using don't appear on the screen. Otherwise, you'll end up with -//! un-configured objects appearing in the upper left corner of the display. - -use bitfrob::{u16_with_bit, u16_with_region, u16_with_value}; - -/// How the object should be displayed. -/// -/// Bit 9 of Attr0 changes meaning depending on Bit 8, so this merges the two -/// bits into a single property. -#[derive(Debug, Clone, Copy, Default)] -#[repr(u16)] -pub enum ObjDisplayStyle { - /// The default, non-affine display - #[default] - Normal = 0 << 8, - /// Affine display - Affine = 1 << 8, - /// The object is *not* displayed at all. - NotDisplayed = 2 << 8, - /// Shows the object using Affine style but double sized. - DoubleSizeAffine = 3 << 8, -} - -/// What special effect the object interacts with -#[derive(Debug, Clone, Copy, Default)] -#[repr(u16)] -pub enum ObjEffectMode { - /// The default, no special effect interaction - #[default] - Normal = 0 << 10, - /// The object counts as a potential 1st target for alpha blending, - /// regardless of the actual blend control settings register configuration. - SemiTransparent = 1 << 10, - /// The object is not displayed. Instead, all non-transparent pixels in this - /// object become part of the "OBJ Window" mask. - Window = 2 << 10, -} - -/// The shape of an object. -/// -/// The object's actual display area also depends on its `size` setting: -/// -/// | Size | Square | Horizontal | Vertical | -/// |:-:|:-:|:-:|:-:| -/// | 0 | 8x8 | 16x8 | 8x16 | -/// | 1 | 16x16 | 32x8 | 8x32 | -/// | 2 | 32x32 | 32x16 | 16x32 | -/// | 3 | 64x64 | 64x32 | 32x64 | -#[derive(Debug, Clone, Copy, Default)] -#[repr(u16)] -#[allow(missing_docs)] -pub enum ObjShape { - #[default] - Square = 0 << 14, - Horizontal = 1 << 14, - Vertical = 2 << 14, -} - -/// Object Attributes, field 0 of the entry. -#[derive(Debug, Clone, Copy, Default)] -#[repr(transparent)] -pub struct ObjAttr0(u16); -impl ObjAttr0 { - /// A new blank attr 0. - #[inline] - pub const fn new() -> Self { - Self(0) - } - /// Sets the `y` position of this object - #[inline] - pub const fn with_y(self, y: u16) -> Self { - Self(u16_with_value(0, 7, self.0, y as u16)) - } - /// The object's display styling. - #[inline] - pub const fn with_style(self, style: ObjDisplayStyle) -> Self { - Self(u16_with_region(8, 9, self.0, style as u16)) - } - /// The special effect mode of the object, if any. - #[inline] - pub const fn with_effect(self, effect: ObjEffectMode) -> Self { - Self(u16_with_region(10, 11, self.0, effect as u16)) - } - /// If the object should use the mosaic effect. - #[inline] - pub const fn with_mosaic(self, mosaic: bool) -> Self { - Self(u16_with_bit(12, self.0, mosaic)) - } - /// If the object draws using 8-bits-per-pixel. - #[inline] - pub const fn with_bpp8(self, bpp8: bool) -> Self { - Self(u16_with_bit(13, self.0, bpp8)) - } - /// The object's shape - #[inline] - pub const fn with_shape(self, shape: ObjShape) -> Self { - Self(u16_with_region(14, 15, self.0, shape as u16)) - } -} - -/// Object Attributes, field 1 of the entry. -#[derive(Debug, Clone, Copy, Default)] -#[repr(transparent)] -pub struct ObjAttr1(u16); -impl ObjAttr1 { - /// A new blank attr 1. - #[inline] - pub const fn new() -> Self { - Self(0) - } - /// Sets the `x` position of this object - #[inline] - pub const fn with_x(self, x: u16) -> Self { - Self(u16_with_value(0, 8, self.0, x as u16)) - } - /// The affine index of the object. - #[inline] - pub const fn with_affine_index(self, index: u16) -> Self { - Self(u16_with_value(9, 13, self.0, index as u16)) - } - /// If the object is horizontally flipped - #[inline] - pub const fn with_hflip(self, hflip: bool) -> Self { - Self(u16_with_bit(12, self.0, hflip)) - } - /// If the object is vertically flipped - #[inline] - pub const fn with_vflip(self, vflip: bool) -> Self { - Self(u16_with_bit(13, self.0, vflip)) - } - /// The object's size - /// - /// The size you set here, combined with the shape of the object, determines - /// the object's actual area. - /// - /// | Size | Square| Horizontal| Vertical| - /// |:-:|:-:|:-:|:-:| - /// | 0 | 8x8 | 16x8 | 8x16 | - /// | 1 | 16x16 | 32x8 | 8x32 | - /// | 2 | 32x32 | 32x16 | 16x32 | - /// | 3 | 64x64 | 64x32 | 32x64 | - #[inline] - pub const fn with_size(self, size: u16) -> Self { - Self(u16_with_value(14, 15, self.0, size as u16)) - } -} - -/// Object Attributes, field 2 of the entry. -#[derive(Debug, Clone, Copy, Default)] -#[repr(transparent)] -pub struct ObjAttr2(u16); -impl ObjAttr2 { - /// A new blank attr 2. - #[inline] - pub const fn new() -> Self { - Self(0) - } - /// The base tile id of the object. - /// - /// All other tiles in the object are automatically selected using the - /// following tiles, according to if - /// [`with_obj_vram_1d`][crate::video::DisplayControl::with_obj_vram_1d] it - /// set or not. - #[inline] - pub const fn with_tile_id(self, id: u16) -> Self { - Self(u16_with_value(0, 9, self.0, id as u16)) - } - /// Sets the object's priority sorting. - /// - /// Lower priority objects are closer to the viewer, and will appear in front - /// other objects that have *higher* priority, and in front of backgrounds of - /// *equal or higher* priority. If two objects have the same priority, the - /// lower index object is shown. - #[inline] - pub const fn with_priority(self, priority: u16) -> Self { - Self(u16_with_value(10, 11, self.0, priority as u16)) - } - /// Sets the palbank value of this object. - #[inline] - pub const fn with_palbank(self, palbank: u16) -> Self { - Self(u16_with_value(12, 15, self.0, palbank as u16)) - } -} - -/// Object Attributes. -/// -/// The fields of this struct are all `pub` so that you can simply alter them as -/// you wish. Some "setter" methods are also provided as a shorthand. -#[derive(Debug, Clone, Copy, Default)] -#[repr(C)] -pub struct ObjAttr(pub ObjAttr0, pub ObjAttr1, pub ObjAttr2); -#[allow(missing_docs)] -impl ObjAttr { - #[inline] - pub const fn new() -> Self { - Self(ObjAttr0::new(), ObjAttr1::new(), ObjAttr2::new()) - } - #[inline] - pub fn set_y(&mut self, y: u16) { - self.0 = self.0.with_y(y); - } - #[inline] - pub fn set_style(&mut self, style: ObjDisplayStyle) { - self.0 = self.0.with_style(style); - } - #[inline] - pub fn set_x(&mut self, x: u16) { - self.1 = self.1.with_x(x); - } - #[inline] - pub fn set_tile_id(&mut self, id: u16) { - self.2 = self.2.with_tile_id(id); - } - #[inline] - pub fn set_palbank(&mut self, palbank: u16) { - self.2 = self.2.with_palbank(palbank); - } -} diff --git a/src/timers.rs b/src/timers.rs index 96626411..d32dd8ce 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -1,5 +1,6 @@ //! Timer related data types. +use super::*; use bitfrob::{u8_with_bit, u8_with_region}; /// Control bits for one of the GBA's four timers. @@ -47,3 +48,41 @@ pub enum CpusPerTick { _256 = 2, _1024 = 3, } + +/// Timer0's current counter value. +pub const TIMER0_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0100) }; +/// Timer1's current counter value. +pub const TIMER1_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0104) }; +/// Timer2's current counter value. +pub const TIMER2_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_0108) }; +/// Timer3's current counter value. +pub const TIMER3_COUNTER: RoAddr = unsafe { VolAddress::new(0x0400_010C) }; + +/// The value for Timer0 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER0_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0100) }; +/// The value for Timer1 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER1_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0104) }; +/// The value for Timer2 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER2_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_0108) }; +/// The value for Timer3 to reload on overflow on when the `start` bit is newly +/// set. +pub const TIMER3_RELOAD: WoAddr = unsafe { VolAddress::new(0x0400_010C) }; + +/// Control bits for Timer 0. +pub const TIMER0_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_0102) }; + +/// Control bits for Timer 1. +pub const TIMER1_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_0106) }; + +/// Control bits for Timer 2. +pub const TIMER2_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_010A) }; + +/// Control bits for Timer 3. +pub const TIMER3_CONTROL: PlainAddr = + unsafe { VolAddress::new(0x0400_010E) }; diff --git a/src/video.rs b/src/video.rs index bd984458..0855bbf9 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,8 +1,11 @@ //! Module for screen-related types and functions. -use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; +use bitfrob::{ + u16_get_bit, u16_with_bit, u16_with_region, u16_with_value, u8x2, +}; +use voladdress::{VolBlock, VolGrid2d, VolGrid2dStrided, VolSeries}; -use crate::mmio::MODE3_VRAM; +use super::*; /// A color value. /// @@ -381,7 +384,7 @@ impl Mode3 { in("r7") x, in("r8") x, count = inout(reg) 240 => _, - ptr = inout(reg) crate::mmio::MODE3_VRAM.as_usize() => _, + ptr = inout(reg) MODE3_VRAM.as_usize() => _, options(nostack), ) }); @@ -420,3 +423,382 @@ impl Mode3 { ); } } + +/// The backdrop color is the color shown when no *other* element is displayed +/// in a given pixel. +pub const BACKDROP_COLOR: PlainAddr = + unsafe { VolAddress::new(0x0500_0000) }; + +/// Palette data for the backgrounds +pub const BG_PALRAM: VolBlock = + unsafe { VolBlock::new(0x0500_0000) }; + +/// Palette data for the objects. +pub const OBJ_PALRAM: VolBlock = + unsafe { VolBlock::new(0x0500_0200) }; + +/// Gets the block for a specific palbank. +/// +/// ## Panics +/// * If the `bank` requested is 16 or greater this will panic. +#[inline] +#[must_use] +#[cfg_attr(feature = "track_caller", track_caller)] +pub const fn obj_palbank(bank: usize) -> VolBlock { + let u = OBJ_PALRAM.index(bank * 16).as_usize(); + unsafe { VolBlock::new(u) } +} + +/// The VRAM byte offset per screenblock index. +/// +/// This is the same for all background types and sizes. +pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; + +/// The VRAM's background tile view, using 4bpp tiles. +pub const VRAM_BG_TILE4: VolBlock = + unsafe { VolBlock::new(0x0600_0000) }; + +/// The VRAM's background tile view, using 8bpp tiles. +pub const VRAM_BG_TILE8: VolBlock = + unsafe { VolBlock::new(0x0600_0000) }; + +/// The text mode screenblocks. +pub const TEXT_SCREENBLOCKS: VolGrid2dStrided< + TextEntry, + SOGBA, + SOGBA, + 32, + 32, + 32, + SCREENBLOCK_INDEX_OFFSET, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The VRAM's object tile view, using 4bpp tiles. +pub const VRAM_OBJ_TILE4: VolBlock = + unsafe { VolBlock::new(0x0601_0000) }; + +/// The VRAM's object tile view, using 8bpp tiles. +pub const VRAM_OBJ_TILE8: VolBlock = + unsafe { VolBlock::new(0x0601_0000) }; + +/// The VRAM's view in Video Mode 3. +/// +/// Each location is a direct color value. +pub const MODE3_VRAM: VolGrid2d = + unsafe { VolGrid2d::new(0x0600_0000) }; + +/// The VRAM's view in Video Mode 4. +/// +/// Each location is a pair of palette indexes into the background palette. +/// Because the VRAM can't be written with a single byte, we have to work with +/// this in units of [`u8x2`]. It's annoying, I know. +pub const MODE4_VRAM: VolGrid2dStrided< + u8x2, + SOGBA, + SOGBA, + { 240 / 2 }, + 160, + 2, + 0xA000, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The VRAM's view in Video Mode 5. +/// +/// Each location is a direct color value, but there's a lower image size to +/// allow for two frames. +pub const MODE5_VRAM: VolGrid2dStrided< + Color, + SOGBA, + SOGBA, + 160, + 128, + 2, + 0xA000, +> = unsafe { VolGrid2dStrided::new(0x0600_0000) }; + +/// The combined object attributes. +pub const OBJ_ATTR_ALL: VolSeries< + ObjAttr, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000) }; + +/// The object 0th attributes. +pub const OBJ_ATTR0: VolSeries< + ObjAttr0, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000) }; + +/// The object 1st attributes. +pub const OBJ_ATTR1: VolSeries< + ObjAttr1, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000 + 2) }; + +/// The object 2nd attributes. +pub const OBJ_ATTR2: VolSeries< + ObjAttr2, + SOGBA, + SOGBA, + 128, + { core::mem::size_of::<[i16; 4]>() }, +> = unsafe { VolSeries::new(0x0700_0000 + 4) }; + +/// How the object should be displayed. +/// +/// Bit 9 of Attr0 changes meaning depending on Bit 8, so this merges the two +/// bits into a single property. +#[derive(Debug, Clone, Copy, Default)] +#[repr(u16)] +pub enum ObjDisplayStyle { + /// The default, non-affine display + #[default] + Normal = 0 << 8, + /// Affine display + Affine = 1 << 8, + /// The object is *not* displayed at all. + NotDisplayed = 2 << 8, + /// Shows the object using Affine style but double sized. + DoubleSizeAffine = 3 << 8, +} + +/// What special effect the object interacts with +#[derive(Debug, Clone, Copy, Default)] +#[repr(u16)] +pub enum ObjEffectMode { + /// The default, no special effect interaction + #[default] + Normal = 0 << 10, + /// The object counts as a potential 1st target for alpha blending, + /// regardless of the actual blend control settings register configuration. + SemiTransparent = 1 << 10, + /// The object is not displayed. Instead, all non-transparent pixels in this + /// object become part of the "OBJ Window" mask. + Window = 2 << 10, +} + +/// The shape of an object. +/// +/// The object's actual display area also depends on its `size` setting: +/// +/// | Size | Square | Horizontal | Vertical | +/// |:-:|:-:|:-:|:-:| +/// | 0 | 8x8 | 16x8 | 8x16 | +/// | 1 | 16x16 | 32x8 | 8x32 | +/// | 2 | 32x32 | 32x16 | 16x32 | +/// | 3 | 64x64 | 64x32 | 32x64 | +#[derive(Debug, Clone, Copy, Default)] +#[repr(u16)] +#[allow(missing_docs)] +pub enum ObjShape { + #[default] + Square = 0 << 14, + Horizontal = 1 << 14, + Vertical = 2 << 14, +} + +/// Object Attributes, field 0 of the entry. +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct ObjAttr0(u16); +impl ObjAttr0 { + /// A new blank attr 0. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Sets the `y` position of this object + #[inline] + pub const fn with_y(self, y: u16) -> Self { + Self(u16_with_value(0, 7, self.0, y as u16)) + } + /// The object's display styling. + #[inline] + pub const fn with_style(self, style: ObjDisplayStyle) -> Self { + Self(u16_with_region(8, 9, self.0, style as u16)) + } + /// The special effect mode of the object, if any. + #[inline] + pub const fn with_effect(self, effect: ObjEffectMode) -> Self { + Self(u16_with_region(10, 11, self.0, effect as u16)) + } + /// If the object should use the mosaic effect. + #[inline] + pub const fn with_mosaic(self, mosaic: bool) -> Self { + Self(u16_with_bit(12, self.0, mosaic)) + } + /// If the object draws using 8-bits-per-pixel. + #[inline] + pub const fn with_bpp8(self, bpp8: bool) -> Self { + Self(u16_with_bit(13, self.0, bpp8)) + } + /// The object's shape + #[inline] + pub const fn with_shape(self, shape: ObjShape) -> Self { + Self(u16_with_region(14, 15, self.0, shape as u16)) + } +} + +/// Object Attributes, field 1 of the entry. +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct ObjAttr1(u16); +impl ObjAttr1 { + /// A new blank attr 1. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// Sets the `x` position of this object + #[inline] + pub const fn with_x(self, x: u16) -> Self { + Self(u16_with_value(0, 8, self.0, x as u16)) + } + /// The affine index of the object. + #[inline] + pub const fn with_affine_index(self, index: u16) -> Self { + Self(u16_with_value(9, 13, self.0, index as u16)) + } + /// If the object is horizontally flipped + #[inline] + pub const fn with_hflip(self, hflip: bool) -> Self { + Self(u16_with_bit(12, self.0, hflip)) + } + /// If the object is vertically flipped + #[inline] + pub const fn with_vflip(self, vflip: bool) -> Self { + Self(u16_with_bit(13, self.0, vflip)) + } + /// The object's size + /// + /// The size you set here, combined with the shape of the object, determines + /// the object's actual area. + /// + /// | Size | Square| Horizontal| Vertical| + /// |:-:|:-:|:-:|:-:| + /// | 0 | 8x8 | 16x8 | 8x16 | + /// | 1 | 16x16 | 32x8 | 8x32 | + /// | 2 | 32x32 | 32x16 | 16x32 | + /// | 3 | 64x64 | 64x32 | 32x64 | + #[inline] + pub const fn with_size(self, size: u16) -> Self { + Self(u16_with_value(14, 15, self.0, size as u16)) + } +} + +/// Object Attributes, field 2 of the entry. +#[derive(Debug, Clone, Copy, Default)] +#[repr(transparent)] +pub struct ObjAttr2(u16); +impl ObjAttr2 { + /// A new blank attr 2. + #[inline] + pub const fn new() -> Self { + Self(0) + } + /// The base tile id of the object. + /// + /// All other tiles in the object are automatically selected using the + /// following tiles, according to if + /// [`with_obj_vram_1d`][crate::video::DisplayControl::with_obj_vram_1d] it + /// set or not. + #[inline] + pub const fn with_tile_id(self, id: u16) -> Self { + Self(u16_with_value(0, 9, self.0, id as u16)) + } + /// Sets the object's priority sorting. + /// + /// Lower priority objects are closer to the viewer, and will appear in front + /// other objects that have *higher* priority, and in front of backgrounds of + /// *equal or higher* priority. If two objects have the same priority, the + /// lower index object is shown. + #[inline] + pub const fn with_priority(self, priority: u16) -> Self { + Self(u16_with_value(10, 11, self.0, priority as u16)) + } + /// Sets the palbank value of this object. + #[inline] + pub const fn with_palbank(self, palbank: u16) -> Self { + Self(u16_with_value(12, 15, self.0, palbank as u16)) + } +} + +/// Object Attributes. +/// +/// The fields of this struct are all `pub` so that you can simply alter them as +/// you wish. Some "setter" methods are also provided as a shorthand. +#[derive(Debug, Clone, Copy, Default)] +#[repr(C)] +pub struct ObjAttr(pub ObjAttr0, pub ObjAttr1, pub ObjAttr2); +#[allow(missing_docs)] +impl ObjAttr { + #[inline] + pub const fn new() -> Self { + Self(ObjAttr0::new(), ObjAttr1::new(), ObjAttr2::new()) + } + #[inline] + pub fn set_y(&mut self, y: u16) { + self.0 = self.0.with_y(y); + } + #[inline] + pub fn set_style(&mut self, style: ObjDisplayStyle) { + self.0 = self.0.with_style(style); + } + #[inline] + pub fn set_x(&mut self, x: u16) { + self.1 = self.1.with_x(x); + } + #[inline] + pub fn set_tile_id(&mut self, id: u16) { + self.2 = self.2.with_tile_id(id); + } + #[inline] + pub fn set_palbank(&mut self, palbank: u16) { + self.2 = self.2.with_palbank(palbank); + } +} + +/// Display Control setting. +/// +/// This sets what background mode is active, as well as various related +/// details. +/// +/// Unlike most MMIO, this doesn't have an "all 0" state at boot. The +/// `forced_blank` bit it left set by the BIOS's startup routine. +pub const DISPCNT: PlainAddr = + unsafe { VolAddress::new(0x0400_0000) }; + +/// Display Status setting. +/// +/// Gives info on the display state, and controls display-based interrupts. +pub const DISPSTAT: PlainAddr = + unsafe { VolAddress::new(0x0400_0004) }; + +/// The current scanline that the display is working on. +/// +/// Values of 160 to 227 indicate that a vertical blank line is happening. +pub const VCOUNT: RoAddr = unsafe { VolAddress::new(0x0400_0006) }; + +/// Background 0 controls +pub const BG0CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_0008) }; + +/// Background 1 controls +pub const BG1CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_000A) }; + +/// Background 2 controls +pub const BG2CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_000C) }; + +/// Background 3 controls +pub const BG3CNT: PlainAddr = + unsafe { VolAddress::new(0x0400_000E) }; From 17371ea0daafc7f92b9574fd5d8ee16ae2a98527 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 12:27:55 -0600 Subject: [PATCH 79/89] good catch CI --- src/critical_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/critical_section.rs b/src/critical_section.rs index 4976e2fc..66023be5 100644 --- a/src/critical_section.rs +++ b/src/critical_section.rs @@ -2,7 +2,7 @@ use critical_section::{set_impl, Impl, RawRestoreState}; -use crate::mmio::IME; +use crate::irq::IME; struct GbaCriticalSection; #[cfg(feature = "on_gba")] From 91dc918eaa60ab1326d27531548decfd3d9648c2 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 13:18:38 -0600 Subject: [PATCH 80/89] docs link --- src/bios.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bios.rs b/src/bios.rs index 89b52ebd..dbbbe6aa 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -17,7 +17,7 @@ use crate::irq::IrqBits; /// occur. This can create a significant savings of the battery while you're /// waiting, so use this function when possible. /// -/// **Important:** This function forces [`IME`](crate::mmio::IME) on. +/// **Important:** This function forces [`IME`](crate::irq::IME) on. /// /// Your interrupt handler (if any) will be run before this function returns. /// From 2c0336a51dc73db06ab9a6f33b404e474140c73e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 15:15:53 -0600 Subject: [PATCH 81/89] a32 halt functions --- src/bios.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/bios.rs b/src/bios.rs index dbbbe6aa..d4b86301 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -45,6 +45,21 @@ pub fn IntrWait(ignore_existing: bool, target_irqs: IrqBits) { }); } +/// As [`IntrWait`], but using the `a32` instruction set. +#[inline] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +pub fn a32_IntrWait(ignore_existing: bool, target_irqs: IrqBits) { + on_gba_or_unimplemented!(unsafe { + core::arch::asm! { + "swi #(0x04 << 24)", + inout("r0") ignore_existing as u32 => _, + inout("r1") target_irqs.0 => _, + out("r3") _, + options(preserves_flags), + } + }); +} + /// `0x05`: Builtin shorthand for [`IntrWait(true, IrqBits::VBLANK)`](IntrWait) #[inline] #[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] @@ -60,6 +75,21 @@ pub fn VBlankIntrWait() { }); } +/// As [`VBlankIntrWait`], but using the `a32` instruction set. +#[inline] +#[cfg_attr(feature = "on_gba", instruction_set(arm::t32))] +pub fn a32_VBlankIntrWait() { + on_gba_or_unimplemented!(unsafe { + core::arch::asm! { + "swi #(0x05 << 24)", + out("r0") _, + out("r1") _, + out("r3") _, + options(preserves_flags), + } + }); +} + /// `0x09`: Arc tangent. /// /// * **Returns:** The output is in the range +/- `pi/2`, but accuracy is worse From f9cc5c72210a0da13238c47b9af3318a5f9e2255 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 17:50:27 -0600 Subject: [PATCH 82/89] Move USER_IRQ_HANDLER to the irq module --- examples/basic_keyinput.rs | 3 +- examples/objects.rs | 3 +- examples/paddle_ball.rs | 3 +- examples/timer.rs | 3 +- src/{asm_runtime.rs => asm.rs} | 81 +++++++++++++++++++++++++++------- src/irq.rs | 7 +++ src/lib.rs | 2 +- src/mem/copy.rs | 13 ++++-- src/mem/mod.rs | 2 + src/mem/set.rs | 49 ++++++++++++++++++++ src/video.rs | 41 ++--------------- 11 files changed, 140 insertions(+), 67 deletions(-) rename src/{asm_runtime.rs => asm.rs} (75%) create mode 100644 src/mem/set.rs diff --git a/examples/basic_keyinput.rs b/examples/basic_keyinput.rs index bc584be3..ec513512 100644 --- a/examples/basic_keyinput.rs +++ b/examples/basic_keyinput.rs @@ -5,9 +5,8 @@ use core::ptr::{addr_of, addr_of_mut}; use gba::{ - asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, - irq::{IrqBits, IE, IME}, + irq::{IrqBits, IE, IME, USER_IRQ_HANDLER}, keys::KEYINPUT, mgba::{MgbaLogLevel, MgbaLogger}, video::{ diff --git a/examples/objects.rs b/examples/objects.rs index 96dfdfaf..dc962668 100644 --- a/examples/objects.rs +++ b/examples/objects.rs @@ -2,10 +2,9 @@ #![no_main] use gba::{ - asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - irq::{IrqBits, IE, IME}, + irq::{IrqBits, IE, IME, USER_IRQ_HANDLER}, keys::KEYINPUT, sample_art::{decompress_cga_face_to_vram_4bpp, Cga}, video::{ diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 416f19e9..5d02409b 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -4,10 +4,9 @@ //! Made by Evan Goemer, Discord: @evangoemer use gba::{ - asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - irq::{IrqBits, IE, IME}, + irq::{IrqBits, IE, IME, USER_IRQ_HANDLER}, keys::{KeyInput, KEYINPUT}, video::{Color, DisplayControl, DisplayStatus, Mode3, DISPCNT, DISPSTAT}, }; diff --git a/examples/timer.rs b/examples/timer.rs index b4bed9ad..43a1320e 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -2,10 +2,9 @@ #![no_main] use gba::{ - asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, gba_cell::GbaCell, - irq::{IrqBits, IE, IME}, + irq::{IrqBits, IE, IME, USER_IRQ_HANDLER}, timers::{CpusPerTick, TimerControl, TIMER0_CONTROL}, video::{ Color, DisplayControl, DisplayStatus, BACKDROP_COLOR, DISPCNT, DISPSTAT, diff --git a/src/asm_runtime.rs b/src/asm.rs similarity index 75% rename from src/asm_runtime.rs rename to src/asm.rs index 3d12d215..63117590 100644 --- a/src/asm_runtime.rs +++ b/src/asm.rs @@ -1,17 +1,28 @@ #![allow(unused_macros)] -//! Assembly runtime and support functions for the GBA. - -// Note(Lokathor): Functions here will *definitely* panic without the `on_gba` -// cargo feature enabled, and so they should all have the `track_caller` -// attribute set whenever the `on_gba` feature is *disabled* - -use crate::gba_cell::GbaCell; +//! Assembly support. +//! +//! ## Startup Code +//! +//! This module includes the `_start` function as well as the default +//! `_asm_runtime_irq_handler` that it sets. Both of which are defined within +//! [`global_asm!`][inline_asm] blocks. They are not intended to be called +//! directly from Rust, and so foreign function definitions for them are not +//! exposed to Rust in this module. +//! +//! [inline_asm]: +//! https://doc.rust-lang.org/nightly/reference/inline-assembly.html +//! +//! ## Assembly Helpers +//! +//! This module also includes a number of functions to allow you to force the +//! generation of particular assembly instructions that Rust and/or LLVM does +//! not otherwise make easy to generate. use bracer::*; /// Inserts a `nop` instruction. -#[inline(always)] +#[inline] #[cfg_attr(not(feature = "on_gba"), track_caller)] pub fn nop() { on_gba_or_unimplemented! { @@ -29,7 +40,6 @@ pub fn nop() { /// This both reads and writes `ptr`, so all the usual rules of that apply. #[inline] #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] -#[cfg_attr(not(feature = "on_gba"), track_caller)] pub unsafe fn swp(mut ptr: *mut u32, x: u32) -> u32 { on_gba_or_unimplemented! { let output: u32; @@ -55,7 +65,6 @@ pub unsafe fn swp(mut ptr: *mut u32, x: u32) -> u32 { /// This both reads and writes `ptr`, so all the usual rules of that apply. #[inline] #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] -#[cfg_attr(not(feature = "on_gba"), track_caller)] pub unsafe fn swpb(mut ptr: *mut u8, x: u8) -> u8 { on_gba_or_unimplemented! { let output: u8; @@ -75,6 +84,50 @@ pub unsafe fn swpb(mut ptr: *mut u8, x: u8) -> u8 { } } +/// Loads a `u16` pointer offset by `bytes` +/// +/// ## Safety +/// This is similar to `ptr.byte_add(bytes).read()`, and thus has all the same +/// safety requirements. +#[inline] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +pub unsafe fn a32_load_u16_reg_offset(ptr: *mut u16, bytes: usize) -> u16 { + on_gba_or_unimplemented! { + let output: u16; + unsafe { + core::arch::asm! { + "ldrh {output}, [{ptr}, {bytes}]", + output = lateout(reg) output, + ptr = in(reg) ptr, + bytes = in(reg) bytes, + } + } + output + } +} + +/// Loads an `i16` pointer offset by `bytes` +/// +/// ## Safety +/// This is similar to `ptr.byte_add(bytes).read()`, and thus has all the same +/// safety requirements. +#[inline] +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +pub unsafe fn a32_load_i16_reg_offset(ptr: *mut i16, bytes: usize) -> i16 { + on_gba_or_unimplemented! { + let output: i16; + unsafe { + core::arch::asm! { + "ldrsh {output}, [{ptr}, {bytes}]", + output = lateout(reg) output, + ptr = in(reg) ptr, + bytes = in(reg) bytes, + } + } + output + } +} + // Proc-macros can't see the target being built for, so we use this declarative // macro to determine if we're on a thumb target (and need to force our asm into // a32 mode) or if we're not on thumb (and our asm can pass through untouched). @@ -211,11 +264,5 @@ core::arch::global_asm! { // return to the BIOS "bx lr", }, - USER_IRQ_HANDLER = sym USER_IRQ_HANDLER, + USER_IRQ_HANDLER = sym crate::irq::USER_IRQ_HANDLER, } - -/// The user-provided interrupt request handler function. -#[cfg(feature = "on_gba")] -pub static USER_IRQ_HANDLER: GbaCell< - Option, -> = GbaCell::new(None); diff --git a/src/irq.rs b/src/irq.rs index 5c7bef47..6d197045 100644 --- a/src/irq.rs +++ b/src/irq.rs @@ -1,6 +1,13 @@ //! Hardware interrupt handling use super::*; +use crate::gba_cell::GbaCell; + +/// The user-provided interrupt request handler function. +#[cfg(feature = "on_gba")] +pub static USER_IRQ_HANDLER: GbaCell< + Option, +> = GbaCell::new(None); /// Interrupt bit flags. #[derive(Clone, Copy, Default)] diff --git a/src/lib.rs b/src/lib.rs index 5b885888..bb993a89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ macro_rules! on_gba_or_unimplemented { } } -pub mod asm_runtime; +pub mod asm; pub mod bios; pub mod dma; pub mod gba_cell; diff --git a/src/mem/copy.rs b/src/mem/copy.rs index 4c3e47f4..e9b9c274 100644 --- a/src/mem/copy.rs +++ b/src/mem/copy.rs @@ -32,11 +32,16 @@ pub unsafe extern "C" fn copy_u8_unchecked( }); } -/// Copies `[u32; 8]` sized blocks, to `dest` from `src` +/// Copies `[u32; 8]` sized chunks, to `dest` from `src` /// -/// Particularly, this is the size of one [`Tile4`][crate::video::Tile4], half a -/// [`Tile8`][crate::video::Tile8], or one complete palbank of -/// [`Color`][crate::video::Color] values. +/// Particularly, this helps with: +/// * [`Tile4`][crate::video::Tile4] (one loop per tile). +/// * [`Tile8`][crate::video::Tile8] (two loops per tile). +/// * A palbank of [`Color`][crate::video::Color] values (one loop per palbank). +/// * A text mode screenblock (64 loops per screenblock). +/// +/// This will, in general, be slightly faster than a generic `memcpy`, but +/// slightly slower than using DMA. /// /// ## Safety /// * As with all copying routines, the source must be readable for the size you diff --git a/src/mem/mod.rs b/src/mem/mod.rs index 88e71630..c41d797d 100644 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -1,5 +1,7 @@ //! Low-level memory manipulation functions. mod copy; +mod set; pub use copy::*; +pub use set::*; diff --git a/src/mem/set.rs b/src/mem/set.rs new file mode 100644 index 00000000..1f66771b --- /dev/null +++ b/src/mem/set.rs @@ -0,0 +1,49 @@ +/// Sets `word` in blocks of 80 per loop. +/// +/// This is intended for clearing VRAM to a particular color when using +/// background modes 3, 4, and 5. +/// * To clear the Mode 3 bitmap, pass `240` as the count. +/// * To clear a Mode 4 frame pass `120`. +/// * To clear a Mode 5 frame pass `128`. +#[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] +#[cfg_attr(feature = "on_gba", link_section = ".iwram.set_u32x80_unchecked")] +pub unsafe extern "C" fn set_u32x80_unchecked( + dest: *mut u32, word: u32, count: usize, +) { + on_gba_or_unimplemented!(unsafe { + core::arch::asm!( + // Note(Lokathor): Same loop logic as `copy_u8_unchecked`, we're just + // processing bigger chunks of data at a time, and also setting rather + // than copying. + "1:", + "subs {count}, {count}, #1", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "stmge {dest}!, {{r1,r3,r4,r5, r7,r8,r12,lr}}", + "bgt 1b", + + // The assembler will give us a warning (that we can't easily disable) + // if the reg_list for `stm` doesn't give the registers in order from + // low to high, so we just manually pick registers. The count register + // and the pointer register can be anything else. + in("r1") word, + in("r3") word, + in("r4") word, + in("r5") word, + in("r7") word, + in("r8") word, + in("r12") word, + in("lr") word, + dest = inout(reg) dest => _, + count = inout(reg) count => _, + options(nostack), + ) + }); +} diff --git a/src/video.rs b/src/video.rs index 0855bbf9..d90fc371 100644 --- a/src/video.rs +++ b/src/video.rs @@ -3,6 +3,7 @@ use bitfrob::{ u16_get_bit, u16_with_bit, u16_with_region, u16_with_value, u8x2, }; +use mem::set_u32x80_unchecked; use voladdress::{VolBlock, VolGrid2d, VolGrid2dStrided, VolSeries}; use super::*; @@ -350,44 +351,10 @@ impl Mode3 { pub const BYTES_TOTAL: usize = Self::BYTES_PER_ROW * Self::HEIGHT_USIZE; /// Clears the entire bitmap to a color of your choosing. - #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] - #[cfg_attr(feature = "on_gba", link_section = ".iwram.mode3.clear_to")] + #[inline] pub fn clear_to(color: Color) { - on_gba_or_unimplemented!(unsafe { - let x: u32 = color.0 as u32 | ((color.0 as u32) << 16); - // now we spam out that `u32`, 10 stm per loop, 8 times per stm. - core::arch::asm!( - "1:", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "stm {ptr}!, {{r0-r5,r7-r8}}", - "subs {count}, {count}, #1", - "bne 1b", - - // The assembler will give us a warning (that we can't easily disable) - // if the reg_list for `stm` doesn't give the registers in order from - // low to high, so we just manually pick registers. The count register - // and the pointer register can be anything else. - in("r0") x, - in("r1") x, - in("r2") x, - in("r3") x, - in("r4") x, - in("r5") x, - in("r7") x, - in("r8") x, - count = inout(reg) 240 => _, - ptr = inout(reg) MODE3_VRAM.as_usize() => _, - options(nostack), - ) - }); + let x: u32 = color.0 as u32 | ((color.0 as u32) << 16); + unsafe { set_u32x80_unchecked(MODE3_VRAM.as_usize() as _, x, 240) }; } /// Fills the given rectangle, clipped to the bounds of the bitmap. From ab39b964ee317bece86157fe3bd0010a1aabe76f Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 3 Jun 2024 17:51:13 -0600 Subject: [PATCH 83/89] docs --- src/asm.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/asm.rs b/src/asm.rs index 63117590..deacf92a 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -4,11 +4,11 @@ //! //! ## Startup Code //! -//! This module includes the `_start` function as well as the default -//! `_asm_runtime_irq_handler` that it sets. Both of which are defined within -//! [`global_asm!`][inline_asm] blocks. They are not intended to be called -//! directly from Rust, and so foreign function definitions for them are not -//! exposed to Rust in this module. +//! When the `on_gba` feature is active, this module includes the `_start` +//! function as well as the default `_asm_runtime_irq_handler` that it sets. +//! Both of which are defined within [`global_asm!`][inline_asm] blocks. They +//! are not intended to be called directly from Rust, and so foreign function +//! definitions for them are not exposed to Rust in this module. //! //! [inline_asm]: //! https://doc.rust-lang.org/nightly/reference/inline-assembly.html From b699e40bcd317d07021096567e3d4c8b249ca10e Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 30 Jun 2024 11:24:42 -0600 Subject: [PATCH 84/89] Resolve https://github.com/rust-console/gba/pull/195#discussion_r1657034215 --- .cargo/config.toml | 2 +- src/per_project_setup.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index cbaf2061..353cf262 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,7 +5,7 @@ target = "thumbv4t-none-eabi" build-std = ["core"] [target.thumbv4t-none-eabi] -runner = "mgba-qt" +runner = ["mgba-qt", "-C", "logToStdout=1", "-C", "logLevel.gba.debug=127"] rustflags = [ "-Zub-checks=no", "-Clinker=arm-none-eabi-ld", diff --git a/src/per_project_setup.rs b/src/per_project_setup.rs index b40ce39a..2ff80013 100644 --- a/src/per_project_setup.rs +++ b/src/per_project_setup.rs @@ -46,8 +46,9 @@ //! build-std = ["core"] //! //! [target.thumbv4t-none-eabi] -//! runner = "mgba-qt" +//! runner = ["mgba-qt", "-C", "logToStdout=1", "-C", "logLevel.gba.debug=127"] //! rustflags = [ +//! "-Zub-checks=no", //! "-Clinker=arm-none-eabi-ld", //! "-Clink-arg=-Tlinker_scripts/mono_boot.ld", //! "-Ctarget-cpu=arm7tdmi", From dbd50327c2c404031ad4670c2ced3975126c4916 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 30 Jun 2024 11:27:15 -0600 Subject: [PATCH 85/89] Resolve https://github.com/rust-console/gba/pull/195#discussion_r1657020576 --- src/gba_cell.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gba_cell.rs b/src/gba_cell.rs index c5400d0a..7260d53a 100644 --- a/src/gba_cell.rs +++ b/src/gba_cell.rs @@ -31,6 +31,7 @@ unsafe impl GbaCellSafe for u32 {} unsafe impl GbaCellSafe for i8 {} unsafe impl GbaCellSafe for i16 {} unsafe impl GbaCellSafe for i32 {} +unsafe impl GbaCellSafe for f32 {} unsafe impl GbaCellSafe for NonZeroI16 {} unsafe impl GbaCellSafe for NonZeroI32 {} unsafe impl GbaCellSafe for NonZeroI8 {} From 12457adb4e88d4c24365b573ff2afdf0e796c9cd Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 30 Jun 2024 11:33:17 -0600 Subject: [PATCH 86/89] Resolve https://github.com/rust-console/gba/pull/195#discussion_r1656978310 --- examples/mode4.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/mode4.rs b/examples/mode4.rs index d1ff8ee9..cbf43869 100644 --- a/examples/mode4.rs +++ b/examples/mode4.rs @@ -64,9 +64,12 @@ pub extern "C" fn main() -> ! { .with_frame1_active(frame > 0), ); - // we have to do some silly dancing here because we can't write just a `u8` - // in VRAM. We have to read a `u8x2` combo, update part of it, then write it - // back to VRAM. + // We have to do some silly dancing here because we can't write just a `u8` + // in VRAM. 8-bit writes in VRAM get duplicated across the aligned 16-bit + // chunk. Instead, we have to read a `u8x2` combo, update part of it, then + // write it back to VRAM. Normally, you probably wouldn't use Mode 4 at all + // for a program like this, but it keeps the Mode 4 demo closer to how the + // Mode 3 demo works. let addr = MODE4_VRAM .get_frame(frame) .unwrap() From 530c15bc29f2fe6596e31d362af928bf7c5b96b5 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 30 Jun 2024 11:37:30 -0600 Subject: [PATCH 87/89] Resolve https://github.com/rust-console/gba/pull/195#discussion_r1656987457 --- src/sample_art.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sample_art.rs b/src/sample_art.rs index a96007bb..f61ed6b1 100644 --- a/src/sample_art.rs +++ b/src/sample_art.rs @@ -25,6 +25,12 @@ pub fn decompress_cga_face_to_vram_4bpp(dest: VolRegion) { } /// An empty type that just serves as a namespace for constants. +/// +/// The CGA data is 256 tiles with character tiles that match with ASCII where +/// appropriate. If you want to write something, `ch as u8` will give you the +/// correct index value for any printable ascii character. +/// +/// The constants here give names to the *other* tiles in the CGA dataset. pub struct Cga; #[allow(missing_docs)] impl Cga { From 429211befb6200557d28807e37cee4421a2ef044 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 30 Jun 2024 11:38:32 -0600 Subject: [PATCH 88/89] Resolve https://github.com/rust-console/gba/pull/195#discussion_r1656989334 --- examples/timer.rs | 2 +- src/timers.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/timer.rs b/examples/timer.rs index 43a1320e..ee77d868 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -26,7 +26,7 @@ extern "C" fn main() -> ! { TimerControl::new() .with_enabled(true) .with_send_irq(true) - .with_cpus_per_tick(CpusPerTick::_64), + .with_cycles_per_tick(CpusPerTick::_64), ); DISPCNT.write(DisplayControl::new().with_bg_mode(3)); diff --git a/src/timers.rs b/src/timers.rs index d32dd8ce..bd073944 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -15,7 +15,7 @@ impl TimerControl { } /// The number of CPU cycles per timer tick. #[inline] - pub const fn with_cpus_per_tick(self, cpus: CpusPerTick) -> Self { + pub const fn with_cycles_per_tick(self, cpus: CpusPerTick) -> Self { Self(u8_with_region(0, 1, self.0, cpus as u8)) } /// If the timer should *only* tick when the lower-number timer overflows. @@ -24,7 +24,7 @@ impl TimerControl { /// instead tick once per overflow of Timer (N-1). /// * This has no effect for Timer 0, since it has no lower numbered timer. #[inline] - pub const fn cascade_ticks(self, cascade: bool) -> Self { + pub const fn with_cascade_ticks(self, cascade: bool) -> Self { Self(u8_with_bit(2, self.0, cascade)) } /// If an overflow of this timer should send an interrupt. From cf3f8f9dbff05294de471b2c7e98af0bdc0153b0 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Sun, 30 Jun 2024 11:43:30 -0600 Subject: [PATCH 89/89] Resolve https://github.com/rust-console/gba/pull/195#discussion_r1657016335 --- src/dma.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dma.rs b/src/dma.rs index fb42413d..4bc531e7 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -98,7 +98,11 @@ impl DmaControl { pub const fn with_irq(self, irq: bool) -> Self { Self(u16_with_bit(14, self.0, irq)) } - /// If the DMA unit is enabled. + /// If the DMA unit should be enabled. + /// + /// When a configuration with an "enabled" flag set is written to the DMA's + /// control, the DMA still won't *actually* start until the appropriate start + /// time. #[inline] pub const fn with_enabled(self, enabled: bool) -> Self { Self(u16_with_bit(15, self.0, enabled))