mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +11:00
friggin do it
This commit is contained in:
parent
2196c15fa9
commit
1036b42f76
|
@ -248,23 +248,10 @@ GBA's CPU <em>can't do any of that</em>. On the GBA, there's a genuine speed dif
|
||||||
between looping over indexes and then indexing each loop (slow) compared to
|
between looping over indexes and then indexing each loop (slow) compared to
|
||||||
using an iterator that just stores an internal pointer and does +1 offset per
|
using an iterator that just stores an internal pointer and does +1 offset per
|
||||||
loop until it reaches the end (fast). The repeated indexing itself can by itself
|
loop until it reaches the end (fast). The repeated indexing itself can by itself
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< HEAD
|
|
||||||
be an expensive step. If you've got a slice of data to process, be sure to go
|
|
||||||
over it with <code>.iter()</code> and <code>.iter_mut()</code> if you can, instead of looping by
|
|
||||||
index. This is Rust and all, so probably you were gonna do that anyway, but just
|
|
||||||
a heads up.</p>
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
be an expensive step. If it's like a 3 element array it's no big deal, but if
|
be an expensive step. If it's like a 3 element array it's no big deal, but if
|
||||||
you've got a big slice of data to process, be sure to go over it with <code>.iter()</code>
|
you've got a big slice of data to process, be sure to go over it with <code>.iter()</code>
|
||||||
and <code>.iter_mut()</code> if you can, instead of looping by index. This is Rust and all,
|
and <code>.iter_mut()</code> if you can, instead of looping by index. This is Rust and all,
|
||||||
so probably you were gonna do that anyway, but just a heads up.</p>
|
so probably you were gonna do that anyway, but just a heads up.</p>
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> object attribute stuff
|
|
||||||
=======
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
<a class="header" href="#get-your-tilemap-ready" id="get-your-tilemap-ready"><h2>Get your Tilemap ready</h2></a>
|
<a class="header" href="#get-your-tilemap-ready" id="get-your-tilemap-ready"><h2>Get your Tilemap ready</h2></a>
|
||||||
<p>I believe that at one point I alluded to a tilemap existing. Well, just as the
|
<p>I believe that at one point I alluded to a tilemap existing. Well, just as the
|
||||||
tiles are arranged into charblocks, the data describing what tile to show in
|
tiles are arranged into charblocks, the data describing what tile to show in
|
||||||
|
@ -372,17 +359,7 @@ separate groups of related registers.</p>
|
||||||
<p>Each of these are a read/write <code>u16</code> location. This is where we get to all of
|
<p>Each of these are a read/write <code>u16</code> location. This is where we get to all of
|
||||||
the important details that we've been putting off.</p>
|
the important details that we've been putting off.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<li>2 bits for the priority of each background (0 being highest). If two
|
|
||||||
backgrounds are set to the same priority the the lower numbered background
|
|
||||||
layer takes prescience.</li>
|
|
||||||
=======
|
|
||||||
<li>2 bits for the priority.</li>
|
<li>2 bits for the priority.</li>
|
||||||
>>>>>>> object attribute stuff
|
|
||||||
=======
|
|
||||||
<li>2 bits for the priority.</li>
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
<li>2 bits for "character base block", the charblock that all of the tile indexes
|
<li>2 bits for "character base block", the charblock that all of the tile indexes
|
||||||
for this background are offset from.</li>
|
for this background are offset from.</li>
|
||||||
<li>1 bit for mosaic effect being enabled (we'll get to that below).</li>
|
<li>1 bit for mosaic effect being enabled (we'll get to that below).</li>
|
||||||
|
|
|
@ -140,11 +140,371 @@
|
||||||
<p>As with backgrounds, objects can be used in both an affine and non-affine way.
|
<p>As with backgrounds, objects can be used in both an affine and non-affine way.
|
||||||
For this section we'll focus on the non-affine elements, and then we'll do all
|
For this section we'll focus on the non-affine elements, and then we'll do all
|
||||||
the affine stuff in a later chapter.</p>
|
the affine stuff in a later chapter.</p>
|
||||||
<p>TODO: tio afero ke mi diris</p>
|
|
||||||
<a class="header" href="#objects-vs-sprites" id="objects-vs-sprites"><h2>Objects vs Sprites</h2></a>
|
<a class="header" href="#objects-vs-sprites" id="objects-vs-sprites"><h2>Objects vs Sprites</h2></a>
|
||||||
|
<p>As <a href="https://www.coranac.com/tonc/text/regobj.htm">TONC</a> helpfully reminds us
|
||||||
|
(and then proceeds to not follow its own advice), we should always try to think
|
||||||
|
in terms of <em>objects</em>, not <em>sprites</em>. A sprite is a logical / software concern,
|
||||||
|
perhaps a player concern, whereas an object is a hardware concern.</p>
|
||||||
|
<p>What's more, a given sprite that the player sees might need more than one object
|
||||||
|
to display. Objects must be either square or rectangular (so sprite bits that
|
||||||
|
stick out probably call for a second object), and can only be from 8x8 to 64x64
|
||||||
|
(so anything bigger has to be two objects lined up to appear as one).</p>
|
||||||
|
<a class="header" href="#general-object-info" id="general-object-info"><h2>General Object Info</h2></a>
|
||||||
|
<p>Unlike with backgrounds, you can enable the object layer in any video mode.
|
||||||
|
There's space for 128 object definitions in OAM.</p>
|
||||||
|
<p>The display gets a number of cycles per scanline to process objects: 1210 by
|
||||||
|
default, but only 954 if you enable the "HBlank interval free" setting in the
|
||||||
|
display control register. The <a href="http://problemkaputt.de/gbatek.htm#lcdobjoverview">cycle cost per
|
||||||
|
object</a> depends on the
|
||||||
|
object's size and if it's using affine or regular mode, so enabling the HBlank
|
||||||
|
interval free setting doesn't cut the number of objects displayable by an exact
|
||||||
|
number of objects. The objects are processed in order of their definitions and
|
||||||
|
if you run out of cycles then the rest just don't get shown. If there's a
|
||||||
|
concern that you might run out of cycles you can place important objects (such
|
||||||
|
as the player) at the start of the list and then less important animation
|
||||||
|
objects later on.</p>
|
||||||
<a class="header" href="#ready-the-palette" id="ready-the-palette"><h2>Ready the Palette</h2></a>
|
<a class="header" href="#ready-the-palette" id="ready-the-palette"><h2>Ready the Palette</h2></a>
|
||||||
|
<p>Objects use the palette the same as the background does. The only difference is
|
||||||
|
that the palette data for objects starts at <code>0x500_0200</code>.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
pub const PALRAM_OBJECT_BASE: VolatilePtr<u16> = VolatilePtr(0x500_0200 as *mut u16);
|
||||||
|
|
||||||
|
pub fn object_palette(slot: usize) -> u16 {
|
||||||
|
assert!(slot < 256);
|
||||||
|
unsafe { PALRAM_OBJECT_BASE.offset(slot as isize).read() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_object_palette(slot: usize, color: u16) {
|
||||||
|
assert!(slot < 256);
|
||||||
|
unsafe { PALRAM_OBJECT_BASE.offset(slot as isize).write(color) }
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
<a class="header" href="#ready-the-tiles" id="ready-the-tiles"><h2>Ready the Tiles</h2></a>
|
<a class="header" href="#ready-the-tiles" id="ready-the-tiles"><h2>Ready the Tiles</h2></a>
|
||||||
|
<p>Objects, as with backgrounds, are composed of 8x8 tiles, and if you want
|
||||||
|
something bigger than 8x8 you have to use more than one tile put together.
|
||||||
|
Object tiles go into the final two charblocks of VRAM (indexes 4 and 5). Because
|
||||||
|
there's only two of them, they are sometimes called the lower block
|
||||||
|
(<code>0x601_0000</code>) and the higher/upper block (<code>0x601_4000</code>).</p>
|
||||||
|
<p>Tile indexes for sprites always offset from the base of the lower block, and
|
||||||
|
they always go 32 bytes at a time, regardless of if the object is set for 4bpp
|
||||||
|
or 8bpp. From this we can determine that there's 512 tile slots in each of the
|
||||||
|
two object charblocks. However, in video modes 3, 4, and 5 the space for the
|
||||||
|
background cuts into the lower charblock, so you can only safely use the upper
|
||||||
|
charblock.</p>
|
||||||
|
<p>With backgrounds you picked every single tile individually with a bunch of
|
||||||
|
screen entry values. Objects don't do that at all. Instead you pick a base tile,
|
||||||
|
size, and shape, then it figures out the rest from there. However, you may
|
||||||
|
recall back with the display control register something about an "object memory
|
||||||
|
1d" bit. This is where that comes into play.</p>
|
||||||
|
<ul>
|
||||||
|
<li>If object memory is set to be 2d (the default) then each charblock is treated
|
||||||
|
as 32 tiles by 32 tiles square. Each object has a base tile and dimensions,
|
||||||
|
and that just extracts directly from the charblock picture as if you were
|
||||||
|
selecting an area. This mode probably makes for the easiest image editing.</li>
|
||||||
|
<li>If object memory is set to be 1d then the tiles are loaded sequentially from
|
||||||
|
the starting point, enough to fill in the object's dimensions. This most
|
||||||
|
probably makes it the easiest to program with about things, since programming
|
||||||
|
languages are pretty good at 1d things.</li>
|
||||||
|
</ul>
|
||||||
|
<p>I'm not sure I explained that well, here's a picture:</p>
|
||||||
|
<p><img src="obj_memory_2d1d.jpg" alt="2d1d-diagram" /></p>
|
||||||
|
<p>In 2d mode, a new row of tiles starts every 32 tile indexes.</p>
|
||||||
|
<p>Of course, the mode that you actually end up using is not particularly
|
||||||
|
important, since it should be the job of your image conversion routine to get
|
||||||
|
everything all lined up and into place anyway.</p>
|
||||||
<a class="header" href="#set-the-object-attributes" id="set-the-object-attributes"><h2>Set the Object Attributes</h2></a>
|
<a class="header" href="#set-the-object-attributes" id="set-the-object-attributes"><h2>Set the Object Attributes</h2></a>
|
||||||
|
<p>The final step is to assign the correct attributes to an object. Each object has
|
||||||
|
three <code>u16</code> values that make up its overall attributes.</p>
|
||||||
|
<p>Before we go into the details, I want to remind you that the hardware will
|
||||||
|
attempt to process every single object every single frame, and also that all of
|
||||||
|
the GBA's memory is cleared to 0 at startup. Why do these two things matter
|
||||||
|
right now? As you'll see in a second an "all zero" set of object attributes
|
||||||
|
causes an 8x8 object to appear at 0,0 using object tile index 0. This is usually
|
||||||
|
<em>not</em> what you want your unused objects to do. When your game first starts you
|
||||||
|
should take a moment to mark any objects you won't be using as objects to not
|
||||||
|
render.</p>
|
||||||
|
<a class="header" href="#objectattributesattr0" id="objectattributesattr0"><h3>ObjectAttributes.attr0</h3></a>
|
||||||
|
<ul>
|
||||||
|
<li>8 bits for row coordinate (marks the top of the sprite)</li>
|
||||||
|
<li>2 bits for object rendering: 0 = Normal, 1 = Affine, 2 = Disabled, 3 = Affine with double rendering area</li>
|
||||||
|
<li>2 bits for object mode: 0 = Normal, 1 = Alpha Blending, 2 = Object Window, 3 = Forbidden</li>
|
||||||
|
<li>1 bit for mosaic enabled</li>
|
||||||
|
<li>1 bit 8bpp color enabled</li>
|
||||||
|
<li>2 bits for shape: 0 = Square, 1 = Horizontal, 2 = Vertical, 3 = Forbidden</li>
|
||||||
|
</ul>
|
||||||
|
<p>If an object is 128 pixels big at Y > 128 you'll get a strange looking result
|
||||||
|
where it acts like Y > -128 and then displays partly off screen to the top.</p>
|
||||||
|
<a class="header" href="#objectattributesattr1" id="objectattributesattr1"><h3>ObjectAttributes.attr1</h3></a>
|
||||||
|
<ul>
|
||||||
|
<li>9 bit for column coordinate (marks the left of the sprite)</li>
|
||||||
|
<li>Either:
|
||||||
|
<ul>
|
||||||
|
<li>3 empty bits, 1 bit for horizontal flip, 1 bit for vertical flip (non-affine)</li>
|
||||||
|
<li>5 bits for affine index (affine)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>2 bits for size.</li>
|
||||||
|
</ul>
|
||||||
|
<table><thead><tr><th align="center"> Size </th><th align="center"> Square </th><th align="center"> Horizontal </th><th align="center"> Vertical</th></tr></thead><tbody>
|
||||||
|
<tr><td align="center"> 0 </td><td align="center"> 8x8 </td><td align="center"> 16x8 </td><td align="center"> 8x16 </td></tr>
|
||||||
|
<tr><td align="center"> 1 </td><td align="center"> 16x16 </td><td align="center"> 32x8 </td><td align="center"> 8x32 </td></tr>
|
||||||
|
<tr><td align="center"> 2 </td><td align="center"> 32x32 </td><td align="center"> 32x16 </td><td align="center"> 16x32 </td></tr>
|
||||||
|
<tr><td align="center"> 3 </td><td align="center"> 64x64 </td><td align="center"> 64x32 </td><td align="center"> 32x64 </td></tr>
|
||||||
|
</tbody></table>
|
||||||
|
<a class="header" href="#objectattributesattr2" id="objectattributesattr2"><h3>ObjectAttributes.attr2</h3></a>
|
||||||
|
<ul>
|
||||||
|
<li>10 bits for the base tile index</li>
|
||||||
|
<li>2 bits for priority</li>
|
||||||
|
<li>4 bits for the palbank index (4bpp mode only, ignored in 8bpp)</li>
|
||||||
|
</ul>
|
||||||
|
<a class="header" href="#objectattributes-summary" id="objectattributes-summary"><h3>ObjectAttributes summary</h3></a>
|
||||||
|
<p>So I said in the GBA memory mapping section that C people would tell you that
|
||||||
|
the object attributes should look like this:</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ObjectAttributes {
|
||||||
|
attr0: u16,
|
||||||
|
attr1: u16,
|
||||||
|
attr2: u16,
|
||||||
|
filler: i16,
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
|
<p>Except that:</p>
|
||||||
|
<ol>
|
||||||
|
<li>It's wasteful when we store object attributes on their own outside of OAM
|
||||||
|
(which we definitely might want to do).</li>
|
||||||
|
<li>In Rust we can't access just one field through a volatile pointer (our
|
||||||
|
pointers aren't actually volatile to begin with, just the ops we do with them
|
||||||
|
are). We have to read or write the whole pointer's value at a time.
|
||||||
|
Similarly, we can't do things like <code>|=</code> and <code>&=</code> with volatile in Rust. So in
|
||||||
|
rust we can't have a volatile pointer to an ObjectAttributes and then write
|
||||||
|
to just the three "real" values and not touch the filler field. Having the
|
||||||
|
filler value in there just means we have to dance around it more, not less.</li>
|
||||||
|
<li>We want to newtype this whole thing to prevent accidental invalid states from
|
||||||
|
being written into memory.</li>
|
||||||
|
</ol>
|
||||||
|
<p>So we will not be using that representation. At the same time we want to have no
|
||||||
|
overhead, so we will stick to three <code>u16</code> values. We could newtype each
|
||||||
|
individual field to be its own type (<code>ObjectAttributesAttr0</code> or something silly
|
||||||
|
like that), since there aren't actual dependencies between two different fields
|
||||||
|
such that a change in one can throw another into a forbidden state. The worst
|
||||||
|
that can happen is if we disable or enable affine mode (<code>attr0</code>) it can change
|
||||||
|
the meaning of <code>attr1</code>. The changed meaning isn't actually in invalid state
|
||||||
|
though, so we <em>could</em> make each field its own type if we wanted.</p>
|
||||||
|
<p>However, when you think about it, I can't imagine a common situation where we do
|
||||||
|
something like make an <code>attr0</code> value that we then want to save on its own and
|
||||||
|
apply to several different <code>ObjectAttributes</code> that we make during a game. That
|
||||||
|
just doesn't sound likely to me. So, we'll go the route where <code>ObjectAttributes</code>
|
||||||
|
is just a big black box to the outside world and we don't need to think about
|
||||||
|
the three fields internally as being separate.</p>
|
||||||
|
<p>First we make it so that we can get and set object attributes from memory:</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
pub const OAM: usize = 0x700_0000;
|
||||||
|
|
||||||
|
pub fn object_attributes(slot: usize) -> ObjectAttributes {
|
||||||
|
assert!(slot < 128);
|
||||||
|
let ptr = VolatilePtr((OAM + slot * (size_of::<u16>() * 4)) as *mut u16);
|
||||||
|
unsafe {
|
||||||
|
ObjectAttributes {
|
||||||
|
attr0: ptr.read(),
|
||||||
|
attr1: ptr.offset(1).read(),
|
||||||
|
attr2: ptr.offset(2).read(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_object_attributes(slot: usize, obj: ObjectAttributes) {
|
||||||
|
assert!(slot < 128);
|
||||||
|
let ptr = VolatilePtr((OAM + slot * (size_of::<u16>() * 4)) as *mut u16);
|
||||||
|
unsafe {
|
||||||
|
ptr.write(obj.attr0);
|
||||||
|
ptr.offset(1).write(obj.attr1);
|
||||||
|
ptr.offset(2).write(obj.attr2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct ObjectAttributes {
|
||||||
|
attr0: u16,
|
||||||
|
attr1: u16,
|
||||||
|
attr2: u16,
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
|
<p>Then we add a billion methods to the <code>ObjectAttributes</code> type so that we can
|
||||||
|
actually set all the different values that we want to set.</p>
|
||||||
|
<p>This code block is the last thing on this page so if you don't wanna scroll past
|
||||||
|
the whole thing you can just go to the next page.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">
|
||||||
|
# #![allow(unused_variables)]
|
||||||
|
#fn main() {
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ObjectRenderMode {
|
||||||
|
Normal,
|
||||||
|
Affine,
|
||||||
|
Disabled,
|
||||||
|
DoubleAreaAffine,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ObjectMode {
|
||||||
|
Normal,
|
||||||
|
AlphaBlending,
|
||||||
|
ObjectWindow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ObjectShape {
|
||||||
|
Square,
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ObjectOrientation {
|
||||||
|
Normal,
|
||||||
|
HFlip,
|
||||||
|
VFlip,
|
||||||
|
BothFlip,
|
||||||
|
Affine(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectAttributes {
|
||||||
|
pub fn row(&self) -> u16 {
|
||||||
|
self.attr0 & 0b1111_1111
|
||||||
|
}
|
||||||
|
pub fn column(&self) -> u16 {
|
||||||
|
self.attr1 & 0b1_1111_1111
|
||||||
|
}
|
||||||
|
pub fn rendering(&self) -> ObjectRenderMode {
|
||||||
|
match (self.attr0 >> 8) & 0b11 {
|
||||||
|
0 => ObjectRenderMode::Normal,
|
||||||
|
1 => ObjectRenderMode::Affine,
|
||||||
|
2 => ObjectRenderMode::Disabled,
|
||||||
|
3 => ObjectRenderMode::DoubleAreaAffine,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn mode(&self) -> ObjectMode {
|
||||||
|
match (self.attr0 >> 0xA) & 0b11 {
|
||||||
|
0 => ObjectMode::Normal,
|
||||||
|
1 => ObjectMode::AlphaBlending,
|
||||||
|
2 => ObjectMode::ObjectWindow,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn mosaic(&self) -> bool {
|
||||||
|
((self.attr0 << 3) as i16) < 0
|
||||||
|
}
|
||||||
|
pub fn two_fifty_six_colors(&self) -> bool {
|
||||||
|
((self.attr0 << 2) as i16) < 0
|
||||||
|
}
|
||||||
|
pub fn shape(&self) -> ObjectShape {
|
||||||
|
match (self.attr0 >> 0xE) & 0b11 {
|
||||||
|
0 => ObjectShape::Square,
|
||||||
|
1 => ObjectShape::Horizontal,
|
||||||
|
2 => ObjectShape::Vertical,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn orientation(&self) -> ObjectOrientation {
|
||||||
|
if (self.attr0 >> 8) & 1 > 0 {
|
||||||
|
ObjectOrientation::Affine((self.attr1 >> 9) as u8 & 0b1_1111)
|
||||||
|
} else {
|
||||||
|
match (self.attr1 >> 0xC) & 0b11 {
|
||||||
|
0 => ObjectOrientation::Normal,
|
||||||
|
1 => ObjectOrientation::HFlip,
|
||||||
|
2 => ObjectOrientation::VFlip,
|
||||||
|
3 => ObjectOrientation::BothFlip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn size(&self) -> u16 {
|
||||||
|
self.attr1 >> 0xE
|
||||||
|
}
|
||||||
|
pub fn tile_index(&self) -> u16 {
|
||||||
|
self.attr2 & 0b11_1111_1111
|
||||||
|
}
|
||||||
|
pub fn priority(&self) -> u16 {
|
||||||
|
self.attr2 >> 0xA
|
||||||
|
}
|
||||||
|
pub fn palbank(&self) -> u16 {
|
||||||
|
self.attr2 >> 0xC
|
||||||
|
}
|
||||||
|
//
|
||||||
|
pub fn set_row(&mut self, row: u16) {
|
||||||
|
self.attr0 &= !0b1111_1111;
|
||||||
|
self.attr0 |= row & 0b1111_1111;
|
||||||
|
}
|
||||||
|
pub fn set_column(&mut self, col: u16) {
|
||||||
|
self.attr1 &= !0b1_1111_1111;
|
||||||
|
self.attr2 |= col & 0b1_1111_1111;
|
||||||
|
}
|
||||||
|
pub fn set_rendering(&mut self, rendering: ObjectRenderMode) {
|
||||||
|
const RENDERING_MASK: u16 = 0b11 << 8;
|
||||||
|
self.attr0 &= !RENDERING_MASK;
|
||||||
|
self.attr0 |= (rendering as u16) << 8;
|
||||||
|
}
|
||||||
|
pub fn set_mode(&mut self, mode: ObjectMode) {
|
||||||
|
const MODE_MASK: u16 = 0b11 << 0xA;
|
||||||
|
self.attr0 &= MODE_MASK;
|
||||||
|
self.attr0 |= (mode as u16) << 0xA;
|
||||||
|
}
|
||||||
|
pub fn set_mosaic(&mut self, bit: bool) {
|
||||||
|
const MOSAIC_BIT: u16 = 1 << 0xC;
|
||||||
|
if bit {
|
||||||
|
self.attr0 |= MOSAIC_BIT
|
||||||
|
} else {
|
||||||
|
self.attr0 &= !MOSAIC_BIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_two_fifty_six_colors(&mut self, bit: bool) {
|
||||||
|
const COLOR_MODE_BIT: u16 = 1 << 0xD;
|
||||||
|
if bit {
|
||||||
|
self.attr0 |= COLOR_MODE_BIT
|
||||||
|
} else {
|
||||||
|
self.attr0 &= !COLOR_MODE_BIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_shape(&mut self, shape: ObjectShape) {
|
||||||
|
self.attr0 &= 0b0011_1111_1111_1111;
|
||||||
|
self.attr0 |= (shape as u16) << 0xE;
|
||||||
|
}
|
||||||
|
pub fn set_orientation(&mut self, orientation: ObjectOrientation) {
|
||||||
|
const AFFINE_INDEX_MASK: u16 = 0b1_1111 << 9;
|
||||||
|
self.attr1 &= !AFFINE_INDEX_MASK;
|
||||||
|
let bits = match orientation {
|
||||||
|
ObjectOrientation::Affine(index) => (index as u16) << 9,
|
||||||
|
ObjectOrientation::Normal => 0,
|
||||||
|
ObjectOrientation::HFlip => 1 << 0xC,
|
||||||
|
ObjectOrientation::VFlip => 1 << 0xD,
|
||||||
|
ObjectOrientation::BothFlip => 0b11 << 0xC,
|
||||||
|
};
|
||||||
|
self.attr1 |= bits;
|
||||||
|
}
|
||||||
|
pub fn set_size(&mut self, size: u16) {
|
||||||
|
self.attr1 &= 0b0011_1111_1111_1111;
|
||||||
|
self.attr1 |= size << 14;
|
||||||
|
}
|
||||||
|
pub fn set_tile_index(&mut self, index: u16) {
|
||||||
|
self.attr2 &= !0b11_1111_1111;
|
||||||
|
self.attr2 |= 0b11_1111_1111 & index;
|
||||||
|
}
|
||||||
|
pub fn set_priority(&mut self, priority: u16) {
|
||||||
|
self.attr2 &= !0b0000_1100_0000_0000;
|
||||||
|
self.attr2 |= (priority & 0b11) << 0xA;
|
||||||
|
}
|
||||||
|
pub fn set_palbank(&mut self, palbank: u16) {
|
||||||
|
self.attr2 &= !0b1111_0000_0000_0000;
|
||||||
|
self.attr2 |= (palbank & 0b1111) << 0xC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#}</code></pre></pre>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
@ -1772,23 +1772,10 @@ GBA's CPU <em>can't do any of that</em>. On the GBA, there's a genuine speed dif
|
||||||
between looping over indexes and then indexing each loop (slow) compared to
|
between looping over indexes and then indexing each loop (slow) compared to
|
||||||
using an iterator that just stores an internal pointer and does +1 offset per
|
using an iterator that just stores an internal pointer and does +1 offset per
|
||||||
loop until it reaches the end (fast). The repeated indexing itself can by itself
|
loop until it reaches the end (fast). The repeated indexing itself can by itself
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< HEAD
|
|
||||||
be an expensive step. If you've got a slice of data to process, be sure to go
|
|
||||||
over it with <code>.iter()</code> and <code>.iter_mut()</code> if you can, instead of looping by
|
|
||||||
index. This is Rust and all, so probably you were gonna do that anyway, but just
|
|
||||||
a heads up.</p>
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
be an expensive step. If it's like a 3 element array it's no big deal, but if
|
be an expensive step. If it's like a 3 element array it's no big deal, but if
|
||||||
you've got a big slice of data to process, be sure to go over it with <code>.iter()</code>
|
you've got a big slice of data to process, be sure to go over it with <code>.iter()</code>
|
||||||
and <code>.iter_mut()</code> if you can, instead of looping by index. This is Rust and all,
|
and <code>.iter_mut()</code> if you can, instead of looping by index. This is Rust and all,
|
||||||
so probably you were gonna do that anyway, but just a heads up.</p>
|
so probably you were gonna do that anyway, but just a heads up.</p>
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> object attribute stuff
|
|
||||||
=======
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
<a class="header" href="#get-your-tilemap-ready" id="get-your-tilemap-ready"><h2>Get your Tilemap ready</h2></a>
|
<a class="header" href="#get-your-tilemap-ready" id="get-your-tilemap-ready"><h2>Get your Tilemap ready</h2></a>
|
||||||
<p>I believe that at one point I alluded to a tilemap existing. Well, just as the
|
<p>I believe that at one point I alluded to a tilemap existing. Well, just as the
|
||||||
tiles are arranged into charblocks, the data describing what tile to show in
|
tiles are arranged into charblocks, the data describing what tile to show in
|
||||||
|
@ -1896,17 +1883,7 @@ separate groups of related registers.</p>
|
||||||
<p>Each of these are a read/write <code>u16</code> location. This is where we get to all of
|
<p>Each of these are a read/write <code>u16</code> location. This is where we get to all of
|
||||||
the important details that we've been putting off.</p>
|
the important details that we've been putting off.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<li>2 bits for the priority of each background (0 being highest). If two
|
|
||||||
backgrounds are set to the same priority the the lower numbered background
|
|
||||||
layer takes prescience.</li>
|
|
||||||
=======
|
|
||||||
<li>2 bits for the priority.</li>
|
<li>2 bits for the priority.</li>
|
||||||
>>>>>>> object attribute stuff
|
|
||||||
=======
|
|
||||||
<li>2 bits for the priority.</li>
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
<li>2 bits for "character base block", the charblock that all of the tile indexes
|
<li>2 bits for "character base block", the charblock that all of the tile indexes
|
||||||
for this background are offset from.</li>
|
for this background are offset from.</li>
|
||||||
<li>1 bit for mosaic effect being enabled (we'll get to that below).</li>
|
<li>1 bit for mosaic effect being enabled (we'll get to that below).</li>
|
||||||
|
@ -1972,16 +1949,6 @@ target pixel. You can't change the target pixel on a block by block basis.</p>
|
||||||
<p>As with backgrounds, objects can be used in both an affine and non-affine way.
|
<p>As with backgrounds, objects can be used in both an affine and non-affine way.
|
||||||
For this section we'll focus on the non-affine elements, and then we'll do all
|
For this section we'll focus on the non-affine elements, and then we'll do all
|
||||||
the affine stuff in a later chapter.</p>
|
the affine stuff in a later chapter.</p>
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<p>TODO: tio afero ke mi diris</p>
|
|
||||||
<a class="header" href="#objects-vs-sprites" id="objects-vs-sprites"><h2>Objects vs Sprites</h2></a>
|
|
||||||
<a class="header" href="#ready-the-palette" id="ready-the-palette"><h2>Ready the Palette</h2></a>
|
|
||||||
<a class="header" href="#ready-the-tiles" id="ready-the-tiles"><h2>Ready the Tiles</h2></a>
|
|
||||||
<a class="header" href="#set-the-object-attributes" id="set-the-object-attributes"><h2>Set the Object Attributes</h2></a>
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
<a class="header" href="#objects-vs-sprites" id="objects-vs-sprites"><h2>Objects vs Sprites</h2></a>
|
<a class="header" href="#objects-vs-sprites" id="objects-vs-sprites"><h2>Objects vs Sprites</h2></a>
|
||||||
<p>As <a href="https://www.coranac.com/tonc/text/regobj.htm">TONC</a> helpfully reminds us
|
<p>As <a href="https://www.coranac.com/tonc/text/regobj.htm">TONC</a> helpfully reminds us
|
||||||
(and then proceeds to not follow its own advice), we should always try to think
|
(and then proceeds to not follow its own advice), we should always try to think
|
||||||
|
@ -2347,10 +2314,6 @@ impl ObjectAttributes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#}</code></pre></pre>
|
#}</code></pre></pre>
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> object attribute stuff
|
|
||||||
=======
|
|
||||||
>>>>>>> 6b631474faca0645ad16822187c230b813ab6793
|
|
||||||
<a class="header" href="#gba-rng" id="gba-rng"><h1>GBA RNG</h1></a>
|
<a class="header" href="#gba-rng" id="gba-rng"><h1>GBA RNG</h1></a>
|
||||||
<p>TODO</p>
|
<p>TODO</p>
|
||||||
<a class="header" href="#memory_game" id="memory_game"><h1>memory_game</h1></a>
|
<a class="header" href="#memory_game" id="memory_game"><h1>memory_game</h1></a>
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue