2020-11-01 01:00:15 +00:00
|
|
|
|
//
|
|
|
|
|
// ADB.cpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 31/10/2020.
|
|
|
|
|
// Copyright © 2020 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "ADB.hpp"
|
|
|
|
|
|
2021-01-24 23:07:05 +00:00
|
|
|
|
#include <cassert>
|
2020-11-01 01:00:15 +00:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
2021-01-27 00:52:30 +00:00
|
|
|
|
// TEST.
|
|
|
|
|
#include "../../../InstructionSets/M50740/Parser.hpp"
|
|
|
|
|
#include "../../../InstructionSets/Disassembler.hpp"
|
|
|
|
|
|
2020-11-01 01:00:15 +00:00
|
|
|
|
using namespace Apple::IIgs::ADB;
|
|
|
|
|
|
2021-01-24 23:07:05 +00:00
|
|
|
|
GLU::GLU() : executor_(*this) {}
|
|
|
|
|
|
2020-11-10 00:05:48 +00:00
|
|
|
|
// MARK: - Configuration.
|
|
|
|
|
|
|
|
|
|
void GLU::set_is_rom03(bool is_rom03) {
|
|
|
|
|
is_rom03_ = is_rom03;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - External interface.
|
|
|
|
|
|
2020-11-01 01:00:15 +00:00
|
|
|
|
uint8_t GLU::get_keyboard_data() {
|
2020-11-01 19:49:04 +00:00
|
|
|
|
// The classic Apple II serial keyboard register:
|
|
|
|
|
// b7: key strobe.
|
|
|
|
|
// b6–b0: ASCII code.
|
|
|
|
|
return 0x00;
|
2020-11-01 01:00:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 02:35:11 +00:00
|
|
|
|
void GLU::clear_key_strobe() {
|
|
|
|
|
// Clears the key strobe of the classic Apple II serial keyboard register.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t GLU::get_any_key_down() {
|
|
|
|
|
// The Apple IIe check-for-any-key-down bit.
|
|
|
|
|
return 0x00;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-01 01:00:15 +00:00
|
|
|
|
uint8_t GLU::get_mouse_data() {
|
2020-11-01 19:49:04 +00:00
|
|
|
|
// Alternates between returning x and y values.
|
|
|
|
|
//
|
|
|
|
|
// b7: 1 = button is up; 0 = button is down.
|
|
|
|
|
// b6: delta sign bit; 1 = negative.
|
|
|
|
|
// b5–b0: mouse delta.
|
2020-11-01 01:00:15 +00:00
|
|
|
|
return 0x80;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t GLU::get_modifier_status() {
|
2020-11-01 19:49:04 +00:00
|
|
|
|
// b7: 1 = command key pressed; 0 = not.
|
|
|
|
|
// b6: option key.
|
|
|
|
|
// b5: 1 = modifier key latch has been updated, no key has been pressed; 0 = not.
|
|
|
|
|
// b4: any numeric keypad key.
|
|
|
|
|
// b3: a key is down.
|
|
|
|
|
// b2: caps lock is pressed.
|
|
|
|
|
// b1: control key.
|
|
|
|
|
// b0: shift key.
|
2020-11-01 01:00:15 +00:00
|
|
|
|
return 0x00;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t GLU::get_data() {
|
2020-11-10 00:05:48 +00:00
|
|
|
|
printf("ADB get data\n");
|
|
|
|
|
if(!pending_response_.empty()) {
|
|
|
|
|
// A bit yucky, pull from the from the front.
|
|
|
|
|
// This keeps my life simple for now, even though it's inefficient.
|
|
|
|
|
const uint8_t next = pending_response_[0];
|
|
|
|
|
pending_response_.erase(pending_response_.begin());
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-01 19:49:04 +00:00
|
|
|
|
// b0–2: number of data bytes to be returned.
|
|
|
|
|
// b3: 1 = a valid service request is pending; 0 = no request pending.
|
|
|
|
|
// b4: 1 = control, command and delete keys have been pressed simultaneously; 0 = they haven't.
|
|
|
|
|
// b5: 1 = control, command and reset have all been pressed together; 0 = they haven't.
|
|
|
|
|
// b6: 1 = ADB controller encountered an error and reset itself; 0 = no error.
|
|
|
|
|
// b7: 1 = ADB has received a response from the addressed ADB device; 0 = no respone.
|
2020-11-01 01:00:15 +00:00
|
|
|
|
return 0x00;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t GLU::get_status() {
|
2020-11-01 19:49:04 +00:00
|
|
|
|
// b7: 1 = mouse data register is full; 0 = empty.
|
|
|
|
|
// b6: 1 = mouse interrupt is enabled.
|
|
|
|
|
// b5: 1 = command/data has valid data.
|
|
|
|
|
// b4: 1 = command/data interrupt is enabled.
|
|
|
|
|
// b3: 1 = keyboard data is full.
|
|
|
|
|
// b2: 1 = keyboard data interrupt is enabled.
|
|
|
|
|
// b1: 1 = mouse x-data is available; 0 = y.
|
|
|
|
|
// b0: 1 = command register is full (set when command is written); 0 = empty (cleared when data is read).
|
2020-11-10 00:05:48 +00:00
|
|
|
|
const uint8_t status =
|
|
|
|
|
(pending_response_.empty() ? 0 : 0x20); // Data is valid if a response is pending.
|
2020-11-10 02:54:25 +00:00
|
|
|
|
// printf("ADB get status : %02x\n", status);
|
2020-11-10 00:05:48 +00:00
|
|
|
|
return status;
|
2020-11-01 01:00:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-01 19:49:04 +00:00
|
|
|
|
void GLU::set_command(uint8_t command) {
|
2020-11-10 00:05:48 +00:00
|
|
|
|
// Accumulate input until a full comamnd is received;
|
|
|
|
|
// the state machine otherwise gets somewhat oversized.
|
|
|
|
|
next_command_.push_back(command);
|
|
|
|
|
|
|
|
|
|
// Command dispatch; each entry should do whatever it needs
|
|
|
|
|
// to do and then either: (i) break, if completed; or
|
|
|
|
|
// (ii) return, if awaiting further input.
|
|
|
|
|
#define RequireSize(n) if(next_command_.size() < (n)) return;
|
|
|
|
|
switch(next_command_[0]) {
|
|
|
|
|
case 0x01: abort(); break;
|
|
|
|
|
case 0x02: reset_microcontroller(); break;
|
|
|
|
|
case 0x03: flush_keyboard_buffer(); break;
|
|
|
|
|
|
|
|
|
|
case 0x04: // Set modes.
|
|
|
|
|
RequireSize(2);
|
|
|
|
|
set_modes(modes_ | next_command_[1]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x05: // Clear modes.
|
|
|
|
|
RequireSize(2);
|
|
|
|
|
set_modes(modes_ & ~next_command_[1]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x06: // Set configuration bytes
|
2020-11-25 22:34:00 +00:00
|
|
|
|
RequireSize(4);
|
2020-11-10 00:05:48 +00:00
|
|
|
|
set_configuration_bytes(&next_command_[1]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x07: // Sync.
|
|
|
|
|
RequireSize(is_rom03_ ? 9 : 5);
|
|
|
|
|
set_modes(next_command_[1]);
|
|
|
|
|
set_configuration_bytes(&next_command_[2]);
|
|
|
|
|
|
|
|
|
|
// ROM03 seems to expect to send an extra four bytes here,
|
|
|
|
|
// with values 0x20, 0x26, 0x2d, 0x2d.
|
|
|
|
|
// TODO: what does the ROM03-paired ADB GLU do with the extra four bytes?
|
|
|
|
|
break;
|
|
|
|
|
|
2020-11-10 02:20:53 +00:00
|
|
|
|
case 0x09: // Read microcontroller memory.
|
|
|
|
|
RequireSize(3);
|
|
|
|
|
pending_response_.push_back(read_microcontroller_address(uint16_t(next_command_[1] | (next_command_[2] << 8))));
|
|
|
|
|
break;
|
|
|
|
|
|
2020-11-10 00:05:48 +00:00
|
|
|
|
case 0x0d: // Get version number.
|
|
|
|
|
pending_response_.push_back(6); // Seems to be what ROM03 is looking for?
|
|
|
|
|
break;
|
|
|
|
|
|
2020-11-25 22:34:00 +00:00
|
|
|
|
case 0x12: // ???
|
|
|
|
|
RequireSize(3);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x13: // ???
|
|
|
|
|
RequireSize(3);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-11-10 00:05:48 +00:00
|
|
|
|
// Enable device SRQ.
|
|
|
|
|
case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
|
|
|
|
|
case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
|
|
|
|
|
set_device_srq(device_srq_ | (1 << (next_command_[1] & 0xf)));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Disable device SRQ.
|
|
|
|
|
case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
|
|
|
|
|
case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
|
|
|
|
|
set_device_srq(device_srq_ & ~(1 << (next_command_[1] & 0xf)));
|
|
|
|
|
break;
|
|
|
|
|
|
2020-11-25 22:34:00 +00:00
|
|
|
|
// Transmit two bytes.
|
|
|
|
|
case 0xb3:
|
|
|
|
|
RequireSize(3);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-11-10 00:05:48 +00:00
|
|
|
|
default:
|
|
|
|
|
printf("TODO: ADB command %02x\n", next_command_[0]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#undef RequireSize
|
|
|
|
|
|
2020-11-10 02:20:53 +00:00
|
|
|
|
printf("ADB executed: ");
|
|
|
|
|
for(auto c : next_command_) printf("%02x ", c);
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
2020-11-10 00:05:48 +00:00
|
|
|
|
// Input was dealt with, so throw it away.
|
|
|
|
|
next_command_.clear();
|
2020-11-01 01:00:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-01 19:49:04 +00:00
|
|
|
|
void GLU::set_status(uint8_t status) {
|
|
|
|
|
printf("TODO: set ADB status %02x\n", status);
|
2020-11-01 01:00:15 +00:00
|
|
|
|
}
|
2020-11-10 00:05:48 +00:00
|
|
|
|
|
|
|
|
|
// MARK: - Internal commands.
|
|
|
|
|
|
|
|
|
|
void GLU::set_modes(uint8_t modes) {
|
|
|
|
|
modes_ = modes; // TODO: and this has what effect?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLU::abort() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLU::reset_microcontroller() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLU::flush_keyboard_buffer() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLU::set_configuration_bytes(uint8_t *) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GLU::set_device_srq(int mask) {
|
|
|
|
|
// TODO: do I need to do any more than this here?
|
|
|
|
|
device_srq_ = mask;
|
|
|
|
|
}
|
2020-11-10 02:20:53 +00:00
|
|
|
|
|
|
|
|
|
uint8_t GLU::read_microcontroller_address(uint16_t address) {
|
|
|
|
|
printf("Read from microcontroller %04x\n", address);
|
2020-11-25 22:34:00 +00:00
|
|
|
|
if(address == 0xe8)
|
|
|
|
|
return 0x40;
|
2020-11-10 02:20:53 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-01-18 21:59:49 +00:00
|
|
|
|
|
|
|
|
|
void GLU::set_microcontroller_rom(const std::vector<uint8_t> &rom) {
|
|
|
|
|
executor_.set_rom(rom);
|
2021-01-27 00:52:30 +00:00
|
|
|
|
|
2021-01-27 02:29:17 +00:00
|
|
|
|
// TEST invocation.
|
2021-01-27 00:52:30 +00:00
|
|
|
|
InstructionSet::Disassembler<InstructionSet::M50740::Parser, 0x1fff, InstructionSet::M50740::Instruction, uint8_t, uint16_t> disassembler;
|
|
|
|
|
disassembler.disassemble(rom.data(), 0x1000, uint16_t(rom.size()), 0x1000);
|
2021-01-27 02:29:17 +00:00
|
|
|
|
|
|
|
|
|
const auto instructions = disassembler.instructions();
|
|
|
|
|
const auto entry_points = disassembler.entry_points();
|
|
|
|
|
for(const auto &pair : instructions) {
|
|
|
|
|
std::cout << std::hex << pair.first << "\t\t";
|
|
|
|
|
if(entry_points.find(pair.first) != entry_points.end()) {
|
|
|
|
|
std::cout << "L" << pair.first << "\t";
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << "\t\t";
|
|
|
|
|
}
|
|
|
|
|
std::cout << operation_name(pair.second.operation) << " ";
|
|
|
|
|
std::cout << address(pair.second.addressing_mode, &rom[pair.first - 0x1000], pair.first);
|
|
|
|
|
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
}
|
2021-01-18 21:59:49 +00:00
|
|
|
|
}
|
2021-01-24 03:55:12 +00:00
|
|
|
|
|
|
|
|
|
void GLU::run_for(Cycles cycles) {
|
|
|
|
|
executor_.run_for(cycles);
|
|
|
|
|
}
|
2021-01-24 23:07:05 +00:00
|
|
|
|
|
|
|
|
|
// MARK: - M50470 port handler
|
|
|
|
|
|
|
|
|
|
void GLU::set_port_output(int port, uint8_t value) {
|
2021-01-25 03:30:42 +00:00
|
|
|
|
ports_[port] = value;
|
2021-01-24 23:07:05 +00:00
|
|
|
|
switch(port) {
|
|
|
|
|
case 0:
|
2021-01-26 03:02:39 +00:00
|
|
|
|
// printf("Set R%d: %02x\n", register_address_, value);
|
2021-01-24 23:07:05 +00:00
|
|
|
|
registers_[register_address_] = value;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
// printf("Keyboard write: %02x???\n", value);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
// printf("ADB data line input: %d???\n", value >> 7);
|
|
|
|
|
// printf("IIe keyboard reset line: %d\n", (value >> 6)&1);
|
|
|
|
|
// printf("IIgs reset line: %d\n", (value >> 5)&1);
|
|
|
|
|
// printf("GLU strobe: %d\n", (value >> 4)&1);
|
2021-01-25 22:43:22 +00:00
|
|
|
|
// printf("Select GLU register: %d [%02x]\n", value & 0xf, value);
|
2021-01-24 23:07:05 +00:00
|
|
|
|
register_address_ = value & 0xf;
|
|
|
|
|
break;
|
2021-01-25 22:43:22 +00:00
|
|
|
|
case 3: {
|
2021-01-24 23:07:05 +00:00
|
|
|
|
// printf("IIe KWS: %d\n", (value >> 6)&3);
|
2021-01-25 22:43:22 +00:00
|
|
|
|
// printf("ADB data line output: %d\n", (value >> 3)&1);
|
|
|
|
|
|
2021-01-26 03:02:39 +00:00
|
|
|
|
const bool new_adb_level = !(value & 0x08);
|
2021-01-25 22:43:22 +00:00
|
|
|
|
if(new_adb_level != adb_level_) {
|
|
|
|
|
if(!new_adb_level) {
|
|
|
|
|
// Transition to low.
|
|
|
|
|
constexpr float clock_rate = 894886.25;
|
|
|
|
|
const float seconds = float(total_period_.as<int>()) / clock_rate;
|
|
|
|
|
|
|
|
|
|
// Check for a valid bit length — 70 to 130 microseconds.
|
|
|
|
|
// (Plus a little).
|
|
|
|
|
if(seconds >= 0.000'56 && seconds <= 0.001'04) {
|
|
|
|
|
printf("Attention\n");
|
|
|
|
|
} else if(seconds >= 0.000'06 && seconds <= 0.000'14) {
|
|
|
|
|
printf("bit: %d\n", (low_period_.as<int>() * 2) < total_period_.as<int>());
|
|
|
|
|
// printf("tested: %0.2f\n", float(low_period_.as<int>()) / float(total_period_.as<int>()));
|
|
|
|
|
} else {
|
|
|
|
|
printf("Rejected %d microseconds\n", int(seconds * 1'000'000.0f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
total_period_ = low_period_ = Cycles(0);
|
|
|
|
|
}
|
|
|
|
|
adb_level_ = new_adb_level;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
2021-01-24 23:07:05 +00:00
|
|
|
|
|
|
|
|
|
default: assert(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t GLU::get_port_input(int port) {
|
|
|
|
|
switch(port) {
|
|
|
|
|
case 0:
|
2021-01-26 03:02:39 +00:00
|
|
|
|
// printf("Get R%d\n", register_address_);
|
2021-01-24 23:07:05 +00:00
|
|
|
|
return registers_[register_address_];
|
|
|
|
|
case 1:
|
|
|
|
|
// printf("IIe keyboard read\n");
|
|
|
|
|
return 0x06;
|
|
|
|
|
case 2:
|
2021-01-25 22:43:22 +00:00
|
|
|
|
// printf("ADB data line input, etc\n");
|
2021-01-25 03:30:42 +00:00
|
|
|
|
return ports_[2];
|
2021-01-24 23:07:05 +00:00
|
|
|
|
case 3:
|
|
|
|
|
// printf("ADB data line output, etc\n");
|
2021-01-25 03:30:42 +00:00
|
|
|
|
return ports_[3];
|
2021-01-24 23:07:05 +00:00
|
|
|
|
|
|
|
|
|
default: assert(false);
|
|
|
|
|
}
|
|
|
|
|
return 0xff;
|
|
|
|
|
}
|
2021-01-25 22:43:22 +00:00
|
|
|
|
|
|
|
|
|
void GLU::run_ports_for(Cycles cycles) {
|
|
|
|
|
total_period_ += cycles;
|
|
|
|
|
if(!adb_level_) {
|
|
|
|
|
low_period_ += cycles;
|
|
|
|
|
}
|
|
|
|
|
}
|