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.
|
|
|
|
|
//
|
|
|
|
|
|
2021-10-25 20:34:36 +00:00
|
|
|
|
#ifndef NDEBUG
|
2021-09-17 01:17:23 +00:00
|
|
|
|
#define NDEBUG
|
2021-10-25 20:34:36 +00:00
|
|
|
|
#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;
|
|
|
|
|
|
2021-11-27 16:36:15 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
bool satisfies_raster(uint16_t position, uint16_t blitter_status, uint16_t *instruction) {
|
|
|
|
|
const uint16_t mask = 0x8000 | (instruction[1] & 0x7ffe);
|
|
|
|
|
return
|
2021-12-13 00:18:18 +00:00
|
|
|
|
(position & mask) >= (instruction[0] & mask) &&
|
2021-12-12 22:26:33 +00:00
|
|
|
|
(!(blitter_status & 0x4000) || (instruction[1] & 0x8000));
|
2021-11-27 16:36:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// 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.
|
2021-11-27 16:36:15 +00:00
|
|
|
|
//
|
|
|
|
|
// Move writes a value to one of the Chipset registers; it is encoded as:
|
|
|
|
|
//
|
|
|
|
|
// First word:
|
|
|
|
|
// b0: 0
|
|
|
|
|
// b1–b8: register address
|
|
|
|
|
// b9+: unused ("should be set to 0")
|
|
|
|
|
//
|
|
|
|
|
// Second word:
|
|
|
|
|
// b0–b15: 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
|
|
|
|
|
// b1–b7: horizontal beam position
|
|
|
|
|
// b8+: vertical beam position
|
|
|
|
|
//
|
|
|
|
|
// Second word:
|
|
|
|
|
// b0: 0
|
|
|
|
|
// b1–b7: horizontal beam comparison mask
|
|
|
|
|
// b8–b14: 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
|
|
|
|
|
// b1–b7: horizontal beam position
|
|
|
|
|
// b8+: vertical beam position
|
|
|
|
|
//
|
|
|
|
|
// Second word:
|
|
|
|
|
// b0: 1
|
|
|
|
|
// b1–b7: horizontal beam comparison mask
|
|
|
|
|
// b8–b14: 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.
|
2021-11-27 16:36:15 +00:00
|
|
|
|
//
|
|
|
|
|
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:
|
2021-12-13 00:18:18 +00:00
|
|
|
|
if(satisfies_raster(position, blitter_status, instruction_)) {
|
|
|
|
|
LOG("Unblocked waiting for " << PADHEX(4) << instruction_[0] << " at " << PADHEX(4) << position << " with mask " << PADHEX(4) << (instruction_[1] & 0x7ffe));
|
|
|
|
|
state_ = State::FetchFirstWord;
|
2021-09-17 01:17:23 +00:00
|
|
|
|
}
|
2021-12-13 00:18:18 +00:00
|
|
|
|
return false;
|
2021-09-17 01:17:23 +00:00
|
|
|
|
|
|
|
|
|
case State::FetchFirstWord:
|
|
|
|
|
instruction_[0] = ram_[address_ & ram_mask_];
|
|
|
|
|
++address_;
|
|
|
|
|
state_ = State::FetchSecondWord;
|
2021-12-12 22:26:33 +00:00
|
|
|
|
LOG("First word fetch at " << PADHEX(4) << position);
|
2021-09-17 01:17:23 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case State::FetchSecondWord: {
|
2021-11-27 16:36:15 +00:00
|
|
|
|
// 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;
|
|
|
|
|
|
2021-11-27 16:36:15 +00:00
|
|
|
|
// Read in the second instruction word.
|
2021-09-17 01:17:23 +00:00
|
|
|
|
instruction_[1] = ram_[address_ & ram_mask_];
|
|
|
|
|
++address_;
|
2021-12-12 22:26:33 +00:00
|
|
|
|
LOG("Second word fetch at " << PADHEX(4) << position);
|
2021-09-17 01:17:23 +00:00
|
|
|
|
|
2021-11-27 16:36:15 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-19 00:39:41 +00:00
|
|
|
|
chipset_.write(instruction_[0], instruction_[1]);
|
2021-09-17 01:17:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Roll onto the next command.
|
|
|
|
|
state_ = State::FetchFirstWord;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-27 16:36:15 +00:00
|
|
|
|
// Got to here => this is a WAIT or a SKIP.
|
2021-09-17 01:17:23 +00:00
|
|
|
|
|
2021-12-13 00:18:18 +00:00
|
|
|
|
const bool raster_is_satisfied = satisfies_raster(position, blitter_status, instruction_);
|
|
|
|
|
|
2021-09-17 01:17:23 +00:00
|
|
|
|
if(!(instruction_[1] & 1)) {
|
2021-12-12 22:26:33 +00:00
|
|
|
|
// A WAIT. Empirically, I don't think this waits at all if the test is
|
|
|
|
|
// already satisfied.
|
2021-12-13 00:18:18 +00:00
|
|
|
|
state_ = raster_is_satisfied ? State::FetchFirstWord : State::Waiting;
|
|
|
|
|
if(raster_is_satisfied) LOG("Will wait from " << PADHEX(4) << position);
|
2021-09-17 01:17:23 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Neither a WAIT nor a MOVE => a SKIP.
|
|
|
|
|
|
2021-11-27 16:36:15 +00:00
|
|
|
|
skip_next_ = satisfies_raster(position, blitter_status, instruction_);
|
2021-09-17 01:17:23 +00:00
|
|
|
|
state_ = State::FetchFirstWord;
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|