1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-07 08:28:57 +00:00
CLK/Machines/Amiga/Copper.cpp

148 lines
3.8 KiB
C++
Raw Normal View History

2021-09-17 01:17:23 +00:00
//
// Copper.cpp
// Clock Signal
//
// Created by Thomas Harte on 16/09/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#ifndef NDEBUG
2021-09-17 01:17:23 +00:00
#define NDEBUG
#endif
2021-09-17 01:17:23 +00:00
#define LOG_PREFIX "[Copper] "
#include "../../Outputs/Log.hpp"
#include "Chipset.hpp"
#include "Copper.hpp"
using namespace Amiga;
namespace {
bool satisfies_raster(uint16_t position, uint16_t blitter_status, uint16_t *instruction) {
const uint16_t mask = 0x8000 | (instruction[1] & 0x7ffe);
return
(position & mask) >= (instruction[0] & mask)
&& (!(blitter_status & 0x4000) || (instruction[1] & 0x8000));
}
}
//
// Quick notes on the Copper:
//
2021-11-27 16:38:41 +00:00
// There are three instructions: move, wait and skip. All are two words in length.
//
// Move writes a value to one of the Chipset registers; it is encoded as:
//
// First word:
// b0: 0
// b1b8: register address
// b9+: unused ("should be set to 0")
//
// Second word:
// b0b15: value to move.
//
//
// Wait waits until the raster gets to at least a certain position, and
// optionally until the Blitter has finished. It is encoded as:
//
// First word:
// b0: 1
// b1b7: horizontal beam position
// b8+: vertical beam position
//
// Second word:
// b0: 0
// b1b7: horizontal beam comparison mask
// b8b14: vertical beam comparison mask
// b15: 1 => don't also wait for the Blitter to be finished; 0 => wait.
//
//
// Skip skips the next instruction if the raster has already reached a certain
// position, and optionally only if the Blitter has finished, and only if the
// next instruction is a move.
//
// First word:
// b0: 1
// b1b7: horizontal beam position
// b8+: vertical beam position
//
// Second word:
// b0: 1
// b1b7: horizontal beam comparison mask
// b8b14: vertical beam comparison mask
2021-11-27 16:38:41 +00:00
// b15: 1 => don't also test whether the Blitter is finished; 0 => test.
//
bool Copper::advance_dma(uint16_t position, uint16_t blitter_status) {
2021-09-17 01:17:23 +00:00
switch(state_) {
default: return false;
case State::Waiting:
if(satisfies_raster(position, blitter_status, instruction_)) {
LOG("Unblocked waiting for " << PADHEX(4) << instruction_[0] << " at " << position);
2021-09-17 01:17:23 +00:00
state_ = State::FetchFirstWord;
}
return false;
case State::FetchFirstWord:
instruction_[0] = ram_[address_ & ram_mask_];
++address_;
state_ = State::FetchSecondWord;
break;
case State::FetchSecondWord: {
// Get and reset the should-skip-next flag.
2021-09-17 01:17:23 +00:00
const bool should_skip_move = skip_next_;
skip_next_ = false;
// Read in the second instruction word.
2021-09-17 01:17:23 +00:00
instruction_[1] = ram_[address_ & ram_mask_];
++address_;
// Check for a MOVE.
2021-09-17 01:17:23 +00:00
if(!(instruction_[0] & 1)) {
if(!should_skip_move) {
// Stop if this move would be a privilege violation.
instruction_[0] &= 0x1fe;
if((instruction_[0] < 0x10) || (instruction_[0] < 0x20 && !(control_&1))) {
LOG("Invalid MOVE to " << PADHEX(4) << instruction_[0] << "; stopping");
state_ = State::Stopped;
break;
}
// Construct a 68000-esque Microcycle in order to be able to perform the access.
CPU::MC68000::Microcycle cycle;
cycle.operation = CPU::MC68000::Microcycle::SelectWord;
uint32_t full_address = instruction_[0];
CPU::RegisterPair16 data = instruction_[1];
cycle.address = &full_address;
cycle.value = &data;
chipset_.perform(cycle);
}
// Roll onto the next command.
state_ = State::FetchFirstWord;
break;
}
// Got to here => this is a WAIT or a SKIP.
2021-09-17 01:17:23 +00:00
if(!(instruction_[1] & 1)) {
// A WAIT. Just note that this is now waiting; the proper test
// will be applied from the next potential `advance_dma` onwards.
2021-09-17 01:17:23 +00:00
state_ = State::Waiting;
break;
}
// Neither a WAIT nor a MOVE => a SKIP.
skip_next_ = satisfies_raster(position, blitter_status, instruction_);
2021-09-17 01:17:23 +00:00
state_ = State::FetchFirstWord;
} break;
}
return true;
}