64dd working yet again, isv brought back, dma fixes, usb path rewrite, pc code rewrite

This commit is contained in:
Polprzewodnikowy 2022-07-27 00:28:34 +02:00
parent 4ea0abd022
commit 1374033733
24 changed files with 1475 additions and 230 deletions

View File

@ -123,7 +123,7 @@ module memory_bram (
// DD memory
logic [15:0] dd_bram [0:511];
logic [15:0] dd_bram [0:1023];
logic [15:0] dd_bram_rdata;
always_ff @(posedge clk) begin

View File

@ -197,26 +197,14 @@ module memory_dma (
// Mem bus controller
always_ff @(posedge clk) begin
if (reset || dma_scb.stop) begin
dma_scb.busy <= 1'b0;
end else begin
if (dma_start) begin
dma_scb.busy <= 1'b1;
end
if (dma_scb.busy) begin
if (!trx_enabled) begin
dma_scb.busy <= 1'b0;
end
end
end
dma_scb.busy <= mem_bus.request || trx_enabled;
end
always_ff @(posedge clk) begin
if (reset || dma_scb.stop) begin
if (reset) begin
mem_bus.request <= 1'b0;
end else begin
if (dma_scb.busy && !mem_bus.request) begin
if (!mem_bus.request) begin
if (mem_bus.write) begin
if (rx_buffer_valid) begin
mem_bus.request <= 1'b1;

View File

@ -17,11 +17,13 @@ SRC_FILES = \
cfg.c \
cic.c \
dd.c \
debug.c \
flash.c \
flashram.c \
fpga.c \
gvr.c \
hw.c \
isv.c \
main.c \
rtc.c \
task.c \

View File

@ -1,7 +1,9 @@
#include <stdbool.h>
#include "cfg.h"
#include "dd.h"
#include "flash.h"
#include "fpga.h"
#include "isv.h"
#include "rtc.h"
@ -16,6 +18,8 @@ typedef enum {
CFG_ID_CIC_SEED,
CFG_ID_TV_TYPE,
CFG_ID_FLASH_ERASE_BLOCK,
CFG_ID_DD_DRIVE_TYPE,
CFG_ID_DD_DISK_STATE,
} cfg_id_t;
typedef enum {
@ -111,6 +115,7 @@ void cfg_query (uint32_t *args) {
args[1] = (fpga_reg_get(REG_CFG_SCR) & CFG_SCR_DD_ENABLED);
break;
case CFG_ID_ISV_ENABLE:
args[1] = isv_get_enabled();
break;
case CFG_ID_BOOT_MODE:
args[1] = p.boot_mode;
@ -126,6 +131,10 @@ void cfg_query (uint32_t *args) {
break;
case CFG_ID_FLASH_ERASE_BLOCK:
break;
case CFG_ID_DD_DRIVE_TYPE:
break;
case CFG_ID_DD_DISK_STATE:
break;
}
}
@ -144,6 +153,7 @@ void cfg_update (uint32_t *args) {
change_scr_bits(CFG_SCR_DD_ENABLED | CFG_SCR_DDIPL_ENABLED, args[1]);
break;
case CFG_ID_ISV_ENABLE:
isv_set_enabled(args[1]);
break;
case CFG_ID_BOOT_MODE:
p.boot_mode = args[1];
@ -161,6 +171,12 @@ void cfg_update (uint32_t *args) {
case CFG_ID_FLASH_ERASE_BLOCK:
flash_erase_block(args[1]);
break;
case CFG_ID_DD_DRIVE_TYPE:
dd_set_drive_type(args[1]);
break;
case CFG_ID_DD_DISK_STATE:
dd_set_disk_state(args[1]);
break;
}
}
@ -185,7 +201,7 @@ void cfg_set_time (uint32_t *args) {
void cfg_init (void) {
// fpga_reg_set(REG_CFG_SCR, 0);
fpga_reg_set(REG_CFG_SCR, 0);
set_save_type(SAVE_TYPE_NONE);
p.cic_seed = 0xFFFF;
@ -193,14 +209,6 @@ void cfg_init (void) {
p.boot_mode = BOOT_MODE_MENU_SD;
}
#include "stm32g030xx.h"
void uart_print (const char *str) {
while (*str != '\0') {
while (!(USART1->ISR & USART_ISR_TXE_TXFNF));
USART1->TDR = *str++;
}
}
void cfg_process (void) {
uint32_t args[2];
@ -209,10 +217,7 @@ void cfg_process (void) {
args[0] = fpga_reg_get(REG_CFG_DATA_0);
args[1] = fpga_reg_get(REG_CFG_DATA_1);
char cmd = (char) fpga_reg_get(REG_CFG_CMD);
uart_print("GOT_CMD");
char tmp[2] = {cmd, 0};
uart_print(tmp);
uart_print("\n");
switch (cmd) {
case 'v':
args[0] = cfg_get_version();
@ -234,10 +239,6 @@ void cfg_process (void) {
cfg_set_time(args);
break;
case 'U':
// uart_put((char) (args[0] & 0xFF));
break;
default:
fpga_reg_set(REG_CFG_CMD, CFG_CMD_ERROR | CFG_CMD_DONE);
return;

View File

@ -1,13 +1,24 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "dd.h"
#include "fpga.h"
#include "hw.h"
#include "rtc.h"
#include "usb.h"
#define DD_SECTOR_MAX_SIZE (232)
#define DD_BLOCK_DATA_SECTORS_NUM (85)
#define DD_BLOCK_BUFFER_ADDRESS (0x03BC0000UL - (DD_SECTOR_MAX_SIZE * DD_BLOCK_DATA_SECTORS_NUM))
#define DD_SECTOR_BUFFER_ADDRESS (0x06006000UL)
#define DD_DRIVE_ID_RETAIL (0x0003)
#define DD_DRIVE_ID_DEVELOPMENT (0x0004)
#define DD_VERSION_RETAIL (0x0114)
#define DD_SPIN_UP_TIME (2000)
typedef enum {
DD_CMD_SEEK_READ = 0x01,
@ -27,89 +38,373 @@ typedef enum {
} dd_cmd_t;
static rtc_time_t time;
enum state {
STATE_IDLE,
STATE_START,
STATE_BLOCK_READ_WAIT,
STATE_SECTOR_READ,
STATE_SECTOR_WRITE,
STATE_BLOCK_WRITE,
STATE_BLOCK_WRITE_WAIT,
STATE_NEXT_BLOCK,
};
void dd_init (void) {
fpga_reg_set(REG_DD_SCR, 0);
fpga_reg_set(REG_DD_HEAD_TRACK, 0);
fpga_reg_set(REG_DD_DRIVE_ID, DD_DRIVE_ID_RETAIL);
typedef union sector_info {
uint32_t full;
struct {
uint8_t sector_num;
uint8_t sector_size;
uint8_t sector_size_full;
uint8_t sectors_in_block;
};
} sector_info_t;
struct process {
enum state state;
rtc_time_t time;
bool disk_spinning;
bool cmd_response_delayed;
bool cmd_response_ready;
bool bm_running;
bool transfer_mode;
bool full_track_transfer;
bool starting_block;
uint16_t head_track;
uint8_t current_sector;
sector_info_t sector_info;
bool block_ready;
bool block_valid;
};
static struct process p;
static uint16_t dd_track_head_block (void) {
uint16_t track = ((p.head_track & DD_TRACK_MASK) << 2);
uint16_t head = (((p.head_track & DD_HEAD_MASK) ? 1 : 0) << 1);
uint16_t block = (p.starting_block ? 1 : 0);
return (track | head | block);
}
static bool dd_block_read_request (void) {
usb_tx_info_t packet_info;
packet_info.cmd = PACKET_CMD_DD_REQUEST;
packet_info.data_length = 12;
packet_info.data[0] = 1;
packet_info.data[1] = DD_BLOCK_BUFFER_ADDRESS;
packet_info.data[2] = dd_track_head_block();
packet_info.dma_length = 0;
p.block_ready = false;
return usb_enqueue_packet(&packet_info);
}
static bool dd_block_write_request (void) {
usb_tx_info_t packet_info;
packet_info.cmd = PACKET_CMD_DD_REQUEST;
packet_info.data_length = 12;
packet_info.data[0] = 2;
packet_info.data[1] = DD_BLOCK_BUFFER_ADDRESS;
packet_info.data[2] = dd_track_head_block();
packet_info.dma_length = (p.sector_info.sector_size + 1) * DD_BLOCK_DATA_SECTORS_NUM;
packet_info.dma_address = DD_BLOCK_BUFFER_ADDRESS;
p.block_ready = false;
return usb_enqueue_packet(&packet_info);
}
static void dd_set_cmd_response_ready (void) {
p.cmd_response_ready = true;
}
void dd_process (void) {
void dd_set_block_ready (bool valid) {
p.block_ready = true;
p.block_valid = valid;
}
void dd_set_drive_type (dd_drive_type_t type) {
switch (type) {
case DD_DRIVE_TYPE_RETAIL:
fpga_reg_set(REG_DD_DRIVE_ID, DD_DRIVE_ID_RETAIL);
break;
case DD_DRIVE_TYPE_DEVELOPMENT:
fpga_reg_set(REG_DD_DRIVE_ID, DD_DRIVE_ID_DEVELOPMENT);
break;
}
}
void dd_set_disk_state (dd_disk_state_t state) {
uint32_t scr = fpga_reg_get(REG_DD_SCR);
scr &= ~(DD_SCR_DISK_CHANGED | DD_SCR_DISK_INSERTED);
switch (state) {
case DD_DISK_STATE_EJECTED:
break;
case DD_DISK_STATE_INSERTED:
scr |= DD_SCR_DISK_INSERTED;
break;
case DD_DISK_STATE_CHANGED:
scr |= (DD_SCR_DISK_CHANGED | DD_SCR_DISK_INSERTED);
break;
}
fpga_reg_set(REG_DD_SCR, scr);
}
void dd_init (void) {
fpga_reg_set(REG_DD_SCR, DD_SCR_DISK_INSERTED);
fpga_reg_set(REG_DD_HEAD_TRACK, 0);
fpga_reg_set(REG_DD_DRIVE_ID, DD_DRIVE_ID_RETAIL);
p.state = STATE_IDLE;
p.cmd_response_delayed = false;
p.cmd_response_ready = false;
p.disk_spinning = false;
p.bm_running = false;
}
void dd_process (void) {
uint32_t starting_scr = fpga_reg_get(REG_DD_SCR);
uint32_t scr = starting_scr;
if (scr & DD_SCR_HARD_RESET) {
fpga_reg_set(REG_DD_SCR, scr & ~(DD_SCR_DISK_CHANGED));
fpga_reg_set(REG_DD_HEAD_TRACK, 0);
p.state = STATE_IDLE;
p.cmd_response_delayed = false;
p.cmd_response_ready = false;
p.disk_spinning = false;
p.bm_running = false;
p.head_track = 0;
scr &= ~(DD_SCR_DISK_CHANGED);
}
if (scr & DD_SCR_CMD_PENDING) {
uint32_t cmd_data = fpga_reg_get(REG_DD_CMD_DATA);
uint8_t cmd = (cmd_data >> 16) & 0xFF;
uint16_t data = cmd_data & 0xFFFF;
fpga_reg_set(REG_DD_CMD_DATA, data);
switch (cmd) {
case DD_CMD_CLEAR_DISK_CHANGE:
fpga_reg_set(REG_DD_SCR, scr & ~(DD_SCR_DISK_CHANGED));
break;
if (p.cmd_response_delayed) {
if (p.cmd_response_ready) {
p.cmd_response_delayed = false;
fpga_reg_set(REG_DD_HEAD_TRACK, DD_HEAD_TRACK_INDEX_LOCK | data);
scr |= DD_SCR_CMD_READY;
}
} else if ((cmd == DD_CMD_SEEK_READ) || (cmd == DD_CMD_SEEK_WRITE)) {
p.cmd_response_delayed = true;
p.cmd_response_ready = false;
if (!p.disk_spinning) {
p.disk_spinning = true;
hw_tim_setup(TIM_ID_DD, DD_SPIN_UP_TIME, dd_set_cmd_response_ready);
} else {
p.cmd_response_ready = true;
}
fpga_reg_set(REG_DD_HEAD_TRACK, p.head_track & ~(DD_HEAD_TRACK_INDEX_LOCK));
p.head_track = data & DD_HEAD_TRACK_MASK;
} else {
switch (cmd) {
case DD_CMD_CLEAR_DISK_CHANGE:
scr &= ~(DD_SCR_DISK_CHANGED);
break;
case DD_CMD_CLEAR_RESET_STATE:
fpga_reg_set(REG_DD_SCR, scr & ~(DD_SCR_DISK_CHANGED));
fpga_reg_set(REG_DD_SCR, scr | DD_SCR_HARD_RESET_CLEAR);
break;
case DD_CMD_CLEAR_RESET_STATE:
scr &= ~(DD_SCR_DISK_CHANGED);
scr |= DD_SCR_HARD_RESET_CLEAR;
break;
case DD_CMD_READ_VERSION:
fpga_reg_set(REG_DD_CMD_DATA, DD_VERSION_RETAIL);
break;
case DD_CMD_READ_VERSION:
data = DD_VERSION_RETAIL;
break;
case DD_CMD_SET_DISK_TYPE:
break;
case DD_CMD_SET_DISK_TYPE:
break;
case DD_CMD_REQUEST_STATUS:
fpga_reg_set(REG_DD_CMD_DATA, 0);
break;
case DD_CMD_REQUEST_STATUS:
data = 0;
break;
case DD_CMD_SET_RTC_YEAR_MONTH:
time.year = ((data >> 8) & 0xFF);
time.month = (data & 0xFF);
break;
case DD_CMD_SET_RTC_YEAR_MONTH:
p.time.year = ((data >> 8) & 0xFF);
p.time.month = (data & 0xFF);
break;
case DD_CMD_SET_RTC_DAY_HOUR:
time.day = ((data >> 8) & 0xFF);
time.hour = (data & 0xFF);
break;
case DD_CMD_SET_RTC_DAY_HOUR:
p.time.day = ((data >> 8) & 0xFF);
p.time.hour = (data & 0xFF);
break;
case DD_CMD_SET_RTC_MINUTE_SECOND:
time.minute = ((data >> 8) & 0xFF);
time.second = (data & 0xFF);
rtc_set_time(&time);
break;
case DD_CMD_SET_RTC_MINUTE_SECOND:
p.time.minute = ((data >> 8) & 0xFF);
p.time.second = (data & 0xFF);
rtc_set_time(&p.time);
break;
case DD_CMD_GET_RTC_YEAR_MONTH:
fpga_reg_set(REG_DD_CMD_DATA, (time.year << 8) | time.month);
break;
case DD_CMD_GET_RTC_YEAR_MONTH:
data = (p.time.year << 8) | p.time.month;
break;
case DD_CMD_GET_RTC_DAY_HOUR:
fpga_reg_set(REG_DD_CMD_DATA, (time.day << 8) | time.hour);
break;
case DD_CMD_GET_RTC_DAY_HOUR:
data = (p.time.day << 8) | p.time.hour;
break;
case DD_CMD_GET_RTC_MINUTE_SECOND:
rtc_get_time(&time);
fpga_reg_set(REG_DD_CMD_DATA, (time.minute << 8) | time.second);
break;
case DD_CMD_GET_RTC_MINUTE_SECOND:
rtc_get_time(&p.time);
data = (p.time.minute << 8) | p.time.second;
break;
case DD_CMD_READ_PROGRAM_VERSION:
fpga_reg_set(REG_DD_CMD_DATA, 0);
break;
case DD_CMD_READ_PROGRAM_VERSION:
data = 0;
break;
default:
break;
default:
break;
}
fpga_reg_set(REG_DD_CMD_DATA, data);
scr |= DD_SCR_CMD_READY;
}
}
if (scr & DD_SCR_BM_STOP) {
scr |= DD_SCR_BM_STOP_CLEAR;
scr &= ~(DD_SCR_BM_MICRO_ERROR | DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_TRANSFER_DATA);
p.bm_running = false;
} else if (scr & DD_SCR_BM_START) {
scr |= DD_SCR_BM_CLEAR | DD_SCR_BM_ACK_CLEAR | DD_SCR_BM_START_CLEAR;
scr &= ~(DD_SCR_BM_MICRO_ERROR | DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_TRANSFER_DATA);
p.state = STATE_START;
p.bm_running = true;
p.transfer_mode = (scr & DD_SCR_BM_TRANSFER_MODE);
p.full_track_transfer = (scr & DD_SCR_BM_TRANSFER_BLOCKS);
p.sector_info.full = fpga_reg_get(REG_DD_SECTOR_INFO);
p.starting_block = (p.sector_info.sector_num == (p.sector_info.sectors_in_block + 1));
} else if (p.bm_running) {
if (scr & DD_SCR_BM_PENDING) {
scr |= DD_SCR_BM_CLEAR;
if (p.transfer_mode) {
if (p.current_sector < (p.sector_info.sectors_in_block - 4)) {
p.state = STATE_SECTOR_READ;
} else if (p.current_sector == (p.sector_info.sectors_in_block - 4)) {
p.current_sector += 1;
scr &= ~(DD_SCR_BM_TRANSFER_DATA);
scr |= DD_SCR_BM_READY;
} else if (p.current_sector >= p.sector_info.sectors_in_block) {
p.state = STATE_NEXT_BLOCK;
} else {
}
} else {
if (p.current_sector < (p.sector_info.sectors_in_block - 4)) {
p.state = STATE_SECTOR_WRITE;
}
}
}
if (scr & DD_SCR_BM_ACK) {
scr |= DD_SCR_BM_ACK_CLEAR;
if (p.transfer_mode) {
if ((p.current_sector <= (p.sector_info.sectors_in_block - 4))) {
} else if (p.current_sector < p.sector_info.sectors_in_block) {
p.current_sector += 1;
scr |= DD_SCR_BM_READY;
} else if (p.current_sector == p.sector_info.sectors_in_block) {
p.current_sector += 1;
scr |= DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_READY;
}
} else {
if (p.current_sector == (p.sector_info.sectors_in_block - 4)) {
p.bm_running = false;
}
}
}
fpga_reg_set(REG_DD_SCR, scr | DD_SCR_CMD_READY);
switch (p.state) {
case STATE_IDLE:
break;
case STATE_START:
p.current_sector = 0;
if (dd_block_read_request()) {
p.state = STATE_BLOCK_READ_WAIT;
}
break;
case STATE_BLOCK_READ_WAIT:
if (p.block_ready) {
if (p.transfer_mode) {
if (p.block_valid) {
p.state = STATE_SECTOR_READ;
scr |= DD_SCR_BM_TRANSFER_DATA;
} else {
p.state = STATE_SECTOR_READ;
scr |= DD_SCR_BM_MICRO_ERROR;
}
} else {
p.state = STATE_IDLE;
if (p.block_valid) {
scr |= DD_SCR_BM_TRANSFER_DATA | DD_SCR_BM_READY;
} else {
scr |= DD_SCR_BM_MICRO_ERROR | DD_SCR_BM_READY;
}
}
}
break;
case STATE_SECTOR_READ:
fpga_mem_copy(
DD_BLOCK_BUFFER_ADDRESS + (p.current_sector * (p.sector_info.sector_size + 1)),
DD_SECTOR_BUFFER_ADDRESS,
p.sector_info.sector_size + 1
);
p.state = STATE_IDLE;
p.current_sector += 1;
scr |= DD_SCR_BM_READY;
break;
case STATE_SECTOR_WRITE:
fpga_mem_copy(
DD_SECTOR_BUFFER_ADDRESS,
DD_BLOCK_BUFFER_ADDRESS + (p.current_sector * (p.sector_info.sector_size + 1)),
p.sector_info.sector_size + 1
);
p.current_sector += 1;
if (p.current_sector < (p.sector_info.sectors_in_block - 4)) {
p.state = STATE_IDLE;
scr |= DD_SCR_BM_READY;
} else {
p.state = STATE_BLOCK_WRITE;
}
break;
case STATE_BLOCK_WRITE:
if (dd_block_write_request()) {
p.state = STATE_BLOCK_WRITE_WAIT;
}
break;
case STATE_BLOCK_WRITE_WAIT:
if (p.block_ready) {
p.state = STATE_NEXT_BLOCK;
}
break;
case STATE_NEXT_BLOCK:
if (p.full_track_transfer) {
p.state = STATE_START;
p.full_track_transfer = false;
p.starting_block = !p.starting_block;
scr &= ~(DD_SCR_BM_TRANSFER_C2);
} else {
if (p.transfer_mode) {
p.bm_running = false;
} else {
p.state = STATE_IDLE;
scr &= ~(DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_TRANSFER_DATA);
scr |= DD_SCR_BM_READY;
}
}
break;
}
}
if (scr != starting_scr) {
fpga_reg_set(REG_DD_SCR, scr);
}
}

View File

@ -2,6 +2,24 @@
#define DD_H__
#include <stdbool.h>
typedef enum {
DD_DRIVE_TYPE_RETAIL = 0,
DD_DRIVE_TYPE_DEVELOPMENT = 1,
} dd_drive_type_t;
typedef enum {
DD_DISK_STATE_EJECTED = 0,
DD_DISK_STATE_INSERTED = 1,
DD_DISK_STATE_CHANGED = 2,
} dd_disk_state_t;
void dd_set_drive_type (dd_drive_type_t type);
void dd_set_disk_state (dd_disk_state_t state);
void dd_set_block_ready (bool valid);
void dd_init (void);
void dd_process (void);

34
sw/controller/src/debug.c Normal file
View File

@ -0,0 +1,34 @@
#include <string.h>
#include "debug.h"
#include "hw.h"
static char hex_chars[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
};
void debug_print_text (char *text) {
hw_uart_write((uint8_t *) (text), strlen(text));
}
void debug_print_8bit (uint8_t value) {
char buffer[3] = {
hex_chars[(value >> 4) & 0x0F],
hex_chars[value & 0x0F],
'\0'
};
debug_print_text(buffer);
}
void debug_print_16bit (uint16_t value) {
debug_print_8bit((value >> 8) & 0xFF);
debug_print_8bit(value & 0xFF);
}
void debug_print_32bit (uint32_t value) {
debug_print_8bit((value >> 24) & 0xFF);
debug_print_8bit((value >> 16) & 0xFF);
debug_print_8bit((value >> 8) & 0xFF);
debug_print_8bit(value & 0xFF);
}

14
sw/controller/src/debug.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef DEBUG_H__
#define DEBUG_H__
#include <stdint.h>
void debug_print_text (char *text);
void debug_print_8bit (uint8_t value);
void debug_print_16bit (uint16_t value);
void debug_print_32bit (uint32_t value);
#endif

View File

@ -2,10 +2,16 @@
#include "fpga.h"
#define ERASE_BLOCK_SIZE (64 * 1024)
void flash_erase_block (uint32_t offset) {
uint8_t dummy[2];
while (fpga_reg_get(REG_FLASH_SCR) & FLASH_SCR_BUSY);
fpga_reg_set(REG_FLASH_SCR, offset);
fpga_mem_read(offset, 2, dummy);
for (int i = 0; i < 2; i++) {
while (fpga_reg_get(REG_FLASH_SCR) & FLASH_SCR_BUSY);
fpga_reg_set(REG_FLASH_SCR, offset);
fpga_mem_read(offset, 2, dummy);
offset += ERASE_BLOCK_SIZE;
}
}

View File

@ -62,8 +62,7 @@ void flashram_process (void) {
break;
case OP_WRITE_PAGE:
fpga_mem_read(FLASHRAM_BUFFER_OFFSET, FLASHRAM_PAGE_SIZE, buffer);
fpga_mem_write(address, FLASHRAM_PAGE_SIZE, buffer);
fpga_mem_copy(FLASHRAM_BUFFER_OFFSET, address, FLASHRAM_PAGE_SIZE);
fpga_reg_set(REG_FLASHRAM_SCR, FLASHRAM_SCR_DONE);
break;

View File

@ -67,6 +67,16 @@ void fpga_mem_write (uint32_t address, size_t length, uint8_t *buffer) {
while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY);
}
void fpga_mem_copy (uint32_t src, uint32_t dst, size_t length) {
fpga_reg_set(REG_MEM_ADDRESS, src);
fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START);
while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY);
fpga_reg_set(REG_MEM_ADDRESS, dst);
fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START);
while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY);
}
uint8_t fpga_usb_status_get (void) {
fpga_cmd_t cmd = CMD_USB_STATUS;
uint8_t status;

View File

@ -152,6 +152,7 @@ uint32_t fpga_reg_get (fpga_reg_t reg);
void fpga_reg_set (fpga_reg_t reg, uint32_t value);
void fpga_mem_read (uint32_t address, size_t length, uint8_t *buffer);
void fpga_mem_write (uint32_t address, size_t length, uint8_t *buffer);
void fpga_mem_copy (uint32_t src, uint32_t dst, size_t length);
uint8_t fpga_usb_status_get (void);
uint8_t fpga_usb_pop (void);
void fpga_usb_push (uint8_t data);

View File

@ -2,6 +2,7 @@
#include "cfg.h"
#include "flashram.h"
#include "fpga.h"
#include "isv.h"
#include "rtc.h"
#include "usb.h"
@ -9,16 +10,18 @@
void gvr_task (void) {
while (fpga_id_get() != FPGA_ID);
dd_init();
cfg_init();
dd_init();
flashram_init();
isv_init();
usb_init();
while (1) {
dd_process();
cfg_process();
dd_process();
flashram_process();
usb_process();
isv_process();
rtc_process();
usb_process();
}
}

View File

@ -52,8 +52,8 @@ static volatile uint8_t *i2c_data_rxptr;
static volatile uint32_t i2c_next_cr2;
static void (*volatile i2c_callback)(void);
static const TIM_TypeDef *tims[] = { TIM14, TIM16, TIM17 };
static void (*volatile tim_callbacks[3])(void);
static const TIM_TypeDef *tims[] = { TIM14, TIM16, TIM17, TIM3 };
static void (*volatile tim_callbacks[4])(void);
void hw_gpio_init (gpio_id_t id, gpio_mode_t mode, gpio_ot_t ot, gpio_ospeed_t ospeed, gpio_pupd_t pupd, gpio_af_t af, int value) {
@ -113,6 +113,13 @@ void hw_gpio_reset (gpio_id_t id) {
gpio->BSRR = (GPIO_BSRR_BR0 << pin);
}
void hw_uart_write (uint8_t *data, int length) {
for (int i = 0; i < length; i++) {
while (!(USART1->ISR & USART_ISR_TXE_TXFNF));
USART1->TDR = *data++;
}
}
void hw_spi_start (void) {
hw_gpio_reset(GPIO_ID_SPI_CS);
}
@ -213,6 +220,9 @@ void hw_tim_disable_irq (tim_id_t id) {
case TIM_ID_GVR:
NVIC_DisableIRQ(TIM17_IRQn);
break;
case TIM_ID_DD:
NVIC_DisableIRQ(TIM3_IRQn);
break;
default:
break;
}
@ -229,6 +239,9 @@ void hw_tim_enable_irq (tim_id_t id) {
case TIM_ID_GVR:
NVIC_EnableIRQ(TIM17_IRQn);
break;
case TIM_ID_DD:
NVIC_EnableIRQ(TIM3_IRQn);
break;
default:
break;
}
@ -261,7 +274,8 @@ void hw_init (void) {
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
RCC->APBENR1 |= (
RCC_APBENR1_DBGEN |
RCC_APBENR1_I2C1EN
RCC_APBENR1_I2C1EN |
RCC_APBENR1_TIM3EN
);
RCC->APBENR2 |= (
RCC_APBENR2_TIM17EN |
@ -317,7 +331,7 @@ void hw_init (void) {
hw_gpio_init(GPIO_ID_SPI_CS, GPIO_OUTPUT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 1);
hw_gpio_init(GPIO_ID_SPI_CLK, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_SPI_MISO, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_SPI_MISO, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_DOWN, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_SPI_MOSI, GPIO_ALT, GPIO_PP, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_AF_0, 0);
hw_gpio_init(GPIO_ID_FPGA_INT, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0);
@ -345,6 +359,7 @@ void hw_init (void) {
NVIC_EnableIRQ(TIM14_IRQn);
NVIC_EnableIRQ(TIM16_IRQn);
NVIC_EnableIRQ(TIM17_IRQn);
NVIC_EnableIRQ(TIM3_IRQn);
}
void EXTI0_1_IRQHandler (void) {
@ -443,3 +458,11 @@ void TIM17_IRQHandler (void) {
tim_callbacks[2] = 0;
}
}
void TIM3_IRQHandler (void) {
TIM3->SR &= ~(TIM_SR_UIF);
if (tim_callbacks[3]) {
tim_callbacks[3]();
tim_callbacks[3] = 0;
}
}

View File

@ -33,6 +33,7 @@ typedef enum {
TIM_ID_CIC = 0,
TIM_ID_RTC = 1,
TIM_ID_GVR = 2,
TIM_ID_DD = 3,
} tim_id_t;
typedef enum {
@ -45,6 +46,7 @@ void hw_gpio_irq_setup (gpio_id_t id, gpio_irq_t irq, void (*callback)(void));
uint32_t hw_gpio_get (gpio_id_t id);
void hw_gpio_set (gpio_id_t id);
void hw_gpio_reset (gpio_id_t id);
void hw_uart_write (uint8_t *data, int length);
void hw_spi_start (void);
void hw_spi_stop (void);
void hw_spi_trx (uint8_t *data, int length, spi_direction_t direction);

67
sw/controller/src/isv.c Normal file
View File

@ -0,0 +1,67 @@
#include "fpga.h"
#include "isv.h"
#include "usb.h"
#define ISV_READ_POINTER_ADDRESS (0x03FF0014)
#define ISV_BUFFER_ADDRESS (0x03FF0020)
#define ISV_BUFFER_SIZE ((64 * 1024) - 0x20)
struct process {
bool enabled;
uint32_t current_read_pointer;
};
static struct process p;
static uint32_t isv_get_read_pointer (void) {
uint32_t read_pointer;
fpga_mem_read(ISV_READ_POINTER_ADDRESS, 4, (uint8_t *) (&read_pointer));
return (
(read_pointer & 0x000000FF) << 24 |
(read_pointer & 0x0000FF00) << 8 |
(read_pointer & 0x00FF0000) >> 8 |
(read_pointer & 0xFF000000) >> 24
);
}
void isv_set_enabled (bool enabled) {
if (enabled) {
p.enabled = true;
p.current_read_pointer = 0;
} else {
p.enabled = false;
}
}
bool isv_get_enabled (void) {
return p.enabled;
}
void isv_init (void) {
p.enabled = false;
}
void isv_process (void) {
if (p.enabled) {
uint32_t read_pointer = isv_get_read_pointer();
if (read_pointer < ISV_BUFFER_SIZE && read_pointer != p.current_read_pointer) {
bool wrap = read_pointer < p.current_read_pointer;
uint32_t length = ((wrap ? ISV_BUFFER_SIZE : read_pointer) - p.current_read_pointer);
uint32_t offset = ISV_BUFFER_ADDRESS + p.current_read_pointer;
usb_tx_info_t packet_info;
packet_info.cmd = PACKET_CMD_ISV_OUTPUT;
packet_info.data_length = 0;
packet_info.dma_length = length;
packet_info.dma_address = offset;
if (usb_enqueue_packet(&packet_info)) {
p.current_read_pointer = wrap ? 0 : read_pointer;
}
}
}
}

14
sw/controller/src/isv.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef ISV_H__
#define ISV_H__
#include <stdbool.h>
void isv_set_enabled (bool enabled);
bool isv_get_enabled (void);
void isv_init (void);
void isv_process (void);
#endif

View File

@ -1,5 +1,6 @@
#include <stdbool.h>
#include <stdint.h>
#include "dd.h"
#include "cfg.h"
#include "fpga.h"
#include "rtc.h"
@ -7,28 +8,51 @@
#include "flash.h"
enum state {
STATE_IDLE,
STATE_ARGS,
STATE_DATA,
STATE_RESPONSE
enum rx_state {
RX_STATE_IDLE,
RX_STATE_ARGS,
RX_STATE_DATA,
};
struct process {
enum state state;
uint32_t counter;
uint8_t cmd;
uint32_t args[2];
bool error;
bool dma_in_progress;
enum tx_state {
TX_STATE_IDLE,
TX_STATE_TOKEN,
TX_STATE_DATA,
TX_STATE_DMA,
TX_STATE_FLUSH,
};
struct process {
enum rx_state rx_state;
uint8_t rx_counter;
uint8_t rx_cmd;
uint32_t rx_args[2];
bool rx_dma_running;
enum tx_state tx_state;
uint8_t tx_counter;
usb_tx_info_t tx_info;
uint32_t tx_token;
bool tx_dma_running;
bool response_pending;
bool response_error;
usb_tx_info_t response_info;
bool packet_pending;
usb_tx_info_t packet_info;
};
static struct process p;
static const char CMD_TOKEN[3] = { 'C', 'M', 'D' };
static const uint32_t CMP_TOKEN = (0x434D5000UL);
static const uint32_t ERR_TOKEN = (0x45525200UL);
static const uint32_t PKT_TOKEN = (0x504B5400UL);
static bool usb_rx_byte (uint8_t *data) {
if (fpga_usb_status_get() & USB_STATUS_RXNE) {
@ -96,11 +120,201 @@ static bool usb_rx_cmd (uint8_t *cmd) {
}
static void usb_rx_process (void) {
if (p.rx_state == RX_STATE_IDLE) {
if (!p.response_pending && usb_rx_cmd(&p.rx_cmd)) {
p.rx_state = RX_STATE_ARGS;
p.rx_counter = 0;
p.rx_dma_running = false;
p.response_error = false;
p.response_info.cmd = p.rx_cmd;
p.response_info.data_length = 0;
p.response_info.dma_length = 0;
}
}
if (p.rx_state == RX_STATE_ARGS) {
while (usb_rx_word(&p.rx_args[p.rx_counter])) {
p.rx_counter += 1;
if (p.rx_counter == 2) {
p.rx_counter = 0;
p.rx_state = RX_STATE_DATA;
break;
}
}
}
if (p.rx_state == RX_STATE_DATA) {
switch (p.rx_cmd) {
case 'v':
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
p.response_info.data_length = 4;
p.response_info.data[0] = cfg_get_version();
break;
case 'c':
cfg_query(p.rx_args);
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
p.response_info.data_length = 4;
p.response_info.data[0] = p.rx_args[1];
break;
case 'C':
cfg_update(p.rx_args);
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
break;
case 't':
cfg_get_time(p.rx_args);
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
p.response_info.data_length = 8;
p.response_info.data[0] = p.rx_args[0];
p.response_info.data[1] = p.rx_args[1];
break;
case 'T':
cfg_set_time(p.rx_args);
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
break;
case 'd':
dd_set_block_ready(p.rx_args[0] == 0);
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
break;
case 'm':
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
p.response_info.dma_address = p.rx_args[0];
p.response_info.dma_length = p.rx_args[1];
break;
case 'M':
case 'D':
if (!((fpga_reg_get(REG_USB_DMA_SCR) & DMA_SCR_BUSY))) {
if (!p.rx_dma_running) {
fpga_reg_set(REG_USB_DMA_ADDRESS, p.rx_args[0]);
fpga_reg_set(REG_USB_DMA_LENGTH, p.rx_args[1]);
fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_DIRECTION | DMA_SCR_START);
p.rx_dma_running = true;
} else {
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
if (p.rx_cmd == 'D') {
dd_set_block_ready(true);
}
}
}
break;
default:
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
p.response_error = true;
p.response_info.data_length = 4;
p.response_info.data[0] = 0xFF;
}
}
}
static void usb_tx_process (void) {
if (p.tx_state == TX_STATE_IDLE) {
if (p.response_pending) {
p.response_pending = false;
p.tx_state = TX_STATE_TOKEN;
p.tx_counter = 0;
p.tx_info = p.response_info;
p.tx_token = p.response_error ? ERR_TOKEN : CMP_TOKEN;
p.tx_dma_running = false;
} else if (p.packet_pending) {
p.packet_pending = false;
p.tx_state = TX_STATE_TOKEN;
p.tx_counter = 0;
p.tx_info = p.packet_info;
p.tx_token = PKT_TOKEN;
p.tx_dma_running = false;
}
}
if (p.tx_state == TX_STATE_TOKEN) {
if (p.tx_counter == 0) {
if (usb_tx_word(p.tx_token | p.tx_info.cmd)) {
p.tx_counter += 1;
}
}
if (p.tx_counter == 1) {
if (usb_tx_word(p.tx_info.data_length + p.tx_info.dma_length)) {
p.tx_state = TX_STATE_DATA;
p.tx_counter = 0;
}
}
}
if (p.tx_state == TX_STATE_DATA) {
if (p.tx_info.data_length > 0) {
while (usb_tx_word(p.tx_info.data[p.tx_counter])) {
p.tx_counter += 1;
if (p.tx_counter == (p.tx_info.data_length / 4)) {
p.tx_state = TX_STATE_DMA;
p.tx_counter = 0;
break;
}
}
} else {
p.tx_state = TX_STATE_DMA;
}
}
if (p.tx_state == TX_STATE_DMA) {
if (p.tx_info.dma_length > 0) {
if (!((fpga_reg_get(REG_USB_DMA_SCR) & DMA_SCR_BUSY))) {
if (!p.tx_dma_running) {
p.tx_dma_running = true;
fpga_reg_set(REG_USB_DMA_ADDRESS, p.tx_info.dma_address);
fpga_reg_set(REG_USB_DMA_LENGTH, p.tx_info.dma_length);
fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_START);
} else {
p.tx_state = TX_STATE_FLUSH;
}
}
} else {
p.tx_state = TX_STATE_FLUSH;
}
}
if (p.tx_state == TX_STATE_FLUSH) {
fpga_reg_set(REG_USB_SCR, USB_SCR_WRITE_FLUSH);
p.tx_state = TX_STATE_IDLE;
}
}
bool usb_enqueue_packet (usb_tx_info_t *info) {
if (p.packet_pending) {
return false;
}
p.packet_pending = true;
p.packet_info = *info;
return true;
}
void usb_init (void) {
fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_STOP);
fpga_reg_set(REG_USB_SCR, USB_SCR_FIFO_FLUSH);
p.state = STATE_IDLE;
p.rx_state = RX_STATE_IDLE;
p.tx_state = TX_STATE_IDLE;
p.response_pending = false;
p.packet_pending = false;
usb_rx_word_counter = 0;
usb_rx_word_buffer = 0;
@ -109,105 +323,11 @@ void usb_init (void) {
}
void usb_process (void) {
uint32_t scr = fpga_reg_get(REG_USB_SCR);
if (scr & USB_SCR_RESET_PENDING) {
if (fpga_reg_get(REG_USB_SCR) & USB_SCR_RESET_PENDING) {
usb_init();
fpga_reg_set(REG_USB_SCR, USB_SCR_RESET_ACK);
return;
}
switch (p.state) {
case STATE_IDLE: {
if (usb_rx_cmd(&p.cmd)) {
p.counter = 0;
p.error = false;
p.dma_in_progress = false;
p.state = STATE_ARGS;
}
break;
}
case STATE_ARGS: {
if (usb_rx_word(&p.args[p.counter])) {
p.counter += 1;
if (p.counter == 2) {
p.counter = 0;
p.state = STATE_DATA;
}
}
break;
}
case STATE_DATA: {
switch (p.cmd) {
case 'v':
if (usb_tx_word(cfg_get_version())) {
p.state = STATE_RESPONSE;
}
break;
case 'c':
if (p.counter == 0) {
cfg_query(p.args);
p.counter += 1;
}
if (usb_tx_word(p.args[1])) {
p.state = STATE_RESPONSE;
}
break;
case 'C':
cfg_update(p.args);
p.state = STATE_RESPONSE;
break;
case 't':
if (p.counter == 0) {
cfg_get_time(p.args);
p.counter += 1;
}
if ((p.counter == 1) && usb_tx_word(p.args[0])) {
p.counter += 1;
}
if ((p.counter == 2) && usb_tx_word(p.args[1])) {
p.state = STATE_RESPONSE;
}
break;
case 'T':
cfg_set_time(p.args);
p.state = STATE_RESPONSE;
break;
case 'm':
case 'M':
if (!((fpga_reg_get(REG_USB_DMA_SCR) & DMA_SCR_BUSY))) {
if (!p.dma_in_progress) {
fpga_reg_set(REG_USB_DMA_ADDRESS, p.args[0]);
fpga_reg_set(REG_USB_DMA_LENGTH, p.args[1]);
fpga_reg_set(REG_USB_DMA_SCR, (p.cmd == 'M' ? DMA_SCR_DIRECTION : 0) | DMA_SCR_START);
p.dma_in_progress = true;
} else {
p.state = STATE_RESPONSE;
}
}
break;
default:
p.error = true;
p.state = STATE_RESPONSE;
break;
}
break;
}
case STATE_RESPONSE: {
if (usb_tx_word((p.error ? ERR_TOKEN : CMP_TOKEN) | p.cmd)) {
p.state = STATE_IDLE;
fpga_reg_set(REG_USB_SCR, USB_SCR_WRITE_FLUSH);
}
break;
}
} else {
usb_rx_process();
usb_tx_process();
}
}

View File

@ -2,6 +2,26 @@
#define USB_H__
#include <stdbool.h>
#include <stdint.h>
typedef enum packet_cmd {
PACKET_CMD_DD_REQUEST = 'D',
PACKET_CMD_ISV_OUTPUT = 'I',
} usb_packet_cmd_e;
typedef struct usb_tx_info {
uint8_t cmd;
uint32_t data_length;
uint32_t data[4];
uint32_t dma_length;
uint32_t dma_address;
} usb_tx_info_t;
bool usb_enqueue_packet (usb_tx_info_t *info);
void usb_init (void);
void usb_process (void);

2
sw/pc/.gitignore vendored
View File

@ -1,4 +1,4 @@
/__pycache__
**/__pycache__
/backup
/roms
/saves

254
sw/pc/lib/sc64.py Normal file
View File

@ -0,0 +1,254 @@
from io import BufferedReader
from time import sleep
from sc64_64dd import BadBlockError, SixtyFourDiskDrive
from sc64_transport import SC64Transport
class SC64Exception(Exception):
pass
class SC64:
__VERSION_V2 = b'SCv2'
__CFG_ID_BOOTLOADER_SWITCH = 0
__CFG_ID_ROM_WRITE_ENABLE = 1
__CFG_ID_ROM_SHADOW_ENABLE = 2
__CFG_ID_DD_MODE = 3
__CFG_ID_ISV_ENABLE = 4
__CFG_ID_BOOT_MODE = 5
__CFG_ID_SAVE_TYPE = 6
__CFG_ID_CIC_SEED = 7
__CFG_ID_TV_TYPE = 8
__CFG_ID_FLASH_ERASE_BLOCK = 9
__CFG_ID_DD_DRIVE_TYPE = 10
__CFG_ID_DD_DISK_STATE = 11
__SDRAM_ADDRESS = 0x00000000
__SDRAM_LENGTH = (64 * 1024 * 1024)
__DDIPL_ADDRESS = 0x03BC0000
__DDIPL_LENGTH = (4 * 1024 * 1024)
__SAVE_ADDRESS = 0x03FE0000
__SAVE_LENGTH = (128 * 1024)
__FLASH_ADDRESS = 0x04000000
__FLASH_LENGTH = (16 * 1024 * 1024)
__EXTENDED_ROM_ADDRESS = 0x04000000
__EXTENDED_ROM_LENGTH = (14 * 1024 * 1024)
__BOOTLOADER_ADDRESS = 0x04E00000
__BOOTLOADER_LENGTH = (1920 * 1024)
__SHADOW_ADDRESS = 0x04FE0000
__SHADOW_LENGTH = (128 * 1024)
__BUFFER_ADDRESS = 0x06000000
__BUFFER_LENGTH = (8 * 1024)
__EEPROM_ADDRESS = 0x06002000
__EEPROM_LENGTH = (2 * 1024)
__DD_SECTOR_ADDRESS = 0x06006000
__DD_SECTOR_LENGTH = (2 * 1024)
__FLASH_ERASE_SIZE = (128 * 1024)
__CHUNK_SIZE = (128 * 1024)
__MAX_ROM_SIZE = __SDRAM_LENGTH + __EXTENDED_ROM_LENGTH
__BOOT_MODE = [
'sd',
'usb',
'rom',
'ddipl',
'direct',
]
__SAVE_TYPE = [
'none',
'eeprom4k',
'eeprom16k',
'sram',
'flashram',
'sram3x',
]
__SAVE_LENGTH = [
0,
512,
(2 * 1024),
(32 * 1024),
(128 * 1024),
(3 * 32 * 1024),
]
__SAVE_ADDRESS = [
0,
__EEPROM_ADDRESS,
__EEPROM_ADDRESS,
__SAVE_ADDRESS,
__SAVE_ADDRESS,
__SAVE_ADDRESS,
]
__DD_MODE = [
'none',
'full',
'ddipl',
]
__DD_DRIVE_TYPE = [
'retail',
'development',
]
__DD_DISK_STATE = [
'ejected',
'inserted',
'changed',
]
def __init__(self) -> None:
self.__transport = SC64Transport()
self.__transport.connect()
version = self.__transport.execute_cmd(cmd=b'v', args=[0, 0])
if (version != self.__VERSION_V2):
raise SC64Exception("SC64 version different than expected")
def __get_int(self, data: bytes) -> int:
return int.from_bytes(data[:4], byteorder="big")
def __set_config(self, config: int, value: int) -> None:
self.__transport.execute_cmd(b'C', [config, value])
def __get_config(self, config: int) -> int:
return int.from_bytes(self.__transport.execute_cmd(b'c', args=[config, 0]), byteorder="big")
def __write_memory(self, address: int, data: bytes) -> None:
self.__transport.execute_cmd(
cmd=b'M', args=[address, len(data)], data=data)
def __read_memory(self, address: int, length: int):
return self.__transport.execute_cmd(cmd=b'm', args=[address, length])
def __erase_flash_block(self, address: int):
self.__set_config(self.__CFG_ID_FLASH_ERASE_BLOCK, address)
def __erase_flash(self, address: int, length: int):
if (address < self.__FLASH_ADDRESS):
raise ValueError
if (address + length >= self.__FLASH_ADDRESS + self.__FLASH_LENGTH):
raise ValueError
if (address % self.__FLASH_ERASE_SIZE != 0):
raise ValueError
if (length % self.__FLASH_ERASE_SIZE != 0):
raise ValueError
for offset in range(address, address + length, self.__FLASH_ERASE_SIZE):
self.__erase_flash_block(offset)
def set_boot_mode(self, boot_mode: str) -> None:
value = self.__BOOT_MODE.index(boot_mode)
self.__set_config(self.__CFG_ID_BOOT_MODE, value)
def set_save_type(self, save_type: str) -> None:
value = self.__SAVE_TYPE.index(save_type)
self.__set_config(self.__CFG_ID_SAVE_TYPE, value)
def set_dd_mode(self, dd_mode: str) -> None:
value = self.__DD_MODE.index(dd_mode)
self.__set_config(self.__CFG_ID_DD_MODE, value)
def set_dd_drive_type(self, dd_drive_type: str) -> None:
value = self.__DD_DRIVE_TYPE.index(dd_drive_type)
self.__set_config(self.__CFG_ID_DD_DRIVE_TYPE, value)
def set_dd_disk_state(self, dd_disk_state: str) -> None:
value = self.__DD_DISK_STATE.index(dd_disk_state)
self.__set_config(self.__CFG_ID_DD_DISK_STATE, value)
def upload_rom(self, data: bytes):
if (len(data) > self.__SDRAM_LENGTH):
raise ValueError
self.__write_memory(self.__SDRAM_ADDRESS, data)
def upload_ddipl(self, data: bytes) -> None:
if (len(data) > self.__DDIPL_LENGTH):
raise ValueError
self.__write_memory(self.__DDIPL_ADDRESS, data)
def upload_save(self, data: bytes) -> None:
save_type = self.__get_config(self.__CFG_ID_SAVE_TYPE)
if (save_type < 0 or save_type >= len(self.__SAVE_TYPE)):
raise ValueError
if (len(data) != self.__SAVE_LENGTH[save_type]):
raise ValueError
self.__write_memory(self.__SAVE_ADDRESS[save_type], data)
def upload_bootloader(self, data: bytes) -> None:
if (len(data) > self.__BOOTLOADER_LENGTH):
raise ValueError
self.__erase_flash(self.__BOOTLOADER_ADDRESS, self.__BOOTLOADER_LENGTH)
self.__write_memory(self.__BOOTLOADER_ADDRESS, data)
def __handle_dd_packet(self, dd: SixtyFourDiskDrive, data: bytes) -> None:
CMD_READ_BLOCK = 1
CMD_WRITE_BLOCK = 2
cmd = self.__get_int(data[0:])
address = self.__get_int(data[4:])
track_head_block = self.__get_int(data[8:])
track = (track_head_block >> 2) & 0xFFF
head = (track_head_block >> 1) & 0x1
block = track_head_block & 0x1
try:
if (cmd == CMD_READ_BLOCK):
block_data = dd.read_block(track, head, block)
self.__transport.execute_cmd(cmd=b'D', args=[address, len(block_data)], data=block_data)
elif (cmd == CMD_WRITE_BLOCK):
dd.write_block(track, head, block, data[12:])
self.__transport.execute_cmd(cmd=b'd', args=[0, 0])
else:
self.__transport.execute_cmd(cmd=b'd', args=[-1, 0])
except BadBlockError:
self.__transport.execute_cmd(cmd=b'd', args=[1, 0])
def __handle_isv_packet(self, data: bytes) -> None:
print(data.decode("EUC-JP", errors="backslashreplace"), end="")
def debug_server(self, disk_path: str) -> None:
dd = SixtyFourDiskDrive()
dd.load(disk_path)
self.set_dd_drive_type(dd.get_drive_type())
self.set_dd_disk_state('inserted')
self.__set_config(self.__CFG_ID_ROM_WRITE_ENABLE, 1)
self.__set_config(self.__CFG_ID_ISV_ENABLE, 1)
self.__set_config(self.__CFG_ID_TV_TYPE, 1)
while (True):
packet = self.__transport.get_packet()
if (packet != None):
(cmd, data) = packet
if (cmd == b'D'):
self.__handle_dd_packet(dd, data)
if (cmd == b'I'):
self.__handle_isv_packet(data)
if __name__ == "__main__":
sc64 = SC64()
sc64.set_boot_mode('rom')
print('set boot mode')
sc64.set_dd_mode('none')
print('set dd mode')
with open('S:/n64/roms/ZELOOTD.z64', 'rb') as f:
sc64.upload_rom(f.read())
print('uploaded rom')
# with open('S:/n64/64dd/ipl/NDDJ2.n64', 'rb') as f:
# sc64.upload_ddipl(f.read())
# print('uploaded ddipl')
sc64.set_save_type('sram')
print('set save type')
# with open('S:/n64/saves/SM64.eep', 'rb') as f:
# sc64.upload_save(f.read())
# print('uploaded save')
try:
print('starting debug server')
sc64.debug_server(disk_path='S:/n64/64dd/rtl/Mario Artist Polygon Studio.ndd')
except KeyboardInterrupt:
pass

220
sw/pc/lib/sc64_64dd.py Normal file
View File

@ -0,0 +1,220 @@
import sys
from io import BufferedReader
from typing import Optional
class BadBlockError(Exception):
pass
class SixtyFourDiskDrive:
__DISK_HEADS = 2
__DISK_TRACKS = 1175
__DISK_BLOCKS_PER_TRACK = 2
__DISK_SECTORS_PER_BLOCK = 85
__DISK_BAD_TRACKS_PER_ZONE = 12
__DISK_SYSTEM_SECTOR_SIZE = 232
__DISK_ZONES = [
(0, 232, 158, 0),
(0, 216, 158, 158),
(0, 208, 149, 316),
(0, 192, 149, 465),
(0, 176, 149, 614),
(0, 160, 149, 763),
(0, 144, 149, 912),
(0, 128, 114, 1061),
(1, 216, 158, 157),
(1, 208, 158, 315),
(1, 192, 149, 464),
(1, 176, 149, 613),
(1, 160, 149, 762),
(1, 144, 149, 911),
(1, 128, 149, 1060),
(1, 112, 114, 1174),
]
__DISK_VZONE_TO_PZONE = [
[0, 1, 2, 9, 8, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10],
[0, 1, 2, 3, 10, 9, 8, 4, 5, 6, 7, 15, 14, 13, 12, 11],
[0, 1, 2, 3, 4, 11, 10, 9, 8, 5, 6, 7, 15, 14, 13, 12],
[0, 1, 2, 3, 4, 5, 12, 11, 10, 9, 8, 6, 7, 15, 14, 13],
[0, 1, 2, 3, 4, 5, 6, 13, 12, 11, 10, 9, 8, 7, 15, 14],
[0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8, 15],
[0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8],
]
__DISK_DRIVE_TYPES = [(
"development",
192,
[11, 10, 3, 2],
[0, 1, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23],
), (
"retail",
232,
[9, 8, 1, 0],
[2, 3, 10, 11, 12, 16, 17, 18, 19, 20, 21, 22, 23],
)]
__file: Optional[BufferedReader]
__drive_type: Optional[str]
__block_info_table: list[tuple[int, int]]
def __init__(self) -> None:
self.__file = None
self.__drive_type = None
block_info_table_length = self.__DISK_HEADS * self.__DISK_TRACKS * self.__DISK_BLOCKS_PER_TRACK
self.__block_info_table = [None] * block_info_table_length
def __del__(self) -> None:
self.unload()
def __check_system_block(self, lba: int, sector_size: int, check_disk_type: bool) -> tuple[bool, bytes]:
self.__file.seek(lba * self.__DISK_SYSTEM_SECTOR_SIZE * self.__DISK_SECTORS_PER_BLOCK)
system_block_data = self.__file.read(sector_size * self.__DISK_SECTORS_PER_BLOCK)
system_data = system_block_data[:sector_size]
for sector in range(1, self.__DISK_SECTORS_PER_BLOCK):
sector_data = system_block_data[(sector * sector_size):][:sector_size]
if (system_data != sector_data):
return (False, None)
if (check_disk_type):
if (system_data[4] != 0x10):
return (False, None)
if ((system_data[5] & 0xF0) != 0x10):
return (False, None)
return (True, system_data)
def __parse_disk(self) -> None:
disk_system_data = None
disk_id_data = None
disk_bad_lbas = []
drive_index = 0
while (disk_system_data == None) and (drive_index < len(self.__DISK_DRIVE_TYPES)):
(drive_type, system_sector_size, system_data_lbas, bad_lbas) = self.__DISK_DRIVE_TYPES[drive_index]
disk_bad_lbas.clear()
disk_bad_lbas.extend(bad_lbas)
for system_lba in system_data_lbas:
(valid, system_data) = self.__check_system_block(system_lba, system_sector_size, check_disk_type=True)
if (valid):
self.__drive_type = drive_type
disk_system_data = system_data
else:
disk_bad_lbas.append(system_lba)
drive_index += 1
for id_lba in [15, 14]:
(valid, id_data) = self.__check_system_block(id_lba, self.__DISK_SYSTEM_SECTOR_SIZE, check_disk_type=False)
if (valid):
disk_id_data = id_data
else:
disk_bad_lbas.append(id_lba)
if not (disk_system_data and disk_id_data):
raise ValueError("Provided 64DD disk file is not valid")
disk_zone_bad_tracks = []
for zone in range(len(self.__DISK_ZONES)):
zone_bad_tracks = []
start = 0 if zone == 0 else system_data[0x07 + zone]
stop = system_data[0x07 + zone + 1]
for offset in range(start, stop):
zone_bad_tracks.append(system_data[0x20 + offset])
for ignored_track in range(self.__DISK_BAD_TRACKS_PER_ZONE - len(zone_bad_tracks)):
zone_bad_tracks.append(self.__DISK_ZONES[zone][2] - ignored_track - 1)
disk_zone_bad_tracks.append(zone_bad_tracks)
disk_type = disk_system_data[5] & 0x0F
current_lba = 0
starting_block = 0
disk_file_offset = 0
for zone in self.__DISK_VZONE_TO_PZONE[disk_type]:
(head, sector_size, tracks, track) = self.__DISK_ZONES[zone]
for zone_track in range(tracks):
current_zone_track = (
(tracks - 1) - zone_track) if head else zone_track
if (current_zone_track in disk_zone_bad_tracks[zone]):
track += (-1) if head else 1
continue
for block in range(self.__DISK_BLOCKS_PER_TRACK):
index = (track << 2) | (head << 1) | (starting_block ^ block)
if (current_lba not in disk_bad_lbas):
self.__block_info_table[index] = (disk_file_offset, sector_size * self.__DISK_SECTORS_PER_BLOCK)
else:
self.__block_info_table[index] = None
disk_file_offset += sector_size * self.__DISK_SECTORS_PER_BLOCK
current_lba += 1
track += (-1) if head else 1
starting_block ^= 1
def __check_track_head_block(self, track: int, head: int, block: int) -> None:
if (track < 0 or track >= self.__DISK_TRACKS):
raise ValueError("Track outside of possible range")
if (head < 0 or head >= self.__DISK_HEADS):
raise ValueError("Head outside of possible range")
if (block < 0 or block >= self.__DISK_BLOCKS_PER_TRACK):
raise ValueError("Block outside of possible range")
def __get_table_index(self, track: int, head: int, block: int) -> int:
return (track << 2) | (head << 1) | (block)
def __get_block_info(self, track: int, head: int, block: int) -> Optional[tuple[int, int]]:
if (self.__file.closed):
return None
self.__check_track_head_block(track, head, block)
index = self.__get_table_index(track, head, block)
return self.__block_info_table[index]
def load(self, path: str) -> None:
self.unload()
self.__file = open(path, 'rb+')
self.__parse_disk()
def unload(self) -> None:
if (self.__file != None and not self.__file.closed):
self.__file.close()
self.__drive_type = None
def get_drive_type(self) -> str:
return self.__drive_type
def read_block(self, track: int, head: int, block: int) -> bytes:
info = self.__get_block_info(track, head, block)
if (info == None):
raise BadBlockError
(offset, block_size) = info
self.__file.seek(offset)
return self.__file.read(block_size)
def write_block(self, track: int, head: int, block: int, data: bytes) -> None:
info = self.__get_block_info(track, head, block)
if (info == None):
raise BadBlockError
(offset, block_size) = info
if (len(data) != block_size):
raise ValueError(f"Provided data block size is different than expected ({len(data)} != {block_size})")
self.__file.seek(offset)
self.__file.write(data)
if __name__ == "__main__":
id_lba_locations = [
(7, 0, 1),
(7, 0, 0)
]
if (len(sys.argv) >= 2):
dd = SixtyFourDiskDrive()
dd.load(sys.argv[1])
print(dd.get_drive_type())
for (track, head, block) in id_lba_locations:
try:
print(dd.read_block(track, head, block)[:4])
except BadBlockError:
print(f"Bad ID block [track: {track}, head: {head}, block: {block}]")
dd.unload()
else:
print(f"[{sys.argv[0]}]: Expected disk image path as first argument")

171
sw/pc/lib/sc64_transport.py Normal file
View File

@ -0,0 +1,171 @@
from serial.tools import list_ports
from threading import Thread
from typing import Optional
import queue
import serial
import time
class ConnectionException(Exception):
pass
class SC64Transport:
__disconnect = False
__serial = None
__thread_read = None
__thread_write = None
__queue_output = queue.Queue()
__queue_input = queue.Queue()
__queue_packet = queue.Queue()
def __del__(self) -> None:
self.__disconnect = True
if (self.__thread_read.is_alive()):
self.__thread_read.join(5)
if (self.__thread_write.is_alive()):
self.__thread_write.join(5)
if (self.__serial != None and self.__serial.is_open):
self.__serial.close()
def connect(self) -> None:
ports = list_ports.comports()
device_found = False
if (self.__serial != None and self.__serial.is_open):
raise ConnectionException("Serial port is already open")
for p in ports:
if (p.vid == 0x0403 and p.pid == 0x6014 and p.serial_number.startswith("SC64")):
try:
self.__serial = serial.Serial(
p.device, timeout=1.0, write_timeout=5.0)
self.__reset_link()
except (serial.SerialException, ConnectionException):
if (self.__serial):
self.__serial.close()
continue
device_found = True
break
if (not device_found):
raise ConnectionException("No SummerCart64 device was found")
self.__thread_read = Thread(
target=self.__serial_process_input, daemon=True)
self.__thread_write = Thread(
target=self.__serial_process_output, daemon=True)
self.__thread_read.start()
self.__thread_write.start()
def __reset_link(self) -> None:
self.__serial.reset_output_buffer()
retry_counter = 0
self.__serial.dtr = 1
while (self.__serial.dsr == 0):
time.sleep(0.1)
retry_counter += 1
if (retry_counter >= 10):
raise ConnectionException
self.__serial.reset_input_buffer()
retry_counter = 0
self.__serial.dtr = 0
while (self.__serial.dsr == 1):
time.sleep(0.1)
retry_counter += 1
if (retry_counter >= 10):
raise ConnectionException
def __write(self, data: bytes) -> None:
try:
if (self.__disconnect):
raise ConnectionException
self.__serial.write(data)
self.__serial.flush()
except serial.SerialTimeoutException:
raise ConnectionException
except serial.SerialException:
raise ConnectionException
def __read(self, length: int) -> bytes:
try:
data = b''
while (len(data) < length and not self.__disconnect):
data += self.__serial.read(length - len(data))
if (self.__disconnect):
raise ConnectionException
return data
except serial.SerialException:
raise ConnectionException
def __read_int(self) -> int:
return int.from_bytes(self.__read(4), byteorder="big")
def __serial_process_output(self) -> None:
while (not self.__disconnect and self.__serial != None and self.__serial.is_open):
try:
packet: bytes = self.__queue_output.get(timeout=0.1)
self.__write(packet)
self.__queue_output.task_done()
except queue.Empty:
continue
except ConnectionException:
break
def __serial_process_input(self) -> None:
while (not self.__disconnect and self.__serial != None and self.__serial.is_open):
try:
token = self.__read(4)
if (len(token) == 4):
identifier = token[0:3]
command = token[3:4]
if (identifier == b'PKT'):
lenss = self.__read_int()
data = self.__read(lenss)
self.__queue_packet.put((command, data))
elif (identifier == b'CMP' or identifier == b'ERR'):
data = self.__read(self.__read_int())
success = identifier == b'CMP'
self.__queue_input.put((command, data, success))
else:
raise Exception
except ConnectionException:
break
def __queue_cmd(self, command: bytes, args: list[int] = [], data: bytes = b''):
packet: bytes = b'CMD'
packet += command
for arg in args:
packet += arg.to_bytes(4, byteorder="big")
packet += data
self.__queue_output.put(packet)
def __pop_response(self, command: bytes) -> bytes:
try:
(response_command, data, success) = self.__queue_input.get(timeout=5)
if (command != response_command or success == False):
raise ConnectionException
return data
except queue.Empty:
raise ConnectionException
def execute_cmd(self, cmd: bytes, args: list[int] = [], data: bytes = b'', response: bool = True) -> Optional[bytes]:
if (len(cmd) != 1):
raise ValueError("Length of command is different than 1 byte")
command = cmd[0:1]
if (len(args) != 2):
raise ValueError("Number of arguments is different than 2")
self.__queue_cmd(command, args, data)
if (response):
return self.__pop_response(command)
return None
def get_packet(self) -> Optional[tuple[bytes, bytes]]:
try:
return self.__queue_packet.get(timeout=1)
except queue.Empty:
return None

View File

@ -22,23 +22,6 @@ class SC64Exception(Exception):
class SC64:
# __CFG_ID_DD_ENABLE = 3
# __CFG_ID_SAVE_TYPE = 4
# __CFG_ID_CIC_SEED = 5
# __CFG_ID_TV_TYPE = 6
# __CFG_ID_SAVE_OFFEST = 7
# __CFG_ID_DDIPL_OFFEST = 8
# __CFG_ID_BOOT_MODE = 9
# __CFG_ID_FLASH_SIZE = 10
# __CFG_ID_FLASH_READ = 11
# __CFG_ID_FLASH_PROGRAM = 12
# __CFG_ID_RECONFIGURE = 13
# __CFG_ID_DD_DRIVE_ID = 14
# __CFG_ID_DD_DISK_STATE = 15
# __CFG_ID_DD_THB_TABLE_OFFSET = 16
# __CFG_ID_IS_VIEWER_ENABLE = 17
__CFG_ID_BOOTLOADER_SWITCH = 0
__CFG_ID_ROM_WRITE_ENABLE = 1
__CFG_ID_ROM_SHADOW_ENABLE = 2
@ -57,7 +40,7 @@ class SC64:
__BOOTLOADER_OFFSET = (64 + 14) * 1024 * 1024
__SAVE_OFFSET = (64 * 1024 * 1024) - (128 * 1024)
__EEPROM_OFFSET = (96 * 1024 * 1024) + 8192
__DDIPL_OFFSET = 0x3bc0000 # (64 - 16 - 4) * 1024 * 1024
__DDIPL_OFFSET = 0x03BC0000
__CHUNK_SIZE = 256 * 1024