Skip to content

Commit

Permalink
Merge pull request #35 from OrangeFox86/29-expose-raw-maple-bus-commu…
Browse files Browse the repository at this point in the history
…nication-to-usb-cdc-serial-device

29 expose raw maple bus communication to usb cdc serial device
  • Loading branch information
Tails86 authored Dec 18, 2022
2 parents 793f6bb + f6b6d8c commit 45331bf
Show file tree
Hide file tree
Showing 22 changed files with 575 additions and 70 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ Below defines a location word which is used to address blocks of memory in some

### CRC

CRC byte transmits last, just before the end sequence is transmitted. It is the value after starting with 0 and applying XOR to each other byte in the packet.
CRC byte transmits last, just before the end sequence is transmitted. It is the value after starting with 0 and applying XOR against each byte in the packet.

---

Expand All @@ -380,7 +380,7 @@ All button bits are 0 when pressed and 1 when released.

## Screen

The "block write" command (0x0C) with screen function code and 48 data words is used to write monochrome images to the screen. A screen is 48 bits wide and 32 bits tall. For each bit in the 48 data words, a value of 1 means the pixel is on (black) and 0 means the pixel is off (white). Data is written from left to right and top to bottom. The most significant bit of the first word sets the pixel on the top, left of the screen. The two most significant bytes write to the 33rd through 48th bit of the first row. The next two bytes write to the 1st through 16th bits of the second row. This is repeated for the right of the 48 words like pictured below.
The "block write" command (0x0C) with screen function code and 48 data words is used to write monochrome images to the screen. A screen is 48 bits wide and 32 bits tall. For each bit in the 48 data words, a value of 1 means the pixel is on (black) and 0 means the pixel is off (white). Data is written from left to right and top to bottom. The most significant bit of the first word sets the pixel on the top, left of the screen. The two most significant bytes write to the 33rd through 48th bit of the first row. The next two bytes write to the 1st through 16th bits of the second row. This is repeated for the rest of the 48 words like pictured below.

<p align="center">
<img src="images/Dreamcast_Screen_Words.png?raw=true" alt="Screen Words"/>
Expand Down Expand Up @@ -432,7 +432,7 @@ Specifically, the text `Fm:4 - 30Hz`. This correlates to `(value + 1) / 2` and m
- `0xX0`: single stable vibration (i.e. no inclination) at power X
- `0xX8`: positive inclination (ramp up) from power X up to max
- `0x8X`: negative inclination (ramp down) from power X down to min
- A values of `0x00`, `0x08`, or `0x80` immediately stops the currently executing vibration sequence
- A value of `0x00`, `0x08`, or `0x80` immediately stops the currently executing vibration sequence

There is a very noticeable change from one vibration power to the next when inclination is used and a long cycle period is selected.

Expand Down Expand Up @@ -468,8 +468,16 @@ Bit 0 may be set to 1 to augment duration, but the meaning is not completely und

**Maple Bus Resources**

https://archive.org/details/MaplePatent/page/n7/mode/1up

http://mc.pp.se/dc/maplebus.html and http://mc.pp.se/dc/controller.html

https://tech-en.netlify.app/articles/en540236/index.html

https://www.raphnet.net/programmation/dreamcast_usb/index_en.php

https://web.archive.org/web/20100425041817/http://www.maushammer.com/vmu.html

https://hackaday.io/project/170365-blueretro/log/180790-evolution-of-segas-io-interface-from-sg-1000-to-saturn

https://segaretro.org/History_of_the_Sega_Dreamcast/Development
3 changes: 3 additions & 0 deletions inc/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
// Warning: enabling debug messages drastically degrades communication performance
#define SHOW_DEBUG_MESSAGES false

// true to enable USB CDC (serial) interface to directly control the maple bus
#define USB_CDC_ENABLED true

// Adjust the CPU clock frequency here (133 MHz is maximum documented stable frequency)
#define CPU_FREQ_KHZ 133000

Expand Down
20 changes: 14 additions & 6 deletions inc/hal/System/LockGuard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,29 @@ class LockGuard
LockGuard() = delete;

//! Initializes data and locks mutex as long as it wouldn't cause a deadlock
inline LockGuard(MutexInterface& mutex) :
inline LockGuard(MutexInterface& mutex, bool allowDeadlock = false) :
mMutex(mutex),
mIsLocked(false)
{
int8_t lockValue = mMutex.tryLock();
if (lockValue == 0)
if (allowDeadlock)
{
mMutex.lock();
mIsLocked = true;
}
else if (lockValue > 0)
else
{
mIsLocked = true;
int8_t lockValue = mMutex.tryLock();
if (lockValue == 0)
{
mMutex.lock();
mIsLocked = true;
}
else if (lockValue > 0)
{
mIsLocked = true;
}
// Else: not locked, would cause deadlock - due to simultaneous IRQ access on same core
}
// Else: not locked, would cause deadlock
}

//! Unlocks mutex if it was previously locked
Expand Down
25 changes: 25 additions & 0 deletions inc/hal/Usb/CommandParser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <stdint.h>
#include <vector>
#include <memory>

#include "hal/System/MutexInterface.hpp"

// Command structure: [whitespace]<command-char>[command]<\n>

//! Command parser for processing commands from a TTY stream
class CommandParser
{
public:
virtual ~CommandParser() {}

//! @returns the string of command characters this parser handles
virtual const char* getCommandChars() = 0;

//! Called when newline reached; submit command and reset
virtual void submit(const char* chars, uint32_t len) = 0;

//! Prints help message for this command
virtual void printHelp() = 0;
};
19 changes: 19 additions & 0 deletions inc/hal/Usb/TtyParser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <stdint.h>
#include "hal/Usb/CommandParser.hpp"
#include "hal/System/MutexInterface.hpp"

//! Command parser for processing commands from a TTY stream
class TtyParser
{
public:
//! Virtual destructor
virtual ~TtyParser() {}
//! Adds a command parser to my list of parsers - must be done before any other function called
virtual void addCommandParser(std::shared_ptr<CommandParser> parser) = 0;
//! Called from the process handling maple bus execution
virtual void process() = 0;
};

TtyParser* usb_cdc_create_parser(MutexInterface* m, char helpChar);
5 changes: 4 additions & 1 deletion inc/hal/Usb/usb_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
#include "UsbFileSystem.hpp"
#include "DreamcastControllerObserver.hpp"
#include "hal/System/MutexInterface.hpp"
#include <vector>

//! @returns array of the USB controller observers
DreamcastControllerObserver** get_usb_controller_observers();
//! USB initialization
void usb_init(MutexInterface* mscMutex, MutexInterface* cdcMutex);
void usb_init(
MutexInterface* mscMutex,
MutexInterface* cdcStdioMutex);
//! USB task that needs to be called constantly by main()
void usb_task();
//! @returns number of USB controllers
Expand Down
7 changes: 5 additions & 2 deletions src/coreLib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.12)

set(CMAKE_VERBOSE_MAKEFILE ON)

file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c*" "${CMAKE_CURRENT_SOURCE_DIR}/peripherals/*.c*")
file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c*"
"${CMAKE_CURRENT_SOURCE_DIR}/peripherals/*.c*"
"${CMAKE_CURRENT_SOURCE_DIR}/parsers/*.c*")

add_library(coreLib STATIC ${SRC})

Expand All @@ -19,4 +21,5 @@ target_include_directories(coreLib
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/core>"
"${PROJECT_SOURCE_DIR}/inc"
"${CMAKE_CURRENT_SOURCE_DIR}/peripherals")
"${CMAKE_CURRENT_SOURCE_DIR}/peripherals"
"${CMAKE_CURRENT_SOURCE_DIR}/parsers")
8 changes: 2 additions & 6 deletions src/coreLib/DreamcastMainNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@
#include "DreamcastController.hpp"
#include "EndpointTxScheduler.hpp"

const uint8_t DreamcastMainNode::MAIN_TRANSMISSION_PRIORITY = 0;
const uint8_t DreamcastMainNode::SUB_TRANSMISSION_PRIORITY = 1;
const uint8_t DreamcastMainNode::MAX_PRIORITY = 1;

DreamcastMainNode::DreamcastMainNode(MapleBusInterface& bus,
PlayerData playerData,
std::shared_ptr<PrioritizedTxScheduler> prioritizedTxScheduler) :
DreamcastNode(DreamcastPeripheral::MAIN_PERIPHERAL_ADDR_MASK,
std::make_shared<EndpointTxScheduler>(
prioritizedTxScheduler,
MAIN_TRANSMISSION_PRIORITY,
PrioritizedTxScheduler::MAIN_TRANSMISSION_PRIORITY,
DreamcastPeripheral::getRecipientAddress(
playerData.playerIndex, DreamcastPeripheral::MAIN_PERIPHERAL_ADDR_MASK)
),
Expand All @@ -32,7 +28,7 @@ DreamcastMainNode::DreamcastMainNode(MapleBusInterface& bus,
addr,
std::make_shared<EndpointTxScheduler>(
prioritizedTxScheduler,
SUB_TRANSMISSION_PRIORITY,
PrioritizedTxScheduler::SUB_TRANSMISSION_PRIORITY,
DreamcastPeripheral::getRecipientAddress(playerData.playerIndex, addr)),
mPlayerData));
}
Expand Down
6 changes: 0 additions & 6 deletions src/coreLib/DreamcastMainNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,6 @@ class DreamcastMainNode : public DreamcastNode
public:
//! Number of microseconds in between each info request when no peripheral is detected
static const uint32_t US_PER_CHECK = 16000;
//! Main node has highest priority
static const uint8_t MAIN_TRANSMISSION_PRIORITY;
//! Sub nodes have lower priority
static const uint8_t SUB_TRANSMISSION_PRIORITY;
//! Maximum allowed priority
static const uint8_t MAX_PRIORITY;

protected:
//! The sub nodes under this node
Expand Down
10 changes: 8 additions & 2 deletions src/coreLib/PrioritizedTxScheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@
#include "configuration.h"
#include "utils.h"

#include <assert.h>

// STL
#include <algorithm>

PrioritizedTxScheduler::PrioritizedTxScheduler(uint8_t maxPriority) :
PrioritizedTxScheduler::PrioritizedTxScheduler() :
mNextId(1),
mSchedule()
{
mSchedule.resize(maxPriority + 1);
mSchedule.resize(PRIORITY_COUNT);
}

PrioritizedTxScheduler::~PrioritizedTxScheduler() {}

uint32_t PrioritizedTxScheduler::add(std::shared_ptr<Transmission> tx)
{
assert(tx->priority < mSchedule.size());
std::list<std::shared_ptr<Transmission>>& schedule = mSchedule[tx->priority];
// Keep iterating until correct position is found
std::list<std::shared_ptr<Transmission>>::const_iterator iter = schedule.cbegin();
Expand Down Expand Up @@ -46,6 +49,9 @@ uint32_t PrioritizedTxScheduler::add(uint8_t priority,

uint32_t pktDurationUs = INT_DIVIDE_CEILING(pktDurationNs, 1000);

// This will happen if minimal communication is made constantly for 20 days
assert(mNextId != INVALID_TX_ID);

std::shared_ptr<Transmission> tx =
std::make_shared<Transmission>(mNextId++,
priority,
Expand Down
18 changes: 17 additions & 1 deletion src/coreLib/PrioritizedTxScheduler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,23 @@

class PrioritizedTxScheduler
{
public:
//! Enumerates available priorities to be used with add()
enum Priority : uint8_t
{
//! Priority for external entity taking control of the bus (max)
EXTERNAL_TRANSMISSION_PRIORITY = 0,
//! Priority for main peripheral
MAIN_TRANSMISSION_PRIORITY,
//! Priority for sub peripheral (min)
SUB_TRANSMISSION_PRIORITY,
//! Any selected priority must be less than PRIORITY_COUNT
PRIORITY_COUNT
};

public:
//! Default constructor
PrioritizedTxScheduler(uint8_t maxPriority);
PrioritizedTxScheduler();

//! Virtual destructor
virtual ~PrioritizedTxScheduler();
Expand Down Expand Up @@ -78,6 +92,8 @@ class PrioritizedTxScheduler
public:
//! Use this for txTime if the packet needs to be sent ASAP
static const uint64_t TX_TIME_ASAP = 0;
//! Transmission ID to use in order to flag no ID
static const uint32_t INVALID_TX_ID = 0;

protected:
//! The next transmission ID to set
Expand Down
Loading

0 comments on commit 45331bf

Please sign in to comment.