1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-22 12:33:29 +00:00
CLK/Machines/Amiga/Sprites.cpp

116 lines
3.1 KiB
C++

//
// Sprites.cpp
// Clock Signal
//
// Created by Thomas Harte on 26/11/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#include "Sprites.hpp"
using namespace Amiga;
namespace {
/// Expands @c source from b15 ... b0 to 000b15 ... 000b0.
constexpr uint64_t expand_sprite_word(uint16_t source) {
uint64_t result = source;
result = (result | (result << 24)) & 0x0000'00ff'0000'00ff;
result = (result | (result << 12)) & 0x000f'000f'000f'000f;
result = (result | (result << 6)) & 0x0303'0303'0303'0303;
result = (result | (result << 3)) & 0x1111'1111'1111'1111;
return result;
}
// A very small selection of test cases.
static_assert(expand_sprite_word(0xffff) == 0x11'11'11'11'11'11'11'11);
static_assert(expand_sprite_word(0x5555) == 0x01'01'01'01'01'01'01'01);
static_assert(expand_sprite_word(0xaaaa) == 0x10'10'10'10'10'10'10'10);
static_assert(expand_sprite_word(0x0000) == 0x00'00'00'00'00'00'00'00);
}
// MARK: - Sprites.
void Sprite::set_start_position(uint16_t value) {
v_start_ = (v_start_ & 0xff00) | (value >> 8);
h_start = uint16_t((h_start & 0x0001) | ((value & 0xff) << 1));
}
void Sprite::set_stop_and_control(uint16_t value) {
h_start = uint16_t((h_start & 0x01fe) | (value & 0x01));
v_stop_ = uint16_t((value >> 8) | ((value & 0x02) << 7));
v_start_ = uint16_t((v_start_ & 0x00ff) | ((value & 0x04) << 6));
attached = value & 0x80;
// Disarm the sprite, but expect graphics next from DMA.
visible = false;
dma_state_ = DMAState::FetchImage;
}
void Sprite::set_image_data(int slot, uint16_t value) {
data[slot] = value;
visible |= slot == 0;
}
void Sprite::advance_line(int y, bool is_end_of_blank) {
if(dma_state_ == DMAState::FetchImage && y == v_start_) {
visible = true;
}
if(is_end_of_blank || y == v_stop_) {
dma_state_ = DMAState::FetchControl;
visible = true;
}
}
bool Sprite::advance_dma(int offset) {
if(!visible) return false;
// Fetch another word.
const uint16_t next_word = ram_[pointer_[0] & ram_mask_];
++pointer_[0];
// Put the fetched word somewhere appropriate and update the DMA state.
switch(dma_state_) {
// i.e. stopped.
default: return false;
case DMAState::FetchControl:
if(offset) {
set_stop_and_control(next_word);
} else {
set_start_position(next_word);
}
return true;
case DMAState::FetchImage:
set_image_data(1 - bool(offset), next_word);
return true;
}
return false;
}
template <int sprite> void TwoSpriteShifter::load(
uint16_t lsb,
uint16_t msb,
int delay) {
constexpr int sprite_shift = sprite << 1;
const int delay_shift = delay << 2;
// Clear out any current sprite pixels; this is a reload.
data_ &= 0xcccc'cccc'cccc'ccccull >> (sprite_shift + delay_shift);
// Map LSB and MSB up to 64-bits and load into the shifter.
const uint64_t new_data =
(
expand_sprite_word(lsb) |
(expand_sprite_word(msb) << 1)
) << sprite_shift;
data_ |= new_data >> delay_shift;
overflow_ |= uint8_t((new_data << 8) >> delay_shift);
}
template void TwoSpriteShifter::load<0>(uint16_t, uint16_t, int);
template void TwoSpriteShifter::load<1>(uint16_t, uint16_t, int);