mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2025-01-14 13:39:14 +01:00
64dd working yet again, isv brought back, dma fixes, usb path rewrite, pc code rewrite
This commit is contained in:
parent
4ea0abd022
commit
1374033733
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
34
sw/controller/src/debug.c
Normal 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
14
sw/controller/src/debug.h
Normal 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
67
sw/controller/src/isv.c
Normal 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
14
sw/controller/src/isv.h
Normal 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
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
2
sw/pc/.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
/__pycache__
|
||||
**/__pycache__
|
||||
/backup
|
||||
/roms
|
||||
/saves
|
||||
|
254
sw/pc/lib/sc64.py
Normal file
254
sw/pc/lib/sc64.py
Normal 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
220
sw/pc/lib/sc64_64dd.py
Normal 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
171
sw/pc/lib/sc64_transport.py
Normal 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
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user