2016-10-12 02:20:13 +00:00
|
|
|
//
|
|
|
|
// Oric.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 11/10/2016.
|
|
|
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "Oric.hpp"
|
2016-10-20 01:31:50 +00:00
|
|
|
#include "../MemoryFuzzer.hpp"
|
2016-10-12 02:20:13 +00:00
|
|
|
|
|
|
|
using namespace Oric;
|
|
|
|
|
2016-11-04 02:14:40 +00:00
|
|
|
Machine::Machine() :
|
2016-11-22 14:22:00 +00:00
|
|
|
cycles_since_video_update_(0),
|
|
|
|
use_fast_tape_hack_(false),
|
|
|
|
typer_delay_(2500000),
|
|
|
|
keyboard_read_count_(0),
|
2016-11-25 12:15:48 +00:00
|
|
|
keyboard_(new Keyboard),
|
|
|
|
ram_top_(0xbfff),
|
2016-11-25 12:30:27 +00:00
|
|
|
paged_rom_(rom_),
|
|
|
|
microdisc_is_enabled_(false)
|
2016-10-12 02:20:13 +00:00
|
|
|
{
|
|
|
|
set_clock_rate(1000000);
|
2016-11-22 14:22:00 +00:00
|
|
|
via_.set_interrupt_delegate(this);
|
|
|
|
via_.keyboard = keyboard_;
|
2016-10-15 01:18:03 +00:00
|
|
|
clear_all_keys();
|
2016-11-22 14:22:00 +00:00
|
|
|
via_.tape->set_delegate(this);
|
|
|
|
Memory::Fuzz(ram_, sizeof(ram_));
|
2016-10-12 02:20:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::configure_as_target(const StaticAnalyser::Target &target)
|
|
|
|
{
|
2016-10-16 01:32:59 +00:00
|
|
|
if(target.tapes.size())
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
via_.tape->set_tape(target.tapes.front());
|
2016-10-16 01:32:59 +00:00
|
|
|
}
|
2016-11-04 02:14:40 +00:00
|
|
|
|
|
|
|
if(target.loadingCommand.length()) // TODO: and automatic loading option enabled
|
|
|
|
{
|
|
|
|
set_typer_for_string(target.loadingCommand.c_str());
|
|
|
|
}
|
2016-11-15 02:39:16 +00:00
|
|
|
|
2016-11-25 12:15:48 +00:00
|
|
|
if(target.oric.has_microdisc)
|
|
|
|
{
|
|
|
|
microdisc_is_enabled_ = true;
|
|
|
|
microdisc_did_change_paging_flags(µdisc_);
|
|
|
|
microdisc_.set_delegate(this);
|
|
|
|
}
|
|
|
|
|
2016-11-25 12:53:38 +00:00
|
|
|
int drive_index = 0;
|
|
|
|
for(auto disk : target.disks)
|
|
|
|
{
|
|
|
|
if(drive_index < 4) microdisc_.set_disk(disk, drive_index);
|
|
|
|
drive_index++;
|
|
|
|
}
|
|
|
|
|
2016-11-15 02:39:16 +00:00
|
|
|
if(target.oric.use_atmos_rom)
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
memcpy(rom_, basic11_rom_.data(), std::min(basic11_rom_.size(), sizeof(rom_)));
|
2016-11-15 03:05:53 +00:00
|
|
|
|
2016-11-22 14:22:00 +00:00
|
|
|
is_using_basic11_ = true;
|
|
|
|
tape_get_byte_address_ = 0xe6c9;
|
|
|
|
scan_keyboard_address_ = 0xf495;
|
|
|
|
tape_speed_address_ = 0x024d;
|
2016-11-15 02:39:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
memcpy(rom_, basic10_rom_.data(), std::min(basic10_rom_.size(), sizeof(rom_)));
|
2016-11-15 03:05:53 +00:00
|
|
|
|
2016-11-22 14:22:00 +00:00
|
|
|
is_using_basic11_ = false;
|
|
|
|
tape_get_byte_address_ = 0xe630;
|
|
|
|
scan_keyboard_address_ = 0xf43c;
|
|
|
|
tape_speed_address_ = 0x67;
|
2016-11-15 02:39:16 +00:00
|
|
|
}
|
2016-10-12 02:20:13 +00:00
|
|
|
}
|
|
|
|
|
2016-11-15 02:39:16 +00:00
|
|
|
void Machine::set_rom(ROM rom, const std::vector<uint8_t> &data)
|
2016-10-12 22:51:02 +00:00
|
|
|
{
|
2016-11-21 12:59:25 +00:00
|
|
|
switch(rom)
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
case BASIC11: basic11_rom_ = std::move(data); break;
|
|
|
|
case BASIC10: basic10_rom_ = std::move(data); break;
|
|
|
|
case Microdisc: microdisc_rom_ = std::move(data); break;
|
2016-11-21 12:59:25 +00:00
|
|
|
}
|
2016-10-12 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2016-10-12 02:20:13 +00:00
|
|
|
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
|
|
|
|
{
|
2016-11-25 12:15:48 +00:00
|
|
|
if(address > ram_top_)
|
2016-10-12 22:51:02 +00:00
|
|
|
{
|
2016-11-25 12:15:48 +00:00
|
|
|
if(isReadOperation(operation)) *value = paged_rom_[address - ram_top_ - 1];
|
2016-11-03 01:09:49 +00:00
|
|
|
|
2016-11-03 02:30:53 +00:00
|
|
|
// 024D = 0 => fast; otherwise slow
|
|
|
|
// E6C9 = read byte: return byte in A
|
2016-11-25 12:15:48 +00:00
|
|
|
if(address == tape_get_byte_address_ && paged_rom_ == rom_ && use_fast_tape_hack_ && operation == CPU6502::BusOperation::ReadOpcode && via_.tape->has_tape() && !via_.tape->get_tape()->is_at_end())
|
2016-10-31 02:39:47 +00:00
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
uint8_t next_byte = via_.tape->get_next_byte(!ram_[tape_speed_address_]);
|
2016-11-03 02:30:53 +00:00
|
|
|
set_value_of_register(CPU6502::A, next_byte);
|
|
|
|
set_value_of_register(CPU6502::Flags, next_byte ? 0 : CPU6502::Flag::Zero);
|
|
|
|
*value = 0x60; // i.e. RTS
|
2016-10-31 02:39:47 +00:00
|
|
|
}
|
2016-10-12 22:51:02 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-10-14 00:50:55 +00:00
|
|
|
if((address & 0xff00) == 0x0300)
|
|
|
|
{
|
2016-11-25 12:15:48 +00:00
|
|
|
if(microdisc_is_enabled_ && address >= 0x0310)
|
|
|
|
{
|
|
|
|
switch(address)
|
|
|
|
{
|
|
|
|
case 0x0310: case 0x0311: case 0x0312: case 0x0313:
|
|
|
|
if(isReadOperation(operation)) *value = microdisc_.get_register(address);
|
|
|
|
else microdisc_.set_register(address, *value);
|
|
|
|
break;
|
2016-11-25 13:28:11 +00:00
|
|
|
case 0x314: case 0x315: case 0x316: case 0x317:
|
2016-11-25 12:15:48 +00:00
|
|
|
if(isReadOperation(operation)) *value = microdisc_.get_interrupt_request_register();
|
|
|
|
else microdisc_.set_control_register(*value);
|
|
|
|
break;
|
2016-11-25 13:30:45 +00:00
|
|
|
case 0x318: case 0x319: case 0x31a: case 0x31b:
|
2016-11-25 12:15:48 +00:00
|
|
|
if(isReadOperation(operation)) *value = microdisc_.get_data_request_register();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(isReadOperation(operation)) *value = via_.get_register(address);
|
|
|
|
else via_.set_register(address, *value);
|
|
|
|
}
|
2016-10-14 00:50:55 +00:00
|
|
|
}
|
2016-10-12 22:51:02 +00:00
|
|
|
else
|
2016-10-12 23:20:23 +00:00
|
|
|
{
|
2016-10-14 00:50:55 +00:00
|
|
|
if(isReadOperation(operation))
|
2016-11-22 14:22:00 +00:00
|
|
|
*value = ram_[address];
|
2016-10-14 00:50:55 +00:00
|
|
|
else
|
|
|
|
{
|
2016-11-25 12:15:48 +00:00
|
|
|
if(address >= 0x9800 && address <= 0xc000) { update_video(); typer_delay_ = 0; }
|
2016-11-22 14:22:00 +00:00
|
|
|
ram_[address] = *value;
|
2016-10-14 00:50:55 +00:00
|
|
|
}
|
2016-10-12 23:20:23 +00:00
|
|
|
}
|
2016-10-12 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2016-11-22 14:22:00 +00:00
|
|
|
if(_typer && address == scan_keyboard_address_ && operation == CPU6502::BusOperation::ReadOpcode)
|
2016-11-04 02:14:40 +00:00
|
|
|
{
|
2016-11-15 03:05:53 +00:00
|
|
|
// the Oric 1 misses any key pressed on the very first entry into the read keyboard routine, so don't
|
|
|
|
// do anything until at least the second, regardless of machine
|
2016-11-22 14:22:00 +00:00
|
|
|
if(!keyboard_read_count_) keyboard_read_count_++;
|
2016-11-15 03:05:53 +00:00
|
|
|
else if(!_typer->type_next_character())
|
2016-11-05 19:28:03 +00:00
|
|
|
{
|
|
|
|
clear_all_keys();
|
|
|
|
_typer.reset();
|
|
|
|
}
|
2016-11-04 02:14:40 +00:00
|
|
|
}
|
|
|
|
|
2016-11-22 14:22:00 +00:00
|
|
|
via_.run_for_cycles(1);
|
2016-11-25 12:51:39 +00:00
|
|
|
if(microdisc_is_enabled_) microdisc_.run_for_cycles(8);
|
2016-11-22 14:22:00 +00:00
|
|
|
cycles_since_video_update_++;
|
2016-10-12 02:20:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-10-13 01:29:21 +00:00
|
|
|
void Machine::synchronise()
|
|
|
|
{
|
|
|
|
update_video();
|
2016-11-22 14:22:00 +00:00
|
|
|
via_.synchronise();
|
2016-10-13 01:29:21 +00:00
|
|
|
}
|
|
|
|
|
2016-10-12 23:20:23 +00:00
|
|
|
void Machine::update_video()
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
video_output_->run_for_cycles(cycles_since_video_update_);
|
|
|
|
cycles_since_video_update_ = 0;
|
2016-10-12 23:20:23 +00:00
|
|
|
}
|
|
|
|
|
2016-10-12 02:20:13 +00:00
|
|
|
void Machine::setup_output(float aspect_ratio)
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
video_output_.reset(new VideoOutput(ram_));
|
|
|
|
via_.ay8910.reset(new GI::AY38910());
|
|
|
|
via_.ay8910->set_clock_rate(1000000);
|
2016-10-12 02:20:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::close_output()
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
video_output_.reset();
|
|
|
|
via_.ay8910.reset();
|
2016-10-12 02:20:13 +00:00
|
|
|
}
|
2016-10-14 00:50:55 +00:00
|
|
|
|
|
|
|
void Machine::mos6522_did_change_interrupt_status(void *mos6522)
|
|
|
|
{
|
2016-11-25 12:15:48 +00:00
|
|
|
set_interrupt_line();
|
2016-10-14 00:50:55 +00:00
|
|
|
}
|
2016-10-15 01:18:03 +00:00
|
|
|
|
2016-11-05 18:47:09 +00:00
|
|
|
void Machine::set_key_state(uint16_t key, bool isPressed)
|
2016-10-15 01:18:03 +00:00
|
|
|
{
|
|
|
|
if(key == KeyNMI)
|
|
|
|
{
|
|
|
|
set_nmi_line(isPressed);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(isPressed)
|
2016-11-22 14:22:00 +00:00
|
|
|
keyboard_->rows[key >> 8] |= (key & 0xff);
|
2016-10-15 01:18:03 +00:00
|
|
|
else
|
2016-11-22 14:22:00 +00:00
|
|
|
keyboard_->rows[key >> 8] &= ~(key & 0xff);
|
2016-10-15 01:18:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::clear_all_keys()
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
memset(keyboard_->rows, 0, sizeof(keyboard_->rows));
|
2016-10-15 01:18:03 +00:00
|
|
|
}
|
2016-10-16 01:21:18 +00:00
|
|
|
|
2016-11-03 11:59:30 +00:00
|
|
|
void Machine::set_use_fast_tape_hack(bool activate)
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
use_fast_tape_hack_ = activate;
|
2016-11-03 11:59:30 +00:00
|
|
|
}
|
|
|
|
|
2016-10-16 01:21:18 +00:00
|
|
|
void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player)
|
|
|
|
{
|
|
|
|
// set CB1
|
2016-11-22 14:22:00 +00:00
|
|
|
via_.set_control_line_input(VIA::Port::B, VIA::Line::One, tape_player->get_input());
|
2016-10-16 01:21:18 +00:00
|
|
|
}
|
2016-10-21 00:53:17 +00:00
|
|
|
|
2016-10-31 02:51:08 +00:00
|
|
|
std::shared_ptr<Outputs::CRT::CRT> Machine::get_crt()
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
return video_output_->get_crt();
|
2016-10-31 02:51:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Outputs::Speaker> Machine::get_speaker()
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
return via_.ay8910;
|
2016-10-31 02:51:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::run_for_cycles(int number_of_cycles)
|
|
|
|
{
|
|
|
|
CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles);
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:53:17 +00:00
|
|
|
#pragma mark - The 6522
|
|
|
|
|
2016-11-08 02:57:58 +00:00
|
|
|
Machine::VIA::VIA() :
|
|
|
|
MOS::MOS6522<Machine::VIA>(),
|
2016-11-22 14:22:00 +00:00
|
|
|
cycles_since_ay_update_(0),
|
2016-11-08 02:57:58 +00:00
|
|
|
tape(new TapePlayer) {}
|
2016-10-31 02:51:08 +00:00
|
|
|
|
2016-10-21 00:53:17 +00:00
|
|
|
void Machine::VIA::set_control_line_output(Port port, Line line, bool value)
|
|
|
|
{
|
|
|
|
if(line)
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
if(port) ay_bdir_ = value; else ay_bc1_ = value;
|
2016-10-21 00:53:17 +00:00
|
|
|
update_ay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 01:20:13 +00:00
|
|
|
void Machine::VIA::set_port_output(Port port, uint8_t value, uint8_t direction_mask)
|
|
|
|
{
|
2016-10-21 00:53:17 +00:00
|
|
|
if(port)
|
|
|
|
{
|
|
|
|
keyboard->row = value;
|
|
|
|
tape->set_motor_control(value & 0x40);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ay8910->set_data_input(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 01:20:13 +00:00
|
|
|
uint8_t Machine::VIA::get_port_input(Port port)
|
|
|
|
{
|
2016-10-21 00:53:17 +00:00
|
|
|
if(port)
|
|
|
|
{
|
|
|
|
uint8_t column = ay8910->get_port_output(false) ^ 0xff;
|
|
|
|
return (keyboard->rows[keyboard->row & 7] & column) ? 0x08 : 0x00;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return ay8910->get_data_output();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::VIA::synchronise()
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
ay8910->run_for_cycles(cycles_since_ay_update_);
|
2016-11-10 02:17:50 +00:00
|
|
|
ay8910->flush();
|
2016-11-22 14:22:00 +00:00
|
|
|
cycles_since_ay_update_ = 0;
|
2016-10-21 00:53:17 +00:00
|
|
|
}
|
|
|
|
|
2016-10-31 02:51:08 +00:00
|
|
|
void Machine::VIA::run_for_cycles(unsigned int number_of_cycles)
|
2016-10-21 00:53:17 +00:00
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
cycles_since_ay_update_ += number_of_cycles;
|
2016-10-31 02:51:08 +00:00
|
|
|
MOS::MOS6522<VIA>::run_for_cycles(number_of_cycles);
|
2016-11-03 11:59:30 +00:00
|
|
|
tape->run_for_cycles((int)number_of_cycles);
|
2016-10-21 00:53:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::VIA::update_ay()
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
ay8910->run_for_cycles(cycles_since_ay_update_);
|
|
|
|
cycles_since_ay_update_ = 0;
|
|
|
|
ay8910->set_control_lines( (GI::AY38910::ControlLines)((ay_bdir_ ? GI::AY38910::BCDIR : 0) | (ay_bc1_ ? GI::AY38910::BC1 : 0) | GI::AY38910::BC2));
|
2016-10-21 00:53:17 +00:00
|
|
|
}
|
2016-11-03 02:30:53 +00:00
|
|
|
|
|
|
|
#pragma mark - TapePlayer
|
|
|
|
|
|
|
|
Machine::TapePlayer::TapePlayer() :
|
2016-11-08 02:57:58 +00:00
|
|
|
Storage::Tape::BinaryTapePlayer(1000000)
|
2016-11-03 02:30:53 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
uint8_t Machine::TapePlayer::get_next_byte(bool fast)
|
|
|
|
{
|
2016-11-22 14:22:00 +00:00
|
|
|
return (uint8_t)parser_.get_next_byte(get_tape(), fast);
|
2016-11-03 11:34:48 +00:00
|
|
|
}
|
2016-11-25 12:15:48 +00:00
|
|
|
|
|
|
|
#pragma mark - Microdisc
|
|
|
|
|
|
|
|
void Machine::microdisc_did_change_paging_flags(class Microdisc *microdisc)
|
|
|
|
{
|
|
|
|
int flags = microdisc->get_paging_flags();
|
|
|
|
if(!(flags&Microdisc::PagingFlags::BASICDisable))
|
|
|
|
{
|
|
|
|
ram_top_ = 0xbfff;
|
|
|
|
paged_rom_ = rom_;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(flags&Microdisc::PagingFlags::MicrodscDisable)
|
|
|
|
{
|
|
|
|
ram_top_ = 0xffff;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ram_top_ = 0xdfff;
|
|
|
|
paged_rom_ = microdisc_rom_.data();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::wd1770_did_change_interrupt_request_status(WD::WD1770 *wd1770)
|
|
|
|
{
|
|
|
|
set_interrupt_line();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::wd1770_did_change_data_request_status(WD::WD1770 *wd1770)
|
|
|
|
{
|
|
|
|
// Don't care.
|
|
|
|
}
|
|
|
|
|
|
|
|
void Machine::set_interrupt_line()
|
|
|
|
{
|
|
|
|
set_irq_line(
|
|
|
|
via_.get_interrupt_line() ||
|
|
|
|
(microdisc_is_enabled_ && microdisc_.get_interrupt_request_line()));
|
|
|
|
}
|