Browse Source

Added basic Go Client, implemented serial command protocol, added writing to cartridges

develop
PHENOM 10 months ago
parent
commit
aa2e14d202

+ 5
- 1
.gitignore View File

@@ -1,3 +1,7 @@
**/Debug/*
**/*.componentinfo.xml
.vs/**
.vs/**
debug
*.exe
*.hex
*.elf

+ 7
- 0
.vscode/Gameboy-Color-Cart-Reader.code-workspace View File

@@ -0,0 +1,7 @@
{
"folders": [
{
"path": ".\\.."
}
]
}

+ 14
- 0
.vscode/launch.json View File

@@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/Game-Boy-Cartreader-Client",
"env": {},
"args": []
}
]
}

+ 0
- 40
Game-Boy-Cart-Reader/drivers/cartridge.cpp View File

@@ -1,40 +0,0 @@
#include "cartridge.h"

volatile Uart* uart;

Cartridge::Cartridge(Uart* uartDriver) {
// Disable Interrupts
cli();
// Set UART Driver
uart = uartDriver;
// Set Address Ports as Outputs
A0ToA7DDR = 0xFF;
A0ToA7Port = 0x00;
A8ToA15DDR = 0xFF;
A8ToA15Port = 0x00;
// Set Data Ports as Outputs
DataDDR= 0x00;
DataPort = 0x00;
// Set Control Pins as Outputs
CTRLDDR = 0xFF;
CTRLPort = (1<<READ) | (1<<WRITE) | (1<<RESET) | (1<<CS);
// Setup Timer1
TCCR1B=(1<<WGM12) | (1<<CS10); // CTC-Mode; Prescaler=1
TCCR1A=(1<<COM1B0); // Toggle OC1B
TCNT1= 0; // Reset Data Register
OCR1A=OCRB_VAL; // Set Compare-Match
TIMSK=(1<<OCIE1B); // Enable Compare-Match Interrupt for Timer1

// Enable Interrupts
sei();
}

// Max 8 Cycles
ISR(TIMER1_COMPB_vect, ISR_NAKED) {
}

+ 0
- 20
Game-Boy-Cart-Reader/drivers/cartridge.h View File

@@ -1,20 +0,0 @@
#ifndef CARTRIDGE_H_
#define CARTRIDGE_H_
#include "../configuration.h"
#include <avr/interrupt.h>
#include "uart.h"


class Cartridge {
public:
void writeByte(uint16_t address, uint8_t val);
uint8_t readByte(uint16_t address);
void writeWord(uint16_t address, uint16_t val);
uint16_t readWord(uint16_t address);
void reset();
Cartridge(Uart* uartDriver);
};



#endif /* CARTRIDGE_H_ */

+ 0
- 13
Game-Boy-Cart-Reader/main.cpp View File

@@ -1,13 +0,0 @@
#include "main.h"

int main(void)
{
Uart uart(CONTROL_BAUD);
Cartridge cart(&uart);
while (1)
{
uart.writeString("Hello World\n");
}
}


+ 0
- 10
Game-Boy-Cart-Reader/main.h View File

@@ -1,10 +0,0 @@
#ifndef MAIN_H_
#define MAIN_H_
#include <avr/io.h>
#include <stdlib.h>
#include "drivers/uart.h"
#include "drivers/cartridge.h"
#include "configuration.h"


#endif /* MAIN_H_ */

+ 117
- 0
Game-Boy-Cartreader-Client/gameboyCartreader.go View File

@@ -0,0 +1,117 @@
package main

import (
"bytes"
"fmt"
"github.com/tarm/serial"
"log"
"time"
)

func main() {
if port, err := openComPort(2, 9600); err == nil {
if resetCart(port) {
log.Println("OK!")
} else {
log.Println("FAIL!")
}
log.Printf("0x%02X", readBytesCart(port, 0x0100, 0x4F))
} else {
log.Panicln(err)
}
}

func openComPort(comNr, baud int) (*serial.Port, error) {
c := &serial.Config{Name: fmt.Sprintf("COM%d", comNr), Baud: baud}
s, err := serial.OpenPort(c)
return s, err
}

func changeBaud(comNr, baud int) {
// Check if Port is already opened
s, err := serial.OpenPort(&serial.Config{Name: fmt.Sprintf("COM%d", comNr)})

if err != nil {
// Close Port first
if err := s.Close(); err != nil {
openComPort(comNr, baud)
} else {
panic(err)
}
}
// Do nothing in case the port isn't opened
}

func enumerateComPorts() []string {
var ports []string
for i := 0; i < 256+1; i++ {
s, err := openComPort(i, 9600)
if err == nil {
if err := s.Close(); err == nil {
ports = append(ports, fmt.Sprintf("COM%d", i))
} else {
panic(err)
}
}
}
return ports
}

func writeSerial(port *serial.Port, buf []byte) {
if _, err := port.Write(buf); err != nil {
panic(err)
}
}

func readSerial(port *serial.Port, bufferSize int) []byte {
buf := make([]byte, bufferSize)
_, err := port.Read(buf)
if err != nil {
panic(err)
}
buf = bytes.TrimRight(buf, "\x00")

return buf
}

func readResponse(port *serial.Port, bufferSize int, timeToWait time.Duration) []byte {
waitTimer := time.NewTimer(timeToWait)
<-waitTimer.C
waitTimer.Stop()
return readSerial(port, 10)
}

func resetCart(port *serial.Port) bool {
writeSerial(port, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) // Write a few zeroes just in case previously a command got executed and interrupted
time.Sleep(time.Second)
if string(readSerial(port, 5)) == "READY" {
return true
}
return false
}

func makeWordNibbles(word uint16) (byte, byte) {
h := byte((word & 0xFF00) >> 8)
l := byte(word & 0x00FF)
return h, l
}

func readByteCart(port *serial.Port, address uint16) byte {
h, l := makeWordNibbles(address)
writeSerial(port, []byte{0x03, h, l})
return readSerial(port, 1)[0]
}

func writeByteCart(port *serial.Port, address uint16, value byte) {
h, l := makeWordNibbles(address)
writeSerial(port, []byte{0x04, h, l, value})
}

func readBytesCart(port *serial.Port, address, length uint16) []byte {
hAddr, lAddr := makeWordNibbles(address)
hLen, lLen := makeWordNibbles(length)
writeSerial(port, []byte{0x05, hAddr, lAddr, hLen, lLen})
time.Sleep(time.Second)
return readSerial(port, int(length))
//return readResponse(port, int(length), 250*time.Millisecond)
}

Game-Boy-Cart-Reader.atsln → Game-Boy-Cartreader-Firmware.atsln View File

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Atmel Studio Solution File, Format Version 11.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "Game-Boy-Cart-Reader", "Game-Boy-Cart-Reader\Game-Boy-Cart-Reader.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "Game-Boy-Cartreader-Firmware", "Game-Boy-Cartreader-Firmware\Game-Boy-Cartreader-Firmware.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

Game-Boy-Cart-Reader/Game-Boy-Cart-Reader.cppproj → Game-Boy-Cartreader-Firmware/Game-Boy-Cartreader-Firmware.cppproj View File

@@ -40,12 +40,10 @@
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<avrtool>com.atmel.avrdbg.tool.simulator</avrtool>
<avrtoolserialnumber>
</avrtoolserialnumber>
<avrtool>com.atmel.avrdbg.tool.ispmk2</avrtool>
<avrtoolserialnumber>000200016717</avrtoolserialnumber>
<avrdeviceexpectedsignature>0x1E9502</avrdeviceexpectedsignature>
<avrtoolinterface>
</avrtoolinterface>
<avrtoolinterface>ISP</avrtoolinterface>
<avrtoolinterfaceclock>125000</avrtoolinterfaceclock>
<com_atmel_avrdbg_tool_ispmk2>
<ToolOptions>
@@ -181,6 +179,8 @@
<avrgcccpp.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcccpp.assembler.debugging.DebugLevel>
</AvrGccCpp>
</ToolchainSettings>
<OutputFileName>Game-Boy-Cartreader-Firmware</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
</PropertyGroup>
<ItemGroup>
<Compile Include="configuration.h">
@@ -204,6 +204,12 @@
<Compile Include="main.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="serialController.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="serialController.h">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="Drivers" />

Game-Boy-Cart-Reader/Makefile → Game-Boy-Cartreader-Firmware/Makefile View File

@@ -1,6 +1,6 @@
TARGET=game-boy-cart-reader
TARGET=game-boy-cartreader-firmware
MCU=atmega32
SOURCES=main.cpp ./drivers/uart.cpp ./drivers/cartridge.cpp
SOURCES=main.cpp serialController.cpp ./drivers/uart.cpp ./drivers/cartridge.cpp
OBJECTS=$(SOURCES:.c=.o)
CFLAGS=-c -Os
LDFLAGS=

Game-Boy-Cart-Reader/configuration.h → Game-Boy-Cartreader-Firmware/configuration.h View File

@@ -1,3 +1,5 @@
// Configuration for the Game Boy Cart Reader

#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
#include <avr/io.h>
@@ -11,8 +13,8 @@
#define TRANSFER_BAUD 1000000

// Cartridge Settings
#define F_CLK 1000000
#define OCRB_VAL (((F_CPU/F_CLK)-1)/2)
#define F_CLK 100000
#define OCRA_VAL (((F_CPU/F_CLK)-1)/2)
// Cartridge Address Lines
#define A0ToA7Port PORTA

+ 102
- 0
Game-Boy-Cartreader-Firmware/drivers/cartridge.cpp View File

@@ -0,0 +1,102 @@
#include "cartridge.h"

Cartridge::Cartridge() {
// Set Address Ports as Outputs
A0ToA7DDR = 0xFF;
A0ToA7Port = 0x00;
A8ToA15DDR = 0xFF;
A8ToA15Port = 0x00;
// Set Control Pins as Outputs
CTRLDDR = 0xFF;
CTRLPort = (1<<READ) | (1<<WRITE) | (1<<RESET) | (1<<CS);
// Reset Memory Bank Controller
reset();
// Setup Timer1
TCCR1B = 0; // Stop and reset Timer Control Register
TCNT1 = 0; // Reset Data Register
OCR1A = OCRA_VAL; // Set Compare-Match Value
TCCR1B = (1<<WGM12) | (1<<CS10); // Start Timer in CTC-Mode; Prescaler=1
// Setup Timer0
TCCR0 = 0; // Stop and reset Timer Control Register
OCR0 = OCRA_VAL / 2; // Set Compare-Match Value twice as fast
TCCR0 = (1<<WGM01) | (1<<CS00); // Start Timer in CTC-Mode; Prescaler=1
TCNT0 = TCNT1L + 3; // Sync Timer0 with Timer1, Takes 3 cycles to complete
}

uint8_t Cartridge::readByte(uint16_t address) {
// Set Data Port as Input
DataDDR= 0x00;
DataPort=0x00;
// Set Address ports
A0ToA7Port= address & 0x00FF;
A8ToA15Port= address >> 8;
// WR Rises
CTRLPort|=(1<<WRITE);
// CLK & CS Rises
while((TIFR & (1<<OCF1A))==0);
CTRLPort|=(1<<CLK) | (1<<CS);
TIFR |= (1<<OCF1A);

// RD falls
CTRLPort&=~(1<<READ);
// Wait until CLK is at half duty cycle & CS falls
while((TIFR & (1<<OCF0))==0);
CTRLPort&=~(1<<CS);
TIFR |= (1<<OCF0);
// CLK Falls & Read Data
while((TIFR & (1<<OCF0))==0);
CTRLPort&=~(0<<CLK);
TIFR |= (1<<OCF0);
return DataPin;
}

void Cartridge::writeByte(uint16_t address, uint8_t val) {
// Set Data Port as Output
DataDDR= 0xFF;
// CLK Rises
while((TIFR & (1<<OCF1A))==0);
CTRLPort|=(1<<CLK);
TIFR |= (1<<OCF1A);
// RD Rises & Set Address ports
CTRLPort|=(1<<READ);
A0ToA7Port= address & 0x00FF;
A8ToA15Port= address >> 8;
// Wait until CLK is at half duty cycle & CS falls
while((TIFR & (1<<OCF0))==0);
CTRLPort&=~(1<<CS);
TIFR |= (1<<OCF0);
// Wait until CLK falls & WR falls & Set Data Port
while((TIFR & (1<<OCF0))==0);
DataPort=val;
CTRLPort&=~((1<<WRITE) | (1<<CLK));
TIFR |= (1<<OCF0);
// Wait until CLK is at half duty cycle & WR Rises
while((TIFR & (1<<OCF0))==0);
CTRLPort|= (1<<WRITE);
}

void Cartridge::reset() {
_delay_ms(100);
CTRLPort&= ~(1<<RESET);
_delay_ms(100);
CTRLPort|=(1<<RESET);
}

void* Cartridge::operator new(size_t size) {
return malloc(size);
}

+ 34
- 0
Game-Boy-Cartreader-Firmware/drivers/cartridge.h View File

@@ -0,0 +1,34 @@
// Driver for reading and writing to a Game Boy Cartridge

/* Timings to replicate (See Game Boy CPU Manual [http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf] Page 137)
Shows a write followed by two reads
_________ _________ _________ ___
CLK:____/ \_________/ \_________/ \_________/
_________________
/RD:_______/ \__________________________________________
__________________________________________________________
/WR:\_______/
_________ _____ _____ ___
/CS: \_____________/ \_____________/ \_____________/
*/

#ifndef CARTRIDGE_H_
#define CARTRIDGE_H_
#include "../configuration.h"
#include <util/delay.h>
#include <avr/interrupt.h>
#include "uart.h"


class Cartridge {
public:
void writeByte(uint16_t address, uint8_t val);
uint8_t readByte(uint16_t address);
void reset();
void* operator new(size_t size);
Cartridge();
};



#endif /* CARTRIDGE_H_ */

Game-Boy-Cart-Reader/drivers/uart.cpp → Game-Boy-Cartreader-Firmware/drivers/uart.cpp View File

@@ -52,3 +52,22 @@ void Uart::setBaudRate(uint32_t baudRate) {
UCSRC = (1 << URSEL)|(1<<UCSZ1) | (1<<UCSZ0);
}


uint16_t Uart::readWord() {
uint16_t a = (uint16_t) readChar();
uint16_t b = (uint16_t) readChar();
return ((a << 8) | (b));
}

uint32_t Uart::readLong() {
uint32_t a = (uint32_t) readChar();
uint32_t b = (uint32_t) readChar();
uint32_t c = (uint32_t) readChar();
uint32_t d = (uint32_t) readChar();
return ((a << 24) | (b << 16) | (c << 8) | (d));
}


void* Uart::operator new(size_t size) {
return malloc(size);
}

Game-Boy-Cart-Reader/drivers/uart.h → Game-Boy-Cartreader-Firmware/drivers/uart.h View File

@@ -1,3 +1,5 @@
// Serial UART Driver

#ifndef UART_H_
#define UART_H_
#include <stdlib.h>
@@ -11,6 +13,9 @@ class Uart {
char readChar();
uint16_t readString(char* buffer, uint16_t maxLength);
void setBaudRate(uint32_t baudRate);
uint16_t readWord();
uint32_t readLong();
void* operator new(size_t size);
Uart(uint32_t baudRate);
};
#endif /* UART_H_ */

+ 53
- 0
Game-Boy-Cartreader-Firmware/main.cpp View File

@@ -0,0 +1,53 @@
#include "main.h"

int main(void)
{
// Reserve Memory
uint8_t symbol = 0;
uint16_t address = 0;
uint16_t length = 0;
// Initialize Drivers
Uart uart(CONTROL_BAUD);
Cartridge cartridge;
SerialController serial(&uart, &cartridge);
// Send READY
uart.writeString("READY");
while(1) {
symbol = uart.readChar();
switch(symbol) {
case 0x00: // CPU Reset
wdt_enable(WDTO_500MS);
while(1) {} // Endless loop to trigger watchdog
// Never reached anyway -> No break (:
case 0x01: // Set BaudRate
uart.setBaudRate(uart.readLong());
break;
case 0x02: // Reset MBC
cartridge.reset();
uart.writeString("OK");
break;
case 0x03: // Read Byte
serial.readByte(uart.readWord());
break;
case 0x04: // Write Byte
serial.writeByte(uart.readWord());
break;
case 0x05: // Read n Bytes
address = uart.readWord();
length = uart.readWord();
serial.readBlock(address, length);
break;
case 0x06: // Write n Bytes
address = uart.readWord();
length = uart.readWord();
serial.writeBlock(address, length);
break;
case 0x07: // Selftest
uart.writeChar(uart.readChar());
break;
}
}
}

+ 28
- 0
Game-Boy-Cartreader-Firmware/main.h View File

@@ -0,0 +1,28 @@
// A Game Boy Cartridge Reader / Writer

#ifndef MAIN_H_
#define MAIN_H_
#include "configuration.h"
#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include "serialController.h"

/*
Commandlist:
+---------+-----------------------------------+-------------------------------------------------------------------------------+
| Command | Parameter List (Length Name, ...) | Description |
+---------+-----------------------------------+-------------------------------------------------------------------------------+
| 0x00 | - | Trigger CPU Reset and print READY(0x52 0x45 0x41 0x44 0x59) after back Online |
| 0x01 | 0x04 Baudrate | Set Baudrate (Initially 9600) |
| 0x02 | - | Reset Memory Banking Controller and print OK (0x4F 0x4B) after done |
| 0x03 | 0x02 Address | Read Byte from Address |
| 0x04 | 0x02 Address, 0x01 Value | Write Byte on Address |
| 0x05 | 0x02 Address, 0x02 Length | Read n Bytes from Address to (Address + Length) |
| 0x06 | 0x02 Address, 0x02 Length | Writes n Bytes from Serial from Address to (Address + Length) |
| 0x07 | 0x01 Value | Selftest: Returns the Value over Serial |
+---------+-----------------------------------+-------------------------------------------------------------------------------+
*/

#endif /* MAIN_H_ */

+ 43
- 0
Game-Boy-Cartreader-Firmware/serialController.cpp View File

@@ -0,0 +1,43 @@
#include "serialController.h"

SerialController::SerialController(Uart* uartDriver, Cartridge* cartridgeDriver) {
uart = uartDriver;
cartridge = cartridgeDriver;
}

void SerialController::readBlock(uint16_t start, uint16_t length) {
for(uint16_t addr = start; addr < (start + length); addr++) {
uart->writeChar(cartridge->readByte(addr));
}
}

void SerialController::readByte(uint16_t address) {
uart->writeChar(cartridge->readByte(address));
}

void SerialController::writeBlock(uint16_t start, uint16_t length) {
for(uint16_t addr = start; addr < (start + length); addr++) {
cartridge->writeByte(addr ,uart->readChar());
}
}

void SerialController::writeByte(uint16_t address) {
cartridge->writeByte(address ,uart->readChar());
}


void SerialController::setBaud(uint32_t baudRate) {
uart->setBaudRate(baudRate);
}

void* SerialController::operator new(size_t size) {
return malloc(size);
}

Uart* SerialController::getUartDriver() {
return uart;
}

Cartridge* SerialController::getCartridgeDriver() {
return cartridge;
}

+ 28
- 0
Game-Boy-Cartreader-Firmware/serialController.h View File

@@ -0,0 +1,28 @@
// A Serial Controller that handles Cartridge Output/ Input and forwards it to the Serial Line or vice versa

#ifndef SERIALCONTROL_H_
#define SERIALCONTROL_H_
#include "drivers/uart.h"
#include "drivers/cartridge.h"
#include <stdlib.h>

class SerialController {
private:
Uart* uart; // UART Driver
Cartridge *cartridge; // Cartridge Driver
public:
void readBlock(uint16_t start, uint16_t length); // Reads a Block of Memory with a length from the Cartridge and transmits it via Serial Line
void writeBlock(uint16_t start, uint16_t length); // Writes a Block of Memory with a length from the Serial Line to the Cartridge
void readByte(uint16_t address); // Reads a Byte from the Cart at address and transmits it via Serial Line
void writeByte(uint16_t address); // Writes a byte from the Serial Line to the Cartridge at address
void setBaud(uint32_t baudRate); // Sets the BaudRate
Uart* getUartDriver(); // Returns the UART Driver
Cartridge* getCartridgeDriver(); // Returns the Game Boy Cartridge Drivers
void* operator new(size_t size);
SerialController(Uart* uartDriver, Cartridge* cartridgeDriver);
};



#endif /* SERIALCONTROL_H_ */

+ 57
- 5
Jenkinsfile View File

@@ -1,17 +1,69 @@
def firmwareBuildBadge = addEmbeddableBadgeConfiguration(id: "firmwarebuild", subject: "Firmware Build")
def winBuildBadge = addEmbeddableBadgeConfiguration(id: "winbuild", subject: "Windows x64 Build")

pipeline {
agent none
environment {
HOME="."
}
stages {
stage('Build') {
stage('Build AVR ATMega32 Firmware') {
agent {
docker {
image 'dea82/avr-gcc-docker:latest'
}
}
environment { HOME="." }
steps {
dir("Game-Boy-Cart-Reader") {
sh "make"
archiveArtifacts 'game-boy-cart-reader.hex, game-boy-cart-reader_eeprom.hex, game-boy-cart-reader.elf'
script {
firmwareBuildBadge.setStatus('running')
try {
dir("Game-Boy-Cartreader-Firmware") {
sh "make"
archiveArtifacts 'game-boy-cartreader-firmware.hex, game-boy-cartreader-firmware_eeprom.hex, game-boy-cartreader-firmware.elf'
firmwareBuildBadge.setStatus('passing')
}
} catch (Exception err) {
firmwareBuildBadge.setStatus('failing')
echo err.toString()
error 'Firmware Build failed'
}
}
}
}
stage('Build Serial Client Windows x64') {
agent {
docker {
image 'golang:latest'
}
}
environment {
GOOS="windows"
GOARCH="amd64"
GODIR = "$WORKSPACE/src/git.phenomic.net/phenom/gameboy-color-cart-reader/game-boy-cartreader-client"
GOPATH = "$WORKSPACE"
GOCACHE = "$WORKSPACE/.cache"
}
steps {
script {
winBuildBadge.setStatus('running')
try {
echo "mkdir -p $GODIR"
sh "mkdir -p $GODIR"
dir("Game-Boy-Cartreader-Client") {
echo "ls -a | grep -vw -e '.' -e 'src' -e '.cache' | xargs mv -t $GODIR"
sh "ls -a | grep -vw -e '.' -e 'src' -e '.cache' | xargs mv -t $GODIR"
}
dir("$GODIR") {
sh "go get ./..."
sh "go build -o gbCartreader.exe"
archiveArtifacts("gbCartreader.exe")
}
winBuildBadge.setStatus('passing')
} catch (Exception err) {
winBuildBadge.setStatus('failing')
error err.toString()
error 'Windows x64 Build failed'
}
}
}
}

+ 5
- 0
LICENSE View File

@@ -0,0 +1,5 @@
MIT License
Copyright (c) 2019 PHENOM
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 121
- 11
README.md View File

@@ -1,15 +1,125 @@
# Gameboy Color Cartridge Reader
[![Build Status](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/badge/icon?style=flat-square)](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/)
# Game Boy Cartreader & -Writer
[![Build Status](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/badge/icon?style=flat-square)](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/)[![Build Status Firmware](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/badge/icon?config=firmwarebuild&style=flat-square)](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/)[![Build Status Windows](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/badge/icon?config=winbuild&style=flat-square)](https://ci.phenomic.net/job/git.phenomic.net/job/Gameboy-Color-Cart-Reader/job/master/)

This is a Gameboy cartridge reader for the Atmel Atmega 32 Microcontroller.
This is a Game Boy Cartreader and -Writer for the Atmel AVR ATmega32 Microcontroller. It is able to read ROM, RAM Saves and the RTC Registers on Game Boy Cartridges. It also is able to write to the RTC Registers and RAM Saves.

## Working features
- Reading ROM
- Reading RAM
- Reading RTC
[Go to the Repository](https://git.phenomic.net/PHENOM/Gameboy-Color-Cart-Reader)

## Non-Working features
- Writing RAM
![Image of Cartreader](images/cartreader.jpg)

## Configuration
Connect with a BaudRate of 1000000
## Table of Contents

- [Game Boy Cartreader & -Writer](#game-boy-cartreader---writer)
- [Table of Contents](#table-of-contents)
- [Motivation](#motivation)
- [Circuitboard](#circuitboard)
- [Getting Started](#getting-started)
- [Required Hardware](#required-hardware)
- [Prerequisites](#prerequisites)
- [Compiling the Firmware](#compiling-the-firmware)
- [Flashing the Firmware](#flashing-the-firmware)
- [Installation of the Client](#installation-of-the-client)
- [Usage](#usage)
- [Serial Commandlist](#serial-commandlist)
- [Go Cartreader Client](#go-cartreader-client)
- [Dependencies](#dependencies)
- [Game Boy Documentation used](#game-boy-documentation-used)
- [License](#license)

## Motivation

I started this project in 2014 and working on it further and overhauling it an will be used in combination with the [Game Boy Emulator](https://git.phenomic.net/PHENOM/GameBoyEmulator).

## Circuitboard

**TODO:** Schematics and board layout will follow in later commits, once I've reworked the board layout for a more production ready state.

## Getting Started

### Required Hardware

- AVR ISP Programmer (In-System-Programmer) to program the Microcontroller
- Game Boy Cartreader & -Writer Circuit Board
- USB 2.0 Cable Type B for the Cartreader

### Prerequisites

In order to use this project you need to have all the required Hardware and have either [AVR Toolchain](https://www.microchip.com/mplab/avr-support/avr-and-arm-toolchains-c-compilers) installed for 8-Bit Controllers or use [AtmelStudio](https://www.microchip.com/mplab/avr-support/atmel-studio-7) to compile the project. Afterwards you want to use a Flashing tool such as avrdude to write the firmware on the controller. You also need to have the [Go Binaries](https://golang.org/dl/) installed and having set the [$GOPATH](https://github.com/golang/go/wiki/SettingGOPATH) environment variable to your Go workspace.

### Compiling the Firmware

```bash
sudo apt-get install gcc-avr avr-libc
cd Game-Boy-Cartreader-Firmware
make
```

### Flashing the Firmware

```bash
sudo apt-get install avrdude
cd Game-Boy-Cartreader-Firmware

# Writing the Firmware
avrdude -c <programmer> -P <port> -p m32 -U flash:w:game-boy-cartreader-firmware.hex
avrdude -c <programmer> -P <port> -p m32 -U eeprom:w:game-boy-cartreader-firmware_eeprom.hex

# Setting the fuses
avrdude -c <programmer> -P <port> -p m32 -U hfuse:w:0xC1:m
avrdude -c <programmer> -P <port> -p m32 -U lfuse:w:0xFF:m
```

If you need a comprehensive guide on how to use avrdude please take a look [here](http://www.ladyada.net/learn/avr/avrdude.html).

### Installation of the Client

Currently only Windows is supported. Cross-Platform support will follow.

```bash
# Clone Project and receive dependencies
go get git.phenomic.net/phenom/gameboy-color-cart-reader/...

# Install Client
cd $GOPATH/src/git.phenomic.net/phenom/gameboy-color-cart-reader
go install ./...
```

## Usage

### Serial Commandlist

This describes the Protocol being used for Communication over the serial line. When rebooting the device the Baudrate is set to **9600 Symbols per second** on reset.

```
+---------+-----------------------------------+-------------------------------------------------------------------------------+
| Command | Parameter List (Length Name, ...) | Description |
+---------+-----------------------------------+-------------------------------------------------------------------------------+
| 0x00 | - | Trigger CPU Reset and print READY(0x52 0x45 0x41 0x44 0x59) after back Online |
| 0x01 | 0x04 Baudrate | Set Baudrate (Initially 9600) |
| 0x02 | - | Reset Memory Banking Controller and print OK (0x4F 0x4B) after done |
| 0x03 | 0x02 Address | Read Byte from Address |
| 0x04 | 0x02 Address, 0x01 Value | Write Byte on Address |
| 0x05 | 0x02 Address, 0x02 Length | Read n Bytes from Address to (Address + Length) |
| 0x06 | 0x02 Address, 0x02 Length | Writes n Bytes from Serial from Address to (Address + Length) |
| 0x07 | 0x01 Value | Selftest: Returns the Value over Serial |
+---------+-----------------------------------+-------------------------------------------------------------------------------+
```

### Go Cartreader Client

**TODO:** This will describe how to use the Client for the Cartreader device.

## Dependencies

- [Serial](https://github.com/tarm/serial)

## Game Boy Documentation used

- [Cartridge Header](http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header)
- [Memory Bank Controllers](http://gbdev.gg8.se/wiki/articles/Memory_Bank_Controllers)
- [Emulating a GameBoy Cartridge with an STM32F4](https://dhole.github.io/post/gameboy_cartridge_emu_1/)
- [Game Boy CPU Manual - Page 137-138: Typical Timings](http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf)

## License

Distributed under the MIT License. See `LICENSE` for more information.

BIN
images/cartreader.jpg View File


Loading…
Cancel
Save