Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start sketching out a PC compatible machine. #1207

Merged
merged 31 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e927fd0
Do just enough to include x86 code in the main build.
TomHarte Nov 15, 2023
1676ed9
Add to SDL and Qt builds.
TomHarte Nov 15, 2023
7323af0
Avoid shadowing template parameter.
TomHarte Nov 15, 2023
af7069a
Include and fetch a BIOS.
TomHarte Nov 15, 2023
1a3b2b0
Add necessary wiring for File -> New...
TomHarte Nov 15, 2023
6f48ffb
Add enough of a ScanProducer to run.
TomHarte Nov 15, 2023
3b84299
Edge closer to PCCompatible doing _something_.
TomHarte Nov 15, 2023
2bc9dfb
Albeit with no BIOS present, execute.
TomHarte Nov 15, 2023
62b6219
Install BIOS, albeit in writeable storage.
TomHarte Nov 16, 2023
164a7fe
Log port IO.
TomHarte Nov 16, 2023
832e31f
Add note to self.
TomHarte Nov 16, 2023
233ec7b
Soften some warnings.
TomHarte Nov 16, 2023
25f0a37
Don't sign-extend ports (!).
TomHarte Nov 16, 2023
0953590
Log first unhandled port.
TomHarte Nov 16, 2023
99e7de5
Colocate memory.
TomHarte Nov 16, 2023
1c7bb6d
Add CI diagnosis trap.
TomHarte Nov 16, 2023
33486e6
Remove CI trap.
TomHarte Nov 16, 2023
e154154
Play hit and hope.
TomHarte Nov 16, 2023
8af173c
Remove hopeful hit.
TomHarte Nov 16, 2023
4b730c2
Satisfy GCC warning.
TomHarte Nov 17, 2023
effddca
Hide PC option by default.
TomHarte Nov 17, 2023
fbe02e3
Randomly try a different explicit instantiation.
TomHarte Nov 17, 2023
3f27338
New guess: the definition of size_t varies?
TomHarte Nov 17, 2023
f2fdfe8
Reduce repetition.
TomHarte Nov 17, 2023
83c8f99
Switch back to the natural type.
TomHarte Nov 17, 2023
a0ca5e6
Remove outdated comment.
TomHarte Nov 17, 2023
f4b1279
Try a different GCC version.
TomHarte Nov 17, 2023
ac12b25
Tweak syntax.
TomHarte Nov 17, 2023
626e4fe
Just pull requests will do.
TomHarte Nov 17, 2023
ec2d878
End run around the template.
TomHarte Nov 17, 2023
d202cfc
Add TODO.
TomHarte Nov 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ccpp.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: SDL/Ubuntu

on: [push, pull_request]
on: [pull_request]

jobs:
build:
Expand All @@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: sudo apt-get --allow-releaseinfo-change update && sudo apt-get --fix-missing install libsdl2-dev scons
run: sudo apt-get --allow-releaseinfo-change update && sudo apt-get --fix-missing install gcc-10 libsdl2-dev scons
- name: Make
working-directory: OSBindings/SDL
run: scons -j$(nproc --all)
1 change: 1 addition & 0 deletions Analyser/Machines.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum class Machine {
MasterSystem,
MSX,
Oric,
PCCompatible,
Vic20,
ZX8081,
ZXSpectrum,
Expand Down
35 changes: 25 additions & 10 deletions InstructionSets/x86/Decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
using namespace InstructionSet::x86;

template <Model model>
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(const uint8_t *source, size_t length) {
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(const uint8_t *source, std::size_t length) {
// Instruction length limits:
//
// 8086/80186: none*
Expand All @@ -26,8 +26,8 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
// be back to wherever it started, so it's safe to spit out a NOP and reset parsing
// without any loss of context. This reduces the risk of the decoder tricking a caller into
// an infinite loop.
constexpr int max_instruction_length = model >= Model::i80386 ? 15 : (model == Model::i80286 ? 10 : 65536);
const uint8_t *const end = source + std::min(length, size_t(max_instruction_length - consumed_));
static constexpr std::size_t max_instruction_length = model >= Model::i80386 ? 15 : (model == Model::i80286 ? 10 : 65536);
const uint8_t *const end = source + std::min(length, max_instruction_length - consumed_);

// MARK: - Prefixes (if present) and the opcode.

Expand Down Expand Up @@ -58,14 +58,16 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
#define RegAddr(op, dest, op_size, addr_size) \
SetOpSrcDestSize(op, DirectAddress, dest, op_size); \
displacement_size_ = addr_size; \
phase_ = Phase::DisplacementOrOperand
phase_ = Phase::DisplacementOrOperand; \
sign_extend_displacement_ = false

/// Handles instructions of the form jjkk, Ax where the former is implicitly an address.
#define AddrReg(op, source, op_size, addr_size) \
SetOpSrcDestSize(op, source, DirectAddress, op_size); \
displacement_size_ = addr_size; \
destination_ = Source::DirectAddress; \
phase_ = Phase::DisplacementOrOperand
phase_ = Phase::DisplacementOrOperand; \
sign_extend_displacement_ = false

/// Covers both `mem/reg, reg` and `reg, mem/reg`.
#define MemRegReg(op, format, size) \
Expand Down Expand Up @@ -1017,11 +1019,20 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
if(bytes_to_consume == outstanding_bytes) {
phase_ = Phase::ReadyToPost;

switch(displacement_size_) {
case DataSize::None: displacement_ = 0; break;
case DataSize::Byte: displacement_ = int8_t(inward_data_); break;
case DataSize::Word: displacement_ = int16_t(inward_data_); break;
case DataSize::DWord: displacement_ = int32_t(inward_data_); break;
if(!sign_extend_displacement_) {
switch(displacement_size_) {
case DataSize::None: displacement_ = 0; break;
case DataSize::Byte: displacement_ = uint8_t(inward_data_); break;
case DataSize::Word: displacement_ = uint16_t(inward_data_); break;
case DataSize::DWord: displacement_ = int32_t(inward_data_); break;
}
} else {
switch(displacement_size_) {
case DataSize::None: displacement_ = 0; break;
case DataSize::Byte: displacement_ = int8_t(inward_data_); break;
case DataSize::Word: displacement_ = int16_t(inward_data_); break;
case DataSize::DWord: displacement_ = int32_t(inward_data_); break;
}
}
inward_data_ >>= bit_size(displacement_size_);

Expand Down Expand Up @@ -1112,3 +1123,7 @@ template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i8086>;
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80186>;
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80286>;
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80386>;

std::pair<int, Instruction<false>> Decoder8086::decode(const uint8_t *source, std::size_t length) {
return decoder.decode(source, length);
}
23 changes: 19 additions & 4 deletions InstructionSets/x86/Decoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,12 @@ template <Model model> class Decoder {
supplied cannot form a valid instruction.

@discussion although instructions also contain an indicator of their length, on chips prior
to the 80286 there is no limit to instruction length and that could in theory overflow the available
storage, which can describe instructions only up to 1kb in size.
to the 80286 there is no limit to potential instruction length.

The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
cannot overflow the field.
*/
std::pair<int, InstructionT> decode(const uint8_t *source, size_t length);
std::pair<int, InstructionT> decode(const uint8_t *source, std::size_t length);

/*!
Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it.
Expand Down Expand Up @@ -173,7 +172,7 @@ template <Model model> class Decoder {

// Ephemeral decoding state.
Operation operation_ = Operation::Invalid;
int consumed_ = 0, operand_bytes_ = 0;
uint32_t consumed_ = 0, operand_bytes_ = 0;

// Source and destination locations.
Source source_ = Source::None;
Expand All @@ -193,6 +192,8 @@ template <Model model> class Decoder {
DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any.
DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation.

bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address
// size; otherwise it'll be zero-padded.
bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size;
// otherwise it'll be zero-padded.

Expand Down Expand Up @@ -223,9 +224,23 @@ template <Model model> class Decoder {
next_inward_data_shift_ = 0;
inward_data_ = 0;
sign_extend_operand_ = false;
sign_extend_displacement_ = true;
}
};

// This is a temporary measure; for reasons as-yet unknown, GCC isn't picking up the
// explicit instantiations of the template above at link time, even though is is
// unambiguously building and linking in Decoder.cpp.
//
// So here's a thin non-templated shim to unblock initial PC Compatible development.
class Decoder8086 {
public:
std::pair<int, Instruction<false>> decode(const uint8_t *source, std::size_t length);

private:
Decoder<Model::i8086> decoder;
};

}

#endif /* InstructionSets_x86_Decoder_hpp */
6 changes: 3 additions & 3 deletions InstructionSets/x86/Flags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ class Flags {
using FlagT = uint32_t;

// Flag getters.
template <Flag flag> bool flag() const {
switch(flag) {
template <Flag flag_v> bool flag() const {
switch(flag_v) {
case Flag::Carry: return carry_;
case Flag::AuxiliaryCarry: return auxiliary_carry_;
case Flag::Sign: return sign_;
Expand Down Expand Up @@ -163,7 +163,7 @@ class Flags {
set_from<Flag::Interrupt>(value & FlagValue::Interrupt);
set_from<Flag::Direction>(value & FlagValue::Direction);

set_from<uint8_t, Flag::Sign>(value);
set_from<uint8_t, Flag::Sign>(uint8_t(value));

set_from<Flag::Zero>((~value) & FlagValue::Zero);
set_from<Flag::ParityOdd>((~value) & FlagValue::Parity);
Expand Down
6 changes: 4 additions & 2 deletions InstructionSets/x86/Implementation/FlowControl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "Stack.hpp"
#include "../AccessType.hpp"

#include <type_traits>

namespace InstructionSet::x86::Primitive {

template <typename IntT, typename ContextT>
Expand All @@ -34,7 +36,7 @@ void jump(

// TODO: proper behaviour in 32-bit.
if(condition) {
context.flow_controller.jump(context.registers.ip() + displacement);
context.flow_controller.jump(uint16_t(context.registers.ip() + displacement));
}
}

Expand Down Expand Up @@ -76,7 +78,7 @@ void loopne(

template <typename IntT, typename ContextT>
void call_relative(
IntT offset,
typename std::make_signed<IntT>::type offset,
ContextT &context
) {
push<uint16_t, false>(context.registers.ip(), context);
Expand Down
8 changes: 5 additions & 3 deletions InstructionSets/x86/Implementation/PerformImplementation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ template <
case Operation::ESC:
case Operation::NOP: return;

case Operation::AAM: Primitive::aam(context.registers.axp(), instruction.operand(), context); return;
case Operation::AAD: Primitive::aad(context.registers.axp(), instruction.operand(), context); return;
case Operation::AAM: Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context); return;
case Operation::AAD: Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context); return;
case Operation::AAA: Primitive::aaas<true>(context.registers.axp(), context); return;
case Operation::AAS: Primitive::aaas<false>(context.registers.axp(), context); return;
case Operation::DAA: Primitive::daas<true>(context.registers.al(), context); return;
Expand Down Expand Up @@ -232,7 +232,9 @@ template <
case Operation::NEG: Primitive::neg<IntT>(source_rmw(), context); break; // TODO: should be a destination.
case Operation::NOT: Primitive::not_<IntT>(source_rmw()); break; // TODO: should be a destination.

case Operation::CALLrel: Primitive::call_relative<AddressT>(instruction.displacement(), context); return;
case Operation::CALLrel:
Primitive::call_relative<AddressT>(instruction.displacement(), context);
return;
case Operation::CALLabs: Primitive::call_absolute<IntT>(destination_r(), context); return;
case Operation::CALLfar: Primitive::call_far(instruction, context); return;

Expand Down
4 changes: 2 additions & 2 deletions InstructionSets/x86/Implementation/Resolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ typename Accessor<IntT, access>::type resolve(
//
// * if this is a memory access, set target_address and break;
// * otherwise return the appropriate value.
uint32_t target_address;
uint32_t target_address = 0;
switch(source) {
// Defer all register accesses to the register-specific lookup.
case Source::eAX: return *register_<IntT, access, Source::eAX>(context);
Expand All @@ -183,7 +183,7 @@ typename Accessor<IntT, access>::type resolve(
case Source::None: return *none;

case Source::Immediate:
*immediate = instruction.operand();
*immediate = IntT(instruction.operand());
return *immediate;

case Source::Indirect:
Expand Down
Loading